CakePHPがコンポーネントを読み込む仕組みを調べてみた
SecurityComponentのチェックにかかってにっちもさっちもいかなくなったんだが、そもそも基本的な動作フローを理解していないのでどこから手を付けていいのか分からない。というわけで、コンポーネントが読み込まれる仕組みを調べてみた。
対象バージョンは1.3.6だけど、2011/05/19時点のgithubのmasterを見てもこのあたりは変わっていないらしい。
■Dispatcherから始める
コケたところでdebug_backtraceを取って、そこからさかのぼってみる。
Dispatcher(cake/dispatcher.php)のdispatchメソッドの最後で$this->_invokeが呼ばれていて、ここから読み始めることにした。
アクションメソッドを呼び出す前に以下の処理があり、ここでコンポーネントの読み込みと初期化を行っているようだ。
<?php function _invoke(&$controller, $params) { $controller->constructClasses(); $controller->startupProcess();
■Controllerを読む
Controller(cake/libs/controller/controller.php)の中を見る。まずはControllerの中で定義されているComponent関連と思われるメンバ変数の定義。
<?php /** * Instance of Component used to handle callbacks. * * @var string * @access public */ var $Component = null; /** * Array containing the names of components this controller uses. Component names * should not contain the "Component" portion of the classname. * * Example: `var $components = array('Session', 'RequestHandler', 'Acl');` * * @var array * @access public * @link http://book.cakephp.org/view/961/components-helpers-and-uses */ var $components = array('Session');
componentsとComponentは別物という扱いのようだ。componentsに列挙されたコンポーネントを回して、それぞれがコールバックを叩くというイメージを持っていたが、どうやら違うらしい。
Componentがstringということは、callbackを持てるコンポーネントは1つだけで、その名前を入れるとかだろうか。
$this->Componentはコンストラクタの中で以下のように初期化している。
<?php $this->Component =& new Component();
…えっと、なんかインスタンス突っ込んでるんだけど「@var string」ってコメントの記述はドキュメントバグでしょうか。
気を取り直して、Dispatcherの_invokeで呼ばれている処理をそれぞれ追ってみる。まずはconstructClasses()から。ここでは、
<?php $this->Component->init($this);
してるだけ。そして、startupProcess()の全処理。
<?php function startupProcess() { $this->Component->initialize($this); $this->beforeFilter(); $this->Component->triggerCallback('startup', $this); }
まとめると、beforeFilterで特別なことをしなければ、以下のフローで処理されていることになる。
<?php $this->Component =& new Component(); $this->Component->init($this); $this->Component->initialize($this); $this->Component->triggerCallback('startup', $this);
■Componentを読む
Controller内のComponentとcomponentsの関係が怪しい感じなので、コメントを確認してみると、
<?php /** * Handler for Controller::$components * * @package cake * @subpackage cake.cake.libs.controller * @link http://book.cakephp.org/view/993/Components */
という、やっぱり怪しいことが書いてある。
SecurityComponentiを確認してみると、Componentを継承しておらず、Componentと同じくObjectを継承している。
Componentクラスはコンポーネントの基底クラスではなく、Controllerがcomponentsを管理する際に必要な処理をまとめるための物らしい。こんなの絶対おかしいよ。
●init(&$controller)
controllerのcomponentsで指定されたコンポーネントを順次ロードする。実際の処理は_loadComponents()が受け持っており、このあたりの実装は相当気持ち悪い。
コンポーネントからコンポーネントを呼ぶことにも対応しており、controllerから直接呼ばれたコンポーネントは$this->_primary[]に入っていく。
$this->_primaryが何者かというと、こういうことらしい。
<?php /** * List of components attached directly to the controller, which callbacks * should be executed on. * * @var object * @access protected */ var $_primary = array();
「@var object」って書いておいて、すぐに「var $_primary = array();」で初期化しているが気にしてはいけない。
●initialize(&$controller)
各コンポーネントのinitialize()を呼ぶ。