# Der Standard Router

## Einführung

Zend_Controller_Router_Rewrite ist der Standard Router des Frameworks. Routing ist der Prozess der Übernahme und Zerteilung einer URI (dem Teil der URI der nach der Basis URL kommt), um zu ermitteln, welches Modul, welcher Controller und welche Aktion des Controllers die Anfrage erhalten soll. Die Definition des Moduls, des Controllers, der Aktion sowie weiterer Parameter wird in einem Objekt mit Namen Zend_Controller_Dispatcher_Token gekapselt, das dann vom Zend_Controller_Dispatcher_Standard verarbeitet wird. Das Routing geschieht nur einmal: wenn zu Beginn die Anfrage erhalten wird und bevor der erste Controller aufgerufen wird.

Zend_Controller_Router_Rewrite wurde entwickelt, um mit reinen PHP Strukturen eine mod_rewrite ähnliche Funktionalität zu erlauben. Es richtet sich sehr frei nach dem Ruby on Rails Routing und benötigt kein tieferes Wissen über URL Weiterleitung des Webservers. Es wurde entwickelt, um mit einer einzigen mod_rewrite Regel zu arbeiten.

    RewriteEngine on
    RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php

oder (bevorzugt):

    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} -s [OR]
    RewriteCond %{REQUEST_FILENAME} -l [OR]
    RewriteCond %{REQUEST_FILENAME} -d
    RewriteRule ^.*$ - [NC,L]
    RewriteRule ^.*$ index.php [NC,L]

Der Rewrite Router kann auch mit dem IIS Webserver verwendet werden (Versionen <= 7.0), wenn Isapi_Rewrite als Isapi Erweiterung installiert wurde und folgende Umschreibungsregel verwendet wird:

    RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css|html)$)[\w\%]*$)? /index.php [I]

IIS Isapi_Rewrite

Bei Verwendung von IIS, wird `$_SERVER['REQUEST_URI']` entweder nicht vorhanden oder auf einen leeren String gesetzt sein. In diesem Fall wird Zend_Controller_Request_Http versuchen, den durch die Isapi_Rewrite Erweiterung gesetzten Wert `$_SERVER['HTTP_X_REWRITE_URL']` zu verwenden.

IIS 7.0 führt ein natives URL Rewriting Modul ein, und kann wie folgt konfiguriert werden:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
         <system.webServer>
             <rewrite>
                 <rules>
                     <rule name="Imported Rule 1" stopProcessing="true">
                         <match url="^.*$" />
                         <conditions logicalGrouping="MatchAny">
                             <add input="{REQUEST_FILENAME}"
                                 matchType="IsFile" pattern=""
                                 ignoreCase="false" />
                             <add input="{REQUEST_FILENAME}"
                                 matchType="IsDirectory"
                                 pattern="" ignoreCase="false" />
                         </conditions>
                         <action type="None" />
                     </rule>
                     <rule name="Imported Rule 2" stopProcessing="true">
                         <match url="^.*$" />
                         <action type="Rewrite" url="index.php" />
                     </rule>
                 </rules>
             </rewrite>
         </system.webServer>
    </configuration>

Bei der Verwendung von Lighttpd, ist folgende Umschreibungsregel gültig:

    url.rewrite-once = (
        ".*\?(.*)$" => "/index.php?$1",
        ".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
        "" => "/index.php"
    )

## Einen Router verwenden

Um den Rewrite Router richtig zu verwenden, muß er instanziiert, einige benutzerdefinierte Routen hinzufügt und in den Controller einbunden werden. Der folgende Code veranschaulicht die Vorgehensweise:

    // Einen Router erstellen

    $router = $ctrl->getRouter(); // gibt standardmäßig einen Rewrite Router zurück
    $router->addRoute(
        'user',
        new Zend_Controller_Router_Route('user/:username',
                                         array('controller' => 'user',
                                               'action' => 'info'))
    );

## Grundsätzliche Rewrite Router Operationen

Das Herz des RewriteRouters ist die Definition von Benutzerdefinierten Routen. Routen werden durch aufruf der addRoute Methode des RewriteRouters hinzugefügt und übergeben eine neue Instanz einer Klasse die Zend_Controller_Router_Route_Interface implementiert. Z.B.:

    $router->addRoute('user',
                      new Zend_Controller_Router_Route('user/:username'));

Der Rewrite Router kommt mit sechs Basistypen von Routen (eine von denen ist speziell): is special):

- Zend_Controller_Router_Route

- Zend_Controller_Router_Route_Static

- Zend_Controller_Router_Route_Regex

- Zend_Controller_Router_Route_Hostname

- Zend_Controller_Router_Route_Chain

- Zend_Controller_Router_Rewrite *

Routen können unzählige Male verwendet werden um eine Kette oder benutzerdefinierte Routing Schemas von Anwendungen zu erstellen. Es kann jede beliebige Anzahl von Routen in jeder beliebigen Konfiguration verwendet werden, mit Ausnahme der Modul Route, welche nur einmal verwendet werden sollte, und möglicherweise die am meisten standardmäßige Route ist (z.B., als ein Standard). Jede Route wird später detailiert beschrieben.

Der erste Parameter für addRoute ist der Name der Route. Er wird als Handle verwendet um die Route außerhalb des Routers zu erhalten (z.B. für den Zweck der URL Erzeugung). Der zweite Parameter ist die Route selbst.

Die gewöhnlichste Verwendung des Namens der Route ist gegeben durch die Zwecke des Zend_View Url Helfers:

    <a href=
    "<?php echo $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>

Was zu folgender href führt: `user/martel`.

Routen ist ein einfacher Prozess des Durchlaufens aller vorhandenen Routen und Vergleichens deren Definitionen mit der aktuellen Anfrage URI. Wenn ein positiver Vergleich gefunden wird, werden variable Werte von der Instanz des Routers zurückgegeben, und werden für die spätere Verwendung im Dispatcher in das Zend_Controller_Request Objekt iniziiert, sowie in von Benutzern erstellten Controllern. Bei einem negativen Ergebnis des Vergleiches, wird die nächste Route in der Kette geprüft.

Wenn man herausfinden will welche Route gepasst hat, kann man die getCurrentRouteName() Methode verwenden, die den Identifikator zurückgibt der verwendet wurde als die Route im Router registriert wurde. Wenn man das aktuelle Route Objekt benötigt, kann getCurrentRoute() verwendet werden.

Umgekehrter Vergleich

Routen werden in umgekehrter Reihenfolge verglichen. Deswegen muß sichergestellt werden das die generellen Routen zuerst definiert werden.

Zurückgegebene Werte

Werte die vom Routen zurückgegeben werden kommen von URL Parametern oder Benutzerdefinierten Router Standards. Diese Variablen sind später durch die Zend_Controller_Request::getParam() oder Zend_Controller_Action::_getParam()Methoden verwendbar.

Es gibt drei spezielle Variablen welche in den Routen verwendet werden können - 'module', 'controller' und 'action'. Diese speziellen Variablen werden durch Zend_Controller_Dispatcher verwendet um einen Controller und die Aktion zu funden zu der verwiesen wird.

Spezielle Variablen

Die Namen dieser speziellen Variablen kann unterschiedlich sein wenn entschieden wird die Standards in Zend_Controller_Request_Http mithilfe der setControllerKey() und setActionKey() Methode zu Ändern.

## Standard Routen

Zend_Controller_Router_Rewrite kommt mit einer Standard Route vorkonfiguriert, welche URIs im Sinn von `controller/action` entspricht. Zusätzlich kann ein Modul Name als erstes Pfad Element definiert werden, welches URIs in der Form von `module/controller/action` erlaubt. Letztendlich wird es auch allen zusätzlichen Parametern entsprechen die der URI standardmäßig hinzugefügt wurden - `controller/action/var1/value1/var2/value2`.

Einige Beispiele wie solche Routen verglichen werden:

    // Folgende Annahme:
    $ctrl->setControllerDirectory(
        array(
            'default' => '/path/to/default/controllers',
            'news'    => '/path/to/news/controllers',
            'blog'    => '/path/to/blog/controllers'
        )
    );

    Nur Modul:
    http://example/news
        module == news

    Ungültiges Modul, geht an den Controller Namen:
    http://example/foo
        controller == foo

    Modul + Controller:
    http://example/blog/archive
        module     == blog
        controller == archive

    Modul + Controller + Aktion:
    http://example/blog/archive/list
        module     == blog
        controller == archive
        action     == list

    Modul + Controller + Aktion + Parameter:
    http://example/blog/archive/list/sort/alpha/date/desc
        module     == blog
        controller == archive
        action     == list
        sort       == alpha
        date       == desc

Die Standardroute ist einfach ein Zend_Controller_Router_Route_Module Objekt welches unter dem Namen (Index) 'default' im RewriteRouter gespeichert ist. Es wird mehr oder weniger wie folgt erstellt:

    $compat = new Zend_Controller_Router_Route_Module(array(),
                                                      $dispatcher,
                                                      $request);
    $this->addRoute('default', $compat);

Wenn diese spezielle Standard Route im eigenen Routing Schema nicht gewünscht ist, kann Sie durch Erstellung einer eigenen 'default' Route überschrieben werden (z.B. durch Speichern unter dem Namen 'default') oder dem kompletten Entfernen durch verwenden von removeDefaultRoutes():

    // Löschen aller Standard Routen
    $router->removeDefaultRoutes();

## Basis URL und Unterverzeichnisse

Der Rewrite Router kann in Unterverzeichnissen verwendet werden (z.B. `http://domain.com/user/application-root/`) und in diesem Fall sollte die Basis URL der Anwendung (`/user/application-root`) automatisch durch Zend_Controller_Request_Http erkannt und auch verwendet werden.

Sollte die Basis URL nicht richtig erkannt werden kann diese mit eigenen Basispfad überschrieben werden durch Verwendung von Zend_Controller_Request_Http und Auruf der setBaseUrl() Methode (siehe Basis URL und Unterverzeichnisse):

    $request->setBaseUrl('/~user/application-root/');

## Globale Parameter

Man kann in einem Router globale Parameter setzen die der Route automatisch zur Verfügung stehen wenn Sie durch setGlobalParam() eingefügt werden. Wenn ein globaler Parameter gesetzt ist, aber auch direkt an die Assemble Methode gegeben wird, überschreibt der Benutzer-Parameter den Globalen-Parameter. Globale Parameter können auf folgendem Weg gesetzt werden:

    $router->setGlobalParam('lang', 'en');

## Router Typen

## Zend_Config mit dem RewriteRouter verwenden

Manchmal ist es praktischer, eine Konfigurationsdatei mit neuen Routen zu aktualisieren, als den Code zu ändern. Dies ist mit Hilfe der addConfig() Methode möglich. Im Wesentlichen kann man eine Zend_Config kompatible Konfiguration erstellen, in seinem Code einlesen und an den RewriteRouter übergeben:

Als Beispiel wird die folgende INI Datei angenommen:

    [production]
    routes.archive.route = "archive/:year/*"
    routes.archive.defaults.controller = archive
    routes.archive.defaults.action = show
    routes.archive.defaults.year = 2000
    routes.archive.reqs.year = "\d+"

    routes.news.type = "Zend_Controller_Router_Route_Static"
    routes.news.route = "news"
    routes.news.defaults.controller = "news"
    routes.news.defaults.action = "list"

    routes.archive.type = "Zend_Controller_Router_Route_Regex"
    routes.archive.route = "archive/(\d+)"
    routes.archive.defaults.controller = "archive"
    routes.archive.defaults.action = "show"
    routes.archive.map.1 = "year"
    ; OR: routes.archive.map.year = 1

Die oben angeführte INI Datei kann dann wie folgt in ein Zend_Config Objekt eingelesen werden:

    $config = new Zend_Config_Ini('/path/to/config.ini', 'production');
    $router = new Zend_Controller_Router_Rewrite();
    $router->addConfig($config, 'routes');

Im oberen Beispiel teilen wir dem Router mit, den 'routes' Bereich der INI Datei für seine Routen zu verwenden. Jeder Schlüssel auf erster Ebene in diesem Bereich wird verwendet, um den Namen der Routen zu definieren; das obige Beispiel definiert die Routen 'archive' und 'news'. Jede Route erfordert dann mindestens einen 'route' Eintrag und einen oder mehrere 'defaults' Einträge; optional können eine oder mehrere 'reqs' (kurz für 'required', d.h. erforderlich) Einträge angegeben werden. Alles in allem entspricht dies den drei Argumenten, die an ein Zend_Controller_Router_Route_Interface Objekt übergeben werden. Ein Optionsschlüssel 'type' kann verwendet werden, um den Typ der Routenklasse für diese Route anzugeben; standardmäßig wird Zend_Controller_Router_Route verwendet. Im obigen Beispiel wird die 'news' Route definiert, um Zend_Controller_Router_Route_Static zu verwenden.

## Erben vom Router

Der Standard Rewrite Router sollte die meisten Funktionalitäten die benötigt werden zur Verfügung stellen; meistens wird es nur notwendig sein einen neuen Router Typen zu erstellen um neue oder modifizierte Funktionalitäten für die verfügbaren Routen zu bieten.

So gesehen, wird man in einigen Fällen ein anderes Routing Paradigma verwenden wollen. Das Interface Zend_Controller_Router_Interface bietet die minimalen Information die benötigt werden um einen Router er erstellen und besteht aus einer einzigen Methode.

    interface Zend_Controller_Router_Interface
    {
      /**
       * @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.