技術日記
[Zend Framework] Zend_Application動作原理の翻訳Edit

Zend_Application動作原理の原文はこちら。

4.3. 動作原理

MVCアプリケーションを設定して動かすためには、データベース、ビューやビューヘルパー、レイアウトなどの設定や、プラグイン、アクションヘルパーの登録などなど、多くの機能を利用するためのコードがたくさん必要になる。 加えて、テストやcronタスク、サービススクリプトなどを実行するために、同じコードを再利用したいこともよくあるだろう。*1そういう処理は、ブートストラップ(起動準備処理*2)に書いてしまうのが簡単なやり方だが、環境依存の初期化処理が必要となることもあるだろう。たとえば、cronタスクにMVCはいらないし、サービススクリプトにはDBレイヤーだけあればいい。 Zend_Applicationでは、OOPによりブートストラップをカプセル化して再利用可能にすることで、そのような要望に応えようとしている。 Zend_Applicationは以下の3つの領域をサポートする。
  • Zend_Application: PHP環境設定の読み込み、インクルードパスおよび(クラスの)オートロードへの対応、対応するブートストラップのインスタンス化。
  • Zend_Application_Bootstrap: ブートストラップへのインターフェース。Zend_Application_Bootstrap_Bootstrapが、依存性チェックや必要に応じたリソースの読み込みなど、ブートストラップで必要となる機能群を提供する。
  • Zend_Application_Resource: ブートストラップによって必要に応じてロードされるリソースや、いくつかのデフォルトのリソースなど、標準的なリソースへのインターフェースを提供する。
開発者はアプリケーションのために、Zend_Application_Bootstrap_Bootstrapクラスを継承するか、あるいはZend_Application_Bootstrap_Bootstrapperインターフェースを(最低限)実装したブートストラップクラスを作成する。その呼び出し部(=public/index.php)ではZend_Applicationが読み込まれ、以下のパラメータを使ってインスタンス化される。
  • 現状の環境(定義名)
  • ブートストラップオプション群
ブートストラップオプション群は、ブートストラップクラスが書かれたファイルへのパスを含み、その他オプションとして以下のような要素を持つ。
  • 追加するインクルードパス
  • オートローダーに追加登録するネームスペース
  • 初期化に利用するphp.ini設定
  • ブートストラップクラスのクラス名(未指定ならば"Bootstrap")
  • ペアで使われるリソースのパスとプレフィックス*3
  • 利用するリソース(クラス名もしくは短縮名)
  • 追加でロードする設定ファイルのパス
  • その他の設定値
設定値はPHPの配列、Zend_Configオブジェクト、もしくは設定ファイルへのパスで表現する。
4.3.1. ブートストラップ
Zend_Applicationの二つ目の役割は、アプリケーションのブートストラップ(起動準備処理)を行うことだ。ブートストラップはZend_Application_Bootstrap_Bootstrapperインターフェースに以下のようなAPIとして定義されており、それらを最低限実装する必要がある。
interface Zend_Application_Bootstrap_Bootstrapper
{
public function __construct($application);
public function setOptions(array $options);
public function getApplication();
public function getEnvironment();
public function getClassResources();
public function getClassResourceNames();
public function bootstrap($resource = null);
public function run();
}
このAPIは、環境(定義名)と設定オプションをアプリケーションオブジェクトから受け取り、ブートストラップ時に用意する必要があるリソース群を知らせ、そしてブートストラップとアプリケーションの実行を行うためのものだ。 これらのインターフェースをスクラッチで、あるいはZend_Application_Bootstrap_BoostrapAbstractを継承して、あるいはZend_Application_Bootstrap_Bootstrapを利用して実装することができる。 これ以外にも、あなたが慣れなければならないことはたくさんある。
4.3.1.1. リソースメソッド
Zend_Application_Bootstrap_BootstrapAbstractの実装では、クラスにリソースメソッドというものを定義するようになっている。_initから始まるprotectedメソッドは、すべてリソースメソッドとして扱われる。 一つのリソースメソッドを実行するには、bootstrap()メソッドにそのリソース名を渡して呼ぶ。リソース名とは、メソッド名から_initを取り除いたものだ。 いくつかのリソースメソッドを実行したいならリソース名の配列を渡せばいい。すべてのリソースメソッドを実行する場合は引数なしで呼ぶ。 以下のブートストラップクラスを見てみよう。
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initFoo()
{
// ...
}
protected function _initBar()
{
// ...
}
protected function _initBaz()
{
// ...
}
}
_initFoo()メソッドを実行したい場合は、以下のようにする。
$bootstrap->bootstrap('foo');
_initFoo()と_initBar()メソッドを実行したい場合は、以下のようになる。
$bootstrap->bootstrap(array('foo', 'bar'));
すべてのリソースメソッドを実行する場合は、引数なしでbootstrap()を呼ぶ。
$bootstrap->bootstrap();
4.3.1.2. リソースプラグインを利用したブートストラップ
よりブートストラップの再利用性を高くしたい人のために、リソースプラグインという仕組みも用意されている。これは(コードではなく)設定を使ってシンプルにリソースを利用できるようにする。後で作り方について説明するが、ここではまず使い方についてのみ説明する。 もしブートストラップでリソースプラグインを使いたいならば、Zend_Application_Bootstrap_ResourceBootstrapperインターフェースを追加実装する必要がある。このインターフェースはリソースプラグインの配置や登録、読み込みを行う。
interface Zend_Application_Bootstrap_ResourceBootstrapper
{
public function registerPluginResource($resource, $options = null);
public function unregisterPluginResource($resource);
public function hasPluginResource($resource);
public function getPluginResource($resource);
public function getPluginResources();
public function getPluginResourceNames();
public function setPluginLoader(Zend_Loader_PluginLoader_Interface $loader);
public function getPluginLoader();
}
リソースプラグインが提供する基本機能は、アプリケーションをまたがって再利用可能なリソースの初期化機構だ。これにより、ブートストラップを比較的クリーンに保つことができる。ブートストラップコードに手を加えず、新しいリソースを導入することができるのだ。 Zend_Application_Bootstrap_BootstrapAbstract(とそれを継承したZend_Application_Bootstrap_Bootstrap)にはこのインターフェースも実装されており、リソースプラグインを利用できるようになっている。 リソースプラグインを利用するには、アプリケーションオブジェクトやブートストラップのオプションとして、その設定を行う必要がある。これらは設定ファイルやオプションパラメータとして渡される。設定はリソース名のキーを持つ値のペアとなる。リソース名はクラスプレフィックスの後に続く部分となる。たとえばZend Frameworkに同梱されるリソースは、"Zend_Application_Resource_"というクラスプレフィックスを持ち、その後にリソース名が続くことになる。例を挙げると、
$application = new Zend_Application(APPLICATION_ENV, array(
'resources' => array(
'FrontController' => array(
'controllerDirectory' => APPLICATION_PATH . '/controllers',
),
),
));
これは、(controllerDirecotry)オプションが指定された"FrontController"リソースが使われることを示している。 独自のリソースプラグインを作る、もしくはサードパーティ製のリソースプラグインを使う場合、ブートストラップにそれらがどこにあるかを教えておく必要がある。ブートストラップは内部でZend_Loader_PluginLoaderを使うので、クラスプレフィックスと検索パスのペアが必要となる。 たとえば、APPLICATION_PATH/resources/に独自のリソースプラグインがあるとして、そのクラスプレフィックスがMy_Resourceだとすると、アプリケーションオブジェクトに以下のような情報を渡すことになる。
$application = new Zend_Application(APPLICATION_ENV, array(
'pluginPaths' => array(
'My_Resource' => APPLICATION_PATH . '/resources/',
),
'resources' => array(
'FrontController' => array(
'controllerDirectory' => APPLICATION_PATH . '/controllers',
),
),
));
これで指定したディレクトリのリソースを使えるようになる。 リソースメソッドのように、bootstrap()メソッドを使ってリソースプラグインを実行できる。リソースメソッド同様に、一つのリソースプラグイン、複数のプラグイン(配列を使って)、すべてのプラグインを指定できる。加えていうと、リソースメソッドと混在して実行することも可能だ。
// 一つ実行
$bootstrap->bootstrap('FrontController');
// 複数実行
$bootstrap->bootstrap(array('FrontController', 'Foo'));
// すべてのプラグインとメソッドを実行
$bootstrap->bootstrap();
4.3.1.3. リソースレジストリ
たいていのリソースメソッドやプラグインは、オブジェクトの初期化を行う。そして多くの場合、そのオブジェクトはアプリケーションの他のところでも利用される。その場合、オブジェクトにはどうやってアクセスすればいいだろうか? Zend_Application_Bootstrap_BootstrapAbstractは、それらのオブジェクトを保存するためのローカルなレジストリを用意しており、(リソースメソッドやプラグインで)リソースオブジェクトをreturnするだけで保存が行われる。 柔軟性を高くするために、レジストリは内部では"container"として参照される。オブジェクトだけがそこに保存される。リソースはリソース名にちなんだプロパティとして登録される。デフォルトでは、Zend_Registryインスタンスが利用されるが、他のオブジェクトを使うこともできる。setContainer()、getContainer()メソッドを使ってコンテナを操作できる。getResource($resource)でコンテナからリソースを取得し、hasResource($resource)でリソースが登録済みかどうかを調べることができる。 以下の例は、基本的なビューリソースだと思って欲しい
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initView()
{
$view = new Zend_View();
// more initialization...
return $view;
}
}
登録されているか確認したり、リソースを取得したりするのは、以下のようなコードとなる。
// has/getResource()の二つを使う
if ($bootstrap->hasResource('view')) {
$view = $bootstrap->getResource('view');
}
// コンテナを通して
$container = $bootstrap->getContainer();
if (isset($container->view)) {
$view = $container->view;
}
レジストリやコンテナがグローバルではないことに気をつけて欲しい。つまり、リソース(オブジェクト)を取得するにはブートストラップにアクセスする必要があるのだ。Zend_Application_Bootstrap_Bootstrapはそのための方法を用意している。run()が実行されている間、ブートストラップは自分自身をフロントコントローラの"bootstrap"パラメータとして登録している。それを使えば、ルーターやディスパッチャー、プラグインやアクションコントローラからリソースオブジェクトにアクセスできるようになる。 たとえばアクションコントローラの中からビューリソースにアクセスしたい場合は、以下のようになる。
class FooController extends Zend_Controller_Action
{
public function init()
{
$bootstrap = $this->getInvokeArg('bootstrap');
$view = $bootstrap->getResource('view');
// ...
}
}
4.3.1.4. 依存性の追跡
リソースメソッドやプラグインを実行するだけでなく、それらが必ず1度だけ実行されることを保証する必要がある。アプリケーションをブートストラップするたびに、(リソースの初期化処理が)複数回実行されてしまうのではオーバーヘッドを招くことになるからだ。 また、いくつかのリソースは他のリソースに依存している可能性がある。この二つの問題を解決するために、Zend_Application_Bootstrap_BootstrapAbstractは簡単で効果的な依存性の追跡機構を備えている。 前記の通り、すべてのリソース(メソッドもしくはプラグイン)は、bootstrap($resource)メソッドの$resource指定によって、一つのリソース、もしくは複数のリソース(配列)、もしくはすべてのリソース(引数なし)がブートストラップされるようになっている。 あるリソースが他のリソースに依存する場合、依存先のリソースがすでにブートストラップ済みであることを保証するために、依存元のリソース(メソッドやプラグイン)コードの中で(依存先のリソースをブートストラップする)bootstrap()メソッドを呼んでおくべきだ。その後でもしも同じリソースがブートストラップされたとしても、その呼び出しは無視されることになる。 リソースメソッド内でのそのような(依存するリソースの)呼び出しを行う場合、以下のように書く。
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initRequest()
{
// フロントコントローラが初期化済みであることを保証する
$this->bootstrap('FrontController');
// ブートストラップレジストリからフロントコントローラを取得する
$front = $this->getResource('FrontController');
$request = new Zend_Controller_Request_Http();
$request->setBaseUrl('/foo');
$front->setRequest($request);
// リクエストオブジェクトがブートストラップレジストリに保存されるようにする
return $request;
}
}

4.3.2. リソースプラグイン

前記の通り、再利用性の高いブートストラップリソースを作成し、あなたのコードから関係が薄い(リソース初期化の)コードを取り除きたいのならば、リソースプラグインを利用するといいだろう。Zend Frameworkはいくつもの標準的なリソースプラグインを同梱しているが、その意図は、開発者は独自に必要となるような初期化コードのみを書くべきだと考えるからだ。 リソースプラグインはZend_Application_Resource_Resourceインターフェースを実装すればいい。より簡単にはZend_Application_Resource_ResourceAbstractクラスを継承すればいい。基本インターフェースはシンプルだ。
interface Zend_Application_Resource_Resource
{
public function __construct($options = null);
public function setBootstrap(
Zend_Application_Bootstrap_Bootstrapper $bootstrap
);
public function getBootstrap();
public function setOptions(array $options);
public function getOptions();
public function init();
}
このインターフェースには、リソースはコンストラクタでオプションを受け取る、オプションの設定や取得が可能、ブートストラップオブジェクトの設定や取得が可能、そして初期化を実行するメソッドが定義されている。 次のサンプルは、アプリケーションで利用される一般的なビューの初期化処理を想定している。ドキュメントタイプ、CSS、JavaScriptを扱い、オプションから得た基本ドキュメントタイトルを渡す。そのような機能を持つリソースは以下のようになる。
class My_Resource_View extends Zend_Application_Resource_ResourceAbstract
{
protected $_view;
public function init()
{
// ビューを返し、レジストリに保存する
return $this->getView();
}
public function getView()
{
if (null === $this->_view) {
$options = $this->getOptions();
$title   = '';
if (array_key_exists('title', $options)) {
$title = $options['title'];
unset($options['title']);
}
$view = new Zend_View($options);
$view->doctype('XHTML1_STRICT');
$view->headTitle($title);
$view->headLink()->appendStylesheet('/css/site.css');
$view->headScript()->appendfile('/js/analytics.js');
$viewRenderer =
Zend_Controller_Action_HelperBroker::getStaticHelper(
'ViewRenderer'
);
$viewRenderer->setView($view);
$this->_view = $view;
}
return $this->_view;
}
}
このリソースプラグインのプレフィックスパスを登録すると、アプリケーションから使えるようになる。プラグインローダーを利用することにより、デフォルトの"View"リソースプラグインが上書きされ、独自のプラグインが代わりに利用されるようになる。

*1:サービススクリプトって、定期実行するわけではないコマンドライン系の処理のことか? たとえば初期セットアップスクリプトとか。日本語でもサービススクリプトっていうんだろうか? cronの方ならバッチが一番近い感じなんだけど。

*2:ブートストラップは日本語にせずに、そのままカタカナで書いてしまうことにした

*3:Bootstrapで使われているpluginpaths設定のことかなー。だったら「リソースのプレフィックスとパスのペア」って感じになる?

Published At2009-06-24 09:00Updated At2019-12-30 23:55