CakePHPのController->__mergeVars()でcomponentsがマージされてない?
CakePHP 1.3.0 を学習中に、もしかしたらバグかも?というのにはまったので、ブログに書いて誰かのアドバイスを期待しようという記事です。
バグっぽいなぁと思いつつも、CakePHPは最近はじめたばかりで、これがバグなのか、自分が何かを間違えてるのか*1ちょっと自信を持てないでいます。
状況説明
AppControllerでコンポーネントを設定したら、親クラスであるControllerで設定されている Session の設定が消えてしまったというのが、その問題です。
オブジェクト指向における一般的な継承に加え、CakePHP は、コントローラで使うコンポーネントやヘルパーといった特別なアトリビュートで、少し気の利いた動作をします。AppController の変数配列が、子のコントローラクラスの配列とマージされるのです。
AppController とアプリケーションの各コントローラの変数のうち、CakePHP がマージするものは次の通りです。
$components
http://book.cakephp.org/ja/view/957/The-App-Controller
$helpers
$uses
CakeBookにこう書いてあるので、AppControllerで設定しても、うまい事マージしてくれるようになっているはずなんですが・・・
<?php class AppController extends Controller{ var $components = array('Acl', 'Auth'); }
この様に、AppController で2つのコンポーネントを設定しました。
実際にはCakebookのチュートリアルをやっていたので http://book.cakephp.org/ja/view/1545/Preparing-to-Add-Auth の下部にある様な、AppControllerを作成しています。
var $components = array('Session');
http://github.com/cakephp/cakephp/blob/master/cake/libs/controller/controller.php#L224
Controllerではこの様に、Sessionコンポーネントが設定されてます。
if ($var === 'components') {
http://github.com/cakephp/cakephp/blob/master/cake/libs/controller/controller.php#L460
$normal = Set::normalize($this->{$var});
$app = Set::normalize($appVars[$var]);
if ($app !== $normal) {
$this->{$var} = Set::merge($app, $normal);
}
実際にマージするロジックはというと、Controller->__mergeVars()に書かれてます。
__mergeVars()の、特にこの部分が、どうやら実際にコンポーネントをマージしている所のようです。
$appVars[$var] の $appVars は404行目で get_class_vars('AppController') で取得している、AppControllerの方です。
という事は、親クラスである Controller のを $this->{$var} で取得しようとしてる事になります。
$var の中は 'components' なので $this->components という事になりますが、これだとうまく行かないような気がします。
子クラスであるAppControllerやさらにその子クラスである、アプリケーション用に作成したコントローラー*2のインスタンスを作成した場合、$this->components は既に隠蔽*3されていて、親クラスの $components は取得できないんじゃないかと思います。
隠蔽の実験コード
<?php class ParentClass{ var $foo ='parent'; function call(){ return $this->foo; } } class ChildClass extends ParentClass{ var $foo = 'child'; } $parent = new ParentClass(); $child = new ChildClass(); var_dump($parent->call(), $child->call());
string(6) "parent" string(5) "child"
結果を見ると、ChildClass のインスタンスからだと、やはり $foo は隠蔽されてます。
*1:もしくは環境のせい?
*2:UsersControllerとか
*3:オーバーライドだと思ってたけどこんな記事を見かけました。ほんとかな?「スーパークラスで定義されているクラスメソッド、メンバ変数をサブクラスで再定義することは隠蔽と言われ、オーバーライドとは区別されます。」http://www.javaroad.jp/java_class8.htm