Zend Framework では、デフォルトで提供されるクラス以外の選択肢も用意しています。 たとえばリクエストオブジェクト、ルータ、 そしてレスポンスオブジェクトなどについて別の選択肢があります。
Zend_Controller_Request_Http
は、HTTP
環境で使用するリクエストオブジェクトです
Zend_Controller_Request_Http
はデフォルトのリクエストクラスであり、
Zend_Controller_Dispatcher
で用いられます。
Zend_Controller_Request_Http
は、関連する値へのアクセスをカプセル化します。
たとえばコントローラやアクションルータの変数のキー名や値、
URI からパースした追加のパラメータの値などにアクセスできます。
Zend_Controller_Request_Http
のプロキシとして動作することで、
スーパーグローバルの値にパブリックメンバとしてアクセスしたり、
現在のベース URL やリクエスト URI を管理することもできます。
スーパーグローバルの値はリクエストオブジェクトに設定することはできません。
そのかわりに setParam/getParam メソッドを使用して、
パラメータを設定あるいは取得します。
スーバーグローバルデータ | |
---|---|
|
特定のスーパーグローバルにアクセスするには、
パブリックメソッドを使用する方法もあります。たとえば、
$_POST['user']
の値を取得するには、リクエストオブジェクト上で
getPost('user')
をコールします。
Zend_Controller_Request_Http
は、
サブディレクトリで Zend_Controller_Router_Rewrite を使用することができます。
Zend_Controller_Request_Http は自動的にベース URL を検出し、
それを適切に設定します。
たとえば、index.php
をウェブサーバのサブディレクトリ
/projects/myapp/index.php
においた場合は、ベース URL
(rewrite base) は /projects/myapp
にしなければなりません。
マッチするルートを見つける前に、この文字列がパスの先頭から取り除かれます。
これにより、すべてのルートに余計な文字を追加する必要がなくなります。
ルート 'user/:username'
は、
http://localhost/projects/myapp/user/martel
および
http://example.com/user/martel
の両方にマッチするようになります。
URL の検出は大文字小文字を区別します | |
---|---|
自動的なベース URL の検出処理は大文字小文字を区別します。そのため、 URL とファイルシステムのサブディレクトリ名が確実に一致する必要があります (たとえ Windows マシンであっても同様です)。大文字小文字が一致しなかった場合は noRoute アクションがコールされます。 |
ベース URL の検出に失敗する場合は、
Zend_Controller_Request_Http
クラス、あるいは
Zend_Controller_Front
クラスの
setBaseUrl()
メソッドを使用して
ベースパスを上書き指定することができます。
一番簡単な方法は Zend_Controller_Front
で設定することです。
この設定はリクエストオブジェクトに引き継がれます。
独自のベース URL を設定する例を示します。
/** * Zend_Controller_Front で独自のベース URL を指定することによるリクエストのディスパッチ */ $router = new Zend_Controller_Router_Rewrite(); $controller = Zend_Controller_Front::getInstance(); $controller->setControllerDirectory('./application/controllers') ->setRouter($router) ->setBaseUrl('/projects/myapp'); // ベース URL を指定します! $response = $controller->dispatch();
Zend_Controller_Router_Rewrite
は、標準のルータです。
ルーティングとは、URI (ベース URL から取得した URI の一部)
を展開し、どのコントローラのどのアクションが
リクエストを処理するのかを決める処理のことです。
コントローラの値やアクション、そしてその他のパラメータが
Zend_Controller_Request_Http
オブジェクトにまとめられます。
このオブジェクトを処理するのが Zend_Controller_Dispatcher_Standard
です。
ルーティングが行われるのは一度だけ、すなわちリクエストを最初に受け取ってから
最初のコントローラに処理が渡される際だけです。
Zend_Controller_Router_Rewrite
は、mod_rewrite 風の機能を
PHP だけで実現できるように設計されています。
この処理は Ruby on Rails のルーティングを多少参考にしており、
ウェブサーバの URL 書き換えに関する前提知識を必要としません。
以下の単純な 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
Rewrite ルータを IIS ウェブサーバで使用するには Isapi_Rewrite を Isapi 拡張モジュールとしてインストールします。そして次のようなルールを記述します。
RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css)$)[\w\%]*$)? /index.php [I]
IIS Isapi_Rewrite | |
---|---|
IIS を使用すると、 |
Lighttpd の場合は、次のようなルールを使用します。
url.rewrite-once = ( ".*\.(js|ico|gif|jpg|png|css)$" => "$0", "" => "/index.php")
Rewrite ルータを適切に使用するには、まずそのインスタンスを作成し、 次にユーザ定義のルーティングを追加し、それをコントローラに注入しなければなりません。 以下にコードの例を示します。
/* ルータを作成します */ $router = $ctrl->getRouter(); // デフォルトで rewrite ルータを返します $router->addRoute( 'user', new Zend_Controller_Router_Route('user/:username', array('controller' => 'user', 'action' => 'info')) );
RewriteRouter で最も重要なのが、ユーザ定義のルーティングです。
これは、RewriteRouter の addRoute メソッドをコールして追加します。
このメソッドに、Zend_Controller_Router_Route_Interface
を実装したクラスの新しいインスタンスを渡します。
$router->addRoute('user', new Zend_Controller_Router_Route('user/:username'));
Rewrite ルータには、4 種類の基本的なルーティング方式があります (そのうちのひとつは特別なものです)。
これらのルーティングは、チェインやユーザ定義のルーティング方式を作成する際に何度も使用します。 任意の設定でお好みの数のルーティングを使用することができますが、 Module ルートだけは例外です。これを使用するのは一度だけで、 もっとも汎用的なルート (デフォルト) として使用します。 個々のルーティング方式については、後ほど詳細に説明します。
addRoute への最初のパラメータはルートの名前です。 これを使用して、ルータがルートを処理します。 たとえば URL の生成などに使用します。 二番目のパラメータはルート自身となります。
注意 | |
---|---|
ルート名のもっとも一般的な使用例は、 Zend_View の url ヘルパーです。 <a href="<?= $this->url('user', array('username' => 'martel')) ?>">Martel</a>
これは |
ルーティング処理は、定義されたすべてのルートから リクエスト URI にマッチする定義を探すことによって行います。 マッチするものが見つかれば、ルートのインスタンスから変数の値が返され、 それを Zend_Controller_Request オブジェクトに注入します。 これを、後にディスパッチャやユーザが作成したコントローラで使用します。 マッチするものが見つからない場合は、チェイン内の次のルートを調べます。
定義の順番 | |
---|---|
一番最後にマッチしたルートが適用されるので、 汎用的なルートは最初に定義するようにしましょう。 |
返される値 | |
---|---|
ルーティングの結果返される値は、URL パラメータあるいは ユーザ定義のルータのデフォルト値です。これらの値は、後ほど Zend_Controller_Request::getParam あるいは Zend_Controller_Action::_getParam メソッドでアクセスできます。 |
ルートで使用される変数のうち、'module'、'controller' および 'action' の 3 つは特別な扱いとなります。これらの特殊変数は、Zend_Controller_Dispatcher がディスパッチ先のコントローラとアクションを決定するために使用されます。
特殊変数 | |
---|---|
これらの特殊変数の名前を変更することもできます。その場合は
|
Zend_Controller_Router_Rewrite がデフォルトのルートとして設定されています。
これは controller/action
形式の URI にマッチします。
さらに、パス要素の最初の部分にモジュール名を指定することができます。つまり
module/controller/action
のような URI も可能です。
また、URI にパラメータを追加した形式、つまり
controller/action/var1/value1/var2/value2
のような URI にもデフォルトで対応しています。
ルータのマッチ処理についての例を示します。
// 以下の設定を前提とします $ctrl->setControllerDirectory( array( 'default' => '/path/to/default/controllers', 'news' => '/path/to/blog/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
デフォルトのルートは、Zend_Controller_Router_Route_Module
オブジェクトを 'default' という名前 (インデックス) で
RewriteRouter に保存したものです。
これは、以下のようにして作成します。
$compat = new Zend_Controller_Router_Route_Module(array(), $dispatcher, $request); $this->addRoute('default', $compat);
このデフォルトルートが不要な場合は、独自の 'デフォルト' ルートで上書きします
(つまり、'default' という名前で保存します)。
あるいは、removeDefaultRoutes()
で削除することもできます。
// すべてのデフォルトルートを削除します $router->removeDefaultRoutes();
Rewrite ルータはサブディレクトリ
(例. http://domain.com/~user/application-root/
>)
内でも使用可能です。この場合、アプリケーションのベース URL
(/~user/application-root
) の自動検出が
Zend_Controller_Request_Http
によって行われ、適切に使用されます。
ベース URL の検出に失敗する場合は、
Zend_Controller_Request_Http
のメソッド setBaseUrl()
を使用してベースパスを上書き指定することができます
(項7.4.2.3. 「ベース URL およびサブディレクトリ」 を参照ください)。
$request->setBaseUrl(/~user/application-root/);
Zend_Controller_Router_Route
はフレームワークの標準のルートです。
簡単に利用でき、柔軟なルート定義が可能です。各ルートには、まず
(静的および動的な) URL のマッピングが含まれ、
そしてデフォルト値および変数についての制限を指定して初期化します。
とある架空のアプリケーションで、コンテンツの作者情報のページが必要になったとしましょう。
ブラウザで http://domain.com/author/martel
にアクセスした際に、"martel" とかいう人についての情報を見たいわけです。
この機能を実現するためのルートは、次のようになります。
$route = new Zend_Controller_Router_Route( 'author/:username', array('controller' => 'profile', 'action' => 'userinfo') ); $router->addRoute('user', $route);
Zend_Controller_Router_Route
のコンストラクタの最初のパラメータは、ルートの定義です。
これを URL にマッチさせます。ルート定義は静的な部分と動的な部分で構成され、
それをスラッシュ ('/') で連結します。
動的な部分を変数と予備、変数名の前にコロンをつけて
(例. :username
) 表します。
静的な部分は単なるテキスト (例. author
) です。
文字の使用法 | |
---|---|
現在の実装では、(スラッシュ以外の) 任意の文字を変数名として使用できます。しかし、 PHP の変数名として使用できる文字だけを用いることを強く推奨します。 このようにしておくことで、 将来実装が変更されたときにバグを引き起こす可能性を抑えられます。 |
この例のルートは、ブラウザで
'http://domain.com/author/martel'
を指した際にマッチします。
この場合、すべての変数の値が Zend_Controller_Request
オブジェクトに注入され、ProfileController からアクセスできるようになります。
この例が返す変数は、以下のようなキーと値のペアを持つ配列となります。
$values = array( 'username' => 'martel', 'controller' => 'profile', 'action' => 'userinfo' );
その後、Zend_Controller_Dispatcher
は
(デフォルトモジュールの) ProfileController クラスにある
userinfoAction メソッドを実行します。変数にアクセスするには、
Zend_Controller_Action::_getParam あるいは Zend_Controller_Request::getParam
メソッドを使用します。
public function userinfoAction() { $request = $this->getRequest(); $username = $request->getParam('username'); $username = $this->_getParam('username'); }
ルート定義には、特殊文字 (ワイルドカード) を含めることができます。これは '*' 記号で表します。 これを使用して、Module ルートと同様にパラメータを扱う (変数名 => 値 のペアを URI で定義する) ことができます。 次のルートは、Module ルートの挙動をまねたものです。
$route = new Zend_Controller_Router_Route(':controller/:action/*'); $router->addRoute('default', $route);
ルートで使用するすべての変数についてデフォルト値を指定することができます。
これは、 Zend_Controller_Router_Route
のコンストラクタの 2 番目のパラメータで指定します。
このパラメータは、変数名をキーとする配列で、
対応する値にそのデフォルト値を指定します。
$route = new Zend_Controller_Router_Route( 'archive/:year', array( 'year' => 2006 ) ); $router->addRoute('archive', $route);
上のルートは 'http://domain.com/archive/2005'
および
'http://example.com/archive'
のような URL にマッチします。後者の場合、変数 year にはデフォルト値である
2006 が設定されます。
この例は、year 変数をリクエストオブジェクトに注入することになります。
そしてルーティング情報が存在しない
(コントローラやアクションのパラメータが定義されていない) ので、
アプリケーションはデフォルトのコントローラのデフォルトアクションメソッド
(ともに Zend_Controller_Dispatcher_Abstract
で定義されています)
にディスパッチします。より使いやすくするには、
ルートのデフォルトとしてコントローラとアクションを定義しておく必要があります。
$route = new Zend_Controller_Router_Route( 'archive/:year', array( 'year' => 2006, 'controller' => 'archive', 'action' => 'show' ) ); $router->addRoute('archive', $route);
このルートは、ArchiveController の showAction を実行します。
Zend_Controller_Router_Route
のコンストラクタの
三番目のパラメータで、変数の制約を指定することができます。
これは、正規表現で指定します。
$route = new Zend_Controller_Router_Route( 'archive/:year', array( 'year' => 2006, 'controller' => 'archive', 'action' => 'show' ), array('year' => '\d+') ); $router->addRoute('archive', $route);
上の例のルートでは、year 変数の値が数値データである場合にのみ
Rewrite ルータにマッチします。つまり
http://domain.com/archive/2345
はマッチしますが
http://example.com/archive/test
はマッチしません。
この場合はチェイン内の次のルートに処理を移します。
これまでの例では、すべて動的なルートを使用していました。 つまり、特定のパターンにマッチするものについてのルートです。 しかし、時には特定のルートを固定してしまい、 わざわざ正規表現エンジンを動かしたくない場合もあるでしょう。 そんなときには静的なルートを使用します。
$route = new Zend_Controller_Router_Route_Static( 'login', array('controller' => 'auth', 'action' => 'login') ); $router->addRoute('login', $route);
上のルートは http://domain.com/login
という URL
にマッチします。
デフォルトのルートや静的なルートに加えて、正規表現によるルートも使用可能です。 このルートは他のものに比べてより強力で柔軟なものですが、 多少複雑になってしまいます。そして、より高速になります。
標準のルートと同様、このルートを初期化する際にはルートの定義とデフォルトを指定する必要があります。 サンプルとして、archive ルートを作成してみましょう。 これは先ほど定義してものとほぼ同じですが、今回は Regex ルートを使用しています。
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( 'controller' => 'archive', 'action' => 'show' ) ); $router->addRoute('archive', $route);
定義された正規表現のパターンが、リクエストオブジェクトに注入されます。
上の例では、http://domain.com/archive/2006
がマッチした後の結果の値は次のような配列になります。
$values = array( 1 => '2006', 'controller' => 'archive', 'action' => 'show' );
注意 | |
---|---|
ルータとのマッチングを行う前に、URL の先頭と最後のスラッシュは取り除かれます。
結果として、URL |
注意 | |
---|---|
行頭と行末を表す文字 (それぞれ '^' および '$') が、すべての式の前後に自動的に付加されます。 したがって、これらは正規表現で指定する必要はありません。 |
注意 | |
---|---|
このルートクラスは、区切り文字として |
定義されたサブパターンの内容は、通常通りの方法で取得できます。
public function showAction() { $request = $this->getRequest(); $year = $request->getParam(1); // $year = '2006'; }
注意 | |
---|---|
このキーは、文字列 ('1') ではなく数値の 1 であることに注意しましょう。 |
このルートは、標準のルートとまったく同様に動作するわけではありません。 'year' のデフォルトが設定されていないからです。 また、year のデフォルトを設定してこれをオプション扱いにしたとしても、 最後のスラッシュをどうするかという問題が残ります。 これを解決するには、year 部をスラッシュを含めてオプションにし、 その数値部のみを取得するようにします。
$route = new Zend_Controller_Router_Route_Regex( 'archive(?:/(\d+))?', array( 'year' => '2006', 'controller' => 'archive', 'action' => 'show' ) ); $router->addRoute('archive', $route);
まだ問題が残っていることにおそらくお気づきでしょう。 パラメータとして数値のキーを使用するのはなかなか難しく、 長い目で見れば問題を引き起こす可能性が高くなります。 そこで三番目のパラメータの登場です。 このパラメータは、正規表現サブパターンとパラメータ名のキーを関連付けます。 簡単な例を見てみましょう。
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( 'controller' => 'archive', 'action' => 'show' ), array( 1 => 'year' ) ); $router->addRoute('archive', $route);
これは、リクエストオブジェクトに以下のように注入されます。
$values = array( 'year' => '2006', 'controller' => 'archive', 'action' => 'show' );
関連付けは両方の方法で定義でき、任意の環境 (例. Zend_Config) で動作します。 キーには変数名あるいはサブパターン番号のいずれかを含めることができます。
注意 | |
---|---|
サブパターンのキーは整数値でなければなりません。 |
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( ... ), array( 1 => 'year' ) ); // あるいは $route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( ... ), array( 'year' => 1 ) );
リクエストの値から数値キーが消え、代わりに名前がつけられたことに注目しましょう。 もちろん、お望みなら数値での指定と名前での指定を共用することもできます。
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)/page/(\d+)', array( ... ), array( 'year' => 1 ) );
この結果、リクエスト内には数値キーと名前つきキーが共存することになります。
たとえば、URL http://domain.com/archive/2006/page/10
は次のような値になります。
$values = array( 'year' => '2006', 2 => 10, 'controller' => 'archive', 'action' => 'show' );
正規表現を簡単に反転させることはできないので、 url ヘルパーやこのクラスのメソッドを使用するには 逆の URL を準備しておく必要があります。 逆方向のパスは sprintf() 形式の文字列で表し、 コンストラクタの四番目のパラメータとして指定します。
$route = new Zend_Controller_Router_Route_Regex( 'archive/(\d+)', array( ... ), array( 'year' => 1 ), 'archive/%s' );
これまで説明してきたことは、すべて標準のルートオブジェクトでも可能なことです。
それでは、Regex ルートを使用するメリットはいったい何なのでしょう?
これを使用すると、あらゆる形式の URL を制約なしに定義することができます。
仮に、あなたが blog を持っており
http://domain.com/blog/archive/01-Using_the_Regex_Router.html
のような URL を作成したいと考えたとしましょう。
これまでの内容を組み合わせると、答えはこのようになります。
$route = new Zend_Controller_Router_Route_Regex( 'blog/archive/(\d+)-(.+)\.html', array('controller' => 'blog', 'action' => 'view'), array(1 => 'id', 2 => 'description'), 'blog/archive/%d-%s.html' ); $router->addRoute('blogArchive', $route);
新しいルートを追加する際に、
いちいちコードを書き換えるのではなく設定ファイルの変更で対応できると便利でしょう。
そんなときには addConfig()
メソッドを使用します。基本的な使用法は、
まず Zend_Config 互換の設定を作成し、それをコードに読み込み、
そして 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" ; あるいは: 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
を使用するようにしています。
Zend_Controller_Response_Http
は、
HTTP 環境での使用に適したレスポンスオブジェクトです。
ヘッダの設定/取得/消去の機能があります。また、
__toString()
メソッドを使用して、
レスポンス本体の前に全ヘッダを一括送信することもできます。
setHeader()
は、二つの引数を受け取ります。
最初がヘッダの型で、次がヘッダの値です。三番目のオプションのパラメータを
true にすると、同じ型の既存のヘッダを強制的に上書きします。