Child Plugins: Plugins ohne Actions und Filter anpassen

Die WordCamp Saison macht für mich erst einmal eine Pause und daher kann ich mich wieder meiner kleinen Child Plugin Artikelserie widmen. In den letzten drei Beiträgen hatte ich mich ja intensiv mit Hooks, also mit Actions und Filtern beschäftigt. Im heutigen Beitrag möchte ich euch zeigen, welche Möglichkeiten ihr habt, wenn keine Actions oder Filter angeboten werden.

Hooks mit eigenen Funktionen überschreiben

Wenn ein Plugin gut programmiert ist, dann sollte es euch Hooks anbieten. Aber manchmal hat der Plugin-Entwickler einfach euren Anwendungsfall nicht bedacht und es fehlt ein passender Hook. Sofern sich der Entwickler aber an ein paar Standards hält, könnt ihr eventuell trotzdem das Plugin verändern, ohne den Originalcode verändern zu müssen.

Hooks in prozeduralem Code

Wie jedes Mal beim Schreiben dieser Artikel stehe ich vor dem Problem, dass ich hier jetzt ein gutes Beispiel für ein Plugin brauche, an dem ich euch das Prinzip erklären kann. Und welches prozedural geschriebene Plugin würde sich hier besser eignen als “DAS” Plugin überhaupt, das ihr alle kennt und bestimmt auch für unverzichtbar haltet? Ich spreche natürlich von Hello Dolly 😉

Das Plugin ist super simpel geschrieben, bietet aber keine Filter an, um es zu verändern. Nehmen wir einfach mal an, ihr möchtet die Lyrics ändern. Diese werden über eine Funktion hello_dolly_get_lyric() zufällig ausgewählt. Der Code hierzu sieht wie folgt aus:

function hello_dolly_get_lyric() {
	/** These are the lyrics to Hello Dolly */
	$lyrics = "Hello, Dolly
Well, hello, Dolly
...";

	// Here we split it into lines
	$lyrics = explode( "\n", $lyrics );

	// And then randomly choose a line
	return wptexturize( $lyrics[ mt_rand( 0, count( $lyrics ) - 1 ) ] );
}

// This just echoes the chosen line, we'll position it later
function hello_dolly() {
	$chosen = hello_dolly_get_lyric();
	echo "<p id='dolly'>$chosen</p>";
}

// Now we set that function up to execute when the admin_notices action is called
add_action( 'admin_notices', 'hello_dolly' );

Wie ihr sehen könnt gibt es keine Möglichkeit, das Array mit den Lyrics einfach zu überschreiben. Es ist in PHP auch nicht möglich einfach die Funktion hello_dolly_get_lyric() einfach ein zweites Mal zu definieren, um die vorherige zu überschreiben. Es gibt aber einen recht einfachen Weg für das Problem.

Einen Hook entfernen

Die Ausgabe der ausgewählten Zeile passiert in der Funktion hello_dolly(), in der auch die Funktion aufgerufen wird, die wir anpassen möchten. Ausgeführt wird das Ganze innerhalb der admin_notices Action. Genau das ist auch unser Punkt, an dem wir ansetzen können.

Bereits in früheren Artikel habe ich ja schon beschrieben, dass Plugins in alphanumerischer Reihenfolge geladen werden. Um nun Hello Dolly anzupassen, müssen wir die Action entfernen. Dies klappt natürlich nur, wenn die Action bereits hinzugefügt wurde. Es gibt also zwei Möglichkeiten, dies zu tun:

  1. Wir benennen unser Plugin mit einem Namen, der sicherstellt, dass es nach Hello Dolly geladen wird – z.B. “We are the Champions” 🙂
  2. Wir verwenden einen Filter, der unabhängig von der Ladereihenfolge die Action erst dann entfernt, wenn sie registriert wurde

Im 1. Fall würde unser Code also einfach wie folgt aussehen:

remove_action( 'admin_notices', 'hello_dolly' );

Im zweiten Fall ist es etwas komplizierter, aber auch nicht wirklich schwer, die Action zu entfernen:

function remove_hello_dolly_remove_action() {
	remove_action( 'admin_notices', 'hello_dolly' );
}
add_action( 'plugins_loaded', 'remove_hello_dolly_remove_action', 11 );

Wir nutzen also einfach einen Callback auf die Action plugins_loaded, die nach dem Laden aller Plugindateien ausgeführt wird und entfernen darin die Action. Zur Sicherheit verwenden wir auch noch eine erhöhte Priorität im dritten Parameter (Standard wäre 10).

Nachdem wir nun die Action entfernt haben, können wir die Action nun überschreiben und dabei dann eine neue Funktion verwenden, die eine zufällige Zeile aus einem anderen Songtext lädt. Insgesamt sieht es also wie folgt aus:

/**
 * Plugin Name: We are the Champions
 * Description: This plugins replaces the lyrics from Hello Dolly
 */
// remove original "Hello Dolly" output function and register the new one
function remove_hello_dolly_remove_action() {
	remove_action( 'admin_notices', 'hello_dolly' );
	add_action( 'admin_notices', 'we_are_the_champions' );
}
add_action( 'plugins_loaded', 'remove_hello_dolly_remove_action', 11 );

function we_are_the_champions_get_lyric() {
	/** These are the lyrics to We are the Champions */
	$lyrics = "I've paid my dues 
Time after time
...";

	// Here we split it into lines
	$lyrics = explode( "\n", $lyrics );

	// And then randomly choose a line
	return wptexturize( $lyrics[ mt_rand( 0, count( $lyrics ) - 1 ) ] );
}

// This just echoes the chosen line, we'll position it later
function we_are_the_champions() {
	$chosen = we_are_the_champions_get_lyric();
	echo "<p id='dolly'>$chosen</p>";
}

Ich hoffe ihr könnt aus dem Beispiel erkennen, wie es uns möglich war, ein Plugin anzupassen, dass uns eigentlich keine Hooks zur Verfügung stellt. Die Lösung war in diesem Fall recht einfach, wir mussten allerdings im Grunde zwei Funktionen passen.

Das hier gezeigte Vorgehen funktioniert im Übrigen auch bei Plugins, die objektorientiert geschrieben wurden. Dabei ist das Entfernen des Hooks aber nicht immer nach dem gleichen Schema möglich. Es kommt sehr darauf an, die die Callback-Funktionen hinzugefügt wurden. Weiterhin wäre es möglich, eine Klasse per extend zu erweitern und diese neue Klasse stattdessen zu verwenden. Damit dies möglich ist müssen aber die Funktion entsprechende Sichtbarkeiten haben. Dies ist vermutlich alles etwas zu komplex um es in diesem Beitrag zu beschreiben. Ich denke die erfahreneren Entwickler unter euch können sich aber vorstellen, wie es funktionieren könnte.

Nachteile dieses Ansatzes

Auch wenn die Lösung sehr einfach und sauber erscheint, so hat sie doch auch einige Nachteile und birgt Gefahren. Selbst in diesem einfachen Beispiel haben wir zwei Funktionen im Grunde komplett überschreiben müssen. Wenn in einem Update von Hello Dolly beispielsweise das HTML und CSS angepasst würde und anstelle der ID dolly eine CSS Klasse verwendet würde, dann müssten wir auch unsere Funktion anpassen, damit das Layout nicht defekt ist. Noch kritischer ist es natürlich, wenn schwere Fehler oder gar Sicherheitslücken in den Funktionen behoben werden, die wir nicht in unseren Funktionen selbst überschreiben. Oder aber in der Funktion werden wiederum andere Funktionen aufgerufen, die nicht angepasst haben, die aber in einer neuen Version nicht mehr vorhanden sind oder sich grundlegend geändert haben. Es kommt dann sehr schnell zu einem schweren Ausnahmefehler (500 Internal server error). Wir haben hier also das gleiche Problem, das Torsten Landsiedel auch schon beim Child Theme Dilemma beschrieben hat.

Fazit

Es sollte euch nach diesem Artikel also klar sein, wie man Plugins anpassen kann, die nicht wirklich darauf vorbereitet worden sind. Aber ihr habt hoffentlich auch verstanden, welche Gefahren dadurch entstehen können. Ihr solltet also bei jedem Plugin Update immer die Changelogs lesen und euch am besten das Changeset genau ansehen, bevor ihr das “Parent Plugin” aktualisiert. Wie auch schon zuvor mehrmals erwähnt ist es vielleicht die beste Idee, einfach den Plugin-Entwickler zu bitten, einen Hook zur Verfügung zu stellen, damit ihr nicht diesem gefährlichen Weg nehmen müsst.

Im nächsten Beitrag soll es um die Anpassung von Template-Dateien gehen, die manche Plugins mitliefern. Ich hoffe mal ich finde hierzu dann ein schönes Beispiel 🙂

P.S. Falls euch die Idee dieses Beitrags gefallen hat und ihr die Lyrics von Hello Dolly anpassen möchtet, dann habe ich eine gute Nachricht für euch: Ich habe das Plugin bereits vor einer Weile “geforkt” und es unter dem Namen Hello World ins offizielle Plugin-Repository gestellt 😉

Weitere Artikel zu Themenreihe

Dies ist der fünfte Teil der Themenreihe “Child Plugins”. Hier findest du die anderen Beiträge:

Veröffentlicht von

Bernhard ist fest angestellter Webentwickler, entwickelt in seiner Freizeit Plugin, schreibt in seinem Blog über WordPress und andere Themen, treibt sich gerne bei den WP Meetups in Berlin und Potsdam herum und läuft nach Feierabend den ein oder anderen Halbmarathon.

Schreibe einen Kommentar

Pflichtfelder sind mit * markiert.