<sect1 id="zend.controller.router"  xmlns:xi="http://www.w3.org/2001/XInclude">
    <title>Стандартный маршрутизатор: Zend_Controller_Router_Rewrite</title>
    <sect2 id="zend.controller.router.introduction">
        <title>Введение</title>
        <para>
            <code>Zend_Controller_Router_Rewrite</code> является стандартным
            маршрутизатором Zend Framework. Маршрутизация - это процесс принятия
            конечной точки URI (той части URI, которая
            идет после базового URL) и ее разложения на параметры
            для определения того, какой контроллер и какое действие этого
            контроллера должны получить запрос. Значения контроллера,
            действия и необязательных параметров сохраняются в объекте
            <code>Zend_Controller_Request_Http</code>, который затем
            обрабатывается диспетчером
            <code>Zend_Controller_Dispatcher_Standard</code>. Маршрутизация
            производится только один раз – когда вначале
            получен запрос и до того, как первый контроллер будет запущен.
        </para>

        <para>
            <code>Zend_Controller_Router_Rewrite</code> спроектирован для
            того, чтобы обеспечить функциональность, подобную mod_rewrite,
            с использованием чистого PHP. Он отчасти основан на маршрутизации,
            используемой в Ruby on Rails и не требует каких-либо предварительных
            знаний о перезаписи URL веб-сервером. Он спроектирован для работы с
            единственным правилом mod_rewrite, пример которого приведен
            ниже:
        </para>

        <programlisting role="php"><![CDATA[
RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php
]]>
        </programlisting>

        <para>
            или (более предпочтителен):
        </para>

        <programlisting role="php"><![CDATA[
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
]]>
        </programlisting>

        <para>
            Rewrite Router может также использоваться с веб-сервером IIS версии
            7.0 и ниже, если
            <ulink url="http://www.isapirewrite.com">Isapi_Rewrite</ulink>
            был установлен как расширение Isapi со следующими правилами
            перезаписи:
        </para>

        <programlisting role="php"><![CDATA[
RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css|html)$)[\w\%]*$)? /index.php [I]
]]>
        </programlisting>

        <note>
            <title>IIS Isapi_Rewrite</title>
            <para>
                Если используется IIS, то <code>$_SERVER['REQUEST_URI']</code>
                не будет определен, либо будет установлен как пустая
                строка. В этом случае
                <code>Zend_Controller_Request_Http</code> попытается
                использовать <code>$_SERVER['HTTP_X_REWRITE_URL']</code>,
                значение которого устанавливается расширением Isapi_Rewrite.
            </para>
        </note>

        <para>
            IIS 7.0 имеет свой собственный модуль перезаписи URL,
            и он может быть сконфигурирован следующим образом:
        </para>

        <programlisting role="xml"><![CDATA[
<?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>
]]></programlisting>

        <para>
            Если используется Lighttpd, то корректным будет следующее
            правило перезаписи:
        </para>

        <programlisting role="lighttpd"><![CDATA[
url.rewrite-once = (
    ".*\?(.*)$" => "/index.php?$1",
    ".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
    "" => "/index.php"
)
]]>
        </programlisting>
    </sect2>

    <sect2 id="zend.controller.router.usage">
        <title>Использование маршрутизатора</title>

        <para>
            Для того, чтобы правильно использовать маршрутизатор, вы должны
            инстанцировать его, добавить пользовательские маршруты, и внедрить
            его во фронт-контроллер. Следующий код иллюстрирует эту процедуру:
        </para>

        <programlisting role="php"><![CDATA[
// Создание маршрутизатора

$router = $ctrl->getRouter(); // по умолчанию возвращает rewrite router
$router->addRoute(
    'user',
    new Zend_Controller_Router_Route('user/:username',
                                     array('controller' => 'user',
                                           'action' => 'info'))
);
]]>
        </programlisting>
    </sect2>

    <sect2 id="zend.controller.router.basic">
        <title>Базовые операции Rewrite Router</title>

        <para>
            Сущностью RewriteRouter (перезаписывающий маршрутизатор) является
            определение пользовательских маршрутов. Маршруты добавляются
            посредством вызовом метода <code>addRoute()</code> и передачей ему
            экземпляра класса, реализующего
            <code>Zend_Controller_Router_Route</code>. Например:
        </para>

        <programlisting role="php"><![CDATA[
$router->addRoute('user',
                  new Zend_Controller_Router_Route('user/:username'));
]]>
        </programlisting>

        <para>
            Rewrite Router поставляется вместе с пятью базовыми типами
            маршрутов (один из которых является специальным):
        </para>

        <itemizedlist mark="opencircle">
            <listitem><para>
                <xref linkend="zend.controller.router.routes.standard" />
            </para></listitem>
            <listitem><para>
                <xref linkend="zend.controller.router.routes.static" />
            </para></listitem>
            <listitem><para>
                <xref linkend="zend.controller.router.routes.regex" />
            </para></listitem>
            <listitem><para>
                <xref linkend="zend.controller.router.routes.hostname" />
            </para></listitem>
            <listitem><para>
                <xref linkend="zend.controller.router.default-routes" /> *
             </para></listitem>
        </itemizedlist>

        <para>
            Маршруты могут использоваться несколько раз для создания цепочки или
            пользовательской схемы маршрутизации в приложении. Вы можете
            использовать любое количество маршрутов в любой конфигурации, за
            исключением маршрута Module, который предпочтительно должен
            использоваться один раз и, возможно, как наиболее общий маршрут
            (например, в качестве используемого по умолчанию). Каждый маршрут
            будет в подробностях описан ниже.
        </para>

        <para>
            Первым параметром метода <code>addRoute()</code> является имя
            маршрута. Он используется в качестве идентификатора для получения
            маршрутов из маршрутизатора (например, в целях генерации URL).
            Вторым параметром является сам маршрут.
        </para>

        <note>
            <para>
                Наиболее часто имя маршрута используется через хелпер
                <code>Zend_View</code> для URL:
            </para>

            <programlisting role="php"><![CDATA[
<a href=
"<?= $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>
]]>
            </programlisting>

            <para>
               В результате значением атрибута href будет
               <code>user/martel</code>.
            </para>
        </note>

        <para>
            Маршрутизация - простой процесс итерации по всем предоставленным
            маршрутам и сопоставления их определений с текущим URI запроса.
            Когда найдено соответствие, то из объекта маршрута возвращаются
            значения переменных и добавляются в объект
            <code>Zend_Controller_Request</code> для дальнейшего использования в
            диспетчере и пользовательских контроллерах. Если соответствие не
            найдено, то проверяется следующий маршрут в цепочке.
        </para>

        <para>
            Если нужно определить выбранный маршрут,
            то можно использовать метод <code>getCurrentRouteName()</code>,
            он возвращает идентификатор, который использовался при регистрации
            маршрута в маршрутизаторе. Если требуется получить объект,
            то используйте <code>getCurrentRoute()</code>.
        </para>

        <note>
            <title>Обратный порядок сопоставления</title>
            <para>
                Маршруты сопоставляются в обратном порядке, поэтому
                удостоверьтесь, что наиболее общие маршруты определены
                первыми.
            </para>
        </note>

        <note>
            <title>Возвращаемые значения</title>
            <para>
                Значения, возвращаемые при маршрутизации, получаются из
                параметров URL или определенных пользователем значений по
                умолчанию. Эти переменные позднее могут быть позднее получены
                через методы <code>Zend_Controller_Request::getParam()</code>
                и <code>Zend_Controller_Action::_getParam()</code>
            </para>
        </note>

        <para>
            Есть три специальные переменные, которые могут использоваться в
            маршрутах: 'module', 'controller' и 'action'. Эти
            переменные используются диспетчером <code>Zend_Controller_Dispatcher</code>
            для нахождения контроллера и действия, которым передается
            управление.
        </para>

        <note>
            <title>Специальные переменные</title>
            <para>
                Имена этих переменных могут быть другими, если вы измените их
                через методы <code>setControllerKey</code> и
                <code>setActionKey</code>.
            </para>
        </note>

    </sect2>

    <sect2 id="zend.controller.router.default-routes">
        <title>Маршруты по умолчанию</title>

        <para>
            <code>Zend_Controller_Router_Rewrite</code> изначально
            сконфигурирован с одним маршрутом по умолчанию, который будет
            соответствовать URI вида <code>контроллер/действие</code>. Кроме
            того, в качестве первого элемента пути может быть указано имя
            модуля, это позволяет использовать URI вида
            <code>модуль/контроллер/действие</code>.
            Этот маршрут будет также соответствовать любым дополнительным
            параметрам, по умолчанию добавляемым в конец URI -
            <code>контроллер/действие/переменная1/значение1/переменная2/значение2</code>.
        </para>

        <para>
            Некоторые примеры того, чему будут соответствовать такие маршруты:
        </para>

        <programlisting role="php"><![CDATA[
// Допустим, есть следующие настройки:
$ctrl->setControllerDirectory(
    array(
        'default' => '/path/to/default/controllers',
        'news'    => '/path/to/news/controllers',
        'blog'    => '/path/to/blog/controllers'
    )
);

Только модуль:
http://example/news
    module == news

Если модуль не найден, то считается, что это имя контроллера:
http://example/foo
    controller == foo

Модуль + контроллер:
http://example/blog/archive
    module     == blog
    controller == archive

Модуль + контроллер + действие:
http://example/blog/archive/list
    module     == blog
    controller == archive
    action     == list

Модуль + контроллер + действие + параметры:
http://example/blog/archive/list/sort/alpha/date/desc
    module     == blog
    controller == archive
    action     == list
    sort       == alpha
    date       == desc
]]>
        </programlisting>

        <para>
            Маршрутом, используемым по умолчанию, является объект
            <code>Zend_Controller_Router_Route_Module</code>, сохраненный в
            RewriteRouter под именем (индексом) 'default'. Он создается
            приблизительно следующим образом:
        </para>

        <programlisting role="php"><![CDATA[
$compat = new Zend_Controller_Router_Route_Module(array(),
                                                  $dispatcher,
                                                  $request);
$this->addRoute('default', $compat);
]]>
        </programlisting>

        <para>
            Если вы не хотите использовать этот маршрут по умолчанию в своей
            схеме маршрутизации, то можете переопределить его путем создания
            собственного маршрута по умолчанию (т.е. сохранения его под именем
            'default') или полностью удалить его через метод
            <code>removeDefaultRoutes()</code>:
        </para>

        <programlisting role="php"><![CDATA[
// Удаление всех маршрутов по умолчанию
$router->removeDefaultRoutes();
]]>
        </programlisting>

    </sect2>

    <sect2 id="zend.controller.router.rewritebase">
        <title>Базовый URL и поддиректории</title>

        <para>
            RewriteRouter может использоваться в поддиректориях (например,
            <code>http://domain.com/~user/application-root/</code>), в этом
            случае базовый URL приложения (<code>/~user/application-root</code>)
            должен автоматически определяться в объекте
            <code>Zend_Controller_Request_Http</code> и соответствующим образом
            использоваться.
        </para>

        <para>
            Если базовый URL определяется некорректно, то вы можете
            переопределить его через метод <code>setBaseUrl()</code> объекта
            <code>Zend_Controller_Request_Http</code> (см.
            <xref linkend="zend.controller.request.http.baseurl" />):
        </para>

        <programlisting role="php"><![CDATA[
$request->setBaseUrl('/~user/application-root/');
]]>
        </programlisting>

    </sect2>

    <sect2 id="zend.controller.router.global.parameters">
        <title>Глобальные параметры</title>

        <para>
            Используя метод <code>setGlobalParam</code>, вы можете устанавливать
            глобальные параметры в маршрутизаторе, которые будут автоматически
            подставляться в маршрут при сборке. Если был установлен глобальный
            параметр, но при сборке тот же параметр был передан напрямую,
            то переданное значение параметра заменяет собой глобальное.
            Вы можете устанавливать глобальный параметр следующим образом:
        </para>

        <programlisting role="php"><![CDATA[
$router->setGlobalParam('lang', 'en');
]]>
        </programlisting>
    </sect2>

    <sect2 id="zend.controller.router.routes">
        <title>Типы маршрутов</title>
        <xi:include href="Zend_Controller-Router-Route.xml">
            <xi:fallback><xi:include href="../../en/module_specs/Zend_Controller-Router-Route.xml" /></xi:fallback>
        </xi:include>
        <xi:include href="Zend_Controller-Router-Route-Static.xml">
            <xi:fallback><xi:include href="../../en/module_specs/Zend_Controller-Router-Route-Static.xml" /></xi:fallback>
        </xi:include>
        <xi:include href="Zend_Controller-Router-Route-Regex.xml">
            <xi:fallback><xi:include href="../../en/module_specs/Zend_Controller-Router-Route-Regex.xml" /></xi:fallback>
        </xi:include>
        <xi:include href="Zend_Controller-Router-Route-Hostname.xml">
            <xi:fallback><xi:include href="../../en/module_specs/Zend_Controller-Router-Route-Hostname.xml" /></xi:fallback>
        </xi:include>
    </sect2>

    <sect2 id="zend.controller.router.add-config">
        <title>Использование <code>Zend_Config</code> вместе с RewriteRouter</title>

        <para>
            Иногда может быть более удобным обновлять конфигурационный файл
            с новыми маршрутами, чем изменять код. Это возможно благодаря методу
            <code>addConfig()</code>. В сущности, вы создаете конфигурацию,
            совместимую с <code>Zend_Config</code>, считываете ее в своем коде и
            передаете RewriteRouter.
        </para>

        <para>
            В качестве примера рассмотрим следующий INI-файл:
        </para>

        <programlisting role="php"><![CDATA[
[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"
; ИЛИ: routes.archive.map.year = 1
]]>
        </programlisting>

        <para>
            Этот INI-файл может быть затем прочитан в объект
            <code>Zend_Config</code> как показано ниже:
        </para>

        <programlisting role="php"><![CDATA[
$config = new Zend_Config_Ini('/path/to/config.ini', 'production');
$router = new Zend_Controller_Router_Rewrite();
$router->addConfig($config, 'routes');
]]>
        </programlisting>

        <para>
            В примере выше мы говорим маршрутизатору, чтобы он использовал
            раздел 'routes' в файле INI для своих маршрутов. Ключ первого уровня
            в этом разделе используется для определения имени маршрута, в
            примере выше определяются маршруты 'archive' и
            'news'. Каждый маршрут требует, как минимум, запись 'route' и
            одну или более записей 'defaults'; опционально может быть одна
            или более записей 'reqs' (сокращение от 'required'). Все это
            соответствует трем аргументам, передаваемым объекту
            <code>Zend_Controller_Router_Route_Interface</code>.
            Опция с ключом 'type' может использоваться для определения класса,
            используемого для данного маршрута; по умолчанию используется
            класс <code>Zend_Controller_Router_Route</code>. В примере выше
            для маршрута 'news' должен использоваться класс
            <code>Zend_Controller_Router_Route_Static</code>.
        </para>
    </sect2>

    <sect2 id="zend.controller.router.subclassing">
        <title>Создание подклассов маршрутизатора</title>

        <para>
            Стандартный RewriteRouter создан с тем, чтобы предоставлять полный
            набор тех функциональных возможностей, которые могут вам
            понадобиться. Как правило, вам нужно будет только создать новый тип
            маршрута для того, чтобы получить новый или измененный функционал
            сверх уже существующих типов маршрутов.
        </para>

        <para>
            В какой-то момент вы можете захотеть использовать другую парадигму
            маршрутизации. Интерфейс
            <code>Zend_Controller_Router_Interface</code> дает минимальную
            информацию, необходимую для создания маршрута и содержит всего один
            метод.
        </para>

        <programlisting role="php"><![CDATA[
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);
}
]]>
        </programlisting>

        <para>
            Маршрутизация производится только один раз - когда в систему
            поступил первый запрос. Назначение маршрутизатора состоит в
            определении контроллера, действия и опциональных параметров,
            основываясь на переменных запроса, и установке их в запросе. Затем
            объект запроса передается диспетчеру. Если не найден соответствующий
            маршрут, то маршрутизатор не должен ничего делать с объектом
            запроса.
        </para>
    </sect2>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
