Kapitel 6. Standardisierung der Bootstrap-Klasse mit Zend_Application

Inhaltsverzeichnis

6.1. Einleitung
6.2. Schritt 1: Die Klasse ZFExt_Bootstrap bearbeiten
6.3. Schritt 2: Die Index- und htaccess-Dateien editieren
6.4. Schritt 3: Die Konfigurationsdatei für die Anwendung hinzufügen
6.5. Schritt 4: Die Voreinstellungen der Standardkomponenten bearbeiten
6.6. Schritt 5: ZFExt_Bootstrap anpassen
6.7. Schritt 6: Die Anwendungskonfiguration in Ressource-Methoden einbinden
6.8. Schritt 7: Den Autoload-Code optimieren
6.9. Zend_Loader_Autoload erlauben, Klassen mit Namespace zu laden
6.10. Fazit

6.1. Einleitung

Bis vor kurzem war die Erstellung und Verwaltung des Bootstrap einer Zend-Framework-Anwendung allein der Phantasie der Entwickler überlassen. Dadurch entstanden verschiedenste Ansätze vom Einsatz eines monolithischen prozeduralen Skripts bis hin zu Klassen verschiedenster Struktur oder Komplexität - ganz zu schweigen davon, dass es zweifellos sehr schwer wurde, ein Kommandozeilen-Tool zu schreiben. Mit der Einführung von Zend_Application aber existiert nun für die Vorgehensweise beim Bootstraping eine gemeinsame Basis, die in jeder Anwendung von jedem Entwickler verwendet werden kann. Dadurch wird die Standardisierung dieses in jeder Anwendung erforderlichen Teils gefördert. Hätten sie das ganze bloß Zend_Bootstrap genannt, um das auch offensichtlich zu machen...

Zend_Application wurde so konzipiert, dass es modular, anpassbar und möglichst einfach konfigurierbar ist. Da wir Zend_Application über das ganze Buch hinweg verwenden wenden (und das intensiv!), bringen wir die Sache ins Laufen, indem wir uns das vorherige "Hallo Welt"-Beispiel noch einmal ansehen und die Klasse ZFExt_Bootstrap überarbeiten. Eines werde ich aber beibelassen, und zwar wird der Bootstrap weiterhin unter /library/ZFExt/Bootstrap.php zu finden sein. Es gibt keinen Grund, die Datei woanders abzuspeichern, da der Namespace ZFExt die Basis unserer allgemeinen Anwendungsbibliothek bildet, in der wir alle eigenen Zend-Framework-spezifischen Klassen aufbewahren, um sie eventuell in Zukunft in anderen Anwendungen wiederzuverwenden.

6.2. Schritt 1: Die Klasse ZFExt_Bootstrap bearbeiten

Die nötigen Änderungen an unserer früheren Bootstrap-Klasse beginnen ganz einfach:

Wir werden einige Änderungen vornehmen müssen, um das nachzubilden, was wir mit der ursprünglichen Klasse ZFExt_Bootstrap erreicht haben. Falls Sie aber einmal keine Änderungen brauchen, können Sie mit der Bearbeitung des Bootstrap schon wieder aufhören.Zend_Application kann alles ohne zusätzlichen Code korrekt aufsetzen, indem die Standardwerte der benötigten Komponenten verwendet werden.

Wir erweitern hier Zend_Application_Bootstrap_Bootstrap, das wiederum Zend_Application_Bootstrap_BootstrapAbstract erweitert und alles bietet, was man typischerweise für das Aufsetzen eines Bootstrap braucht - unter anderem das Laden von Ressourcen, das Dispatchen durch den Front-Controller und einige Kontrollen, um sicherzugehen, dass der Betriebsmodus (z.B. Produktivsystem oder Entwicklung) funktionsfähig ist..

6.3. Schritt 2: Die Index- und htaccess-Dateien editieren

Wie zuvor binden wir den Bootstrap am Beginn in unserer index.php in /public ein und führen ihn dort aus. Etwas mehr Komplexität müssen wir dem Code aber noch hinzufügen. Dazu zählt das Setzen von Konstanten, die einige Anwendungspfade enthalten, das Laden einer Konfiguration, um das Bootstrapping der Einrichtung der Umgebung zu steuern, und das Starten des Bootstrapping-Vorgangs selbst.

Wir können hier erkennen, dass die umgebungsspezifische Konfiguration ständig in index.php vorgenommen wird. In index.php erstellen wir eine Zahl von Konstanten, definieren unseren include_path und starten den Bootstrapping-Prozess. Intern lädt Zend_Application unsere Klasse ZFExt_Bootstrap, die wir in der Anwendungskonfigurationsdatei /config/application.ini konfigurieren. Unsere Klasse ist zwar leer, doch sie erweitert eine abstrakte Klasse, die alle benötigten Methoden enthält.

Ich muss darauf hinweisen, dass mit diesen Konstanten verantwortlich umgegangen werden muss. Konstante sind globale Werte, die überall in der Anwendung zugänglich sind, und wie jede andere globale Variable sollte ihr Gebrauch minimiert werden und mit Vorsicht erfolgen, da man immer versucht ist, sie überall zu verwenden. Andere Anwendungen erzeugen diese Konstanten vielleicht nicht einmal, weswegen jeder Code, der sich auf sie verlässt, eventuell nicht wiederverwendbar ist. Eine weitaus bessere lösung ist, außerhalb von index.php weiterhin dirname() und realpath() zu verwenden und/oder diese Variablen als Registry-Objekt oder Front-Controller-Parameter für Controller-Aktionen verfügbar zu machen.

Etwas anders als im Referenzhandbuch behandle ich index.php entsprechend dem Zend-Framework-Coding-Standard. Es ist empfehlenswert, nicht die Kurzformen der Befehle zu verwenden, da diese nicht sehr leserlich sind.

Ihnen wird auffallen, dass unsere index.php-Datei nun standardmäßig die Konfiguration für "production" verwendet, falls der Konstante APPLICATION_ENV kein Umgebungswert zugewiesen wird. Wir wollen klarerweise nicht mit einer Produktionskonfiguration entwickeln (außer wir haben Spaß daran, Anwendungen zu debuggen, die es mysteriöserweise nicht schaffen, ihre Fehler anzuzeigen). Wir lösen das Problem, indem wir den notwendigen Umgebungswert zu unserer .htaccess-Datei hinzufügen:

SetEnv APPLICATION_ENV development

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d

RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ /index.php [NC,L]

Bei Apache wird das mit diesem Befehl erreicht, für andere Webserver konsultieren Sie bitte die jeweilige Dokumentation.

6.4. Schritt 3: Die Konfigurationsdatei für die Anwendung hinzufügen

Einer der Hauptvorteile bei der Verwendung von Zend_Application ist, dass man sich (da eigener Code für das Bootstraping nicht mehr nötig ist) auf das Setzen von Standardwerten für Komponenten konzentrieren kann und den Rest über die Konfiguration regelt. Wie der Inhalt unserer index.php oben suggeriert, sollten wir eine Konfigurationsdatei unter /config/application.ini mit folgendem Inhalt erstellen:

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
bootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php"
bootstrap.class = "ZFExt_Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.throwExceptions = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.throwExceptions = 1

Die Konfigurationsdatei ist in vier individuelle Modi aufgeteilt: production, staging, testing und development. Alle Modi erben alle Werte von production, können allerdings Ausnahmen hinzufügen. Zum Beispiel deaktivieren die Einstellungen von production display_errors, während die Modi testing und development sichergehen, dass display_errors aktiviert ist. Sie können sicher sein, dass Zend_Application den richtigen Modus anhand des Wertes lädt, den wir in index.php in der Konstante APPLICATION_ENV gesetzt haben.

Die verwendeten INI-Werte erscheinen vielleicht etwas seltsam, da sie unsere PHP-Konstanten enthalten, gefolgt von einem Leerzeichen und einem in doppelten Anführungszeichen umfassten relativen Pfad, der vom absoluten Pfad ausgeht, der in der Konstante definiert wurde. Zend_Application kümmert sich intern um das Zusammensetzen dieser zwei Teile - behalten Sie einfach in Erinnerung, dass Sie auf diese Weise Pfade für die Komponenten festlegen können. Tatsächlich können Sie sogar Arrays über INI-Konfigurationsdateien definieren, wie wir in späteren Kapiteln sehen werden.

Zend_Application unterteilt alle Konfigurationen in einige Kategorien, die in den Konfigurationsdateien als Präfixe aufscheinen. "phpSettings" bezieht sich auf alles, was in PHP mittels ini_set() eingestellt werden kann. "includePaths" enthält Pfade, die Sie (neben jenen aus der index.php) dem include_path hinzufügen wollen. Zum Beispiel könnte ich Folgendes angeben:

includePaths.foolib = APPLICATION_ROOT "/foolib/lib"

Ich setze den Include-Pfad stattdessen direkt innerhalb der index.php für das Standard-Verzeichnis /library sowie /vendor, aber Sie haben vielleicht noch weitere Pfade, die hinzugefügt werden müssen. "bootstrap" teilt Zend_Application mit, wo unsere Bootstrap-Klasse zu finden ist und wie ihr Klassenname lautet (erinnern Sie sich daran: wir brauchen unsere Klasse ZFExt_Bootstrap weiterhin, um zu modifizieren, wie das Bootstrapping einige Komponenten einrichtet).

Schließlich haben wir noch "resource", das sich auf Zend_Application-Ressourcen bezieht.

Eine Zend_Application-Ressource ist im Prinzip eine beliebige Klasse, die Zend_Application bekannt ist und die Zend_Application während des Bootstrappings für die weitere Verwendung konfiguriert. Oben setzen wir Optionen für einen FrontController (der erste Buchstabe des Optionsnamens wird klein geschrieben), um die Zend_Controller_Front-Instanz zu konfigurieren, die Zend_Application für unsere Anwendung verwenden wird. Vorerst teilen wir Zend_Controller_Front nur mit, wo unser Standardverzeichnis zu finden ist, in welchem die Controller-Klassen liegen, und ob Ausnahmen geworfen werden sollen, wenn wir uns im "development"- oder "testing"-Modus befinden. Falls die Konvention der Konfiguration verwirrend ist: "controllerDirectory" entspricht Zend_Controller_Front::setControllerDirectory(). Wir folgen der gleichen Konvention wie bei der Verwendung eines Arrays von Konfigurationswerten, um die Schlüssel von Arrays auf Setter-Methoden umzulegen, die jeweils dem Schlüssel entsprechen.

6.5. Schritt 4: Die Voreinstellungen der Standardkomponenten bearbeiten

In unserer ursprünglichen Klasse ZFExt_Bootstrap habe ich darauf hingewiesen, dass die Standardkonfiguration einiger Komponenten für uns möglicherweise nicht passend gesetzt ist. Zum Beispiel verwendet Zend_View als Standard-Zeichen-Encoding ISO-8859-1, was auch großartig ist, solange man Multibyte-Zeichen oder solche mit Akzenten vermeidet. Sie können die standardmäßige Zend_View-Instanz mit Zend_Application aber ganz ähnlich modifizieren, wie wir es vorhin schon getan haben!

Wie wir sehen, kann unsere überarbeitete Bootstrap-Klasse eine Instanz von Zend_View mittels des Aufrufs von _initView() erzeugen. Man spricht dabei von einer Ressource-Methode, da sie eine Ressource für die Verwendung vorbereitet oder modifiziert. Hier setzen wir eine Zend_View-Instanz für die Verwendung durch den ViewRenderer-Action-Helfer auf (wir werden Action-Helfer später kennenlernen), wobei sie für die Anwendung als Standard-Zend_View-Instanz agiert, um Templates zu rendern. Alle Ressource-Methoden, die aufgerufen werden sollen, müssen dem Format "_initResourceName()" entsprechen, falls es sich um eine als "protected" deklarierte Methode handelt. Sie können genauso öffentliche Methoden in Form von "bootstrapResourceName()" definieren. Ob Sie Methoden mit "protected" oder "public" verwenden, bleibt Ihnen überlassen.

Innerhalb dieser Ressource-Methoden können wir noch etwas mehr tun, indem wir das Ressourcen-System von Zend_Application verwenden. Prinzipiell definiert Zend_Application eine Menge von Klassen, die Ressource-Plugins genannt werden und die sie alternativ zu den einfachen Ressource-Methoden verwendet, um Ressourcen aufzusetzen und vorzubereiten.

Was ist denn nun eine Ressource? Ressource ist eigentlich ein verwirrender Begriff, da er nicht die ganze Geschichte erzählt. Man kommt der Wirklichkeit sehr nahe, wenn man sich das Ganze als Konfiguration eines einzigartigen ("unique") Objekts vorstellt. Unsere Anwendung braucht gewöhnlicherweise beim Bootstrapping von jedem Objekt immer nur ein einzelnes Exemplar. Sie benötigt eine Instanz von Zend_View, eine Instanz von Zend_Controller_Front, eine Instanz des Routers etc. Eine Ressource ist also einzigartig - die einzige ihrer Art. Die Aktionen, die nötig sind, um diese einzigartigen Ressourcen zu erzeugen, zu konfigurieren und bei Zend_Application (und der Bootstrap-Klasse) zu registrieren, können in zwei verschiedenen, alternativen Formen zusammengefasst werden: als Ressource-Methoden und als Ressource-Plugins. _initView() erzeugt also eine Ressource mit Namen "View". Aber Moment, es gibt doch auch ein Ressource-Plugin (Zend_Application_Resource_View), das ebenfalls eine Ressource namens "View" erzeugen kann. Können wir zwei View-Ressourcen haben? Die Antwort lautet nein - wir können nur eine einzige haben. Wenn wir eine Ressource-Methode definieren, setzen wir Ressource-Plugins außer Kraft, die auf die gleiche Ressource angewendet werden könnten.

Als Ressource-Plugin kann jede Klasse fungieren, die Zend_Application_Resource_ResourceAbstract (oder Zend_Application_Resource_Resource) erweitert, welches die eigentliche Intialisierung, Konfiguration und Übergabe solcher Klassen als Objekte an den Front-Controller übernimmt. Eine Ressource-Methode ist wie ein Ressource-Plugin, aber sie wird in der Bootstrap-Klasse definiert und nicht in eine separate Klasse ausgelagert. Um neue Objekte im bzw. für den Bootstraping-Prozess zu erzeugen, können Sie zu diesem Zweck also entweder eigene Ressource-Plugin-Klassen oder als kürzere Alternative Ressource-Methoden hinzufügen.

Natürlich kann sich Zend_Application selbstständig um einige für Ihre Anwendung nötige Standardklassen kümmern, ohne dass Sie sich damit beschäftigen müssen. Zu den Ressource-Plugins, die bereits mit Zend_Application ausgeliefert werden, zählen:

Zend_Application_Resource_Db
Zend_Application_Resource_FrontController
Zend_Application_Resource_Router
Zend_Application_Resource_Modules
Zend_Application_Resource_Navigation
Zend_Application_Resource_Session
Zend_Application_Resource_View
Zend_Application_Resource_Layout
Zend_Application_Resource_Locale
Zend_Application_Resource_Translate
[Anmerkung] Anmerkung

Einige von diesen Komponenten werden im Referenzhandbuch zu Zend Framework 1.9.0 nicht erwähnt. Nur die unbedingt benötigten Komponenten führen auf jeden Fall zu initialisierten Ressourcen; Zend_Application_Resource_Db wird sich also nicht sofort beschweren, falls keine Konfigurationseinstellungen definiert wurden, da es nicht aufgerufen wird, solange Sie es nicht konfigurieren.

In unserer Methode _initView()haben wir ein Ersatzobjekt vom Typ Zend_View erzeugt, da Zend_Application_Resource_View nur eine Standardinstanz mit der Zeichenkodierung ISO-8859-1 und anderen Standardoptionen erzeugen würde. Wir geben in _initView() die neue Zend_View-Instanz zurück, die von Zend_Application nun als Ersatz akzeptiert wird. Zend_Application wird somit nicht versuchen, unseren Änderungen zu überschreiben, indem es die Ressource Zend_Application_Resource_View aufruft, welche eine Standard-Zend_View-Instanz erzeugt, deren Probleme wir gerade erst beseitigt haben.

Wenn wir eine neue Ressource erstellen, um eine andere zu ersetzen, müssen wir nicht die originale Ressource heranziehen, die vom standardmäßigen Ressource-Plugin in Zend_Application erzeugt wird - außer wir benötigen sie. Lassen Sie uns nun eine weiter Ressource-Methode _initFrontController() hinzufügen, die nur einige Änderungen an der bestehenden Ressource vornimmt, die von Zend_Application_Resource_Frontcontroller vorbereitet wurde.

In unserer originalen Bootstrap-Klasse wollten wir sichergehen, dass alle Ausgaben standardmäßig einen Content-Type-Header mit dem Wert "text/html; charset=UTF-8" verwenden. Hier tun wir das gleiche, indem wir mittels der Methode getResource() eine Instanz von Zend_Controller_Front beziehen, die von Zend_Application_Resource_Frontcontroller erzeugt und konfiguriert wurde. Wir sorgen nur dafür, dass der FrontController unser eigenes Response-Objekt verwendet, anstatt dass er ein Standard-Respone-Objekt erzeugt, wenn er eines braucht. Bevor wir die FrontController-Ressource, also ein Objekt des Typs Zend_Controller_Front beziehen, müssen wir erst dafür sorgen, dass Zend_Application alles Nötige tut, um es erst einmal zu erstellen.

Das machen wir, indem wir der Methode bootstrap() den Ressource-Namen (also den letzten Teil des korrespondierenden Ressourcen-Plugin-Klassennamens) übergeben. Dadurch wird Zend_Application dazu gezwungen, den Bootstrap für eben diese Ressource auszuführen. Die Methode bootstrap() ist ziemlich flexibel und akzeptiert auch ein Array mit Ressource-Namen, falls man sofort mehrere Ressourcen benötigt, bevor der Bootstrap allgemein ausgeführt wurde.

Sie können noch einen Schritt weitergehen, indem Sie einfach die Standard-Ressource-Plugins ersetzen. Ich werde das jetzt nicht behandeln, da wir später in diesem Buch eigene Ressource-Plugins für andere Objekte in unserer Anwendung hinzufügen werden.

Vergleichen Sie nun einmal die originale Klasse ZFExt_Bootstrap mit der überarbeiteten Version. Sie besteht aus weniger Code, wird durch die Konfiguration gesteuert, man kann leicht damit arbeiten und sie ist durch Ressource-Plugins erweiterbar. Die neue Version ist eine klare Verbesserung. Natürlich stellt das Verstehen der Funktionsweise dieser Komponente eine weitere Hürde auf dem Weg zum Zend-Framework-Guru dar, aber diesen Preis muss man immer bezahlen, wenn man abstraktere Klassen verwendet, die dafür besser erweiterbar und wiederverwendbar als ihre monolithischen Alternativen sind.

6.6. Schritt 5: ZFExt_Bootstrap anpassen

Wenn Sie jetzt noch einmal versuchen, auf http://helloworld.tld zuzugreifen, wird Ihnen etwas Merkwürdiges auffallen. Es wird eine relativ nichtssagende Ausnahme geworfen, die uns mitteilt: "Circular resource dependency detected". Das passiert, weil wir eine Ressource-Methode _initFrontController() definiert haben, die mit Zend_Application_Resource_Frontcontroller in Konflikt tritt, wenn wir bootstrap('FrontController') aufrufen. Wir haben die Regel gebrochen, nach der Ressourcen einzigartig sind. Durch die Vorgehensweise, die Ressource-Methode aufzurufen und zugleich (durch den Aufruf von bootstrap()) die Verwendung des Ressource-Plugins zu erzwingen, hat Zend_Application das als Versuch interpretiert, zwei ähnliche Objekte zu erzeugen, obwohl wir nur eines benötigen. Bis wir uns an den Gedanken gewöhnen, dass der Bootstrap nur mit einzigartigen Ressourcen arbeitet, wird der Fehler häufiger auftreten.

Daher muss ich die Bedeutung des Themas Ressource-Methoden vs. Ressource-Plugins so ausdrücklich betonen (und damit ich dieses kleine Schmuckstück erklären kann, wenn wir mit Zend_Application anfangen). Sie können das eine oder das andere verwenden, aber niemals beide mit demselben Ressourcen-Namen zu derselben Zeit. Unsere Methode _initView() vorhin hat funktioniert, da keine Referenz zu Zend_Application_Resource_View verwendet wurde - unsere Ressource-Methode hat sie ersetzt. Wenn wir das von Zend_Application_Resource_View erzeugte Objekt ändern wollen, indem wir die Methoden bootstrap() und getResource() aufrufen, um das Objekt zu generieren und zu holen, dann müssen wir die Ressource-Methode umbenennen, da wir auf der vom Plugin initialisierten Ressource aufbauen und eine modifizierte Version davon erzeugen (tatsächilch handelt es sich um eine andere Ressource, auch wenn der Unterschied nur in der Konfiguration liegt).

Das veranschaulicht einen wichtigen Punkt: auch wenn Zend_Application von Ressourcen spricht, kann ein einzelnes Objekt durch zwei oder mehr benannte Ressourcen repräsentiert werden. Das wirkt etwas dumm, da schlussendlich nur das eine Objekt verwendet wird, egal wieviele Ressource-Namen darauf verweisen. Die Lösung dafür ist aber wirklich einfach - um Objekte nach ihrer initialen Einrichtung durch ein Plugin oder eine Methode zu modifizieren (der Weg über die Methode ist nicht zu empfehlen: es ist knifflig, dieReihenfolge der Methoden zu umgehen), müssen wir einen anderen Ressourcen-Namen verwenden. Es muss keinen Sinn machen, es ist einfach so.

Unser Fehler war, dass wir in _initFrontController() geglaubt haben, wir könnten einen existierenden Front-Controller modifizieren, indem wir ihn von Zend_Application_Resource_FrontController beziehen. Wir haben nicht realisiert, dass wir das in einer Ressourcen-Methode mit demselben Ressourcen-Namen getan haben. In dem Moment, in dem eine Ressourcen-Einheit versucht, eine andere Ressourcen-Einheit mit ähnlichem Namen zu verwenden, herrscht Verwirrung, da wir nicht zwei Objekte haben können, die dieselbe Ressource repräsentieren. Daher wirft Zend_Application eine Ausnahme und beschwert sich über das Chaos von Abhängigkeiten, dass wir zu erzeugen versucht haben. Es handelt sich dabei um ein bewusst eingerichtetes Sicherheitsnetz.

Um diesen Konflikt zu lösen, sollten wir allen Ressourcen-Methoden, die nicht ein Ressourcen-Plugin ersetzen sollen, einen einzigartigen Namen geben. In diesem Fall habe ich beschlossen, den existierenden Ressourcen-Namen "FrontController" um den Präfix "Modified" zu ergänzen, um so anzudeuten, dass unsere Ressource-Methode nicht ein Ressource-Plugin überschreibt, sondern wir z.B. das Ergebnis dieses Ressource-Plugins verwenden, wenn wir die Methode ablaufen lassen, anstatt dass wir selbst etwas erzeugen. Wir geben das Objekt auch nicht zurück, da das Ressource-Plugin das originale Objekt sowieso für die weitere Verwendung registrieren wird.

Nun können wir http://helloworld.tld aufrufen, ohne auf Fehlermeldungen oder Ausnahmen zu treffen.

6.7. Schritt 6: Die Anwendungskonfiguration in Ressource-Methoden einbinden

Wir haben schon festgestellt, dass wir in unserer Anwendungskonfigurationsdatei application.ini die Konfiguration von Ressourcen vornehmen können, indem wir den Präfix "resources" verwenden. Diese Werte werden automatisch von den Zend_Application-Ressource-Plugins verwendet, um den Objekten, die sie erzeugen, Werte zu übergeben. Für unsere FrontController-Ressource können wir also Einträge wie diesen hinzufügen:

resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"

Der Name der Ressource wird hier in CamelCase-Schreibweise angegeben und der erste Buchstabe kleingeschrieben. Aus FrontController wird also frontController. Es wäre jetzt noch nicht dringend notwendig, aber wir können unsere Ersatz-Resource-Methode für Zend_View etwas aufräumen, indem wir sicherstellen, dass Konfigurationsoptionen aus application.ini an die neue Instanz übergeben werden. Nicht zuletzt können wir so die Einstellung für die Zeichenkodierung zurück in die Konfigurationsdatei verlegen.

Nun können wir die Einstellung für die Zeichenkodierung und andere Konfigurationsoptionen, die wir benötigen, in unsere application.ini verlegen.

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
bootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php"
bootstrap.class = "ZFExt_Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.view.encoding = "UTF-8"
resources.view.doctype = "XHTML1_STRICT"
resources.view.contentType = "text/html;charset=utf-8"
resources.modifiedFrontController.contentType = "text/html;charset=utf-8"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.throwExceptions = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.throwExceptions = 1

6.8. Schritt 7: Den Autoload-Code optimieren

In unserer vorangegangenen Bootstrap-Klasse haben wir eine eigene Autoload-Methode erstellt, welche keine Kontrolle der Datei durchführt und einfach versucht, eine include()-Operation unter der Annahme durchzuführen, dass die Klassennamen entsprechend der PEAR-Konvention auf ihre Dateinamen umgelegt werden können. Dadurch sollte der Einsatz der Methode Zend_Loader::loadClass() vermieden werden, die in Zend-Framework-Anwendungen üblicherweise Verwendung findet und viele großteils unnötige Fehlerüberprüfungen durchführt. Zend_Application ist so konzipiert, dass es das Autoloading mittels einer neuen Komponente, Zend_Loader_Autoloader, implementiert. Unglücklicherweise verwendet Zend_Loader_Autoloader (das die Autoloading-Fähigkeiten für Klassen erheblich erweitert) eben jene Methode Zend_Loader::loadClass(), die wir vermeiden möchten.

Wir können unsere leichtgewichtigere Methode zwar immer noch implementieren, jedoch nicht mehr innerhalb der Bootstrap-Klasse. Stattdessen müssen wir die Standard-Methode von Zend_Loader_Autoloader für das Laden von Dateien umstellen, bevor Zend_Application initialisiert wird. Das kann nur in index.php erreicht werden.

Da wir den Bootstrap vor der Initialisierung von Zend_Application nicht referenzieren können, übergeben wir setDefaultAutoloader() stattdessen eine anonyme Funktion, die wir direkt an diesem Ort erstellen.

6.9. Zend_Loader_Autoload erlauben, Klassen mit Namespace zu laden

In PHP tragen alle Klassen von Bibliotheken, die der PEAR-Konvention folgen, einen gemeinsamen Präfix, der allgemein als "namespace" bezeichnet wird (das ist nicht das echte Namespacing, das mit PHP 5.3 eingeführt wurde). Unser Autoloader ist so konfiguriert, dass er Klassennamen erkennt, die mit den Namespaces "Zend_" und "ZendX_" beginnen. Um andere Klassen mit Namespace zu laden, müssen wir dem Autoloader erst mitteilen, dass sie existieren. Bevor wir das tun, können wir hinterfragen, warum das überhaupt notwendig ist.

Das Problem mit der originalen Autoloading-Methode war klar. Sie hat alles geladen. Auf den ersten Blick erscheint das großartig: die Funktionsweise ist simpel, es ist einfach zu verstehen und man benötigt keine Konfiguration. Warum also ist das keine gute Sache? Wenn alles geladen wird, entsteht das Problem, dass man keine Kontrolle ausüben kann. Manchmal wollen Sie sicherstellen, dass nur bestimmte Bibliotheken geladen werden können. Ein zusätzlicher Vorteil ist, dass die Einschränkung zur schnelleren Suche der Datei führt.

Die neue Namespacing-Funktionalität ist nicht perfekt, aber das liegt daran, dass einige Bibliotheken keinen gemeinsamen Top-Level-Namespace-Präfix haben. PEAR-Komponenten beginnen zum Beispiel nicht einheitlich mit PEAR_. Daher können Sie auf HTTP_Client, Crypt_RSA, etc. treffen, die keinen gemeinsamen Präfix teilen. Ein ähnliches Problem existiert mit "Root"-Klassen. Zum Beispiel verwendet HTMLPurifier das Namespace-Präfix HTMLPurifer_ für alle Klassen außer für eine Root-Klase in HTMLPurifier.php. Unter diesen Umständen müssen Sie eventuell zum ursprünglichen Verhalten zurückkehren, in dem Namespaces nicht berücksichtigt werden und die Verwendung nicht eingeschränkt wird. Das bewerkstelligen Sie mit:

Unter Umständen verwendet eine Bibliothek vielleicht auch einen eigenen Autoloader, den man als Alternative zu der Standardimplementierung in Zend_Loader_Autoloader registrieren kann.

Beachten Sie, dass aufgrund des fehlenden Namespaces für HTMLPurifier somit ein neuer globaler Autoloader etabliert wird, der den Standard-Autoloader ähnlich wie die Fallback-Option global und ohne Restriktionen arbeiten lässt. Sie können den Autoloader auch auf einen Namespace beschränken, damit er nur verwendet wird, wenn ein bestimmter Klassen-Namespace erkannt wird.

Wie auch immer: gehen wir vorerst davon aus, dass dieser Fallback und andere Autoloader nicht notwendig sind. Das Ergebnis des neuen Autoloaders ist, dass wir alle Klassen-Namespaces registrieren müssen, die wir verwenden wollen. Wir könnten das zwar im Code von index.php erledigen, doch wenn wir die Wartung in application.ini vornehmen, können wir leichter den Überblick behalten und nach Bedarf Namespaces hinzufügen. Hier haben wir ein Beispiel, in dem wir eine Bibliothek (eigentlich unsere zukünftige Anwendungsbibliothek) verwenden wollen, deren Klassennamen alle einen Präfix "ZFExt_" besitzen. Wie Sie wahrscheinlich erkennen, können Sie weitere Namespaces hinzufügen, indem Sie eckige Klammern [] verwenden. Dadurch werden sie zu einem Array hinzugefügt, sobald die INI-Datei von Zend_Config geparst wird (welches das gesamte Parsing und Laden der Konfiguration innerhalb des Frameworks bewerkstelligt).

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
bootstrap.path = APPLICATION_ROOT "/library/ZFExt/Bootstrap.php"
bootstrap.class = "ZFExt_Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.view.encoding = "UTF-8"
resources.view.doctype = "XHTML1_STRICT"
resources.view.contentType = "text/html;charset=utf-8"
resources.modifiedFrontController.contentType = "text/html;charset=utf-8"
autoloaderNamespaces[] = "ZFExt_"

[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.throwExceptions = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.throwExceptions = 1

6.10. Fazit

Das war eine schnelle Einführung (nun ja, Sie könnten ja noch zusätzlich Schnelllesen lernen) in Zend_Application. Als jemand, der bisher sein eigenes Ding durchgezogen hat, passe ich den Code gerne an diesen Standard an. So ist gesichert, dass andere Entwickler meinem Bootstrapping-Prozess folgen können, ohne einen komplett neuen Ansatz (von den wahrscheinlich hunderten da draußen existierenden) zu lernen. Das ist der Vorteil dabei, dass endlich ein Standardansatz existiert, wie man das Bootstrapping von Anwendungen abwickelt.

Wir werden im Verlauf des Buches mehr von Zend_Application zu sehen bekommen, da wir in unseren Anwendungen das Verhalten vieler Objekte und Anwendungsteile modifizieren werden müssen und der Bootstrap der geeignete Ort ist, um die Initialisierung dafür vorzunehmen. Als abschließende Bemerkung: Zend_Tool, das in diesem Buch noch nicht abgedeckt wird, verwendet Zend_Application, um Anwendungen zu verwalten.