<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<!-- EN-Revision: 24249 -->
<sect1 id="zend.controller.router" xmlns:xi="http://www.w3.org/2001/XInclude">
    <title>標準のルータ</title>

    <sect2 id="zend.controller.router.introduction">
        <title>導入</title>

        <para>
            <classname>Zend_Controller_Router_Rewrite</classname> は、標準のルータです。
            ルーティングとは、<acronym>URI</acronym> (ベース <acronym>URL</acronym> から取得した <acronym>URI</acronym> の一部)
            を展開し、どのコントローラのどのアクションが
            リクエストを処理するのかを決める処理のことです。
            モジュールやコントローラ、アクション、そしてその他のパラメータが
            <classname>Zend_Controller_Request_Http</classname> オブジェクトにまとめられます。
            このオブジェクトを処理するのが <classname>Zend_Controller_Dispatcher_Standard</classname> です。
            ルーティングが行われるのは一度だけ、すなわちリクエストを最初に受け取ってから
            最初のコントローラに処理が渡される際だけです。
        </para>

        <para>
            <classname>Zend_Controller_Router_Rewrite</classname> は、mod_rewrite 風の機能を
            <acronym>PHP</acronym> だけで実現できるように設計されています。
            この処理は Ruby on Rails のルーティングを多少参考にしており、
            ウェブサーバの <acronym>URL</acronym> 書き換えに関する前提知識を必要としません。
            以下の単純な mod_rewrite ルール (のいずれか) で動作するように設計されています。
        </para>

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

        <para>
            あるいは (推奨)
        </para>

        <programlisting language="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 ルータを <acronym>IIS</acronym> ウェブサーバ (バージョン &lt;= 7.0) で使用するには
            <ulink url="http://www.isapirewrite.com">Isapi_Rewrite</ulink>
            を Isapi 拡張モジュールとしてインストールします。そして次のようなルールを記述します。
        </para>

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

        <note>
            <title>IIS Isapi_Rewrite</title>

            <para>
                <acronym>IIS</acronym> を使用すると、<varname>$_SERVER['REQUEST_URI']</varname>
                が存在しないか空の文字列に設定されます。このような場合、
                <classname>Zend_Controller_Request_Http</classname> は
                <varname>$_SERVER['HTTP_X_REWRITE_URL']</varname> の値を使用します。これは
                <classname>Isapi_Rewrite</classname> 拡張モジュールが設定します。
            </para>
        </note>

        <para>
            <acronym>IIS</acronym> 7.0 ではネイティブの <acronym>URL</acronym> リライトモジュールが登場しました。
            次のように設定して使います。
        </para>

        <programlisting language="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 language="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>
            Rewrite ルータを適切に使用するには、まずそのインスタンスを作成し、
            次にユーザ定義のルーティングを追加し、それをコントローラに注入しなければなりません。
            以下にコードの例を示します。
        </para>

        <programlisting language="php"><![CDATA[
// ルータを作成します

$router = $ctrl->getRouter(); // デフォルトで rewrite ルータを返します
$router->addRoute(
    'user',
    new Zend_Controller_Router_Route('user/:username',
                                     array('controller' => 'user',
                                           'action' => 'info'))
);
]]></programlisting>
    </sect2>

    <sect2 id="zend.controller.router.basic">
        <title>基本的な RewriteRouter の操作法</title>

        <para>
            RewriteRouter で最も重要なのが、ユーザ定義のルーティングです。
            これは、RewriteRouter の addRoute メソッドをコールして追加します。
            このメソッドに、<classname>Zend_Controller_Router_Route_Interface</classname>
            を実装したクラスの新しいインスタンスを渡します。
        </para>

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

        <para>
            Rewrite ルータには、6 種類の基本的なルーティング方式があります
            (そのうちのひとつは特別なものです)。
        </para>

        <itemizedlist mark="opencircle">
            <listitem>
                <para>
                    <link
                        linkend="zend.controller.router.routes.standard">Zend_Controller_Router_Route</link>
                </para>
            </listitem>

            <listitem>
                <para>
                    <link
                        linkend="zend.controller.router.routes.static">Zend_Controller_Router_Route_Static</link>
                </para>
            </listitem>

            <listitem>
                <para>
                    <link
                        linkend="zend.controller.router.routes.regex">Zend_Controller_Router_Route_Regex</link>
                </para>
            </listitem>

            <listitem>
                <para>
                    <link
                        linkend="zend.controller.router.routes.hostname">Zend_Controller_Router_Route_Hostname</link>
                </para>
             </listitem>

            <listitem>
                <para>
                    <link
                        linkend="zend.controller.router.routes.chain">Zend_Controller_Router_Route_Chain</link>
                </para>
            </listitem>

            <listitem>
                <para>
                    <link
                        linkend="zend.controller.router.default-routes">Zend_Controller_Router_Rewrite</link>
                    *
                </para>
            </listitem>
        </itemizedlist>

        <para>
            これらのルーティングは、チェインやユーザ定義のルーティング方式を作成する際に何度も使用します。
            任意の設定でお好みの数のルーティングを使用できますが、
            Module ルートだけは例外です。これを使用するのは一度だけで、
            もっとも汎用的なルート (デフォルト) として使用します。
            個々のルーティング方式については、後ほど詳細に説明します。
        </para>

        <para>
            addRoute への最初のパラメータはルートの名前です。
            これを使用して、ルータがルートを処理します。
            たとえば <acronym>URL</acronym> の生成などに使用します。
            二番目のパラメータはルート自身となります。
        </para>

        <note>
            <para>
                ルート名のもっとも一般的な使用例は、
                <classname>Zend_View</classname> の url ヘルパーです。
            </para>

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

            <para>
                これは <filename>user/martel</filename> へのリンクとなります。
            </para>
        </note>

        <para>
            ルーティング処理は、定義されたすべてのルートから
            リクエスト <acronym>URI</acronym> にマッチする定義を探すことによって行います。
            マッチするものが見つかれば、ルートのインスタンスから変数の値が返され、
            それを Zend_Controller_Request オブジェクトに注入します。
            これを、後にディスパッチャやユーザが作成したコントローラで使用します。
            マッチするものが見つからない場合は、チェイン内の次のルートを調べます。
        </para>

        <para>
            どのルートがマッチしたかを知りたい場合は
            <methodname>getCurrentRouteName()</methodname> メソッドを使用します。
            これは、ルートをルータに登録する際に使用した識別子を返します。
            ルートオブジェクトそのものを取得したい場合は
            <methodname>getCurrentRoute()</methodname> を使用します。
        </para>

        <note>
            <title>定義の順番</title>
            <para>
                一番最後にマッチしたルートが適用されるので、
                汎用的なルートは最初に定義するようにしましょう。
            </para>
        </note>

        <note>
            <title>返される値</title>
            <para>
                ルーティングの結果返される値は、<acronym>URL</acronym> パラメータあるいは
                ユーザ定義のルータのデフォルト値です。これらの値は、後ほど
                <methodname>Zend_Controller_Request::getParam()</methodname> あるいは
                <methodname>Zend_Controller_Action::_getParam()</methodname>
                メソッドでアクセスできます。
            </para>
        </note>

        <para>
            ルートで使用される変数のうち、'module'、'controller' および 'action'
            の 3 つは特別な扱いとなります。これらの特殊変数は、<classname>Zend_Controller_Dispatcher</classname>
            がディスパッチ先のコントローラとアクションを決定するために使用されます。
        </para>

        <note>
            <title>特殊変数</title>
            <para>
                これらの特殊変数の名前を変更することもできます。その場合は
                <classname>Zend_Controller_Request_Http</classname> の
                <methodname>setControllerKey()</methodname> メソッドや
                <methodname>setActionKey()</methodname> メソッドを使用します。
            </para>
        </note>

    </sect2>

    <sect2 id="zend.controller.router.default-routes">
        <title>デフォルトのルート</title>

        <para>
            <classname>Zend_Controller_Router_Rewrite</classname> がデフォルトのルートとして設定されています。
            これは <filename>controller/action</filename> 形式の <acronym>URI</acronym> にマッチします。
            さらに、パス要素の最初の部分にモジュール名を指定できます。つまり
            <filename>module/controller/action</filename> のような <acronym>URI</acronym> も可能です。
            また、<acronym>URI</acronym> にパラメータを追加した形式、つまり
            <filename>controller/action/var1/value1/var2/value2</filename>
            のような <acronym>URI</acronym> にもデフォルトで対応しています。
        </para>

        <para>
            ルータのマッチ処理についての例を示します。
        </para>

        <programlisting language="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>
            デフォルトのルートは、<classname>Zend_Controller_Router_Route_Module</classname>
            オブジェクトを 'default' という名前 (インデックス) で
            RewriteRouter に保存したものです。
            これは、以下のようにして作成します。
        </para>

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

        <para>
            このデフォルトルートが不要な場合は、独自の 'デフォルト' ルートで上書きします
            (つまり、'default' という名前で保存します)。
            あるいは、<methodname>removeDefaultRoutes()</methodname>
            で削除することもできます。
        </para>

        <programlisting language="php"><![CDATA[
// すべてのデフォルトルートを削除します
$router->removeDefaultRoutes();
]]></programlisting>

    </sect2>

    <sect2 id="zend.controller.router.rewritebase">
        <title>ベース URL およびサブディレクトリ</title>

        <para>
            Rewrite ルータはサブディレクトリ
            (例. <filename>http://domain.com/user/application-root/</filename>)
            内でも使用可能です。この場合、アプリケーションのベース <acronym>URL</acronym>
            (<filename>/user/application-root</filename>) の自動検出が
            <classname>Zend_Controller_Request_Http</classname> によって行われ、適切に使用されます。
        </para>

        <para>
            ベース <acronym>URL</acronym> の検出に失敗する場合は、
            <classname>Zend_Controller_Request_Http</classname> のメソッド <methodname>setBaseUrl()</methodname>
            を使用してベースパスを上書き指定できます
            (<link linkend="zend.controller.request.http.baseurl">ベース URL およびサブディレクトリ</link>を参照ください)。
        </para>

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

    </sect2>

    <sect2 id="zend.controller.router.global.parameters">
        <title>グローバルパラメータ</title>

        <para>
            グローバルパラメータをルータ内で設定できます。
            これは <methodname>setGlobalParam()</methodname>
            によってルートに自動的に適用されます。
            グローバルパラメータが設定されているにもかかわらず
            直接メソッドによっても設定された場合は、
            ユーザが設定したパラメータのほうがグローバルパラメータより優先されます。
            グローバルパラメータは、このように設定します。
        </para>

        <programlisting language="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:include href="Zend_Controller-Router-Route-Static.xml" />
        <xi:include href="Zend_Controller-Router-Route-Regex.xml" />
        <xi:include href="Zend_Controller-Router-Route-Hostname.xml" />
        <xi:include href="Zend_Controller-Router-Route-Chain.xml" />
        <xi:include href="Zend_Controller-Router-Route-Rest.xml" />
    </sect2>

    <sect2 id="zend.controller.router.add-config">
        <title>RewriteRouter での Zend_Config の使用法</title>

        <para>
            新しいルートを追加する際に、
            いちいちコードを書き換えるのではなく設定ファイルの変更で対応できると便利でしょう。
            そんなときには <methodname>addConfig()</methodname> メソッドを使用します。基本的な使用法は、
            まず <classname>Zend_Config</classname> 互換の設定を作成し、それをコードに読み込み、
            そして RewriteRouter に渡すことです。
        </para>

        <para>
            例として、次のような <acronym>INI</acronym> ファイルを考えてみましょう。
        </para>

        <programlisting language="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>
            上の <acronym>INI</acronym> ファイルを、次のようにして
            <classname>Zend_Config</classname> オブジェクトに読み込みます。
        </para>

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

        <para>
            上の例では、<acronym>INI</acronym> ファイルの 'routes' セクションを使用してルートを決めるよう、
            ルータに指定しています。このセクションの第一レベルのキーがルート名に対応します。
            上の例だと 'archive' と 'news' がこれにあたります。
            ルートの各エントリには、最低限 'route' エントリとひとつ以上の 'defaults'
            エントリが必要となります。また、オプションでひとつ以上の 'reqs'
            ('required' の略) も指定できます。ここで指定したものが、それぞれ
            <classname>Zend_Controller_Router_Route_Interface</classname>
            オブジェクトに対する引数となります。オプションのキー 'type' を使用すると、
            特定のルートで使用するルートクラスの型を指定できます。デフォルトでは、これは
            <classname>Zend_Controller_Router_Route</classname> となります。上の例では、
            'news' ルートで
            <classname>Zend_Controller_Router_Route_Static</classname>
            を使用するようにしています。
        </para>
    </sect2>

    <sect2 id="zend.controller.router.subclassing">
        <title>ルータのサブクラスの作成</title>

        <para>
            標準の rewrite ルータには、必要となるであろう機能のほとんどが組み込まれています。
            もし新しいルータ型を作成する必要があるとすれば、
            それは既存のルートに対して新しい機能を追加したり機能を変更したりしたい場合くらいでしょう。
        </para>

        <para>
            どこかで、既存のものとはまったく異なるルーティング処理が必要となったとしましょう。
            そんな場合には <classname>Zend_Controller_Router_Interface</classname>
            を使用します。これは、ルータとして最低限必要なひとつのメソッドのみを定義したインターフェイスです。
        </para>

        <programlisting language="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:
-->
