# 标准路由器

## 简介

`Zend_Controller_Router_Rewrite`是标准的框架路由器。路由是个过程，在这个过程中它取出URI的端点（跟着基本URL的URI的那部分）并把它分解成参数来决定哪个模块、哪个控制器和控制器中的哪个动作应该接受请求。模块、控制器、动作和其它参数被打包到`Zend_Controller_Request_Http`对象，接着这个对象由`Zend_Controller_Dispatcher_Standard`来处理。路由只发生一次：当请求最初被接收和第一个控制器被派遣之前。

`Zend_Controller_Router_Rewrite`被设计来考虑使用纯php结构时mod_rewrite-like的功能性。它非常宽松地基于Ruby on Rails并且不要求任何先前的web服务器URL rewriting的知识。它被设计来和单个Apache的 mod_rewrite规则（其中之一）一起工作：

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

或者：

    RewriteEngine on
    RewriteCond %{SCRIPT_FILENAME} !-f
    RewriteCond %{SCRIPT_FILENAME} !-d
    RewriteRule ^(.*)$ index.php/$1

如果Isapi_Rewrite已经用下列的rewrite规则被安装为一个Isapi扩展， rewrite路由器也可以和IIS web服务器一起使用：

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

### IIS Isapi_Rewrite

当使用IIS，`$_SERVER['REQUEST_URI']`将要么不存在，要么被设置成一个空串。在这个例子中，`Zend_Controller_Request_Http`将企图使用被Isapi_Rewrite扩展设置的`$_SERVER['HTTP_X_REWRITE_URL']`的值。

如果使用 Lighttpd，下面的 rewrite 规则有效：

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

## 使用路由器

为正确使用rewrite路由器你必须初始化它，添加一些用户定义的路由并注入到控制器。下面的代码示例这个过程：

    // Create a router

    $router = $ctrl->getRouter(); // returns a rewrite router by default
    $router->addRoute(
        'user',
        new Zend_Controller_Router_Route('user/:username',
                                         array('controller' => 'user',
                                               'action' => 'info'))
    );

## 基本的Rewrite路由器操作

RewriteRouter的核心是用户定义路由的定义。路由通过调用RewriteRouter的addRoute方法和传递一个由类实现的`Zend_Controller_Router_Route_Interface`的新的实例被添加。例如：

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

Rewrite 路由器带有四个基本类型的路由（其中一个是特殊的）：

- *

路由可以被使用无数次来创建链或用户定义的应用程序路由计划。你可以在任何配置中使用任何数量的路由，除了模块路由以外，它最好被用一次并作为通用路由（例如，作为缺省的）。每个路由将在稍后详细描述。

addRoute的第一个参数是路由名。它用来作为权柄从路由器中取得路由（例如，for URL generation purposes）。第二个参数是路由自己。

路由名最普通的用法是通过Zend_View_url助手的方法：

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

它将导致在 href： `user/martel`.

路由是一个简单的过程，这个过程通过所有提供的路由和匹配它的当前请求的URI定义来迭代。当一个正匹配被发现，变量值从路由实例返回并注入到`Zend_Controller_Request`对象以备将来在派遣器和用户创建的控制器中使用。如果是负匹配，在链中的下个路由被检查。

### 倒序匹配

用倒序来匹配路由确保最通用的路由被首先定义。

### 返回的值

从路由返回的值来自于URL参数或用于定义的缺省值。这些变量以后可通过`Zend_Controller_Request::getParam()` 或 `Zend_Controller_Action::_getParam()` 方法来访问。

有三个特殊的变量可用于你的路由－'module'、 'controller' 和 'action'。这些特殊的变量被Zend_Controller_Dispatcher用来找出控制器和动作然后派遣过去。

### 特殊变量

如果你选择通过 `setControllerKey` 和 `setActionKey`方法的方式来改变缺省值，这些特殊变量的名字可能会不同。

## 缺省路由

Zend_Controller_Router_Rewrite 和缺省路由一起预先配置，它将以`controller/action`的形式匹配URIs。另外，模块名可以被指定作为第一个路径参数，允许这种`module/controller/action`形式的URIs。最后，它也将缺省地匹配任何另外的追加到URI的参数－`controller/action/var1/value1/var2/value2`。

一些路由如何匹配的例子：

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

    Module only:
    http://example/news
        module == news

    Invalid module maps to controller name:
    http://example/foo
        controller == foo

    Module + controller:
    http://example/blog/archive
        module     == blog
        controller == archive

    Module + controller + action:
    http://example/blog/archive/list
        module     == blog
        controller == archive
        action     == list

    Module + controller + action + params:
    http://example/blog/archive/list/sort/alpha/date/desc
        module     == blog
        controller == archive
        action     == list
        sort       == alpha
        date       == desc

缺省路由是存储在RewriteRouter名（index）为'default'的简单的`Zend_Controller_Router_Route_Module`对象。它被创建多多少少象下面这样：

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

如果你不想这个特别的缺省路由在你的路由计划中，你可以重写你自己的‘缺省’路由（例如，把它存储在'default'名下）或用`removeDefaultRoutes()`完全清除它：

    // Remove any default routes
    $router->removeDefaultRoutes();

## 基本 URL 和子目录

rewrite路由器可以被用在子目录（例如，`http://domain.com/~user/application-root/`），在此例中，应用程序 (`/~user/application-root`)的基本URL应该能自动被`Zend_Controller_Request_Http`检测到并使用。

如果基本URL被误删除，你可以通过`Zend_Controller_Request_Http` 和调用 `setBaseUrl()` 方法（参见）用你自己的基本路径重写它。

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

## Route Types

## 使用 Zend_Config with the RewriteRouter

有时候，用新路由更新配置文件比修改代码更方便。这个可能通过`addConfig()`方法来做。基本上，你创建一个Zend_Config-compatible配置，并在你的代码中读入然后传递给RewriteRouter。

作为例子，考虑下面的 INI 文件：

    [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

上述的INI文件可以被读进`Zend_Config`对象：

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

在上面的例子中，我们告诉路由器去使用INI文件'routes'一节给它的路由。每个在这个节下的顶级键将用来定义路由名；上述例子定义了路由'archive'和'news'。每个路由接着要求，至少，一个'route'条目和一个或更多'defaults'条目；可选地，一个或更多'reqs'（'required'的简写）可能要求提供。总之，这些相对应的三个参数提供给`Zend_Controller_Router_Route_Interface`对象。一个选项键，'type'，可用来指定路由类的类型给特殊的路由；缺省地，它使用`Zend_Controller_Router_Route`。在上述例子中，'news'路由被定义来使用`Zend_Controller_Router_Route_Static`。

## Subclassing the Router

标准的rewrite路由器应当最大限度提供你所需的功能；大多时候，为了通过已知的路由提供新的或修改的功能，你将只需要创建一个新的路由类型

那就是说，你可能想要用不同的路由范例。接口`Zend_Controller_Router_Interface`提供了需要最少的信息来创建路由器，并包含一个单个的方法。

    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);
    }

路由只发生一次：当请求第一次接收到系统。路由器的意图是基于请求的环境决定控制器、动作和可选的参数，并把它们发给请求。请求对象接着传递给派遣器。如果不可能映射一个路由到一个派遣令牌，路由器对请求对象就什么也不做。