Das Zend_Controller
System wurde im Sinne der Erweiterungsmöglichkeiten
entwickelt, entweder durch Erstellen von Subklassen, welche die bestehenden Klassen
erweitern, oder durch Erstellen neuer Klassen, welche die Interfaces
Zend_Controller_Router_Interface
und
Zend_Controller_Dispatcher_Interface
implementieren oder die Klassen
classes Zend_Controller_Request_Abstract
,
Zend_Controller_Response_Abstract
, und
Zend_Controller_Action
erweitern.
Mögliche Gründe für weitere Klassen könnten sein:
Das vorhandene System zum URI Routing ist nicht verwendbar, wenn es in eine bestehende Website integriert werden soll, die eigene Konventionen für das Routing verwendet, die nicht mit dem vom Zend Framework bereit gestellten Routing Mechanismus übereinstimmen.
Du benötigst das Routing für etwas völlig anderes. Die
Zend_Controller_Router
Klasse arbeitet nur mit URIs. Es ist
möglich und wahrscheinlich, dass Du das MVC Entwurfsmuster für die
Entwicklung eines anderen Programmtyps verwenden möchtest, z.B. für eine
Konsolenanwendung. Im Fall einer Konsolenanwendung könnte ein
maßgeschneiderter Router die Kommandozeilenparameter für das Routing
verwenden.
Der vom Zend_Controller_Dispatcher
bereitgestellte Mechanismus
ist nicht verwendbar. Die vorgegebene Konfiguration setzt die Konvention
voraus, dass Controller Klassen und Aktionen die Methoden dieser Klassen
sind. Allerdings gibt es hierfür auch viele andere Strategien. Ein Beispiel
wäre, dass Controller Verzeichnisse und Aktionen Dateien in diesen
Verzeichnissen sind.
Du möchtest zusätzliche Möglichkeiten bereitstellen, die von allen
Controllern geerbt werden sollen. Zum Beispiel wird Zend_View
standardmäßig nicht von Zend_Controller_Action
integriert.
Stattdessen könntest Du deinen eigenen Controller hierfür erweitern und
durch die Verwendung müssen die bereitgestellten
Zend_Controller_Router
oder
Zend_Controller_Dispatcher
nicht geändert werden.
Du möchtest abgefangene Ausnahmen deiner Applikation loggen und auf eine
generische Fehlerseite umleiten. Beim Erweitern von
Zend_Controller_Response_Http
könntest du
__toString()
ändern, um auf registrierte Ausnahmen zu prüfen,
diese zu loggen und dann auf eine Fehlerseite umzuleiten.
Bitte sei vorsichtig beim Überschreiben wesentlicher Teile des System, besonders beim
Dispatcher! Einer der Vorteile des Zend_Controller
ist, dass er einfache
Konventionen für den Aufbau von Applikationen einführt. Wenn zuviel dieses
vorgegebenen Verhaltens geändert wird, gehen einige dieser Vorteile verloren. Allerdings
gibt es viele verschiedene Anforderungen und eine Lösung kann nicht alle erfüllen.
Deshalb wird die Freiheit geboten, wenn sie benötigt wird.
Beim Erweitern von Zend_Controller Klassen befolge bitte diese Konventionen für das Bezeichnen und Ablegen von Dateien. Dadurch wird sichergestellt, dass andere Programmierer, die mit dem Zend Framework vertraut sind, dein Projekt leichter verstehen können.
Klassen, die im Zend Framework enthalten sind, befolgen die Konvention, dass jeder Klasse ein "Zend_" vorangestellt wird. Dies ist der Präfix. Wir empfehlen, dass Du alle deine Klassen in ähnlicher Weise bezeichnest. Wenn dein Firmennamen z.B. Widget, Inc. ist, könnte das Präfix "Widget_" heißen.
Die Zend_Controller
Klassen sind im Bibliotheksverzeichnis wie folgt
abgelegt::
/library /Zend /Controller Action.php Dispatcher.php Router.php
Wenn du die Zend_Controller
erweiterst, wird empfohlen, dass die neuen
Klassen in der gleichen Struktur unterhalb deines Präfix abgelegt werde. Dies macht
es einfacher, sie zu finden, wenn sich jemand in dem Lernprozess befindet, bei dem
er sich einen Überblick über dein Projekt beschafft.
Zum Beispiel könnte ein Projekt von Widget, Inc., das nur einen kundenspezifischen Router implementiert, so aussehen:
/library /Zend /Widget /Controller Router.php README.txt
Beachte an diesem Beispiel, dass das Widget/Controller/
Verzeichnis
das Zend/Controller/
Verzeichnis widerspiegelt, wo immer es möglich
ist. In diesem Fall wird die Klasse Widget_Controller_Router
bereitgestellt, die entweder eine Subklasse für
Zend_Controller_Router
oder ein Ersatz ist, bei dem
Zend_Controller_Router_Interface
implementiert wird.
Beachte außerdem, dass in dem obigen Beispiel eine README.txt
Datei im
Widget/Controller/
Verzeichnis abgelegt worden ist. Zend möchte dich
ermuntern, deine Projekte durch Bereitstellung von separaten Tests und
Dokumentation für Kunden zu dokumentieren. Wir empfehlen dir, eine einfache
README.txt
Datei genau in diesem Verzeichnis zu platzieren, um kurz
deine Änderungen und deren Funktionsweise zu erklären.
Zend_Controller_Front implementiert einen Front Controller. Zusätzlich ist sie eine Singleton Klasse, was bedeutet, dass nur einen Instanz von ihr zu jeder Zeit verfügbar ist.
Um von ihr abzuleiten, muss man minimal nur die
getInstance()
Methode überschreiben:
class My_Controller_Front extends Zend_Controller_Front { public static function getInstance() { if (null === self::$_instance) { self::$_instance = new self(); } return self::$_instance; } }
Das Überschrieben der getInstance() Methode stellt sicher, dass nachfolgende Aufrufe von
Zend_Controller_Front::getInstance()
einen Instanz der neuen Unterklasse
statt der Zend_Controller_Front Instant zurückgeben -- dies ist besonders nützlich für
einige der alternativen Router und View Helfer.
Zusätzlich zu getInstance() können viele andere Methoden überschrieben werden:
/** * Resets all object properties of the singleton instance * * Primarily used for testing; could be used to chain front controllers. * * @return void */ public function resetInstance(); /** * Convenience feature, calls setControllerDirectory()->setRouter()->dispatch() * * In PHP 5.1.x, a call to a static method never populates $this -- so run() * may actually be called after setting up your front controller. * * @param string|array $controllerDirectory Path to Zend_Controller_Action * controller classes or array of such paths * @return void * @throws Zend_Controller_Exception if called from an object instance */ public static function run($controllerDirectory); /** * Add a controller directory to the controller directory stack * * If $args is presented and is a string, uses it for the array key mapping * to the directory specified. * * @param string $directory * @param mixed $args Optional argument; if string value, used as array key map * @return Zend_Controller_Front */ public function addControllerDirectory($directory, $args = null); /** * Set controller directory * * Stores controller directory to pass to dispatcher. May be an array of * directories or a string containing a single directory. * * @param string|array $directory Path to Zend_Controller_Action controller * classes or array of such paths * @return Zend_Controller_Front */ public function setControllerDirectory($directory); /** * Retrieve controller directory * * Retrieves stored controller directory * * @return string|array */ public function getControllerDirectory(); /** * Set the default controller (unformatted string) * * @param string $controller * @return Zend_Controller_Front */ public function setDefaultController($controller); /** * Retrieve the default controller (unformatted string) * * @return string */ public function getDefaultController(); /** * Set the default action (unformatted string) * * @param string $action * @return Zend_Controller_Front */ public function setDefaultAction($action); /** * Retrieve the default action (unformatted string) * * @return string */ public function getDefaultAction(); /** * Set request class/object * * Set the request object. The request holds the request environment. * * If a class name is provided, it will instantiate it * * @param string|Zend_Controller_Request_Abstract $request * @throws Zend_Controller_Exception if invalid request class * @return Zend_Controller_Front */ public function setRequest($request); /** * Return the request object. * * @return null|Zend_Controller_Request_Abstract */ public function getRequest(); /** * Set router class/object * * Set the router object. The router is responsible for mapping * the request to a controller and action. * * If a class name is provided, instantiates router with any parameters * registered via {@link setParam()} or {@link setParams()}. * * @param string|Zend_Controller_Router_Interface $router * @throws Zend_Controller_Exception if invalid router class * @return Zend_Controller_Front */ public function setRouter($router); /** * Return the router object. * * Instantiates a Zend_Controller_Router object if no router currently set. * * @return null|Zend_Controller_Router_Interface */ public function getRouter(); /** * Set the base URL used for requests * * Use to set the base URL segment of the REQUEST_URI to use when * determining PATH_INFO, etc. Examples: * - /admin * - /myapp * - /subdir/index.php * * Note that the URL should not include the full URI. Do not use: * - http://example.com/admin * - http://example.com/myapp * - http://example.com/subdir/index.php * * If a null value is passed, this can be used as well for autodiscovery (default). * * @param string $base * @return Zend_Controller_Front * @throws Zend_Controller_Exception for non-string $base */ public function setBaseUrl($base = null); /** * Retrieve the currently set base URL * * @return string */ public function getBaseUrl(); /** * Set the dispatcher object. The dispatcher is responsible for * taking a Zend_Controller_Request_Abstract object, instantiating the controller, and * calling the action method of the controller. * * @param Zend_Controller_Dispatcher_Interface $dispatcher * @return Zend_Controller_Front */ public function setDispatcher(Zend_Controller_Dispatcher_Interface $dispatcher); /** * Return the dispatcher object. * * @return Zend_Controller_DispatcherInteface */ public function getDispatcher(); /** * Set response class/object * * Set the response object. The response is a container for action * responses and headers. Usage is optional. * * If a class name is provided, instantiates a response object. * * @param string|Zend_Controller_Response_Abstract $response * @throws Zend_Controller_Exception if invalid response class * @return Zend_Controller_Front */ public function setResponse($response); /** * Return the response object. * * @return null|Zend_Controller_Response_Abstract */ public function getResponse(); /** * Add or modify a parameter to use when instantiating an action controller * * @param string $name * @param mixed $value * @return Zend_Controller_Front */ public function setParam($name, $value); /** * Set parameters to pass to action controller constructors * * @param array $params * @return Zend_Controller_Front */ public function setParams(array $params); /** * Retrieve a single parameter from the controller parameter stack * * @param string $name * @return mixed */ public function getParam($name); /** * Retrieve action controller instantiation parameters * * @return array */ public function getParams(); /** * Clear the controller parameter stack * * By default, clears all parameters. If a parameter name is given, clears * only that parameter; if an array of parameter names is provided, clears * each. * * @param null|string|array single key or array of keys for params to clear * @return Zend_Controller_Front */ public function clearParams($name = null); /** * Register a plugin. * * @param Zend_Controller_Plugin_Abstract $plugin * @return Zend_Controller_Front */ public function registerPlugin(Zend_Controller_Plugin_Abstract $plugin); /** * Unregister a plugin. * * @param Zend_Controller_Plugin_Abstract $plugin * @return Zend_Controller_Front */ public function unregisterPlugin(Zend_Controller_Plugin_Abstract $plugin); /** * Set whether exceptions encounted in the dispatch loop should be thrown * or caught and trapped in the response object * * Default behaviour is to trap them in the response object; call this * method to have them thrown. * * @param boolean $flag Defaults to true * @return boolean Returns current setting */ public function throwExceptions($flag = null); /** * Set whether {@link dispatch()} should return the response without first * rendering output. By default, output is rendered and dispatch() returns * nothing. * * @param boolean $flag * @return boolean Returns current setting */ public function returnResponse($flag = null); /** * Dispatch an HTTP request to a controller/action. * * @param Zend_Controller_Request_Abstract|null $request * @param Zend_Controller_Response_Abstract|null $response * @return void|Zend_Controller_Response_Abstract Returns response object if returnResponse() is true */ public function dispatch(Zend_Controller_Request_Abstract $request = null, Zend_Controller_Response_Abstract $response = null);
Der Zweck des Front Controller ist es, die Anfrageumgebung aufzusetzen, die eingehende Anfrage zu verarbeiten und dann alle erforderlichen Aktionen abzuarbeiten. Schlußendlich aggregiert es alle Antworten und Rückgaben.
Die Hauptgründe für eine Erweiterung des Front Controllers könnten das Verändern der Logik für eine der Zugriffsmethoden (zum Beispiel, um einen anderen Standardrouter oder Dispatcher zu laden oder um eine andere Logik anzugeben, wie Controller Verzeichnisse gehandhabt werden) oder das Ändern des Routings oder der Verarbeitung sein.
Die abstrakte Zend_Controller_Request_Abstract
Klasse definiert eine
Hand voll Methoden:
/** * @return string */ public function getControllerName(); /** * @param string $value * @return self */ public function setControllerName($value); /** * @return string */ public function getActionName(); /** * @param string $value * @return self */ public function setActionName($value); /** * @return string */ public function getControllerKey(); /** * @param string $key * @return self */ public function setControllerKey($key); /** * @return string */ public function getActionKey(); /** * @param string $key * @return self */ public function setActionKey($key); /** * @param string $key * @return mixed */ public function getParam($key); /** * @param string $key * @param mixed $value * @return self */ public function setParam($key, $value); /** * @return array */ public function getParams(); /** * @param array $array * @return self */ public function setParams(array $array); /** * @param boolean $flag * @return self */ public function setDispatched($flag = true); /** * @return boolean */ public function isDispatched(); }
Das Request Objekt ist ein Container für die Anfrageumgebung. Die Controller Kette braucht wirklich nur zu wissen, wie Controller, Aktion, optionale Parameter und der Verarbeitungsstatus zu setzen und abzufragen ist. Standardmäßig wird ein Request seine eigenen Parameter durchsuchen und die Controller und Aktionsschlüssel verwenden, um den Controller und die Aktion zu ermitteln.
Das Interface Zend_Controller_Router_Interface
stellt die Definition
für eine einzige Methode bereit:
<?php /** * @param Zend_Controller_Request_Abstract $request * @throws Zend_Controller_Router_Exception * @return Zend_Controller_Request_Abstract */ public function route(Zend_Controller_Request_Abstract $request); ?>
Das Routing findet nur einmal statt, wenn die Anfrage das erste Mal vom System erhalten wird. Der Zweck des Routers ist es, Controller, Aktion und optionale Parameter auf Basis der Anfrageumgebung zu ermitteln und im Request zu setzen. Das Request Objekt wird dann an den Dispatcher übergeben. Wenn es nicht möglich ist, eine Route auf einen Dispatch Token abzubilden, soll der Router nichts mit dem Request Objekt machen.
Zend_Controller_Front
ruft zuerst den Router auf, um die erste zu
verarbeitende Aktion für den Request zu ermitteln. Danach startet es die Dispatch
Schleife.
In der Schleife setzt er zuerst den Verarbeitungsstatus und verarbeitet dann den Request (instanziert den Controller, ruft die Aktion auf). Wenn die Aktionsmethode (oder ein pre/postDispatch Plugin) den Verarbeitungsstatus des Request Objektes zurück setzt, wird der Front Controller einen weiteren Durchlauf für die Dispatch Schleife mit der gerade im Request Objekt gesetzten Aktion durchführen. Dies erlaubt esm Aktionen sequentiell abzuarbeiten, bis alle Arbeiten erledigt sind.
Das Interface Zend_Controller_Dispatcher_Interface
stellt Definitionen
für zwei Methoden bereit:
<?php /** * @param Zend_Controller_Request_Abstract $request * @return boolean */ public function isDispatchable(Zend_Controller_Request_Abstract $request); ?>
isDispatchable()
prüft, ob ein Request ausführbar ist. Falls ja, wird
TRUE
zurückgegeben, andernfalls wird FALSE
zurückgegeben. Die
Definition, was ausführbar ist, bleibt der Klasse vorbehalten, die das Interface
implementiert. Im Falle des vorgegebenen Implementation vom
Zend_Controller_Dispatcher
bedeutet dies, dass die Controller Datei
existiert, die Klasse in der Datei vorhanden ist und die Aktionsmethode innerhalb
dieser Klasse vorhanden ist.
<?php /** * @param Zend_Controller_Request_Abstract $route * @return Zend_Controller_Request_Abstract */ public function dispatch(Zend_Controller_Request_Abstract $request); ?>
In dispatch()
wird die Arbeit erledigt. Diese Methode muß die Aktion des
Controllers ausführen. Sie muss ein Request Objekt zurückgeben.
Der Action Controller verarbeitet die verschiedenen Aktionen einer Applikation. Diese abstrakte Klasse stellt die folgenden Methoden bereit:
/** * @param Zend_Controller_Request_Abstract $request Request Objekt * @param Zend_Controller_Response_Abstract $response Response Objekt * @param array $args Optionales assoziatives Array mit Konfigurations/Umgebungseinstellungen */ public function __construct(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response, array $args = array()); /** * @return void */ public function init(); /** * @return Zend_Controller_Request_Abstract */ public function getRequest(); /** * @param Zend_Controller_Request_Abstract $request * @return self */ public function setRequest(Zend_Controller_Request_Abstract $request); /** * @return Zend_Controller_Response_Abstract */ public function getResponse(); /** * @param Zend_Controller_Response_Abstract $response * @return self */ public function setResponse(Zend_Controller_Response_Abstract $response); /** * @return array */ public function getInvokeArgs(); /** * @return mixed */ public function getInvokeArg($name); public function preDispatch(); public function postDispatch(); /** * @param string $methodName * @param array $args */ public function __call($methodName, $args); /** * @param null|Zend_Controller_Request_Abstract $request Optional zu verwendenes Request Objekt * @param null|Zend_Controller_Response_Abstract $response Optional zu verwendenes Response Objekt * @return Zend_Controller_Response_Abstract */ public function run(Zend_Controller_Request_Abstract $request = null, Zend_Controller_Response_Abstract $response = null);
Der Konstruktor registriert das Request und das Response Objekt sowie ein Array mit
zusätzlichen Konfigurationsparametern innerhalb des Objektes. Dieses Array besteht aus
Parametern, die mit den Methoden setParam()
oder setParams()
des Front Controllers registriert worden sind. Sobald fertig, übergibt der Konstruktur
die Verarbeitung an init()
.
Obwohl man den Konstruktor überschreiben kann, empfehlen wir dennoch alle
Initialisierungsschritte in init()
durchzuführen, um sicherzustellen, dass
die Request und Response Objekte richtig registriert wurden.
Auf alle Konfigurationsparameter, die an den Konstruktor übergeben worden sind, kann
später über getInvokeArg()
und getInvokeArgs()
zugegriffen
werden. Es wird empfohlen, solche Aufrufparameter zu verwenden, um Objekte wie View,
Authentifikation/Autorisierung oder Registry Objekte zu übergeben. Zum Beispiel:
$front = Zend_Controller_Front::getInstance(); $front->setParam('view', new Zend_View()) ->setControllerDirectory($config->controller->directory); $response = $front->dispatch(); // Einem einem Beispiel Action Controller: class FooController extends Zend_Controller_Action { protected $_view = null; public function init() { $this->_view = $this->getInvokeArg('view'); } }
Wenn eine Aktion verarbeitet wird, kann die Verarbeitung vor und nach der Aktion in den
preDispatch()
und postDispatch()
Methoden durchgeführt werden.
Standardmäßig sind diese leer und machen nichts.
Die __call()
Methode verarbeitet alle nicht registrierten Aktionen in der
Klasse. Standardmäßig wird eine Ausnahme geworfen, wenn die Aktion nicht definiert
worden ist. Dies soll wirklich nur dann passieren, wenn die Standard Aktionsmethode
nicht definiert worden ist.
Die standardmäßige Namenskonvetion für Aktionsmethoden ist lowercaseAction, wobei
'lowercase' den Namen der Aktion angibt und 'Action', dass es eine Aktionsmethode
ist. Demnach wird http://framework.zend.com/foo/bar
die Methode
FooController::barAction()
aufrufen.
Action Controller können auch als Page Controller verwendet werden. Die gängigste Verwendung würde wie folgt aussehen:
$controller = new FooController( new Zend_Controller_Request_Abstract(), new Zend_Controller_Response_Abstract() ); $controller->run();
Verwende Front-/Action Controller | |
---|---|
Wir empfehlen die Verwendung der Front Controller/Action Controller Kombination anstelle des Page Controller Denkmusters, um das Schreiben von Applikationen, die interagieren können, zu fördern. |
Das Response Objekt sammelt Inhalt und Header von den verschiedenen aufgerufenen Aktionen und gibt diese an den Client zurück. Es beinhaltet die folgenden Methoden:
/** * @param string $name Header Name * @param string $value Header Wert * @param boolean $replace Ob Header mit den selben Namen, die bereits im Objekt registriert * sind, ersetzt werden sollen oder nicht * @return self */ public function setHeader($name, $value, $replace = false); /** * @return array */ public function getHeaders(); /** * @return void */ public function clearHeaders(); /** * Versende alle Header * @return void */ public function sendHeaders(); /** * @param string $content * @return self */ public function setBody($content); /** * @param string $content * @return self */ public function appendBody($content); /** * @return string */ public function getBody(); /** * Gibt Hauptteilinhalte aus * @return void */ public function outputBody(); /** * @param Exception $e * @return self */ public function setException(Exception $e); /** * @return null|Exception */ public function getException(); /** * @return boolean */ public function isException(); /** * @param boolean $flag * @return boolean */ public function renderExceptions($flag = null); /** * @return string */ public function __toString();
setBody()
ersetzt den kompletten Inhaltsteil; wir empfehlen die Verwendung
von appendBody()
stattdessen. __toString()
gibt alle Inhalte
aus und sendet alle Header.
Das Response Objekt ist auch der Ort, in dem Ausnahmen aus dem Action Controller
schlußendlich aufgefangen und registriert werden (solange
Zend_Controller_Front::throwExceptions()
aktiviert wurde).
isException()
sollte einen booleschen Wert zurückgeben und angeben, ob
eine Ausnahme aufgetreten ist. renderExceptions()
sollte verwendet werden,
um anzugeben, ob __toString()
die Ausnahmen ausgeben soll, wenn welche
gefangen werden.