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.


Wohin mit den Daten – Ab in den DataContainer

Beim Schreiben von Plugins und Themes stößt man früher oder später auf ein Problem das WordPress recht pragmatisch gelöst hat: Der globale Zugriff auf Daten.

Auf die Variable x die in Funktion y definiert wurde, kann nicht ohne weiteres in Funktion z zugegriffen werden. Das Problem ist der sogenannte Scope, also der Gültigkeitsbereich in PHP. WordPress umgeht das Problem einfach dadurch, indem es recht rücksichtslos fast alle seine Variablen mit den Schlüsselwort global in den globalen Namensraum verfrachtet und somit überall verfügbar macht. Das ist zwar eine sehr einfache Lösung, jedoch auch eine sehr egoistische. Nehmen wir einfach mal an wir hätten ein Plugin oder Theme das die Variable foo verwendet.

<?php
function set_foo( $var ) {
  global $foo;
  $foo = $var;
}

function print_something() {
  global $foo;
  print( "Foo is {$foo}<br>" );
}

function do_something() {
  global $foo;
  $foo = (int) $foo;
  $foo++;
}

Prefixen

Nun kommt die nächste Version von WordPress raus und BÄMM, WordPress verwendet selber die Variable foo und macht sie mittels global auch noch überall verfügbar. Eine Katastrophe, denn nun müssten wir unser Plugin/Theme komplett umschreiben.
Bisher vermied man dieses Problem indem man alle seine Variablen und Funktionen mit einem Prefix versah, also anstatt einfach nur $foo mit einem Prefix wie z.B. $fb_foo. Diese Lösung hat jedoch auch seine Nachteile. Frank Bültge hatte seit jeher den Prefix fb_ verwendet, dieser wird jedoch mittlerweile häufig im Zusammenhang mit FaceBook verwendet. Einen kurzen und recht eindeutigen Prefix zu finden, dürfte so langsam schwer werden. Könnte man hingehen und einen längeren Prefix verwenden, zum Beispiel frabue_ der so lange funktioniert, bis jemand anderes ihn ebenfalls verwendet. Eine Rüstungsspirale die dazu führt das der Prefix irgendwann länger ist als der Variablen- oder Funktionsname.

Namespaces

Seit PHP5.3 haben wir die Möglichkeit Namespaces zu nutzen. Namspaces lösen eine ganze Reihe solcher und vergleichbarer Probleme, weshalb ich inzwischen nur noch für PHP5.3+ schreibe. Man sollte aber auch nicht verschweigen, dass man sich angewöhnen muss seinen Code anders zu schreiben. Am Anfang bedeutet dies etwas Frust, der schwindet jedoch recht schnell weil die Vorteile überwiegen. Leider hat nicht jeder die Möglichkeit PHP5.2 einfach zu ignorieren. Arbeitet man im Kundenauftrag und weigert der Kunde sich beharrlich auf PHP5.3+ zu wechseln, kann man nicht auf Namespaces zurück greifen.

Containern

Aber wir können zumindest für unsere Variablen unseren eigenen kleinen Namespace basteln. Und den können wir dann auch noch wesentlich komfortabler ausstatten als den globalen Namensraum. Dazu nutzen wir eine Klasse die in der Grundversion aus lediglich zwei magischen Methoden besteht, __set() und __get(). Der Klasse geben wir noch eine statische Eigenschaft mit in der wir dann alle unsere Variablen speichern können. Der große “Zauber” an dieser Klasse besteht im Grunde genommen darin, dass statische Eigenschaften immer ihren Wert behalten, egal wie viele Instanzen wir von der Klasse erstellen.

<?php 
class DataContainer
{
  public static $data = array();

  public function __set( $name, $value ) {
    self::$data[$name] = $value;
  }

  public function __get( $name ) {
    if ( isset( self::$data[$name] ) )
      return self::$data[$name];
    else
      return null;
  }
}

Die Verwendung der Klasse ist recht simpel. Wollen wir eine Variable setzen, erzeugen wir eine Instanz der Klasse und setzen die Variable. Wollen wir an einer anderen Stelle die gleiche Variable wieder zurück lesen, erzeugen wir eine Instanz und lesen ganz einfach die Variable.

<?php
function set_foo( $var ) {
  $dc = new DataContainer;
  $dc->foo = $var;
}

function print_something() {
  $dc = new DataContainer;
  $foo = $dc->foo;
  print( "Foo inside DataContainer: {$dc->foo}
 Foo as local variable: {$foo}
" );
}

function do_something() {
  $dc = new DataContainer;
  $foo = &$dc::$data['foo'];
  $foo = (int) $foo;
  $foo++;
}

set_foo( '1' );
do_something();

$dc = new DataContainer();
echo "Foo is now: {$dc->foo}";

Wie man an der Funktion do_something() sieht, ist es auch möglich die Variablen innerhalb vom DataContainer mittels Referenz zu bearbeiten, so dass man bearbeitete Werte nicht zwangsweise wieder zurück in den DataContainer schreiben muss.

Vererbung

Nun ist es unter Umständen etwas mühselig in jeden Funktionsaufruf eine neue Instanz des DataContainers zu erzeugen. Arbeitet man objektorientiert, ist es etwas einfacher, da die Klassen den DataContainer erweitern können.

<?php
class Bar extends DataContainer
{
  public function hello_foo() {
    printf( 'Foo used inside %s: %s
', __CLASS__, $this->foo );
  }
}

$bar = new Bar();
$bar->hello_foo();

Alle Variablen die im DataContainer abgelegt, gelöscht oder geändert werden, sind in der Klasse Bar genauso verfügbar. Vererbung ist nicht gerade die Paradedisziplin von PHP weswegen man wohl eher auf Dependency Injection zurück greifen würde. Ein weiterer, meinem persönlichen Geschmack nach, besonders wertvoller Vorteil von DI ist, man kann seinen DataContainer Mocken und die testbarkeit seiner Klassen deutlich erhöhen. DI, Unittest und Mockings sind aber ein ganz anderes Thema.

RODC – Read Only DataContainer

Es kann natürlich sein das unser Theme oder Plugin durch Filter und Hooks erweitert werden kann, dann könnte es durchaus sein das wir einmal gesetzte Variablen davor schützen wollen das sie von unbekannten Code überschrieben werden. Nutzt man den globalen Namensraum, so ist das nahezu unmöglich. In WordPress gibt es z.B. keine Möglichkeit die globale Variable $post, die eine zentrale Rolle spielt, vor dem Überschreiben zu schützen. Jeder kann sie jederzeit löschen oder ändern, was dazu führt das andere Codeabschnitte, die diese Variable benötigen, unter Umständen versagen. Mit einem DataContainer ist das deutlich einfacher zu handhaben, wir bauen einfach eine Methode ein die bestimmte Variablen als Read Only kennzeichnet.

<?php
class DataContainer
{
  public static $data = array();

  public static $protected = array();

  public function __set( $name, $value ) {
    if ( ! in_array( $name, self::$protected ) )
      self::$data[$name] = $value;
  }

  public function __get( $name ) {
    if ( isset( self::$data[$name] ) )
      return self::$data[$name];
    else
      return null;
  }

  public static function protect( $name ) {
    if ( ! in_array( $name, self::$protected ) )
      array_push( self::$protected, $name );
  }
}

function good_foo() {
  $dc = new DataContainer();
  $dc->foo = 'protected foo';
  $dc->bar = 'unprotected bar';
  $dc->protect( 'foo' );
}

function bad_bar() {
  $dc = new DataContainer();
  $dc->foo = 'hahaha!';
  $dc->bar = 'I change everything!';
}

$dc = new DataContainer();

echo "Set <code>foo</code> to <b>protected foo</b> and <code>bar</code> to <b>unprotected bar</b><br>";
good_foo();

echo "Try to overwrite <code>foo</code> and <code>bar</code><br>";
bad_bar();

echo "<code>foo</code> is still <b>{$dc->foo}</b> and <code>bar</code> is now <b>{$dc->bar}</b><br>";

Das dürfte nun nicht jeden Tag vorkommen das man seine Variablen schützen muss, zeigt aber schön welche Vorteile ein DataContainer gegenüber den globalen Namensraum hat. Man ihn nämlich nahezu nach belieben an seine Bedürfnisse anpassen.

Shutdown

Zum Schluss noch ein kleines Beispiel wie ich bei einem Plugin, an dem ich gerade schreibe, mit Hilfe eines DataContainers ein paar Probleme gelöst habe.
Das Plugin erforderte die Zwischenspeicherung einiger Daten da diese zur Laufzeit erstellt werden und über verschiedene Seitenaufrufe verfügbar bleiben müssen. Gegeben sei also z.B. ein Array $a das zur Laufzeit befüllt wird und bei einem Ajax-Request verfügbar sein muss. Mein erster Lösungsansatz war die Verwendung von $_SESSION, denn genau dazu sind Sessions da. Um Daten über verschiedene Seitenaufrufe breit zu stellen. Um sicher zu gehen das am Ende des Skripts auch alle Daten in der Session gespeichert wurden, habe ich shutdown Hook von WordPress benutzt. Dieser Hook wird bei Beendigung des Skriptes ausgeführt, zu einem Zeitpunkt also, wenn alle Daten vorhanden sein sollten. In der Callback-Funktion habe ich dann einfach alle Daten in die Session geschrieben und die Session geschlossen.
Nun taten sich verschiedene Probleme auf. Zum einen ist das recht unflexibel. Fügt man noch weitere Daten hinzu, muss man sich darum kümmern das auch diese in der Session gespeichert werden. Genauso wenn Daten entfallen, muss man sich darum kümmern das diese, nicht mehr vorhandenen Daten, nicht gespeichert werden. Der erste Lösungsansatz war dann anstatt einzelner Arrays einen DataContainer zu verwenden und dann den kompletten DataContainer in der Session zu speichern. Das funktioniert erstaunlich gut, jedoch musste ich mich immer noch selber darum kümmern das die Daten in der Session gespeichert werden.
Zudem ist der shutdown Hook nicht wirklich sicher. Wird das Skript z.B. mit einem exit() die() beendet bevor der Hook registriert wird, werden die Daten nicht in der Session gespeichert. Dieses Problem lässt sich dadurch lösen, indem man das Speichern und lesen der Daten in bzw. aus der Session in den DataContainer verlagert.

<?php
class DataContainer
{
	const SESSION_KEY = 'Example_DataConatiner';

	public static $data = array();

	public function __construct() {
		( ! session_id() ) AND session_start();

		if ( isset( $_SESSION[self::SESSION_KEY] ) && ! empty( $_SESSION[self::SESSION_KEY] ) )
			self::$data = $_SESSION[self::SESSION_KEY];

		register_shutdown_function( array( $this, '__destruct' ) );
	}

	public function __destruct() {
		( ! session_id() ) AND session_start();

		$_SESSION[self::SESSION_KEY] = self::$data;
		session_write_close();
	}

	public function __set( $name, $value ) {
		self::$data[$name] = $value;
	}

	public function __get( $name ) {
		if ( isset( self::$data[$name] ) )
			return self::$data[$name];
		else
			return null;
	}

	public static function reset() {
		self::$data = array();
		unset($_SESSION[self::SESSION_KEY]);
		session_write_close();
	}
}

$dc  = new DataContainer();
$uri = $_SERVER['PHP_SELF'];

if ( null === $dc->session_data ) {
	$dc->session_data = 'Value from previous page request';
	echo 'This is the first run<br>';
	die( "<a href='{$uri}?run=2'>Klick</a>" );
}

$pagerequest = filter_input( INPUT_GET, 'run', FILTER_SANITIZE_NUMBER_INT );
echo "This is the {$pagerequest}nd run with <b>{$dc->session_data}</b> from session-data";
$dc::reset();

Das funktioniert schon recht wunderbar. Durch die __destruct() Methode wird der Inhalt des DataContainers in der Session gespeichert sobald er zerstört wird. Da PHP am Ende eines Skriptes alle bestehenden Objekte zerstört (schließt), wird die __destruct Methode automatisch am Ende eines Skriptes aufgerufen. Im Beispiel oben wird die __destruct() Methode mittels register_shutdown_function() noch zusätzlich registriert, so dass sie beim Beenden des Skriptes auch definitiv ausgeführt wird. Verwendet man anstatt der magischen __destruct() Methode eine eigene Methode und registriert sie mit register_shutdown_function(), so kann man den DataContainer verwerfen ohne das seine Werte gespeichert werden, die Werte werden jedoch automatisch am Ende des Skriptes gespeichert.

Allerdings wurden Sessions zu einen Zeitpunkt erfunden als Browser keine Tabs kannten und anscheinend geht jeder Browser etwas anders damit um. Ich hatte zumindest massive Probleme mit dem FireFox wenn ich mehr als einen Tab zum gleichen Server offen hatte (z.B. die Startseite in den einen Tab, einen Artikel in der Einzelansicht in einen anderen). Chrome und Opera können das besser, aber ich kann meinen Besuchern ja schlecht vorschreiben welchen Browser sie zu benutzen haben oder wie viele Tabs sie öffnen dürfen.
WordPress bietet uns einen guten Ersatz für Sessions sofern man damit Probleme hat. Wir verwenden einfach Transients zum Speichern der Daten. Man muss hier abwägen ob die zusätzlichen DB-Zugriffe und vor allem die Menge an Daten die man speichern möchte dies rechtfertigt. Transients haben den Vorteil das man recht genau einstellen kann wie lange sie gültig sind. Das geht zwar mit Sessions auch, jedoch erlaubt nicht jeder Hoster das Verändern des Wertes für die Session-Lifetime. Transients sind sogar beständiger als Sessions, so kann man einen Transient an einen Login koppeln und somit eine Art von Server-Cookie umsetzen.

Fazit

Ich hoffe es ist deutlich geworden das der globale Namensraum eigentlich mehr Nach- als Vorteile hat und das man mit nur wenig Aufwand eine bessere Lösung bekommen kann die man nach seinen eigenen Bedürfnissen anpassen kann. Vor allem das automatische Speichern, ob in Sessions oder Transients, erleichtert die tägliche Arbeit enorm da man sich weniger Gedanken darum machen muss ob man nun alles gespeichert hat oder nicht. Der DataContainer lässt sich enorm ausbauen, denkbar wäre z.B. auch eine Methode die Werte aus einen Aufruf der Settings-API validiert und speichert. Am Ende des Skriptes werden die Daten automatisch in die Options geschrieben, stehen aber während der Laufzeit weiterhin zur Verfügung ohne das man sie sich extra aus den Options holen müsste.
Vor allem das man den DataContainer, und somit auch Zugriffe auf Transients und die Options-Tabelle, sehr einfach mocken kann, erleichtert das Unittesting ungemein wodurch man wiederum viel Zeit und Mühe beim Programmieren spart.
Alle in diesen Artikel verwendeten Skripte sind als Gist erhältlich für den Fall das sich jemand dazu ermuntert fühlt ein wenig mit DataContainern zu experimentieren.