WordPress-Child-Plug-ins

Plug-ins in WordPress erweitern? So geht’s!
Keine Kommentare

Mit dem Konzept der Child Themes lassen sich in WordPress Themes updatesicher erweitern. Wie schön wäre es, wenn es das auch für Plug-ins gäbe. In diesem Artikel wollen wir uns ansehen, wie man WordPress-Plug-ins ebenfalls sicher erweitern kann.

In WordPress werden Plug-ins eingesetzt, um die Funktionalität des WordPress-Core zu erweitern oder anzupassen. Der WordPress-Core bietet hierzu verschiedene Möglichkeiten, an denen Plug-ins ansetzen können. Ein Plug-in selbst ist auch wieder eine Funktionseinheit, bei der es – je nach Projekt – Anpassungsbedarf geben kann. Die direkte Editierung von Plug-ins sollte aus den gleichen Gründen vermieden werden, aus denen der Core nicht verändert werden sollte: Bei einem Update des Plug-ins gehen alle Anpassungen verloren.

Das Konzept von Child-Plug-ins

Im Gegensatz zu Child Themes (Kasten „Exkurs: Child Themes“) gibt es in WordPress kein Konzept von Child-Plug-ins. Es ist also nicht einfach möglich, in einem eigenen Plug-in durch das Kopieren von Dateien aus einem anderen Plug-in, diese zu überschreiben. Das hängt vermutlich auch damit zusammen, dass Plug-ins im Gegensatz zu Themes keine feste Ordnerstruktur haben und keine feste Funktion in einer WordPress-Installation erfüllen. Ein allgemein gültiges Konzept für Child-Plug-ins zu finden, ist also nicht möglich.

Exkurs: Child Themes

Im Bereich der Themes hat WordPress hierzu das Konzept der so genannten Child Themes entwickelt. Um Anpassungen an einem Theme vorzunehmen, wird hierbei weder das Original-Theme editiert noch komplett kopiert. Stattdessen wird im Child Theme definiert, welches sein Parent Theme ist. Hierzu wird im Fileheaderkommentar angegeben, welches Template verwendet werden soll:

  • /*
    Theme Name: Twenty Fifteen Child
    Description: Twenty Fifteen Child Theme
    Template: twentyfifteen
    Version: 1.0.0
    License: GNU General Public License v2 or later
    License URI: http://www.gnu.org/licenses/gpl-2.0.html
    Text Domain: twenty-fifteen-child
    */

Nach dieser Definition ist es nun möglich, einzelne Templatedateien aus der WordPress-Templatehierarchie zu überschreiben. Auch die Erweiterung oder der Austausch der style.css-Datei sowie neuen Hilfsfunktionen in der functions.php-Datei sind hierdurch möglich.

Das Ziel von Child Themes ist es, einen Verlust der Anpassungen bei einem Update des Parent Themes zu vermeiden. Allerdings kann es bei Updates des Parent Themes notwendig sein, auch das Child Theme anzupassen. Dies hat der WordPress-Entwickler Torsten Landsiedel als Child-Theme-Dilemma beschrieben; er liefert hierzu auch gleich einen Lösungsansatz in Form eines Plug-ins.

Konstanten definieren

Viele Plug-ins bieten als erste Stufe der Anpassung verschiedene Konstanten an, mit denen das Verhalten verändert werden kann. Hierzu werden diese Konstanten meistens in der wp-config.php-Datei definiert. Konstanten haben allerdings einige Nachteile gegenüber anderen Möglichkeiten der Anpassung. So ist es beispielweise in einer WordPress-Multisiteinstallation mit Konstanten nicht möglich, in unterschiedlichen Sites verschiedene Werte für die Konstante zu nutzen. Die Verwendung von Konstanten sollte daher vermieden werden, stattdessen sollten Hooks zum Einsatz kommen.

Das Hook-System von WordPress

Ein zentrales Konzept von WordPress ist das Hook-System. Mit Hooks bezeichnet man Filter und Actions. Technisch gesehen handelt es sich hierbei um ein System, bei dem verschiedene Callback-Funktionen registriert werden können, die zu bestimmten Zeitpunkten im Programmlauf ausgeführt werden.

Der Unterschied von Filtern und Actions existiert im Grunde nur in ihrer Bedeutung bezüglich der Funktionsweise. Registriert werden beide mit einer ähnlichen Funktion, wobei hier die eine Funktion lediglich die zweite aufruft:

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
  return add_filter($tag, $function_to_add, $priority, $accepted_args);
}

Eine Action in WordPress

Bei einer Action handelt es sich von der Bedeutung her um einen Punkt im Programmablauf, an dem eine bestimmte Aktion ausgelöst werden soll. Alle Callback-Funktionen die zuvor für diese Action registriert wurden, werden dann zu diesem Zeitpunkt in der vorgegebenen Reihenfolge aufgerufen. Im WordPress-Core und auch in vielen Plug-ins und Themes finden sich solche Actions. Ausgelöst werden sie wie folgt:

do_action( 'wp_footer' );

Actions haben in der Regel keinen Rückgabewert, sondern führen lediglich diverse Seiteneffekte aus. Actions aus dem Bereich des Templating geben beispielsweise HTML-Code aus. Hier ein Beispiel für eine solche Action, die am Ende der Seite einen Text ausgibt:

function your_function() {
  echo '<p>This is inserted at the bottom</p>';
}
add_action( 'wp_footer', 'your_function' );

Actions können im Laufe des Programmablaufs auch durchaus mehrfach ausgeführt werden. Es ist daher bei der Registrierung darauf zu achten, dass für Aktionen die nur einmal pro Seitenaufruf ausgeführt werden sollen, ein erneuter Aufruf abgefangen wird.

Ein Filter in WordPress

Im Gegensatz zu Actions hat ein Filter eine etwas andere Funktion. Die Callback-Funktion zu einem Filter verändert eine ihr übergebene Variable und gibt diese anschließend zurück. Hier wird die Rückgabe also immer weiterverwendet.

$protocols = apply_filters( 'kses_allowed_protocols', $protocols );

Ein weiterer Unterschied ist die Vorgabe, dass innerhalb einer Filter-Callback-Funktion keine Seiteneffekte auftreten sollen. Besonders sollten solche Funktionen keinen Quellcode direkt ausgeben, sondern diesen immer nur zurückgeben. Auch für einen Filter wird eine Callback-Funktion analog zu einer Action registriert. Um beispielsweise den Upload von SVG-Dateien zu erlauben, kann folgender Filter verwendet werden:

function allow_svg_mime_types( $mimes ) {
  $mimes['svg'] = 'image/svg+xml';
  return $mimes;
}
add_filter( 'upload_mimes', 'allow_svg_mime_types' );

Wie aus der Definition der Funktion ersichtlich ist, können Filter und Actions mit einer Priorität ausgestattet werden. Des Weiteren können zusätzliche Variablen übergeben werden, die innerhalb der Funktionen verwendet werden. Dieses veränderte Bespiel zeigt eine solche Verwendung zusätzlicher Variablen:

function allow_svg_mime_types( $mimes, $user ) {
  if ( user_can( $user, 'update_core' ) ) {
    $mimes['svg'] = 'image/svg+xml';
  }
  return $mimes;
}
add_filter( 'upload_mimes', 'allow_svg_mime_types', 10, 2 );

Wichtig bei der Registrierung einer solchen Callback-Funktion mit zusätzlichen Variablen ist der vierte Parameter, der die Anzahl der übergebenen Attribute angibt. Wird dieser Wert nicht korrekt gesetzt, werden die Variablen nicht korrekt an die Callback-Funktion beim Aufruf des Filters weitergegeben.

Filter sollten in der Regel die übergebene Variable verändern und nicht überschreiben, was gerade bei Arrays wichtig ist. Hier sollte, wie in den Beispielen zu sehen, immer das Array erweitert werden, statt ein neues Array zurückzugeben. Das hat vor allem den Hintergrund, dass ein Filter mehrmals verwendet werden kann. Gibt eine Callback-Funktion ein neues Array zurück, sind alle vorherigen Erweiterungen verworfen.

In manchen Fällen kann es aber durchaus Sinn ergeben, dass man einen bestimmten Rückgabewert verwendet. Einige dieser Rückgabewerte benötigen keine Callback-Funktion; hierzu kann ein statischer String verwendet werden. Dieses Beispiel zeigt die Rückgabe des Wahrheitswerts false bei jedem Aufruf des Filters:

add_filter( 'screen_options_show_screen', '__return_false' );

Ähnliche spezielle Rückgabewerte gibt es für true, leere Strings, leere Arrays sowie die Zahl 0 und den Wert null.

Anpassung eines Plug-ins mit Actions und Filtern

Bietet ein Plug-in Actions und Filter an, dann sollten diese stets zuerst genutzt werden, um die Funktionalität des Plug-ins in einem Child-Plug-in anzupassen. Eine gute Dokumentation eines Plug-ins listet diese Hooks auf und erklärt, wie sie zu verwenden sind. Sollte es keine Dokumentation geben, lohnt sich eine Suche im Quellcode.

Plug-ins ohne Hooks anpassen

Stellt ein Plug-in keine Hooks zur Verfügung, so kann eine Anpassung dennoch möglich sein. Soll beispielsweise die Ausgabe am Ende der Seite aus dem Beispiel überschrieben werden, aber kein Filter für den Ausgabetext vorgesehen worden sein, so kann die originale Callback-Funktion deaktiviert und mit einer anderen Funktion ersetzt werden. Der Code in Listing 1 soll dieses Prinzip verdeutlichen.

function another_function() {
  echo '<p>Something else inserted at the bottom</p>';
}

function replace_your_function() {
  remove_action( 'wp_footer', 'your_function' );
  add_action( 'wp_footer', 'your_function' );
}
add_action( 'Plugins_loaded', 'replace_your_function' );

Zuerst wird eine neue Funktion definiert. Im Anschluss wird beim Aufruf der Action Plugins_loaded, die nach dem Laden aller Plug-in-Dateien ausgelöst wird, die alte Funktion entfernt und die neue registriert.

Wenn ein Plug-in auf diese Weise angepasst wird, kann es allerdings bei einem Update des Plug-ins zu Problemen kommen. Denn der Entwickler hatte eine Anpassung nicht explizit vorgesehen. Ändert sich beispielsweise der Funktionsname im Original-Plug-in, dann wird der Hook nicht mehr entfernt und es werden dann beide Callback-Funktion ausgeführt. Damit kann es hierdurch zu einem ähnlichen Effekt wie beim Child-Theme-Dilemma kommen. Es empfiehlt sich daher, stets die Änderungen am Original-Plug-in zu verfolgen und neue Versionen mit der Anpassung zu testen, bevor die Plug-ins auf einem System aktualisiert werden.

Plug-in-Templates überschreiben

Viele Plug-ins erweitern nicht nur den Funktionsumfang einer Seite, sie bringen darüber hinaus auch eigene Templates für ihre Inhalte mit. Sollen diese Templates an das Theme angepasst werden, müssen auch sie eventuell überschrieben werden. Hierzu bieten viele Plug-ins die Möglichkeit an, mit einer Ordnerstruktur in einem Theme oder Plug-in die mitgelieferten Templates zu überschreiben. Liegt eine solche Ordnerstruktur im Parent Theme oder Child Theme, dann wird sie in der Regel automatisch durch das Plug-in verwendet. Ist sie aber in einem Plug-in vorhanden, dann muss dieser Ordner in der Regel registriert werden. Hierzu gibt es keine Standardimplementierung. Allerdings bieten solche Plug-ins sehr ähnliche Mechanismen an, die oft über die Verwendung eines speziellen Filters realisiert werden. Listing 2 zeigt dies am Beispiel für das WooCommerce-Plug-in.

function custom_wc_locate_template( $template, $template_name, $template_path ) {
  $template_overwrite = dirname( __FILE__ ) .  '/templates/' . $template_name;

  if ( file_exists( $template_overwrite ) ) {
    $template = $template_overwrite;
  }
  return $template;
}
add_filter( 'woocommerce_locate_template', 'custom_wc_locate_template', 10, 3 );

Unter Verwendung des Filters woocommerce_locate_template wird für die Suche nach einem passenden Template eine neue Callback-Funktion registriert. Diese verwendet einen Unterordner des Plug-ins. Wird hier ein angefordertes Template verwendet, so gibt sie den Dateipfad zurück.

Empfohlene Vorgehensweise bei der Anpassung von Plug-ins

Um ein Plug-in anzupassen, stehen also mehrere Methoden zur Verfügung. Einige sind besser geeignet, andere weniger gut. Daher ist folgende Reihenfolge bei der Wahl der Mittel zu empfehlen:

  1. Verwendung der APIs, Filter, Action, Template-Struktur und Konstanten: Alle diese Methoden wurden vom Plug-in-Entwickler vorgesehen und bieten somit die zuverlässigste Möglichkeit der Anpassung. Die Gefahr, dass es hier bei einer Aktualisierung zu Problemen kommt, ist ebenfalls am geringsten.
  2. Den Plug-in-Entwickler um Hilfe bitten: Fehlt es dem Plug-in an einem API oder an passenden Filtern, so kann versucht werden, den Entwickler um die Implementierung solche Funktionen zu bitten. Für den Plug-in-Entwickler ist nicht von vorne herein jeder Anwendungsfall ersichtlich. Den größten Erfolg hat eine solche Bitte, wenn direkt eine Lösung mitgeliefert wird.
  3. Hooks überschreiben: Sollten die ersten beiden Methoden zu keinem Erfolg führen, kann die Anpassung über das Entfernen vorhandener Funktion und die Registrierung neuer Funktionen erfolgen. Hierbei können dann aber die erwähnten Probleme bei Updates auftreten.
  4. Das Plug-in forken: Ist auch eine solche Anpassung nicht möglich, kann als letzte Möglichkeit das Plug-in selbst angepasst werden. Hierzu sollte das Plug-in kopiert und in der Hauptdatei des Plug-ins der Name im Fileheaderkommentar geändert werden. Hiermit wird vermieden, dass die Anpassungen durch ein Update des Original-Plug-ins verloren gehen. Der Fork sollte regelmäßig bei Aktualisierungen des Original-Plug-ins überprüft und eventuell ebenfalls aktualisiert werden. Dazu bietet es sich an, eine Versionsverwaltung wie Git zu verwenden, um solche Änderungen einfach zu übernehmen. Weiterhin ist es denkbar, dem Plug-in-Entwickler Zugang zum Fork zu geben. Sollten die Änderungen auch für das Original-Plug-in sinnvoll sein, dann werden diese eventuell vom Entwickler übernommen und das zusätzliche Child-Plug-in ist nicht mehr notwendig.

Fazit

Auch wenn es das Konzept von Child-Plug-ins in WordPress nicht gibt, so bietet die Architektur doch einige Ansatzpunkte, um ein bestehendes Plug-in zu verändern ohne den Quellcode selbst anpassen zu müssen. Dies verhindert nicht nur mögliche Probleme bei Aktualisierungen des Original-Plug-ins, es ist auch möglich, solche Anpassungen auf mehreren Installationen wiederzuverwenden, da die Anpassungen in einem zusätzlichen Plug-in zusammengefasst sind und somit schnell kopiert werden können. In Multisiteinstallationen ist es darüber hinaus auch möglich, eine solche Anpassung nur in bestimmten Seiten zu aktivieren.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -