TinyMCE zur Bearbeitung von Mediendateien

Alle sagen es geht nicht bis einer kommt und es einfach macht. So ging es mir bei diesem G+ Post. Das Problem ist einfach umrissen: In der Medienübersicht wird der Standard-Editor zur Bearbeitung der Medienbeschreibung verwendet. Der Wunsch war jedoch den TinyMCE dafür einzusetzen. Normalerweise reicht es dafür aus einen Filter auf die Editor-Einstellungen zu verwenden und das Flag für den TinyMCE auf True zu setzen.

Wir haben im Prinzip drei Möglichkeiten um die Editor-Einstellungen zu beeinflussen. Zum einen dann wenn sie definiert werden.

$editor_args = array(
	'textarea_name' => 'content',
	'textarea_rows' => 5,
	'media_buttons' => false,
	'tinymce' => false,
	'quicktags' => $quicktags_settings,
);

$editor_args = apply_filter( 'some_filter', $editor_args );

Leider ist dies nicht immer möglich, vor allen Dingen im gegebenen Fall nicht. Leider bietet auch die verwendetet Funktion wp_editor() noch die die Klasse _WP_Editors einen entsprechenden Filter an. Warum das so ist, sei mal dahingestellt. Ebenso das es bereits diverse Tickets im Trac zu den Thema gibt. Wenn man sein Ziel nicht auf direkten Weg erreichen kann, dann muss man manchmal einen kleinen Umweg gehen.

Eine Lösung wäre, wenn die Funktion wp_editor() plugable wäre, man sie also gegen eine eigene Funktion austauschen kann. Ist sie aber nicht, muss man den Hebel also woanders ansetzen.

Im betreffenden Fall wurde angeregt in der Klasse einen zusätzlichen Filter einzubauen. Bis dies umgesetzt ist, muss man sich also mit einen kleinen Workaround behelfen.
Wirft man einen Blick in den Code, so sieht man das abgefragt wird ob die Klasse _WP_Editors bereits geladen wurde. Nur wenn sie nicht geladen wurde, wird die Klasse aus dem WordPress-Core geladen. Für uns bedeutet dies also, wir können die Original-Klasse durch eine gleichnamige eigene Klasse ersetzen. Wir müssen nur dafür sorgen das unsere Klasse möglichst früh geladen wird. Am einfachsten erreicht man dies indem man einen der sehr früh ausgelösten Hooks benutzt um die eigene Klasse mittels require_once()/include_once() einzubinden.

Wie man sieht, gibt es in WordPress nicht nur eine Möglichkeit um etwas zu beeinflussen. Der einfachste Weg sind Filter und Actions um Variablen direkt zu beeinflussen. Sind diese nicht gegeben, schaut man sich an ob die betreffende Funktion etwas hergibt, also ob diese ggf. einen Filter/Action besitzt oder ob man sie ersetzen kann. Ist dies auch nicht gegeben, so kann man, wie hier im Beispiel, noch eine letzte Chance nutzen und eine Klasse komplett austauschen.

Der erste Schritt zum Plugin besteht also darin die Klasse _WP_Editors komplett in sein Plugin-Ordner zu kopieren. Der nächste Schritt ist das Einbinden der kopierten Klasse:

/**
 * Including the modified WP class _WP_Editors
 */
add_action(
	'plugins_loaded',
	function() {
		require_once dirname( __FILE__ ) . '/class-wp-editor.php';
	},
	0,
	0
);

Um zu überprüfen ob nun die kopierte oder die Original-Klasse geladen wird, kann man am Anfang der Kopie einfach mal ein die( 'Kopierte Klasse' ); setzen. Bricht WordPress nach einem Neuladen mit einen weißen Bildschirm ab auf dem nur “Kopierte Klasse” steht, so können wir sicher sein das nur unsere kopierte Klasse geladen wird. Nun geht es daran die kopierte Klasse zu modifizieren. Wie im Ticket vorgeschlagen ergänzen wir die Klasse um einen zusätzlichen Filter.

Da wir nun in der kopierten Klasse einen Filter haben den wir nutzen können, reicht es aus diesen zu setzen und in einer passenden Funktion die Werte entsprechend zu ändern.

/**
 * Add the filter to modify the editor settings
 */

add_filter( 'wp_editor_settings', 'modify_editor_settings', 0, 2 );

/**
 * Modify the editor settings. Currently only set the value for using the TinyMCE to true.
 *
 * @param array $settings Array with editor settings
 * @param array $editor_id The ID of the editor
 * @return array
 */
function modify_editor_settings( $settings, $editor_id ) {

	if ( 'attachment_content' == $editor_id )
		$settings['tinymce'] = true;

	return $settings;

}

Ich habe hier bewusst auf umständliche Prüfungen verzichtet um das Beispiel einfach zu halten. Man könnte zusätzlich darauf prüfen ob man sich auch tatsächlich in der Medienübersicht bzw. Medienbearbeitung befindet. Es wird lediglich geprüft ob die Editor-ID attachment_content ist und dann das Flag für den TinyMCE auf true gesetzt.
Durch Anpassung der Funktion modify_editor_settings() lässt sich so der TinyMCE für nahezu alle Eingabefelder aktivieren sofern die Editor-ID bekannt ist und das Eingabefeld über die Funktion wp_editor() erzeugt wird.

Das Plugin ist recht wartungsfreundlich. Sollte eines Tages ein Filter zum Modifizieren der Editor-Einstellungen vorhanden sein, so muss man nicht das komplette Plugin neu konstruieren. Es reicht aus die Einbindung der eigenen Klasse zu entfernen und ggf. den Filter-Namen anzupassen.


WordPress in einer Nussschale

Es gibt verschiedene Gründe warum man WordPress in einer Sandbox installieren möchte, ich habe nun einen weiteren Weg entdeckt den ich hier vorstellen möchte. Mit “Sandbox” meine ich hierbei, eine relativ abgeschlossene Umgebung die alles benötigte (Server, PHP, MySQL) mitbringt.

Sandboxes

Eine Möglichkeit ist Instant WordPress, welches auf Apache, PHP und MySQL setzt. Mit diesen Programm lässt sich WordPress inkl. der nötigen Umgebung z.B. auf einen USB-Stick installieren. Soweit eine tolle Sache, jedoch kann man die installierte Umgebung nur schlecht bis gar nicht anpassen. WordPress liegt i.d.R. in einer sehr alten Version bei, so dass man dies erst einmal updaten darf. Ob man PHP und MySQL überhaupt updaten kann, damit habe ich mich schon gar nicht mehr beschäftigt. Das Paket selber kann man ohne Fachwissen nicht anpassen, man muss nehmen was der Entwickler einen liefert.
So toll Instant WordPress auf den ersten Blick ist, für mein Problem, das mich aktuell beschäftigt, ist es nicht das richtige. Schnell fiel mir wieder Nginx ein, ein Webserver der vor einiger Zeit wegen seiner hohen Geschwindigkeit für viel Aufsehen sorgte. Für mich war Nginx eher deshalb interessant, da man ihn unabhängig von anderen, bereits installierten Komponenten, verwenden kann. Es wäre also möglich sich seine eigene Sandbox mit Nginx, PHP und MySQL zu basteln, was ich auch versucht habe. Jedoch ist das Frustlevel recht schnell sehr hoch. Denn im Netz findet man häufig nur Anleitungen zu Nginx 0.8.x, die aktuelle Version (Stand zur Veröffentlichung des Artikels) ist jedoch schon 1.5.8. Wobei “Anleitung” häufig geschmeichelt ist. Viele “Anleitungen” setzen enormes Wissen voraus und verschweigen das meiste. Irgendwann habe ich es dennoch geschafft Nginx mit PHP zum laufen zu bekommen, hatte dann aber Probleme mit dem von XAMPP installierten MySQL.

WT-NMP

Eine weitere Suche im Netz förderte dann WT-NMP zu Tage. WT-NMP kann man zwischen XAMPP und Instant WordPress ansiedeln. Das Paket liegt u.a. als Zip-Archive vor und kann sowohl als Verzeichnis auf der Festplatte als auch auf einen portablen Medium wie z.B. einen USB-Stick installiert werden. Der große Vorteil an dem Paket ist, es konfiguriert sich selbst, man muss also nicht händisch viel anpassen damit man es zum Laufen bekommt. Der dickste Bonuspunkt ist, wenn man das Paket z.B. auf der Festplatte installiert und eingerichtet hat und anschließend auf einen USB-Stick kopiert, ändert es automatisch die Konfiguration so um, dass es auch auf dem USB-Stick weiterhin läuft.
Ein weiterer Bonuspunkt ist das Fehlen von WordPress. Hört sich merkwürdig an, jedoch kann man sich so selber aussuchen welche Version oder gar welche Software man mit dem Paket bündelt. Da ich das Zip-Archiv und nicht den WT-NMP Installer verwende, kann ich ohne große Probleme alles ins Zip-Archiv schieben was ich brauche, sogar eine Auswahl an Plugins die ich immer wieder einsetze. Im Ergebnis habe ich dann ein Zip-Archiv das meinen Bedürfnissen entspricht, das ich selber Up-To-Date halten kann und an dem ich nicht jedes mal rumfummeln muss wenn ich eine neue WordPress-Sandbox benötige.
Nach der Installation schlägt das Herz dann sogar noch einen Ticken höher, denn schlauerweise sind neben Nginx, MySQL und PHP noch HeidiSQL und Adminer zur Verwaltung der Datenbanken installiert. Obendrein gibt es noch einen RegExp-Tester für PHP (PCRE) und JavaScript. Das man das PHP Error-Log mit einen Mausklick aufrufen kann, Testmails verschicken und XDebug, PHP XCache und PHP OPcache vorinstalliert sind, macht die Sache dann komplett rund. Alles in allem ein Traum für alle die WordPress in einer Sandbox betreiben wollen.

Wie geht’s?

Ich möchte nun im folgenden kurz beschreiben wie man zum einen das Paket für den Betrieb mit WordPress bereit macht und wie man WordPress damit zum Laufen bekommt. Und keine Angst, mehr als einen Texteditor und 5-10 Minuten Zeit braucht es nicht. Als erstes brauchen wir natürlich WT-NMP und WordPress.

Es gibt zwar neben dem Zip-Archive auch noch einen Windows-Installer, den kann man jedoch nicht so bequem anpassen wie das Zip-Archive. Nach dem Download entpackt man das Zip-Archive in ein Verzeichnis, bei mir ist es das Laufwerk D:/ und als Verzeichnis WT-NMP. Wenn ihr es woanders hin entpackt, kein Problem. Das Laufwerk ist eh egal, der Verzeichnisname sollte zum besseren Verständnis jedoch WT-NMP lauten.
Im Verzeichnis WT-NMP können wir den Unterordner WWW sehen. Man kann es sich fast denken, dass ist der Document Root, also das Verzeichnis in dem alle Web-Projekte landen werden. Dorthin entpacken wir nun WordPress ins Verzeichnis wordpress (man kann natürlich auch einen anderen Namen wählen). Die Verzeichnisse project1, project2 und project3 kann man löschen. Die letzten beiden genannten sind ohnehin leere Verzeichnisse, in project1 ist lediglich eine Testdatei die uns hier jedoch nicht weiter interessieren soll.
An dieser Stelle könnte man das Verzeichnis WT-NMP zu einen Archive packen und hätte so schon mal eine gute Ausgangsbasis für eine fertige Sandbox. Man kann aber auch WT-NMP und WordPress komplett konfigurieren, so dass man in Zukunft nur noch das Zip-Archive entpacken muss um quasi WordPress Out-Of-The-Box starten zu können. Gehen wir zur Konfiguration über.

Konfiguration der php.ini

Eine Datei muss man auf alle Fälle anfassen, und zwar die php.ini. Hier gilt es einige Erweiterungen (Extensions) zu aktivieren und einige kleinere Einstellungen zu korrigieren. Wichtig ist jedoch das man nicht die php.ini aus dem PHP-Verzeichnis nimmt, sie dient als Vorlage wenn WT-NMP automatisch angepasst wird. Alle Dateien die wir anpassen müssen finden wir im Verzeichnis conf!

Beginnen wir mit den Extensions und öffnen die Datei conf/php.ini im Texteditor unserer Wahl. Die Extension php_xcache und die Zend Extension php_opcache kann man aktivieren, muss man aber nicht. Es gibt wohl einige Probleme mit diesen Erweiterungen, so dass man es mal ausprobieren muss ob man besser mit aktivierten oder deaktivierten Cache-Extensions zurecht kommt. Ein aktiviertes XDebug (die Erweiterung steht ganz am Ende der php.ini) gehört zum guten Ton und sollte aktiviert werden. Zudem musste ich auf meinem System (Windows7 64bit) noch php_mysql aktivieren da ich ansonsten keine Verbindung zur Datenbank bekommen habe.
memory_limit sollte man von 64MB auf 128MB herauf setzen. Bei einer lokalen Installation können es auch gerne mehr sein damit PHP möglichst flüssig arbeitet. short_open_tag stellen wir mal brav auf Off, das sollte nirgendwo mehr aktiviert sein. Ob man allow_url_fopen zulässt oder nicht, muss man ggf. im Einzelfall entscheiden. Bei mir steht es eigentlich immer auf On da es vieles leichter macht.
Eine Besonderheit ist noch unter disable_classes zu beachten. Hier sollte man alle deaktivierten PHP-Klassen entfernen da es sonst unter WordPress von SimplePie Fehlermeldungen hagelt. Auch die Liste darüber mit den disable_functions sollte man mal kurz durchschauen. In einer reinen Entwicklungsumgebung stehen dort einige Funktionen die man ganz gut gebrauchen kann und ansonsten nicht zur Verfügung stehen würden.
Im Großen und Ganzen war es das auch schon mit den Änderungen an der php.ini. Erfahrene Benutzer können noch mal alle Einstellungen durchgehen und ihren Bedürfnissen anpassen, für die Mehrheit sollte es so passen.

Eigener Server und Domäne

Wenn man nicht ständig über http://localhost/wordpress auf sein Blog zugreifen möchte und stattdessen lieber eine eigene Domäne verwenden möchte, so muss man sich noch einen zusätzlichen Server in der nginx.conf anlegen und einen Eintrag in der hosts Datei von Windows.
Der Server ist schnell angelegt. Dazu kopieren wir uns einfach den bereits vorhandenen Server und fügen die Kopie hinter den bestehenden an. Lediglich die Zeilen mit listen, server_name und root müssen wir anpassen. In der Zeile listen löschen wir nur den default_server, den server_name ändern wir auf unsere Wunsch-Domäne und bei root ergänzen wir /wordpress damit der root-Eintrag auch auf unsere WordPress-Installation zeigt.

	server {
		listen       127.0.0.1:80;
		server_name  samplewp.tld;

		root "d:/wt-nmp/www/wordpress";
		autoindex on;

		allow		127.0.0.1;
		deny		all;

		# deny access to .htaccess files, if Apache's document root
		# concurs with nginx's one
		#
		location ~ /\.ht {
		    deny  all;
		}

		location ~ \.php$ {
		    try_files $uri =404; 
	            fastcgi_pass	php_farm;
		    include		nginx.fastcgi.conf;
	        }

	}

So sieht das ganze bei mir aus. Das ist lediglich eine Grundkonfiguration, wer mehr benötigt (z.B. URL-Rewriting), muss sich näher mit Nginx beschäftigen, dass geht etwas über diesen Artikel hinaus. In der Windows hosts-Datei machen wir dann noch den Eintrag für unsere Domäne was bei mir so aussieht: 127.0.0.1 samplewp.tld

An der mysql.ini musste ich nichts verändern, hier kann es aber sinnvoll sein die Einstellungen für wait_timeout und connect_timeout hoch zu setzen. Vor allem wenn man WT-NMP auf einen langsamen USB-Stick installiert können schon mal längere Wartezeiten auftreten.

Erster Start

WT-NMP Launcher

Der WT-NMP Launcher

Nun müssen wir noch Nginx, MySQL und PHP starten. Das erfolgt recht bequem über den WT-NMP Launcher WT-NMP.exe im Verzeichnis WT-NMP. Mit dem Launcher lassen sich auch die einzelnen Prozesse von Nginx, MySQL und PHP starten und stoppen, was recht wichtig ist wenn man Änderungen an den jeweiligen Einstellungs-Dateien (ini-Dateien) vorgenommen hat. Im Launcher sieht man neben den einzelnen Komponenten auch noch ein paar Icons, was sich dahinter verbirgt wird angezeigt wenn man mit dem Mauspfeil über das jeweilige Icon verbleibt. Das einzig wirklich interessante dürfte der Datenbank-Manager (HeidiSQL) neben der MySQL-Komponente sein.
Jetzt kann man auch schon loslegen und im Browser seiner Wahl testweise mal http://localhost/wordpress bzw. seine zuvor angelegte Domäne http://samplewp.tld aufrufen. WordPress wird uns nun mit der einen oder anderen Meldung beglücken das dies und jenes noch nicht vorhanden sei. Bisher haben wir ja auch weder Datenbank noch Datenbank-Benutzer oder eine wp-config.php angelegt.

Datenbank anlegen

WT-NMP Startbildschirm

Der Startbildschirm von WT-NMP im Browser

Beginnen wir mit dem Anlegen einer Datenbank. Dazu rufen wir im Browser einfach http://localhost auf und sollten die Startseite von WT-NMP sehen (siehe Screenshot). Mit einen Klick auf Adminer Database Manager gelangen wir zur Datenbankverwaltung. Es ist schon fast Geschmackssache ob man HeidiSQL oder Adminer verwendet. Adminer ist jedoch deutlich schlanker und übersichtlicher.
Im Adminer oben rechts eine neue Datenbank anlegen, einen Namen für die Datenbank vergeben und den Rest erst mal so belassen wie es ist. Danach sollte man sich einen neuen Benutzer anlegen, dazu klickt man oben rechts auf Rechte. Nun werden die vorhandenen Benutzer angezeigt und unterhalb der Tabelle mit den Benutzern der Link für Neuer Benutzer. Erneut einen Namen eingeben, Passwort und die Checkboxen All Privileges und Grand option anhaken, mit Speichern bestätigen und das war es dann auch schon.

Auf in den Schlussspurt. Dazu öffnen wir die wp-config-sample.php-Datei aus unserer WordPress-Installation und passen sie wie gewohnt mit Datenbankname, Nutzer und Passwort an. Noch bequemer geht es eigentlich nur indem man http://samplewp.tld (bzw. http://localhost/wordpress) aufruft und die wp-config.php von WordPress erstellen lässt. Diejenigen unter uns die öfters WordPress einrichten und zusätzliche Angaben in der wp-config.php machen werden ersten Weg wählen, ansonsten reicht halt auch der bequeme Weg.

Damit sind wir eigentlich auch schon fertig. Ich denke mal wer öfters WordPress installiert und auch schon mal mit XAMPP gearbeitet hat, dürfte für alles zusammen kaum mehr als 10 Minuten benötigen. Wobei man sich die Arbeit ab jetzt auch sparen kann, denn es reicht aus das Verzeichnis WT-NMP als Zip-Archive abzuspeichern und hat ab dann immer ein frisches WordPress komplett installiert parat.

Bonus-Runde

Adminer

Adminer im Einsatz

Also auf zur Bonus-Runde. Es gibt tatsächlich noch Server die mit PHP5.2 arbeiten bzw. Nutzer die PHP5.2 verwenden (müssen). Für einen Entwickler ist das recht lästig, muss man doch ständig irgendwo einen Server mit PHP5.2 parat haben für den Fall der Fälle das man was entwickeln oder testen will. WT-NMP macht uns hier das Leben sehr leicht. Es wird zwar mit PHP5.4 bzw. PHP5.5 ausgeliefert, jedoch ist es sehr einfach auch noch PHP5.2 nachzurüsten. Dazu benötigt man lediglich das Zip-Archive von PHP5.2 Non Thread Safe. Das NTS (Non Thread Safe) ist wichtig, da PHP ansonsten nicht starten will. Das Zip-Archive bekommt man bei PHP.net im Museum (letzte verfügbare Version dort ist PHP-5.2.16-nts (Windows Binairies)). Wer mit der von mir verlinkten Version nichts anfangen kann, muss ein wenig nach der für sein Betriebssystem passenden Version suchen.
Das Zip-Archive entpacken wir einfach in einen Ordner in WT-NMP/bin/ (z.B. WT-NMP/bin/php-5.2.16). Der WT-NMP Launcher bietet uns ab nun die Wahl zwischen den installierten PHP-Versionen an. Zum Wechseln der PHP-Version einfach den PHP-Prozess stoppen, Version wechseln und der Neustart von PHP wird automatisch eingeleitet. Bequemer geht es nun doch wirklich nicht, oder!?
Ich habe es nun nicht ausprobiert mit welchen PHP-Versionen das funktioniert. Da aber immer die gleiche php.ini verwendet wird, hängt es wohl in erster Linie davon ab ob die PHP-Version mit der vorhandenen php.ini zusammen arbeitet.

Fazit

Für mich ist WT-NMP momentan das ultimative Tool wenn es darum geht mit verschiedenen PHP-Versionen zu entwickeln oder WordPress in einer Sandbox oder auf einen USB-Stick zu installieren. Bisher habe ich nur ein wenig mit WT-NMP herum gespielt und mich noch nicht näher z.B. mit der Konfiguration von Nginx beschäftigt. Auch habe ich noch nicht getestet ob die Aktivierung der Cache-Extensions (XCache und OPcache) wirklich so negative Auswirkungen haben wie auf der WT-NMP Webseite beschrieben. Deswegen würde ich mich über ein paar Erfahrungsberichte, gerne auch in Form eines Blog-Posts, freuen.


Tutorial: HookUp

Hooks ist ein Thema über das sich wahrscheinlich die wenigsten Gedanken machen, jedoch essentiell beim Schreiben von Plugins ist. Im Grunde genommen ist es ein relativ einfaches Thema, dass jedoch schnell recht komplex werden kann. Ich werde hier ein wenig auf die Do’s und Don’ts eingehen und dies anhand von einigen Beispielen demonstrieren.

Das Tutorial richtet sich an Fortgeschrittene Anwender. Um es ein wenig kompakter zu halten, werde ich hier nicht auf die Funktionsweise von Hooks oder die Unterschiede zwischen Actions und Filter eingehen. Kernpunkt des Tutorials ist das richtige setzen von Hooks damit diese auch wieder entfernt werden können.
Wer etwas experementieren möchte, für den habe ich ein Git-Repository angelegt. In diesem befinden sich einige Scripte die als fertige Plugins abgespeichert sind. Einfach das Repo in das Pluginverzeichnis klonen und schon kann man testen und probieren.

Etwas Geschichte

WordPress wurde zu einer Zeit geschrieben, als PHP5 noch nicht veröffentlicht war und Objektorientierte Programmierung (OOP) noch nicht sehr verbreitet war. WordPress ist prozedural entwickelt worden und trägt diese Altlast bis heute mit sich herum. Zwar nutzt auch WordPress Klassen und Objekte, jedoch baut das Grundprinzip immer noch auf prozeduraler Programmierung auf. Das Handling von Objekten ist auf diese prozeduraler Basis aufgepflanzt, was jedoch keine saubere Lösung darstellen kann.
So lange wir prozedural programmieren, werden wir kaum oder gar keine Probleme mit Hooks haben. Sobald wir jedoch auf OOP setzen, wird es sehr schnell sehr schwierig die richtige Lösung für die auftretende Probleme zu finden.

Hooks entsprechen dem Observer Pattern. Hierbei können sich Beobachter (Observer) für ein bestimmtes Ereignis (Hook) registrieren und werden benachrichtigt sobald das Ereignis eintritt. In einer prozeduralen Umgebung reicht es den Funktionsnamen als Observer zu speichern und diesen beim Eintritt des Ereignisses als Callback aufzurufen.
In einer OOP-Umgebung ist dies schon nicht mehr ausreichend. Eine Klasse ist lediglich das Entwurfsmuster für ein Objekt und es können verschiedene Objekte von der gleichen Klasse erzeugt werden. Spätestens mit PHP5.3 wird die Lage noch komplizierter, da nun auch anonyme Funktionen als Callback registriert werden können.

Prozedurale Programmierung

In der Prozeduralen Programmierung ist es wie gesagt einfach:

function hooker( $var ) {
  // do something
}

add_action( 'hook', 'hooker', 0, 1 );
do_action( 'hook' );

add_filter( 'hook', 'hooker', 0, 1 );
$var = 'old value';
$var = apply_filters( 'hook', $var );

// removes the action AND filter because both
// use the same callback
remove_action( 'hook', 'hooker' );

Einfach aber nicht ganz unproblematisch. Als WordPress noch klein war und es nur wenige Plugins und Themes gab, war das alles relativ unproblematisch. Als WordPress jedoch wuchs und es immer mehr Themes und Plugins gab, stieg die Wahrscheinlichkeit das jemand anderes eine andere Funktion mit dem gleichen Namen erstellte.
Eine Zwischenlösung war es, die Funktionen mit Prefixen zu versehen. Aber auch die Prefixe werden irgendwann knapp, denn man empfahl häufig die Initialen des Autors als vergleichsweise kurzen Prefix zu nehmen. Das dies nicht lange gut geht, kann man sich denken. Also wurde der Name des Plugins bzw. Themes hinzugefügt. Sowohl Theme- als auch Plugin-Namen sind einzigartig, jedoch nicht immer kurz.
Hier wird eine konzeptionelle Schwäche von WordPress deutlich die vor 10 Jahren, als die Basis von WordPress entwickelt wurde, noch nicht abzusehen war. So lange man alleine oder in einen kleinen Team an einem Projekt arbeitet und die volle Kontrolle hat, funktioniert das Konzept von WordPress. Öffnet man jedoch eine Schnittstelle nach Außen die jeder nutzen kann, scheitert das Konzept.

Namespaces könnten eine Lösung für das Problem sein. Mit Namespaces lassen sich weite Teile des Codes “prefixen” was die Arbeit sehr erleichtert. Jedoch hat sich das Team um WordPress dafür entschieden das PHP5.2 als Mindestanforderung gilt, Namespaces gibt es leider erst ab PHP5.3. Will oder kann man seine Nutzer nicht dazu zwingen PHP5.3 zu verwenden, fällt diese Lösung also raus.

OOP

Objektorientierte Programmierung kommt immer mehr in Mode. Es ist wirklich eine Mode und das “objektorientiert” muss man in Anführungszeichen setzen. Zumindest was WordPress angeht.
Man kann nun lange darüber diskutieren wie oder was objektorientiert bedeutet oder wie stark man das auslegen muss damit es als OOP gilt. Im Fall WordPress hat sich sehr schnell herausgestellt das viele Plugin-Programmierer Klassen als Namespaces missbrauchten. Bestehender Code wurde teilweise lediglich mit einem Klassen-Statement umschlossen und damit war die “objektorientierung” für den Programmierer gegessen. Daraus entstanden gravierende Probleme was das entfernen einmal gesetzter Hooks angeht. Prinzipiell bestehen diese Probleme auch dann, wenn man “streng objektorientiert” arbeitet. Der Grund ist nun einmal weniger die objektorientierte Programmierung, als viel mehr der veraltete Unterbau von WordPress.

Hooks im Detail

Um zu verstehen wo das Problem liegt, müssen wir uns kurz anschauen wie WordPress die Callbacks zu den einzelnen Hooks registriert. Damit beim Aufruf von z.B. do_action( 'sample_hook' ); alle Callback-Funktionen aufgerufen werden die zu diesen Hook registriert wurden, muss man eine Liste der Callback-Funktionen führen. WordPress nutzt dazu das globale Array wp_filter. Schauen wir uns dazu das Plugin “01_HookUp Tutorial – Part One” aus dem Git-Repo an. Nach der Aktivierung zeigt es im Dashboard eine Metabox mit ein paar Ausgaben. Unter anderem wird dort mittels var_dump() das Array $GLOBALS['wp_filter']['test_hook'][0] ausgegeben. Warum nun ausgerechnet dieses Array?
Die Antwort ist relativ simpel. In wp_filter stehen, wie erwähnt, die registrierten Hooks. Mittels add_action() bzw. add_filter() fügt man einen Hook hinzu sofern dieser noch nicht besteht. Die 0 ist die Priorität mit der die Callbacks ausgeführt werden. Nun wird es interessant, denn ab hier wird der Name des Callbacks als Index verwendet. Wir haben die Funktion hook_callback für den Hook test_hook registriert. In der Ausgabe sehen wir dann auch das unter diesen Indexeintrag die Werte function und accepted_args gespeichert sind.
Die Registrierung folgt also nach folgenden Muster ab: [wp_filter][hook name][priorität][array mit callbacks]. Das Array mit den Callbacks ist ein assoziatives Array (Schlüssel (Index) – Werte Paarung) mit dem Muster [funktionsname] => [function], [accepted_args].

Funktionen und Methoden

So lange wir nur Funktionen als Callback registrieren, können diese im Klartext als Index verwendet werden. Schwierig wird es bei der Verwendung von Klassen. Fügen wir in unserem Script einfach mal eine Klasse ein und schauen uns an was passiert wenn wir eine Methode dieser Klasse als Callback registrieren. Zuerst einmal die Klasse, die kann einfach ans Ende des Scripts eingefügt werden:

class Demo_Hook
{
  public function print_var( $var ) {
    printf( '<p>The var <em>%s</em> was send by <code>do_action()</code> to <code>%s</code></p>', $var, __METHOD__ );
  }
}

In der Funktion hooktest_output() fügen wir nach der Zeile mit add_action() noch folgende zwei Zeilen ein:

  $demo = new Demo_Hook();
  add_action( 'test_hook', array( $demo, 'print_var' ), 0, 1 );

Schauen wir nun wieder ins Dashboard, so sehen wir das sich die Ausgabe des var_dump() dahingehend verändert hat, dass dort nun ein zweiter Index erscheint. Dieser neue Index besteht aus einem Hash1 (die lange kryptische Zeichenkette) und den Namen der Methode (hier print_var). Nun drücken wir einfach ein paar mal F5 und beobachten den neuen Index. Wie gut zu erkennen ist, verändert sich der Hash mit jeden Seitenaufruf obwohl wir am Script nichts geändert haben. Und genau das ist unser Problem.

Hooks und Callbacks entfernen

Es wird wohl eher selten vorkommen das jemand einen kompletten Hook entfernen möchte, es wäre jedoch möglich. Dazu reicht es aus den Hook aus dem globalen Array wp_filter mit unset() zu entfernen. Dazu einfach mal testweise nach den Registrieren der Hooks (also vor // [...]) ein unset( $GLOBALS['wp_filter']['test_hook'][0] ); einfügen.
Nun spuckt der var_dump() eine Fehlermeldung aus weil der Index test_hook ja nicht mehr existiert. Aber auch die komplette Ausgabe da drunter ist verschwunden. Komplette Hooks entfernen ist also möglich, jedoch oft eher sinnlos.
Viel häufiger kommt es vor das man einzelne Callbacks entfernen möchte. Wir erweitern den unset() Aufruf einfach mal um ['hook_callback'] (unset( $GLOBALS['wp_filter']['test_hook'][0]['hook_callback'] );) und schauen was passiert. Rufen wir das Dashboard erneut auf (ggf. aktualisieren), sehen wir das sowohl der erste Index (hook_callback) in der Ausgabe des var_dump() verschwunden ist, jedoch auch die Ausgabe da drunter ist weniger geworden. Nun steht nur noch die Ausgabe aus der Klasse Demo_Hook dort.

Durch Manipulation des globalen Arrays wp_filter können wir also Callbacks und sogar ganze Hooks entfernen. Natürlich hat WordPress dafür bereits eine Funktion zur Verfügung gestellt die uns einiges an Arbeit abnimmt. remove_action() und remove_filter() machen im Prinzip nichts anderes als das was wir gerade eben von Hand durchgeführt haben.
Kommen wir wieder zurück zu unseren Problem. Wie wir anhand des unset() gesehen haben, müssen wir einen Index entfernen der aus dem Funktions- bzw. aus dem Hash plus Methoden-Namen besteht. Dementsprechend erwarten remove_action() und remove_filter() neben dem Hook aus dem der Callback entfernt werden soll einen Funktionsnamen. Bei hook_callback würde das dann so aussehen remove_action( 'test_hook', 'hook_callback', 0 );.
Aber was ist wenn wir nicht die Callback-Funktion hook_callback sondern die Callback-Methode aus dem Objekt entfernen wollen? Um an eine Antwort zu gelangen ändern wir noch mal kurz das Script. Zuerst einmal löschen wir den unset() Aufruf wieder. Die Zeile mit $demo = new Demo_Hook(); brauchen wir auch nicht mehr und kann gelöscht oder auskommentiert werden. Den add_action() Aufruf ändern wir zu add_action( 'test_hook', array( 'Demo_Hook', 'print_var' ), 0, 1 ); und die Methode print_var() deklarieren wir als static (public static function print_var( $var ){...}.
Nun das Dashboard neu laden und schon sehen wir das der Hash verschwunden ist und dafür ein Index Demo_Hook::print_var erscheint. Das ist gut, denn das können wir, anders als den Hash, vorhersagen. Klassenname Doppelter-Doppelpunkt Methoden-Name. Das können wir jetzt so in remove_action() einsetzen: remove_action( 'test_hook', 'Demo_Hook::print_var', 0 );. Alternativ können wir auch das gleiche Array verwenden das wir bei add_action() verwendet haben: remove_action( 'test_hook', array( 'Demo_Hook', 'print_var' ), 0 );.
Woher WordPress weiß das der String 'Demo_Hook::print_var' die gleiche Methode meint wie das Array array( 'Demo_Hook', 'print_var' ), dazu kommen wir später. Dann klären wir auch wie man ein Object bzw. nicht statischen Methoden-Aufruf entfernen kann.

1 Die Hash-Werte werden erst ab PHP5.2 erzeugt und verwendet. Unter PHP

Zwischenfazit

  • Hooks in WordPress basieren auf eine Technik aus Zeiten vor OOP. Dadurch kommt es in Verbindung mit Objekten zu Problemen die sich nicht so einfach lösen lassen.
  • Die Callbacks werden in einem assoziativen Array mit Index-Werte-Paarung gespeichert. Zum Entfernen eines Callbacks benötigt man den Funktionsnamen, so wie er im Index erscheint.
  • Verwenden wir eine einfache Funktion als Callback für den Hook, so wird der Funktionsname als Index verwendet.
  • Bei verwendung einer statischen Methode aus einer Klasse, so wird der Klassenname gefolgt von einem Doppelten-Doppelpunkt und den Methoden-Name als Index verwendet. Zum Entfernen eines mittels statischen Methoden-Aufrufes hinzugefügten Callbacks, kann entweder ein String aus Klassenname::Methoden-Name oder ein Array mit den Werten Klassenname und Methodenname verwendet werden.
  • Callbacks aus Methoden in Objekten bzw. nicht statischen Methoden-Aufrufen können nicht ohne weiteres entfernt werden da der Index zum Teil aus einem Hash besteht der bei jeden Seitenaufruf verschieden ist.

Neues Plugin: Password Change Reminder

Einer der häufigsten Tipps zur Sicherheit ist, sein Passwort regelmäßig zu wechseln. In diesen eigentlich simplen Punkt unterstützt WordPress uns leider in keinster Weise. Zumindest habe ich bei einer schnellen Recherche nichts dazu gefunden. Also habe ich mich hingesetzt und selber ein Plugin geschrieben: Password Change Reminder
Zielgruppe des Plugins sind Blogs die mit mehr oder minder unerfahrenen Benutzern (z.B. mehrere Autoren)  arbeiten. Aber auch Firmen die gewisse Vorgaben machen was das Passwort und die Häufigkeit zur Aktualisierung selbigen angeht. Natürlich ist es genauso für all diejenigen interessant, die sich zwar immer wieder vornehmen das Passwort regelmäßig zu wechseln, dies aber genauso regelmäßig vergessen.

Das Plugin ist sehr einfach gestrickt. Es prüft wie alt das aktuelle Passwort ist und gibt, sofern es veraltet ist, einen Warnhinweis im Adminbereich aus. Dieser Warnhinweis (engl. nag screen; deut. Nörgelbildschirm) erscheint auf allen Seiten im Adminbereich.
Neben den Hinweis im Adminbereich kann das Plugin auch einen unübersehbaren Hinweis auf der Startseite ausgeben. Dies ist nützlich für Benutzer die sich um den Adminbereich drücken.

Natürlich gibt es auch ein paar Einstellungen. Die wichtigste ist wohl die Einstellung wie lange es dauern soll bevor ein Passwort veraltet ist. Es lässt sich aber zusätzlich noch einstellen ob der Hinweis auch auf der Startseite erscheinen soll. Ebenso lässt sich einstellen ob Benutzer den Hinweis eine Zeit lang ignorieren (ausblenden) können und nach welcher Zeitspanne der Hinweis wieder eingeblendet werden soll. Als letzte Einstellung lässt sich noch ein Text festlegen der zusätzlich im Hinweis erscheinen kann. So kann man die Benutzer z.B. auf die Risiken eines veralteten Passwortes aufmerksam machen oder auf verbindliche Nutzerregelungen hinweisen.

Da das Plugin, wie gesagt, sehr einfach gehalten ist, gibt es dazu nun auch nicht mehr viel zu sagen. Experten die das Plugin einsetzen und den Hinweis auf der Startseite optisch ändern möchten, steht ein Stylesheet zur Verfügung das angepasst werden kann.

Für Lob, Kritik, Anregungen und vor allem Fehlerberichte steht der Support auf WordPress und das Repo auf GitHub zur Verfügung.


Das Ende einer langen Pause

Seit rund 6 Monaten habe ich nun hier nichts mehr rein geschrieben. In etwa genauso lange hat man von mir generell nichts mehr in Sachen WordPress lesen können. Anfangs war es schlichtweg Zeitmangel aufgrund von mehreren Projekten die ich begonnen hatte. Aber dann kam irgendwann der Tag an dem gar nichts mehr ging. Von jetzt auf Gleich ein Balken im Kopf der die totale Blockade ausgelöst hatte. Nicht eine Zeile Code habe ich mehr hin bekommen. Nicht einen klaren Gedanken fassen können wenn ich ein Problem lösen wollte. Nichts ging mehr.

Zum Glück ist das Programmieren nicht meine Lebensgrundlage, ansonsten wäre ich einige Monate lang berufsunfähig gewesen. So konnte ich mir den Luxus einer langen Auszeit gönnen und habe mich rein gar nicht mehr mit WordPress, PHP oder verwandten Themen befasst.
Andere Dinge rückten in den Mittelpunkt und das war gut so. Ich habe den nötigen Abstand gewonnen um wieder klar denken zu können. Den nötigen Abstand um Projekte, die ich begonnen hatte, wieder mit der nötigen Distanz und Kritikfähigkeit betrachten zu können. Abstand der es mir ermöglichte Wichtiges von Unwichtigen zu trennen.

Bei vielen reicht dafür ein Urlaub. Urlaub bedeutet bei mir jedoch oftmals Zeit zum Coden. Und das war dann wohl auch der Fehler den ich gemacht hatte, weswegen meine Auszeit etwas länger ausfiel.
Dadurch das ich mir Zeit für mich selber und andere Dinge genommen hatte – eher nehmen musste – konnte ich den Akku wieder aufladen. Jetzt macht mir das Coden wieder Spaß, die Zeilen flutschen nur so auf den Bildschirm und ich verbeisse mich nicht mehr an unwichtigen Details.
Viele Projekte habe ich auf Eis gelegt oder gleich ganz aufgegeben, ansonsten hätte ich das Coden wahrscheinlich aus lauter Frust komplett an den Nagel gehängt.
Zu den jetzigen Neuanfang gehört auch ein kleines Plugin, welches ich ohne durch Altlasten aufgehalten zu werden, in den letzten vier Tagen umgesetzt habe.

Entstanden ist das Plugin durch die Beschäftigung mit Menschen die es schon als große Leistung ansehen Facebook zu starten. Denn es kann durchaus hinderlich sein wenn man ständig von Menschen umgeben ist die sich auskennen. Man verliert sehr schnell den Blick dafür was diejenigen benötigen (könnten), die sich weniger bis gar nicht auskennen. Raus aus den Elefenbeinturm ist hier die Devise gewesen.
Und so ist in den letzten Tagen Password Change Reminder (PwCR) entstanden. Bei einer schnellen Recherche konnte ich kein Plugin finden welches den Benutzer daran erinnert regelmäßig sein Passwort zu ändern. PwCR macht genau das. Nicht mehr und nicht weniger.
Das Plugin ist, wie so häufig bei mir, in einem sehr früher Stadium. Da können noch ein paar Fehler dabei sein, die (englischen) Texte sind alle noch etwas schief, Übersetzungsarbeit ist zu leisten und natürlich Dokumentation. Genaueres zu dem Plugin werde ich in eine  seperaten Artikel schreiben. Bis dahin kann jeder der möchte es schon mal ausprobieren.