Kapitel 7. Zend_Controller

Inhaltsverzeichnis

7.1. Überblick
7.1.1. Einführung
7.1.2. Request Objekt
7.1.3. Routing Prozess
7.1.4. Dispatcher Prozess
7.1.5. Response Objekt
7.2. Erste Schritte
7.2.1. Einführung
7.2.2. Server Konfiguration
7.2.3. Ladedatei
7.2.4. Verzeichnisstruktur
7.2.5. Default Controller
7.3. Klassen ableiten
7.3.1. Einführung
7.3.2. Konventionen
7.3.3. Front Controller
7.3.4. Request Abstract
7.3.5. Router Interface
7.3.6. Dispatcher Interface
7.3.7. Action Controller
7.3.8. Response Objekt
7.4. Bereitgestellte Unterklassen
7.4.1. Einführung
7.4.2. Zend_Controller_Request_Http
7.4.3. Zend_Controller_Router_Rewrite
7.4.4. Zend_Controller_Response_Http
7.4.5. Zend_Controller_Response_Cli
7.5. Action Controllers
7.5.1. Introduction
7.5.2. Object initialization
7.5.3. Pre- and Post-Dispatch Hooks
7.5.4. Accessors
7.5.5. Utility Methods
7.6. Plugins
7.6.1. Einführung
7.6.2. Plugins schreiben
7.6.3. Plugins verwenden
7.7. Using a Conventional Modular Directory Structure
7.7.1. Introduction
7.7.2. Specifying Module Controller Directories
7.7.3. Routing to modules
7.7.4. Module or Global Default Controller
7.8. MVC Exceptions
7.8.1. Introduction
7.8.2. How can you handle exceptions?
7.8.3. MVC Exceptions You May Encounter
7.9. Migrieren von früheren Versionen
7.9.1. Migration von 0.6.0 nach 0.8.0
7.9.2. Migration von 0.2.0 oder früher nach 0.6.0

7.1. Überblick

7.1.1. Einführung

Zend_Controller stellt das Fundament für den Aufbau einer Website auf Basis des Model-View-Controller (MVC) Entwurfsmusters bereit.

Das Zend_Controller System wurde leichtgewichtig, modular und erweiterbar aufgebaut. Das Design ist einfach, um den Benutzern viel Flexibilität und reiheiten zu ermöglichen. Dennoch bietet es ausreichend Struktur, damit Systeme, die auf den Zend_Controller aufbauen, gemeinsame Konventionen befolgen und einen ähnlichen Code Aufbau verwenden.

Der Zend_Controller Ablauf wurde mit Hilfe verschiedener Komponenten implementiert. Während es für die Benutzung des Systems nicht notwendig ist, den kompletten Unterbau all dieser Komponenten zu verstehen, ist es hilfreich, über den Ablauf ausreichend Kenntnisse zu haben.

  • Zend_Controller_Front steuert den gesamten Ablauf des Zend_Controller Systems. Es ist eine Interpretation des FrontController Entwurfsmusters. Zend_Controller_Front verarbeitet alle Anfragen, die der Server erhält, und ist letztendlich dafür verantwortlich, die Anfragen an die ActionController (Zend_Controller_Action) zu deligieren.

  • Zend_Controller_Request_Abstract repräsentiert die Umgebung der Anfrage und stellt Methoden für das Setzen und Abfragen der Namen für Controller und Aktion sowie jeder Anfrageparameter bereit. Zusätzlich behält es die Übersicht darüber, ob die enthaltene Aktion von Zend_Controller_Dispatcher verarbeitet wurde oder nicht. Erweiterungen dieses abstrakten Request Objektes können verwendet werden, um die gesamte Anfrageumgebung zu kapseln und Routern zu erlauben, Informationen aus der Anfrageumgebung abzufragen, um die Namen für Controller und Aktion zu setzen..

    Standardmäßig wird Zend_Controller_Request_Http verwendet, welches den Zugriff auf die komplette HTTP Anfrageumgebung ermöglicht.

  • Zend_Controller_Router_Interface wird verwendet, um Router zu definieren. Routing ist der Prozess, bei dem die Anfrageumgebung untersucht wird, um zu ermitteln, welcher Controller und welche Aktion dieses Controllers diese Anfrage verarbeiten soll. Dieser Controller, diese Aktion und optionale Parameter werden dann im Request Object gesetzt, das vom Zend_Controller_Dispatcher_Standard verarbeitet wird. Das Routing wird nur einmal ausgeführt: wenn die Anfrage erhalten wird und bevor der erste Controller aufgerufen wird.

    Der Standardrouter ist Zend_Controller_Router_Rewrite.

    Der Standardrouter Zend_Controller_Router_Rewrite nimmt den URI Endpunkt entgegen, der in Zend_Controller_Request_Http angegeben ist, und zerlegt ihn in einen Controller, eine Aktion und die Parameter basierend auf den Pfadinformationen der URL. Zum Beispiel würde die URL http://localhost/foo/bar/key/value übersetzt, um den foo Controller und die bar Aktion zu verwenden und einen Parameter key mit dem Wert value anzugeben.

    Zend_Controller_Router_Rewrite kann auch für beliebige Pfade verwendet werden; man beachte die Rewrite Router Dokumentation für weitere Informationen.

  • Zend_Controller_Dispatcher_Interface wird verwendet, um Dispatcher zu definieren. Dispatching ist der Prozess, den Controller und die Aktion aus dem Request Objekt abzufragen und auf eine Controller Datei/ Klasse und eine Aktionsmethode in dieser Controller Klasse zu abzubilden. Wenn der Controller oder die Aktion nicht existieren, ermittelt es den zu verarbeitenden Standard Controller und Aktion.

    Der aktuelle Dispatcher Prozess besteht aus dem Instanzieren der Controller Klasse und dem Aufrufen der Aktionsmethode in dieser Klasse. Anders als das Routing, welches immer nur einmal vorkommt, erfolgt das Dispatching in einer Schleife. Wenn der Verarbeitungsstatus des Request Objektes irgendwann zurück gesetzt wird, wird die Schleife wiederholt und die Aktion aufgerufen, die zu diesem Zeitpunkt im Request Objekt gesetzt ist. Das erste Mal, wenn ein Schleifendurchlauf mit gesetztem Verarbeitungsstatus (boolsches true) im Request Objekt beendet wird, wird der Prozess beendet.

    Der Standarddispatcher ist Zend_Controller_Dispatcher_Standard. Er definiert Controller als CamelCasedClasses, die auf das Wort Controller enden, und Aktionsmethoden als camelCasedMethods, die auf das Wort Action enden: SomeFooController::barAction. In diesem Fall wird auf den Controller über somefoo und auf die Aktion über bar zugegriffen.

  • Zend_Controller_Action ist die elementare Controller Komponente. Jeder Controller ist eine einzelne Klasse, welche die Zend_Controller_Action Klasse erweitert und Methoden für die Aktionen enthält.

  • Zend_Controller_Response_Abstract definiert eine grundlegende Response Klasse, um Antworten der Aktion aus den Controllern zu sammeln und zurück zu geben. Es sammelt Header und Inhalte und kann, weil es __toString() implementiert, direkt ausgegeben werden, um alle Header und Inhalte zusammen zu senden.

    Die Standard Response Klasse ist Zend_Controller_Response_Http, welches in einer HTTP Umgebung verwendet werden kann.

Der Ablauf vom Zend_Controller ist relativ einfach. Eine Anfrage wird vom Zend_Controller_Front empfangen, der wiederum Zend_Controller_Router_Rewrite aufruft, um zu ermitteln, welcher Controller (und welche Aktion in dem Controller) ausgeführt werden soll. Zend_Controller_Router_Rewrite zerteilt die URI um den Namen des Controllers und der Aktion für den Request zu setzen. Zend_Controller_Front durchläuft dann eine Dispatcher Schleife. Er ruft Zend_Controller_Dispatcher_Standard auf und übergibt den Request, um den Controller und die Aktion auszuführen, die im Request spezifiziert wurden (oder verwendet die Standardwerte). Wenn der Controller fertig ist, wird die Kontrolle wieder an Zend_Controller_Front übergeben. Wenn der Controller durch das Zurücksetzen des Dispatch Status des Requests angezeigt hat, dass ein weiterer Controller ausgeführt werden soll, wird der Durchlauf fortgesetzt und ein weiterer Dispatcher Prozess wird durchlaufen. Andernfalls endet der Prozess.

7.1.2. Request Objekt

Das Request Objekt ist eine einfaches Wertobjekt, das zwischen Zend_Controller_Front und den Router, Dispatcher und Controller Klassen übergeben wird. Es enthält sowohl die Definition des Controllers, der Aktion und der Parameter, die an die Aktion übergeben werden sollen, als auch den Rest der Anfrageumgebung, seit es HTTP, CLI oder PHP-GTK.

  • Auf den Controller Namen kann über getControllerName() und setControllerName() zugegriffen werden.

  • Auf den Namen der Aktion, die in diesem Controller aufgerufen wird, kann über accessed by getActionName() und setActionName() zugegriffen werden.

  • Parameter, die an diese Aktion übergeben werden, bestehen aus einem assoziativen Array mit Schlüssel/Wert Paaren, auf die komplett per getParams() und setParams() oder einzeln per getParam() und setParam() zugegriffen werden kann.

Abhängig vom Typ der Anfrage können auch weitere Methoden verfügbar sein. Das verwendete Standard Request Object Zend_Controller_Request_Http stellt z.B. Methoden zum Abfragen der Request URI, Pfadinformationen, $_GET und $_POST Parameter usw. bereit.

Das Request Objekt wird an den Front Controller übergeben oder, wenn keines bereit gestellt wurde, am Anfang des Dispatcher Prozesses instanziert, bevor das Routing beginnt. Es wird an jedes Objekt in der Dispatcherkette übergeben.

Zusätzlich ist das Request Object besonders beim Testen sehr nützlich. Der Entwickler kann die Anfrageumgebung von Hand erstellen, inklusive Controller, Aktion, Parameter, URI usw. und das Request Objekt an den Front Controller übrgeben, um den Ablauf der Applikation zu testen. Zusammen mit dem Response Objekt sind durchdachte und genaue Unit Tests für eine MVC Applikation möglich.

7.1.3. Routing Prozess

Bevor dein erster Controller erstellt werden kann, musst du die Funkktionsweise des Routing Prozesses verstehen, wie er in Zend_Controller_Router_Rewrite implementiert worden ist. Denke daran, dass der Ablauf unterteilt ist in das Routing, das nur einmal ausgeführt wird, und das Dispatching, welches danach in einer Schleife durchlaufen wird.

Zend_Controller_Front ruft Zend_Controller_Router_Rewrite (oder einen anderen registrierten Router) auf, um die URI auf einen Controller (und eine Aktion in diesem Controller) abzubilden. Zend_Controller_Router_Rewrite empfängt die URI fom Request Objekt und übergibt sie an die Route Objekte in seiner Kette; standardmäßig verwendet er Zend_Controller_Router_Route_Module um eingehende URLs zu zuordnen. Das Route Objekt zerteilt die URL dann, um den Controller, die Aktion und andere, im Pfad übergebene URL Parameter zu ermitteln; der Router setzt diese dann im Request Objekt.

Zend_Controller_Router_Route_Module verwendet eine sehr einfache Zuordnung, um den Namen des Controllers und den Namen der Aktion innerhalb dieses Controllers zu ermitteln.

http://framework.zend.com/controller/action/
			

Beachte im obigen Beispiel, dass das erste Segment immer den Namen des Controllers und das zweite Segment immer den Name der Aktion enthält.

Optional können in der URI auch Parameter definiert werden, die an den Controller übergeben werden. Diese haben die Form eines Schlüssel/Wert Paares:

http://framework.zend.com/controller/action/key1/value1/
			

Wenn weder der Controller noch die Aktion im URI Pfad vorhanden sind, versucht Zend_Controller_Dispatcher_Standard die Werte aus des Parametern des Request Objektes zu bekommen und, falls nicht erfolgreich, die Standardwerte zu verwenden. In beiden Fällen sind die Standardwerte "index". Dieses Beispiel illustriert dies:

http://framework.zend.com/roadmap/future/
Controller: roadmap
Action    : future

http://framework.zend.com/roadmap/
Controller: roadmap
Action    : index

http://framework.zend.com/
Controller: index
Action    : index
        
[Anmerkung] Flexibilität

Wenn man flexiblere Möglichkeiten benötigt, sollte man den die Rewrite Router Dokumentation anschauen.

Der Name des Controllers und der Aktion innerhalb dieses Controllers sowie weitere optionale Parameter werden im Request Objekt gesetzt. Wenn Zend_Controller_Front die Dispatcher Schleife startet wird das Request Objekt an Zend_Controller_Dispatcher_Standard übergeben.

7.1.4. Dispatcher Prozess

Dispatching ist der Prozess, das Request Objekt Zend_Controller_Request_Abstract zu übernehmen, die dort enthaltenen Controller und Aktion Namen sowie die optionalen Parameter zu extrahieren und dann den Controller zu instanzieren und die Aktion dieses Controllers aufzurufen. Wenn kein Controller oder keine Aktion gefunden wurde, werden dafür Standardwerte verwendet. Zend_Controller_Dispatcher_Standard legt index für beide als Standardwert fest, aber erlaubt dem Entwickler auch, diese durch Verwendung der setDefaultController() und setDefaultAction() Methoden zu verändern.

Dispatching läuft innerhalb einer Schleife im Front Controller ab. Vor dem Dispatching fragt der Front Controller den Request ab, um benutzerspezifizierte Werte für Controller, Aktion und optionale Parameter zu finden. Dann startet er die Dispatch Schleife, um die Anfrage zu verarbeiten.

Zu Beginn jeden Durchlaufes setzt er im Request Objekt einen Schalter, der angibt, dass die Aktion verarbeitet worden ist. Wenn eine Aktion oder ein pre/postDispatch Plugin diesen Schalter zurücksetzt, wird die Dispatch Schleife fortgesetzt und versucht, die Anfrage erneut zu verarbeiten. Durch Ändern des Controllers und / oder der Aktion im Request Objekt und Zuürcksetzen des Verarbeitungsstatus, kann der Entwickler eine zu durchlaufende Anfragekette definieren.

Die Controller Methode, die solch eine Verarbeitung kontrolliert lautet _forward(); rufe diese Methode von einer beliebigen pre/postDispatch() oder Aktionsmethode auf und übergebe Controller, Aktion und beliebige optionale Parameter, die du zur neuen Aktion übersenden möchtest.

public function myAction()
{
    // do some processing...
    // forward to another action, FooController::barAction(), in the current module:
    $this->_forward('bar', 'foo', null, array('baz' => 'bogus'));
}

7.1.5. Response Objekt

Das Response Objekt ist das logische Gegenstück zum Request Objekt. Sein Zweck ist es, Inhalte und / oder Header zu vereinigen, um sie in einem Rutsch zu versenden. Zusätzlich übergibt der Front Controller alle aufgefangenen Ausnahmen an das Response Objekt, um dem Entwickler das Verarbeiten von Ausnahmen zu ermöglichen. Dies Funktionalität kann durch Setzen von Zend_Controller_Front::throwExceptions(true) überschrieben werden.

$front->throwExceptions(true);

Um die Ausgabe der Response, inklusiver der gesetzten Header, zu senden, verwendet man sendOutput():

$response->sendOutput();

Entwickler sollten das Response Objekt in ihren Aktionscontrollern verwenden. Statt die Ausgabe direkt zu machen und Header zu versenden, sollten diese an des Response Objekt übergeben werden:

// Innerhalb einer Controller Aktion:
// Setze einen Header
$this->getResponse()
    ->setHeader('Content-Type', 'text/html')
    ->appendBody($content);

Dadurch werden alle Header in einem Rutsch versendet, genau vor der Anzeige des Inhalts.

Sollte in der Applikation eine Ausnahme auftreten, überprüft man den isException() Schalter des Response Objektes und erhält die Ausnahme durch Verwendung von getException(). Zusätzlich kann man ein eigenes Response Objekt erstellen, dass zu einer Fehlerseite umleitet, die Nachricht der Ausnahme loggt, die Nachricht der Ausnahme schön formatiert ausgibt (für Entwicklungsumgebungen), usw.

Man kann das Response Objekt im Anschluß an die dispatch() Methode des Front Controllers erhalten oder den Front Controller auffordern, dass Response Objekt zurückzugeben statt den Inhalt auszugeben.

// Erhalten nach dem Dispatch:
$front->dispatch();
$response = $front->getResponse();
if ($response->isException()) {
    // log, mail, etc...
}

// Oder den Front Controller dispatch Prozess auffordern, es zurück zu geben
$front->returnResponse(true);
$response = $front->dispatch();

// mache irgend was...

// zum Schluß, gebe die Antwort aus
$response->sendResponse();

Standardmäßig werden Ausnahmennachrichten nicht ausgegeben. Dieses Verhalten kann durch den Aufruf von renderException() überschrieben werden oder indem der Front Controller aufgefordert wird, die Exceptions durch throwExceptions() auszuwerfen, wie oben gezeigt:

$response->renderExceptions(true);
$front->dispatch($request, $response);

// oder:
$front->returnResponse(true);
$response = $front->dispatch();
$response->renderExceptions();
$response->sendOutput();

// oder:
$front->throwExceptions(true);
$front->dispatch();