Zend_Application bildet gewissermaßen den bisher fehlenden ersten Schritt einer Anwendung. Dazu gehört Laden einer Konfiguration und Initalisierung diverser Hilfsklassen und Module. Ein wie ich finde sehr interessante Komponente, die mich dazu bewegt schon vor dem offiziellen Release ein Blick darauf zu werfen. Dazu gibt es bereits das angepasste Manual, das bisher aber noch unausgereift wirkt und teilweise noch Fehler und Lücken enthält. Bei einer Alpha-Version ist das verschmerzbar und weckt den Forschergeist
Die Komponente selbst findet sich im Download-Bereich oder im SVN-Trunk, einer eigener Branch existiert zur Zeit noch nicht.
Begriffe/Klassen
Um es gleich vorweg zu nehmen: Jede Klasse oder Schnittstelle, die nicht mit Zend_ beginnt, wurde gekürzt und gehört direkt zu Zend_Application. Ich erhoffe mir damit etwas Übersichtlichkeit zu gewinnen.
Den Kern der Komponente bildet das Interface Bootstrap_IBootstrap. Man darf hier schonmal beachten, dass dies Interface entgegen der (alten) Code-Convention nicht mehr auf “Interface” endet, sondern mit Blick auf PHP5.3 Namespace-konform ist. Eine Klasse, die dies Interface implementiert, bildet den Ausgangspunkt der Anwendung und übernimmt sämtliche Initialisierungen. Das darf man nicht zu wörtlich nehmen, denn sie kann Initialisierungen auch delegieren, dazu später mehr. Die Initialisierungen in der Bootstrap-Klasse werden dabei in protected Methoden vorgenommen, die mit _init beginnen. Hängt eine Methode von einer anderen ab, kann sie dies mittels $this->bootstrap(‘andere’); erzwingen. Eine Sonderstellung nimmt die Methode run() ein: Wie der Name vermuten lässt, wird hier letzten Endes die Anwendung ausgeführt.
Implementiert diese Bootstrap nun zusätzlich Bootstrap_IResourceBootstrap, so kann sie Aufgaben an “Resource Plugins” delegieren. Der empfohlene Weg für die Bootstrap ist die Erweiterung der Klasse Bootstrap_Base, die beide Schnittstellen bereits implementiert. Ein Resource-Plugin implementiert wiederum Resource_IResource. Die wichtige Methode ist hierbei init(), in der letzten Endes der (delegierte) Initialisierungs-Code steht. Auch hier kann über $this->getBootstrap()->bootstrap(‘andere’); die Ausführung einer übergeordneten Initialisierung erzwingt, bzw erzwungen werden. Ebenso gibt es auch hier wieder eine bereits vorimplementierte Klasse Resource_Base zu erweitern. Es gibt bereits einige vordefinierte Resource-Plugins, dessen Source man sich ruhig anschauen sollte, wenn man grundsätzliches Vorgehen anschauen möchte.
Der kleinste, aber wichtigste Teil bildet die Klasse Zend_Application selbst. Sie lädt die Konfiguration, die Bootstrap und die diversen Resource-Plugins und bringt alle zusammen.
Beispiel
Zunächst die Bootstrap selbst, sie bildet schließlich den Ausgangspunkt für alles Weitere. Sie ist entsprechend dem Manual aufgebaut, verursacht aber in der Form Fehler: frontController existiert nicht, eine Abhängigkeit innerhalb der Komponenten, die scheinbar bisher unberücksichtigt blieb, denn (wie man annehmen darf) fehlt das Attribut frontController einfach, was standardmässig vom FrontController-Resource-Plugin angelegt wird.
require_once 'Zend/Application/Bootstrap/Base.php';
class Bootstrap extends Zend_Application_Bootstrap_Base {
public function run()
{
$this->frontController->dispatch();
}
}Für die Bootstrap soll das ausreichen. Auf die _init-Methoden verzichte ich, weil es über die Resource-Plugins eleganter lösbar ist. Jetzt ist es interessant diese Bootstrap einmal auszutesten. Dazu gehört die bekannte index.php, aber auch schon eine Konfiguration (ich habe ini verwendet).
chdir('/path/to/Zend/library');
require_once 'Zend/Application.php';
$app = new Zend_Application('dev','/path/to/application/configs/application.ini');
$app->setIncludePaths(array('/home/sebastian/dev/import/Zend/library'));
chdir('.');Warum hier das chdir()? Das ist ein Workaround von mir, weil paradoxerweise Zend_Application zwar die Methode setIncludePaths zum setzen des Include-Path bereit stellt, selbst aber vorher schon zum Nachladen einzelner (einer?) Komponente auf den Zend-Pfad angewiesen ist. Das soll aber nicht weiter stören. Interessant ist hier nur die Instanzierung der Klasse, in der alles Wichtige bereits gesetzt wird. Da wäre einmal der erste Parameter (hier “dev”), der die Umgebung angibt. Er ist frei wählbar, wird aber auch als Sektion beim Laden der Konfíguration verwendet, die als zweiter Parameter angegeben wird. Der Typ der Konfiguration wird aus der Dateiendung hergeleitet.
Über die Konfiguration lässt sich alles steuern. Da wären einmal die zwei Schlüssel für die Bootstrap-Klasse, die bereits existiert
bootstrap.path = /path/to/application/Bootstrap.php bootstrap.class = "Bootstrap"
Sie sind eigentlich selbsterklärend. Nun kommen endlich die Resource-Plugins ins Spiel. Über den Schlüssel resources.* bindet man diese ein, die dann traditionell automatisiert geladen und eingebunden werden. Dazu hilft ein Beispiel:
resources.frontController.moduleDirectory = /path/to/application/modules resources.frontController.prefixDefaultModule = 1 resources.view.encoding = "UTF-8" resources.view.basePath = /path/to/application/views/scripts resources.modules =
Die letzte Zeile ist kein Tippfehler. Mir fehlt noch die Möglichkeit ein Plugin, was keine Optionen erwartet, einzubinden, ohne ihn als Key verwenden zu müssen. Da muss jetzt ein leerer Wert herhalten. Sobald ein benanntes Plugin auftaucht, wird versucht es zu laden. Die Schlüssel-Wert-Paare, die danach angegeben sind, werden dem Plugin dabei zur Verfügung gestellt. Man kann natürlich auch wieder eigene Plugins schreiben, dessen Herkunft auch wieder ganz ZF-Stil über Prefix-Pfad-Paare bekannt gemacht werden.
pluginpaths.My_Library_Resource = My/Library/Resource
Ein aufmerksamer Beobachter mag stutzen: Im Manual heißt es doch resourcePaths? Richtig, in einem von beiden Quellen mag ein Fehler stecken. Der Source-Code offenbarte mir pluginpaths (also auch mit kleinem, zweiten “p”) als korrekte Angabe.
Besonderer Erwähnung bedarf dem Modules-Resource-Plugin. Dieser ermöglicht es einzelne Module zu initialisieren. Man kann dazu eine Klasse (Modulname)_Bootstrap in einer Datei Bootstrap.php im jeweiligen Modulverzeichnis anlegen. Diese Klasse muss von Modul_Bootstrap erben. Sie erbt von Resource_Base, haben also selbe Möglichkeiten, speziell in Hinblick auf die init()-Methhode.
Abschluss
Eigentlich nur der Vollständigkeit halber. Hat man alles korrekt konfiguriert, muss man den Prozess nur noch anstoßen.
$app = new Zend_Application('dev','/home/sebastian/dev/pdt2/webapp/application/configs/application.ini');
/* other code */
$app->bootstrap();
$app->run();bootstrap() ruft dabei die einzelnen init()-Methoden auf, run() letzten Endes nur noch die gleichnamige Methode der eigenen Bootstrap. Man kann auch über bootstrap($name) einzelne “Initiatoren” gezielt aufrufen, falls im bestimmten Kontext die anderen nicht benötigt werden. Dieses Vorgehen kommt einen schon bekannt vor, nämlich wenn ein abhängiger Initiator den Vorhergehenden aufzurufen (siehe oben).
Fazit
Der Ausblick soll hier ausreichen. Natürlich kann die Komponente noch mehr, aber dadurch wird auch anschaulich, wie komprimiert und abstrahiert der Bootstrap-Prozess ausfallen kann. Der Komponente gelingt eine gute Brücke zwischen der prozeduralen index.php und der MVC-Komponente vom Zend Framework, die (also die Brücke) im Gegensatz zum bisherigen empfohlenen reinen prozeduralen Ansatz ungewohnt erscheint, aber man findet schnell einen Einstieg. Nach den Startschwierigkeiten, die vorallen durch das (noch) mangelnde Manual verursacht wurden, war ich vorallen über die starke Konfigurierbarkeit und damit verbunden den kurzen Code überrascht. Ebenso vorteilhaft ist, dass die einzelnen “Ebenen” die Konfiguration immer weiter tragen, so dass man sich darüber keine weiteren Gedanken machen muss.
Viel Spass beim Ausprobieren,
Das Müsli!
Pingback: Vorschau Zend Framework 1.8: Autoloader | KingCrunch's kleine Welt
Pingback: Zend Framework 1.8 Vorschau: Navigation | KingCrunch's kleine Welt
12. Oktober 2009 um 21:59 Uhr
An sich n cooler post, aber kannst beim nächsten mal n bisschen detailierter sein?
12. Oktober 2009 um 22:59 Uhr
Tatsächlich war dieser Artikel nur als Vorschau auf die Komponente gedacht (man beachte das Datum) und sollte wirklich nicht länger werden. Mittlerweile gibt es allerdings auch schon bessere Artikel über “Application”, da lohnt ein Update nicht.