Erweiterbarkeit ist eine zentrale Anforderung an nahezu jede Applikation. Dabei stellt man relativ schnell fest, dass dies auf verschiedenen Ebenen einer Anwendung passieren kann. Auf einer elementaren Ebene spiegelt sich die Erweiterbarkeit in der grundlegenden Architektur wieder, kann aber auch sehr hoch angesiedelt werden. Beispielsweise in einer Pluginschnittstelle für externe Programmierer.
Ralf beschreibt in seinem Buch eine gute Möglichkeit eine auf dem Zend Framework basierende Applikation modular zu strukturieren (mit Teilmodule, die nach belieben eingefügt oder ausgehängt werden können). Häufig ist aber auch das zu weit gefasst und eine viel klarere und schmalere Schnittstelle reicht vollkommen aus. Gesucht ist also ein Mechanismus, der Teilfunktionen dynamisch nachlädt und in die Gesamtapplikation einbindet.
Ein Beispiel: mein webbasierter RSS Reader rsslounge kann verschiedene Quellen verfolgen und verarbeiten. So liest er gewöhnliche RSS Feeds. Aber weiterführend kann er auch RSS Feeds als Quelle für Bilder verarbeiten (und extrahiert aus diesen RSS Feeds nur die Bilder). Möglich ist aber auch deviantArt als Datenquelle. Sprich jede mögliche Datenquelle ist denkbar. Nicht nur RSS Feeds, sondern auch normale HTML Seiten (die in irgend einer Weise geparst werden) oder auch der Zugriff auf einen Email Account wäre denkbar. Es macht also Sinn hier eine Abstraktionsebene einzubauen und die Möglichkeit zu schaffen einfach neue Datenquellen hinzuzufügen.
Die Vorgehensweise, welche ich für rsslounge gewählt habe ist simpel: jede Datenquelle ist eine Klasse, welche eine fest vorgegebene abstrakte Basisklasse (rsslounge_source) implementieren muss. Diese abstrakte Basisklasse gibt die Methoden vor, die unbedingt angeboten werden müssen (z.B. load() um die Datenquelle abzurufen).
abstract class rsslounge_source {
abstract public function load($url);
abstract public function getHtmlUrl();
abstract public function getId();
//...
}
Alle Datenquellen, welche die abstrakte Basisklasse implementieren, werden in einem festen Verzeichnis abgelegt. Die Gesamtapplikation durchsucht diesen Pfad nach verfügbare Datenquellen, prüft, ob sie die nötige Basisklasse implementieren und verwendet sie dann im Programm. Soll beispielsweise ein neues Feed hinzugefügt werden, so werden alle verfügbaren Datenquellen gesucht und im Dialog gelistet:

Angenommen ich entschließe mich ein Jahr später eine neue Datenquelle für die API von Google Analytics zu realisieren, so muss ich lediglich eine neue Klasse von rssloung_source ableiten, die entsprechenden Methoden implementieren und diese dann in das Verzeichnis ablegen.
Dieses Prinzip wird auch Hollywood Prinzip genannt: “Don’t call us, we call you”. Die Datenquelle Klasse bietet Methoden an, welche von der Gesamtapplikation im Bedarfsfall aufgerufen werden. Das Plugin muss von der Gesamtapplikation nichts wissen und ausschließlich ihre eigene Funktionalität und damit die Schnittstelle realisieren.
Die Implementierung basiert auf den Reflection Mechanismus der seit PHP 5 zur Verfügung steht. Mit Hilfe der Reflection Klassen können zur Laufzeit Klassen geladen und deren Eigenschaften ausgelesen werden. Zudem kann über diesen Weg die Klasse dann auch instanziiert werden:
$class = new ReflectionClass("plugin_rss_feed");
$baseClass = new ReflectionClass("rsslounge_source");
$class->isSubclassOf($baseClass); // true wenn plugin_rss_feed die Klasse rsslounge_source implementiert
$obj = $class->newInstance();
Sollen, wie im obigen Beispiel, alle verfügbaren Datenquellen gelistet werden, so wird das Verzeichnis mit den Datenquellen nach PHP Dateien durchsucht, geprüft ob diese von rsslounge_source abgeleitet sind und falls dies zutrifft instanziiert. Alle Objekte der so gefundenen Klassen werden in ein Array gespeichert und dann für die weitere Verarbeitung an die Gesamtapplikation übergeben (die diese Klassen dann auflistet):
$pluginLocation = "/plugins";
$pluginPrefix = "plugins_";
$plugins = array();
foreach(scandir($pluginLocation) as $file) {
if(is_file($pluginLocation . "/" . $file)
&& substr($file,0,1)!="."
&& strpos($file,".php")!==false) {
$classname = str_replace(".php","",$file);
$class = new ReflectionClass($pluginPrefix.$classname);
if($class->isSubclassOf(new ReflectionClass("base_plugin")))
$plugins[$pluginPrefix.$classname] = $class->newInstance();
}
}
Natürlich macht es hier Sinn einen Cache einzubauen, da sich die verfügbaren Klassen nicht oft ändern. Zend_Cache bietet hier eine einfache Möglichkeit so ein Array dateibasiert oder mittels memcached zu cachen.
Für meinen RSS Reader habe ich diesen Mechanismus zum Laden der Datenquellen in einen Action Helper ausgelagert. So steht diese Funktionalität in der gesamten Applikation zur Verfügung. Wer an Details interessiert ist, sei an dieser Stelle auf Google Code verwiesen, wo der Quellcode von rsslounge zu finden ist.
Die Firma Mayflower ist mir ja schon länger ein Begriff, da sie sehr im Open Source Bereich um PHP tätig ist. Um die Verbreitung des Zend Frameworks zu fördern, verschenkt Mayflower Poster mit den wichtigsten Methodenaufrufen, Argumenten und Beispielaufrufen. Einfach das Kontaktformular ausfüllen und das Poster wird einem versandkostenfrei zugeschickt.
Eine feine Sache, mein Poster ist heute angekommen und echt top:

Ein PHP Web Security Poster soll in Arbeit sein. Ich bin gespannt und werd jetzt einen passenden Platz für mein Poster suchen…
Vor einigen Monaten habe ich hier ein kleines Skript zur Verfügung gestellt, das als kleiner Online-Notizzettel verwendet werden kann. Mittlerweile nutze ich das Skript täglich und trotz seiner Einfachheit ist es ein unheimlich nützlicher Helfer geworden. Für alle die das Skript nicht kennen: es ist nichts anderes als ein in Web abgelegter Richt-Text-Editor, in dem beliebiger Text geschrieben und gespeichert werden kann.
Egal wo man ist, Zuhause, in der Arbeit, in der Uni: von überall hat man Zugriff auf dieses eine Dokument und kann dort kurze Notizen und ToDo Listen speichern.
Dieses Skript habe ich nun aktualisiert. Es basiert nun nicht mehr auf tinyMCE, sondern auf der neuesten Version vom CKEditor. Insgesamt finde ich den Editor ansprechender, schicker und auch praktischer. CKEditor war früher einmal der FCKEditor und ist in der neuesten Version wirklich rundum gelungen. Nur die Dokumentation lässt etwas zu wünschen übrig.
Das Skript ist natürlich frei verfügbar (nur die Lizenz vom CKEditor muss beachtet werden) und darf sonst frei verändert und weiterverwertet werden.
Die Installation ist simpel: einfach die Dateien der ZIP Datei hochladen und für die Datei “data” Schreibrechte setzen. Bei Bedarf mittels .htaccess einen Passwortschutz setzen, sonst kann jeder, der die Url kennt eure Notizen lesen. Mit der Tastenkombination “Alt + S”, oder dem Speichern Icon, kann das Dokument gespeichert werden.
Die letzten Tage ist mir eine Verhaltensweise des Zend Frameworks aufgefallen, die doch sehr zweifelhaft ist. Es kam unerwartet zu der Exception
Could not determine temp directory,
please specify a cache_dir manually
Der Fehler war schnell gefunden: das temporäre Verzeichnis auf dem Server war nicht gesetzt bzw. war schreibgeschützt. Wie im Zend Framework Issue Tracker nachzulesen ist, wird bei der Verwendung von Zend_Locale und Zend_Translate automatisch ein Cache Objekt erzeugt, welches das temporäre Verzeichnis des Webservers verwendet. Ein aus meiner Sicht sehr zweifelhaftes Verhalten bzw. eine Vorgehensweise, die vielen Kritikern Recht gibt: ohne Caching geht es scheinbar nicht mehr. Laut Thomas Weidner ist das auch nicht mehr erwähnenswert:
Using a false cache will also decrease performance or negotate it completly… but I don’t think that this should be mentioned into this chapter [of the Zend Manual]. Anyone with default knowledge should know this already.
(siehe Issue Tracker ZF-6668, Thomas Weidner)
Ich sehe das an dieser Stelle etwas anders. Caching ist zwar ein wichtiges Mittel und muss hinsichtlich der Performance unbedingt im Auge behalten werden, es aber stillschweigend zu aktivieren, geht aus meiner Sicht doch etwas zu weit.
Um das Problem zu lösen, gibt es mehrere Möglichkeiten:
1. Man setzt die Umgebungsvariable für das temporäre Verzeichnis neu
Das Zend Framework durchsucht die Umgebungsvariablen ‘TMPDIR’, ‘TEMP’, ‘TMP’, ‘windir’, ‘SystemRoot’ (die letzten beiden für Unterverzeichnis ‘/temp’) und übernimmt diese, falls diese gesetzt sind. Abhilfe schafft es also, in der .htaccess
SetEnv TMPDIR data/cache
zu setzen.
2. Man deaktiviert das Caching (was gemäß Thomas Weidner nicht empfehlenswert ist und ich an dieser Stelle auch nicht empfehlen will).
Das Deaktivieren geht sehr einfach mit:
Zend_Translate::removeCache() Zend_Locale::removeCache()
3. Die beste Methode ist es, ein eigenes Cache Objekt zu setzen und darin einen eigenen Pfad zu definieren. Dazu muss das Frontend Zend_Cache_Core verwendet werden. Dabei wird auf eine statische Methode setCache zurückgegriffen, das heißt die Einstellung gilt dann für alle Zend_Locale oder Zend_Translate Objekte.
$path = APPLICATION_PATH . '/data/cache';
$cache = Zend_Cache::factory(
'Core',
'File',
array(),
array(
cache_dir => $path
));
Zend_Locale::setCache($cache);
Zend_Translate::setCache($cache);
Nun ist es soweit, endlich ist das Zend Framework in der Version 1.8.0 verfügbar. Ein erster Blick zeigt super Neuerungen, wie z.B. Zend_Tool, einem Command Line Skript zum Erstellen eines Programmrahmens. Zudem gibt es jetzt eine Zend_Application Komponente, die das Bootstrap File ablösen soll und zukünftig alle Programmkomponenten aufnehmen kann. Weiterführend unterstützt die neue Version auch den Cloud Computing Service Amazon S3. Darüber hinaus gibt es noch viele weitere neue Komponenten. Bin gespannt, werde aber so schnell keine Zeit zum Testen haben. Mal sehen, Erfahrungsberichte werden früher oder später aber sicher folgen.
Weitere Informationen gibt es hier.
Achtung: Es gibt mittlerweile eine neue Version dieses Skriptes: hier klicken um zu dieser zu gelangen.
Besonders wenn man immer wieder den Rechner wechselt (zwischen Arbeit, Uni, Zuhause, Notebook etc.) ist es praktisch einen zentralen Notizzettel zu haben, der von überall erreichbar ist. Dazu hab ich mir ein kleines und sehr simples Skript geschrieben, welches ein einfaches Eingabefeld zur Verfügung stellt. Dort können dann Informationen, Links und Notizen eingetragen werden. Ein Klick auf “speichern” schreibt dann die Änderungen zurück (sprich: schreibt diese direkt in eine einfache Textdatei). Also nichts Besonders, aber nachdem ich das nun täglich nutze könnte ich mir vorstellen das es auch für meine Blogleser ganz interessant sein könnte. Hier mal ein Screenshot:

Das Ganze basiert auf TinyMCE. Meine Entwicklungsarbeit lag also bei ganzen 5 Minuten, daher ist es lizenzfrei verfügbar (mit Ausnahme von den Lizenzbestimmungen von TinyMCE natürlich, die beachtet werden müssen).
Bitte kommentiert mir, wenn ihr etwas Besseres benutzt oder das Skript hier verbessert habt.
Wie mittlerweile in mehreren Quellen erwähnt, haben bisherige Zend Framework Versionen (kleiner 1.7.7) eine Sicherheitslücke in der Klasse Zend_Filter_StripTags. Hier können bei Nutzung einer Whitelist trotz Filterung unerlaubte Tags eingeschleust werden. Besonders kritisch ist das bei Anwendungen wie diesen Blog. Es wird empfohlen auf die aktuellste Version zu patchen, oder zumindest die StripTags Datei mit einer gepatchten Version zu ersetzen.
Weitere Infos gibt es im Zend Framework Forum.