Arbeitsweise
Um eine MVC-Anwendung zu konfigurieren und bereit zur Auslieferung
zu bekommen wird ein immer größerer Anteil an Code benötigt, sobald mehr Features verfügbar
sind: Setzen der Datenbank, Konfiguration der View und View Helfer, Konfiguration der
Layouts, Registrierung der Plugins, Registrierung der Aktion Helfer, und andere.
Zusätzlich will man oft den gleichen Code wiederverwenden, um Tests zu bootstrappen, einen
Cronjob, oder ein Service-Skript zu haben. Wärend es sehr einfach ist, sein Bootstrap-Skript
einzubauen, gibt es oft Initialisierungen, die abhängig von der Umgebung sind - man
benötigt kein MVC für einen Cronjob, oder nur den DB-Layer für ein
Service-Skript.
Zend_Application zielt darauf ab, das alles einfacher zu machen und
bietet Wiederverwendbarkeit durch die Kapselung des Bootstrapping in
OOP-Paradigmen.
Zend_Application ist in drei Bereiche geteilt:
Zend_Application: Lädt die PHP-Umgebung,
inklusive Inklude-Pfade und Autoloading, und instanziiert die benötigte
Bootstrap-Klasse.
Zend_Application_Bootstrap: Bietet Interfaces für
Bootstrap-Klassen. Zend_Application_Bootstrap_Bootstrap bietet
übliche Funktionalitäten für die meisten Notwendigkeiten des Bootstrappings,
inklusive Algorithmen um Abhängigkeiten zu prüfen und die Möglichkeit
Bootstrap-Ressourcen bei Bedarf zu laden.
Zend_Application_Resource bietet ein Interface für Standard
Bootstrap-Ressourcen, die bei Bedarf von einer Bootstrap-Instanz geladen werden
können, sowie verschiedene standardmäßige Ressource-Implementationen.
Entwickler erstellen eine Bootstrap-Klasse für ihre Anwendung und erweitern
Zend_Application_Bootstrap_Bootstrap oder implementieren (mindestens)
Zend_Application_Bootstrap_BootstrapAbstract. Der Einstiegspunkt
(z.B. public/index.php) lädt Zend_Application
und initialisiert sie, indem folgendes übergeben wird:
Die aktuelle UmgebungOptionen für das Bootstrapping
Die Bootstrap-Optionen enthalten den Pfad zur Datei, welche die Bootstrap-Klasse enthält und
optional:
Jeden include_path der extra zu setzen istJeden Autoloader Namespace, der zusätzlich zu registrieren istJede php.ini Einstellung, die zu initialisieren ist
Den Klassennamen für die Bootstrap-Klasse (wenn diese nicht "Bootstrap" ist)
Ressourcen-Präfix zu Pfadpaaren, die zu verwenden sindJede Ressource, die zu verwenden ist (durch Name der Klasse oder Kurzname)Zusätzliche Pfade zu einer Konfigurationsdatei, die zu laden istZusätzliche Konfigurationsoptionen
Optionen können ein Array, ein Zend_Config-Objekt, oder der Pfad zu
einer Konfigurationsdatei sein.
Bootstrapping
Der zweite Aufgabenbereich vonZend_Application ist die Ausführung
des Bootstraps der Anwendung. Bootstraps müssen mindestens
Zend_Application_Bootstrap_Bootstrapper implementieren, welches
die folgende API definiert:
Diese API erlaubt es dem Bootstrap die Umgebung und die
Konfiguration vom Objekt der Anwendung zu akzeptieren, die Ressourcen die für das
Bootstrapping verantwortlich sind zu melden, und dann das Bootstrappen ausführen und
die Anwendung zu starten.
Man kann dieses Interface selbst implementieren,
Zend_Application_Bootstrap_BootstrapAbstract erweitern, oder
Zend_Application_Bootstrap_Bootstrap verwenden.
Neben dieser Funktionalität gibt es eine Anzahl anderer Gebiete von Interesse mit denen
man vertraut sein sollte.
Methoden für Ressourcen
Die Implementation von
Zend_Application_Bootstrap_BootstrapAbstract bietet eine
einfache Konvention für die Definition von Methoden für Ressourcen-Klassen. Jede
geschützte Methode (protected), deren Name mit _init beginnt,
wird als Ressourcemethode angenommen.
Um eine einzelne Ressource-Methode zu bootstrappen, muß die
Methode bootstrap() verwendet und der Name der Ressource
übergeben werden. Der Name ist der Name der Methode ohne das
Präfix _init.
Um mehrere Ressourcen-Methoden zu bootstrappen, muß ein Array von Namen übergeben
werden. Um alle Ressourcen-Methoden zu bootstrappen, darf nichts übergeben werden.
Nehmen wir die folgende Bootstrap-Klasse an:
Um nur die Methode _initFoo() zu bootstrappen, machen wir
das folgende:
bootstrap('foo');
]]>
Um die Methoden _initFoo() und
_initBar() zu bootstrappen, machen wir das folgende:
bootstrap(array('foo', 'bar'));
]]>
Um alle Ressourcen-Methoden zu bootstrappen, rufen wir
bootstrap() ohne Argumente auf:
bootstrap();
]]>Bootstraps, die Ressourcen-Plugins benutzen
Um Bootstraps wiederverwendbarer zu machen, wurde die Fähigkeit zur Verfügung
gestellt, Ressourcen in Ressource-Plugin-Klassen zu geben. Das erlaubt es, Ressourcen
einfach über die Konfiguration zu mischen und zu sehen ob sie passen. Wir behandeln
später wie
Ressourcen erstellt werden; in diesem Abschnitt zeigen wir nur, wie sie
angepasst werden können.
Wenn das Bootstrap dazu in der Lage sein soll, Ressource-Plugins zu verwenden, muß
man ein zusätzliches Interface implementieren,
Zend_Application_Bootstrap_ResourceBootstrapper. Dieses
Interface definiert eine API für das Erkennen, Registrieren und
Laden von Ressource-Plugins:
Ressource-Plugins bieten grundsätzlich die Fähigkeit, Ressource-Initialisierer zu
erstellen, die zwischen Anwendungen wiederverwendet werden können. Das erlaubt es,
die aktuelle Bootstrap relativ klein zu halten, und neue Ressourcen einzuführen
ohne sie in der Bootstrap selbst anzugreifen.
Zend_Application_Bootstrap_BootstrapAbstract (und bei
Erweiterung Zend_Application_Bootstrap_Bootstrap)
implementiert dieses Interface genauso, und erlaubt es Ressource-Plugins zu
verwenden.
Um Ressource-Plugins zu verwenden, müssen diese in den Optionen spezifiziert
werden, die dem Anwendungsobjekt und/oder der Bootstrap übergeben werden. Diese
Optionen können von einer Konfigurationsdatei kommen, oder manuell übergeben
werden. Optionen sind Paare von Schlüssel zu Optionen, wobei der Schlüssel
den Namen der Ressource repräsentiert. Der Ressourcename ist das Segment, das
dem Klassenpräfix folgt. Die Ressourcen, die zum Beispiel mit Zend Framework
ausgeliefert werden, haben das Klassenpräfix
"Zend_Application_Resource_"; alles was folgt würde der
Name der Ressource sein. Als Beispiel:
array(
'FrontController' => array(
'controllerDirectory' => APPLICATION_PATH . '/controllers',
),
),
));
]]>
Das bedeutet, dass die Ressource "FrontController" verwendet werden sollte, mit den
spezifizierten Optionen.
Wenn man beginnt, eigene Ressource-Plugins zu schreiben, oder Ressource-Plugins von
dritten anzupassen, muß man der eigenen Bootstrap mitteilen, wo nach ihnen zu suchen
ist. Intern verwendet Bootstrap Zend_Loader_PluginLoader,
so dass man nur die üblichen Klassenpräfix und Pfadpaare angeben muß.
Als Beispiel nehmen wir an, dass wir eigene Ressource-Plugins in
APPLICATION_PATH/resources/ haben, und dass sie das gemeinsame
Klassenpräfix My_Resource teilen. Man würde diese
Information dem Anwendungsobjekt wie folgt übergeben:
array(
'My_Resource' => APPLICATION_PATH . '/resources/',
),
'resources' => array(
'FrontController' => array(
'controllerDirectory' => APPLICATION_PATH . '/controllers',
),
),
));
]]>
Man ist jetzt in der Lage, Ressourcen aus diesem Verzeichnis zu verwenden.
Wie Ressource-Methoden kann die Methode bootstrap()
verwendet werden, um Ressource-Plugins zu verwenden. Wie bei Ressource-Methoden
kann man entweder einzelne Ressource-Plugins spezifizieren, mehrere Plugins
(über ein Array), oder alle Plugins. Zusätzlich kann man mischen und entsprechende
Ressource-Methoden genauso ausführen.
bootstrap('FrontController');
// Mehrere ausführen:
$bootstrap->bootstrap(array('FrontController', 'Foo'));
// Alle Ressource Methoden und Plugins ausführen:
$bootstrap->bootstrap();
]]>Ressource Registry
Viele, wenn nicht sogar alle, der eigenen Ressource-Methoden oder -Plugins
initialisieren Objekte, und in vielen Fällen werden diese Objekte an anderen
Orten der Anwendung benötigt. Wie kann man auf sie zugreifen?
Zend_Application_Bootstrap_BootstrapAbstract
bietet eine lokale Registry für diese Objekte. Um eigene Objekte darin zu
speichern, müssen diese einfach von den eigenen Ressourcen zurückgegeben werden.
Für maximale Flexibilität wird diese Registry intern als "Container" bezeichnet;
die einzige Voraussetzung ist, dass es ein Objekt ist. Ressourcen werden dann als
Eigenschaften registriert, die nach dem Namen der Ressource benannt sind.
Standardmäßig wird eine Instanz von Zend_Registry verwendet,
man kann aber jedes andere Objekt spezifizieren, wenn man das will. Die Methoden
setContainer() und getContainer()
können verwendet werden, um den Container selber zu manipulieren.
getResource($resource) kann verwendet werden, um eine
angegebene Ressource vom Container zu holen, und
hasResource($resource) um zu prüfen, ob die Ressource
aktuell schon registriert wurde.
Als Beispiel nehmen wir eine grundlegenden View Ressource an:
Man kann sie prüfen und/oder sie wie folgt holen:
hasResource('view')) {
$view = $bootstrap->getResource('view');
}
// Über den Container:
$container = $bootstrap->getContainer();
if (isset($container->view)) {
$view = $container->view;
}
]]>
Es ist zu beachten, dass die Registry und auch der Container nicht global sind. Das
bedeutet, dass man auf die Bootstrap zugreifen muß, um Ressourcen zu holen.
Zend_Application_Bootstrap_Bootstrap bietet einigen
Komfort hierfür: während der Ausführung von
run(), registriert sie sich als FrontController-Parameter
"bootstrap", was es erlaubt, sie von Routern, Dispatchern, Plugins und
Action-Controllern zu holen.
Wenn man zum Beispiel auf die View Ressource von oben im eigenen Action
Controller zugreifen will, kann man das wie folgt tun:
getInvokeArg('bootstrap');
$view = $bootstrap->getResource('view');
// ...
}
}
]]>Erkennen von Abhängigkeiten
Zusätzlich zur Ausführung von Ressource-Methoden und -Plugins, ist es notwendig
sicherzustellen, dass diese einmal und wirklich nur einmal ausgeführt werden; sie
sollen eine Anwendung bootstrappen, und die mehrfache Ausführung von ihnen kann
zu einem Overhead von Ressourcen führen.
Zur gleichen Zeit können Ressourcen von anderen ausgeführten Ressourcen abhängen.
Um diese zwei Fälle zu lösen bietet
Zend_Application_Bootstrap_BootstrapAbstract einen einfachen
und effektiven Mechanismus für die Erkennung von Abhängigkeiten.
Wie vorher erwähnt werden alle Ressourcen -- ob Methoden oder Plugins -- durch den
Aufruf von bootstrap($resource) gebootstrappt, wobei
$resource der Name einer Ressource ist, ein Array von Ressourcen
oder leer gelassen wird, was zeigt, dass alle Ressourcen ausgeführt werden sollen.
Wenn eine Ressource von anderen Ressourcen abhängig ist, sollten Sie in Ihrem Code
bootstrap() aufrufen um sicherzustellen, dass die Ressource
ausgeführt wurde. Weitere Aufrufe von ihr werden dann ignoriert.
In einer Ressource-Methode würde so ein Aufruf wie folgt aussehen:
bootstrap('FrontController');
// Den FrontController von der Bootstrap Registry erhalten
$front = $this->getResource('FrontController');
$request = new Zend_Controller_Request_Http();
$request->setBaseUrl('/foo');
$front->setRequest($request);
// Sicherstellen, dass die Anfrage in der Bootstrap Registry
// gespeichert ist
return $request;
}
}
]]>Ressource Plugins
Wie
vorher erwähnt ist die Verwendung von Ressource Plugins ein guter Weg, um
wiederverwendbare Bootstrap-Ressourcen zu erstellen und um so viel eigenen Code wie
möglich in diskrete Klassen auszulagern. Wärend Zend Framework mit einer Anzahl
von standardmäßigen Ressource-Plugins geliefert wird, besteht das Ziel darin, dass der
Entwickler eigene schreiben sollte, um seine eigenen Notwendigkeiten der
Initialisierung zu kapseln.
Ressource-Plugins müssen nur Zend_Application_Resource_Resource
implementieren, oder einfach
Zend_Application_Resource_ResourceAbstract erweitern. Das
grundsätzliche Interface ist folgendes:
Das Interface definiert einfach, was ein Ressouce-Plugin an Optionen im Konstruktor
akzeptieren sollte, die Mechanismen für das Setzen und Zurückgeben von Optionen,
die Mechanismen für das Setzen und Zurückgeben des Bootstrap-Objekts und eine
Initialisierungs-Methode.
Als Beispiel nehmen wir an, dass wir eine normale View Initialisierung haben, die in der
eigenen Anwendung verwendet wird. Man hat normale Doctype, CSS und
Javascript, und will in der Lage sein, diese in einem Standard-Dokumententitel über die
Konfiguration zu übergeben. So ein Ressource-Plugin könnte wie folgt aussehen:
getView();
}
public function getView()
{
if (null === $this->_view) {
$options = $this->getOptions();
$title = '';
if (array_key_exists('title', $options)) {
$title = $options['title'];
unset($options['title']);
}
$view = new Zend_View($options);
$view->doctype('XHTML1_STRICT');
$view->headTitle($title);
$view->headLink()->appendStylesheet('/css/site.css');
$view->headScript()->appendfile('/js/analytics.js');
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
$this->_view = $view;
}
return $this->_view;
}
}
]]>
Solange man den Präfix-Pfad für dieses Ressource-Plugin registriert, kann es in der
eigenen Anwendung verwendet werden. Weil der Plugin-Loader verwendet wird, ist es
besser, das gelieferte "View"-Ressource-Plugin zu überschreiben und sicherzustellen,
dass man stattdessen das eigene verwendet.