Dispatcher_Standardでちょっとメモ

ちょっとわかりにくいところがあったのでメモ
Controller_FrontのdispatchからController_Dispatcher_Standardのdispatchが呼ばれる。クラスのロードはloadClassなのだが、その際、_curModuleがセット済みであることが必要になる。_curModuleと_curDirectoryはgetControllerClassでセットされる。また、同じメソッド内で$requestに対してsetModuleされることもある。
また、コントローラーが見つからなかった場合は、getDefaultControllerClassが呼ばれて、同様の処理が走る。
getControllerClass、getDefaultControllerClassは単純なgetterかと思いきや、dispatch処理に必須な初期化処理が行われいている模様。

動作に問題があるわけじゃないので問題ないけれど、自戒も含めて確認。

カップリング

この状態では、loadClassはgetControllerClass内の初期化処理に依存する。dispatchのフローとして必要と言うべきかもしれないが。

参照透過性

一般にgetterはそのオブジェクトのプロパティを取得する。設定されていない場合にデフォルト値をセットしたり、フィルターをかけたりといったことはあるかもしれない。なるべくなら、getterでは、対象にしているプロパティ以外に副作用を発生させないようにしたい(個人的なポリシーで)。getControllerClassを使うときは要注意。

予期せぬ動作?

なんとなく、だが、通常動作中に、アクションコントローラー等からdispatche->getDefaultControllerClass($request)を呼んだりすると、dispatcherの内部プロパティが意図せず変更されたりっていう可能性があるんじゃないかと・・・まぁ、普通しないんだろうけど。

対応

逆にここは自作しようって気になったのでよかったかも。
_prepare($request)みたいなメソッドでディスパッチャ内プロパティを現在のリクエストで初期化するように変更してみた。dispatch内も変更して、すっきりした。
互換クラスを書くにしても、基本仕様が分散しているとやりにくいから。

こんな感じにしてみた。

dispatchで_prepareしてから使う。動作確認したところでは問題ない模様。getDefaultControllerNameやgetControllerNameの初期化処理をオプションにする。

<?php
    protected function _prepare(Zend_Controller_Request_Abstract $request)
    {
        $module              = $request->getModuleName();
        $controllerDirs     = $this->getControllerDirectory();

        if (!$this->isValidModule($module)) {
            if ($this->isValidModule($this->_defaultModule)) {
                $module = $this->_defaultModule;
                $request->setModuleName($module);
            } else {
                require_once 'Zend/Controller/Exception.php';
                throw new Zend_Controller_Exception('No default module defined for this application');
            }
        }
        $this->_curModule    = $module;
        $this->_curDirectory = $controllerDirs[$module];

        $controllerName = $request->getControllerName();
        $this->_curControllerClass = $this->formatControllerName($controllerName);
        /**
         * Get controller class
         */
        $fileSpec    = $this->classToFilename($this->_curControllerClass);
        $dispatchDir = $this->getDispatchDirectory();
        $test        = $dispatchDir . DIRECTORY_SEPARATOR . $fileSpec;
        if (!Zend_Loader::isReadable($test)) {
            if (!$this->getParam('useDefaultControllerAlways') && !empty($controllerName)) {
                require_once 'Zend/Controller/Dispatcher/Exception.php';
                throw new Zend_Controller_Dispatcher_Exception('Invalid controller specified (' . $controllerName . ')');
            }
	    $controllerName = $this->getDefaultControllerName();
	    $this->_curControllerClass = $this->formatControllerName($controllerName);
	    $request->setControllerName($controllerName)
	                ->setActionName(null);
        }
        return $this;
    }