Zend_Application(1)

SVNに1.8のリリースタグが作られました。いよいよZend_Applicationがリリース待ちですね。
というわけで、試してみました。
マニュアル(英語版)はここでリリース済み、日本語版も間もなく出る?でしょう。

ファイルリストはこんな感じ

Application.php
Application
│
│  Exception.php
│
├─Bootstrap
│      Base.php
│      Exception.php
│      IBootstrap.php
│      IResourceBootstrap.php
│
├─Module
│      Autoloader.php
│      Bootstrap.php
│
└─Resource
        Base.php
        Db.php
        Exception.php
        Frontcontroller.php
        IResource.php
        Modules.php
        Session.php
        View.php

ものすごくはしょって書くと、

  • Zend_ApplicationはDIコンテナ。
  • Zend_Application_Bootstrap_Baseはその名の通り、Zend_Applicationを注入して実行するブートストラップの基本例
  • Zend_Application_Autoloaderはリソース毎のオートロードを可能にする

といったところ。
とりあえず最小限の動作確認として順に試してみました。

ZendFrameworkでシンプルにMVCを起動する方法

<?php
Zend_Controller_Front::run($controllerDir);

だと思いますが、フロントコントローラー起動前に各種設定等を処理する必要があったりします。そこで、getInstanceでインスタンスをとってから、各種メソッドで設定等を注入してdispatchするという方法も動作確認やテスト目的には素早く実行できてよいと思います。メソッドベースだとコード補完が利いて設定ファイルベースよりもわかりやすいような気がします。ドキュメントを見なくても用意されたメソッドから動作を追えます。

ZendStudioのサンプルにあるInitializer

<?php
require_once 'Initializer.php';
 
// Prepare the front controller. 
$frontController = Zend_Controller_Front::getInstance(); 

// Change to 'production' parameter under production environemtn
$frontController->registerPlugin(new Initializer('development'));    

// Dispatch the request using the front controller. 
$frontController->dispatch(); 

Zend StudioなどのサンプルやZendのフォーラム等での推奨だとInitializerをプラグインとして作成してフロントコントローラーに注入し、Bootstrapで始動するという方法で初期化フローが整理されていました。アプリケーション動作を一通り学習したあとなら、設定関係をズラーっと書くのは筋が悪いと感じてきます。そこで、Initializerをプラグインとして作成することで初期化タイミングを調整するという発想は自然ですね。
初期化処理をプラグイン内に隠ぺいして、シーケンス内のどのタイミングで初期化するかなどを考慮できるので、これはこれでわかりやすかったのですが・・・
私の場合、Initializerに設定を注入して起動したかったので下記のようにオプションを追加してました。固定的な処理部分はInitializerにハードコーディングしちゃいます。

<?php
    $initializer = new Initializer($env, $options);
    $initializer->getFrontController()->dispatch();

Initializerは内部でフロントコントローラーを初期化する時に自分をプラグインとして登録しています。

Zend_Applicationで起動させる

Zend_Applicationはさらに抽象化を進めて、リソースの設定と自動ロードを進化させています。設定ファイルの記述だけで、アプリケーションの動作部分や使用するクラスも簡単に変更できるようになっています。
Zend_Applicationでの起動自体に不思議があるわけではないですが、とりあえずとっかかりとしては起動かなというところで一応確認。

<?php
    $application = new Zend_Application(
        $env,
        array('bootstrap' => '/path/to/Bootstrap.php')

    );

    $application->bootstrap();
    $application->run();

Zend_Applicationの第2引数はオプションなのですが、ここに何も指定せずにbootstrapを実行すると例外が発生します。(1.8PR)最低限、bootstrapぐらいは設定してあげます。
このオプション部分にマニュアルにあるような環境設定等を投入することで動作を調整することが可能になります。
bootstrapオプションを設定すると、$application->bootstrap()はブートストラップクラスにアプリケーションを注入してインスタンスを作成しbootstrapを呼んでいます。$application内の設定がBootstrapクラスに注入されます。
概念的にはこんな感じ。

<?php
/**
 * Zend_Application::bootstrap()
 * @return void
 */
public function bootstrap()
{
        $this->_bootstrap = new Bootstrap($this);
        $this->_bootstrap->bootstrap();
}

Bootstrapは起動処理を行う。

基本的には、Bootstrapクラスにごちゃごちゃ書いてしまうと、せっかくZend_Applicationを使っている意味がないので、基本的にはやらないんですが、とりあえず動作確認ということでBootstrapに書いてみます。$applicationからBootstrapに完全に委譲される処理なので、起動シーケンスはBootstrap依存です。起動の振る舞いの基本はbootstrap 、runの順に実行するという、ある意味、決まり事ですね。
最も単純なBootstrapはこんな感じ。

<?php
class Bootstrap extends Zend_Application_Bootstrap_Base
{
    public function run()
    {
        Zend_Controller_Front::run('/path/to/controller');
    }
}

ここで、ハードコーディングしてどうするwな話ではありますが、これで動作させることが可能です。
Bootstrapに設定をハードコーディングした方が処理スピード的には速いです。しかしそれでは保守性や再利用性が乏しくなってしまい、拡張するのも大変になってしまいます。

Zend_Applicationに依存性を集約する

そこで、BootstrapでrunされるまでにZend_Applicationに対して必要な情報を格納しておくことで、Bootstrap::bootstrap()で、たとえばfrontControllerに対する設定を自動的に済ませるというのが、Zend_Applicationの目的です。
実際に、Zend_Applicationのコンストラクタでフロントコントローラーへの設定を行ってみます。

<?php
    $application = new Zend_Application(
        $env,
        array('bootstrap' => '/path/to/Bootstrap.php')
              'resources' => array(
                  'FrontController' => array(
                    'controllerDirectory' => APPLICATION_PATH . '/controllers',
                  )
              ),
    );

    $application->bootstrap();
    $application->run();

オプションに指定したresourcesでFrontControllerに関するオプションをセットしますが、デフォルトのBootstrapならZend_Application_Resource_FrontController::init()で設定されたオプションでフロントコントローラーを初期化してくれます。

これで、Bootstrap内にハードコーディングしたり、Bootstrapが独自に設定を読み込んだりするのではなく、Zend_Applicationに設定情報をすべて持ちまわらせることができるようになります。依存性はZend_Applicationの中に集約、隠ぺいして、Bootstrap等の機能クラスから分離してあげるわけです。
他にもデフォルトで用意されたResourceクラスがありますが、設定メソッドに多少漏れがあったりするので、自前でリソースクラスを書いた方がいいのかもしれないとも思います。
ZFの拡張クラスを作っている場合などは、必ずしも便利ではないような気もしますが、使い慣れてくればどこが拡張ポイントなのかはすぐにわかりそうです。
現状でも、

  • Bootstrapで_initHogehogeを実装する
  • リソースクラス名を設定する
  • リソースのインスタンスを注入する
  • Bootstrapのサブクラスを書く

といった具合に複数の設定方法が提供されていますので、あとはお好み次第といったところでしょうか。

一応getterな使い方も

<?php
    $application = new Zend_Application(
        $env,
        array('bootstrap' => '/path/to/Bootstrap.php')
              'resources' => array(
                  'FrontController' => array(
                    'controllerDirectory' => APPLICATION_PATH . '/controllers',
                  )
              ),
    );
$bootstrap = $application->getBootstrap();
$bootstrap->bootstrap();
$bootstrap->run();

なんでZend_Applicationなのか。

現状Zendのコンポーネントは個別にはZend_Configによる依存性注入で動作するようになっていますので、コンポーネントを跨ぐアプリケーションの横断的関心事についてはZend_Applicationに集約して初期化・注入させようというコンセプトだろうと思います。
この方法の利点はZend_Applicationでの設定でアプリケーション動作を変更できるというところまで理解している開発者どうしなら別のプロジェクトでも容易に理解できることです。デメリットは、そもそもZendFrameworkのMVCの詳細を理解していない場合、ブラックボックスを触ることになるので詳細なドキュメントが必要になってしまうのではないでしょうか。
学習コストが上がってしまうので敷居が少し高くなるともいえますが、現状でZFを触っている層は、DIとかDI実装されたものを分解するとか、なんか普通にできそうな人たちのような気がするので、問題ないかな?と思ったり。
ふぅ、Bootstrapにここまで力を入れる必要があんのか?という話は別として、一応試してみましたよ。。というお話でした。次は、AutoLoaderを試してみようかな・・・

FrontControllerではだめなのか。

Zend_ApplicationはDIコンテナとして使われる前提だと思いますが、私は同様の機能をFrontControllerを拡張して実装していました。どのみちFrontControllerはシングルトンでグローバルにどこからでも呼ばれる単一リソースなので、横断的というかグローバルに関心事を集中管理でき、ここをロケータにして実装すると楽なんですよね。
FrontControllerパターンを使う限り、大差ないと思うんですが、まぁ、DIを使いたくない人にとってはFrontControllerをいじられたくないでしょうから、別のコンポーネントとしてリリースするのが当然かなという気はします。開発の粒度の問題で、良しあしとは別の次元かなと。

あわせて読みたい

http://www.infoq.com/jp/articles/drinking-your-guice-too-quickly
シングルトン等スタティックな管理方法からサービスロケータ、DIコンテナへのパラダイムシフトについてわかりやすく説明してあります。
Zendだとまだまだ、そこかしこでスタティックな管理をしているので、この先どうハンドリングしていくのか見守っていきたいところです。