CakePHPのController->__mergeVars()でcomponentsがマージされてない?

CakePHP 1.3.0 を学習中に、もしかしたらバグかも?というのにはまったので、ブログに書いて誰かのアドバイスを期待しようという記事です。
バグっぽいなぁと思いつつも、CakePHPは最近はじめたばかりで、これがバグなのか、自分が何かを間違えてるのか*1ちょっと自信を持てないでいます。

状況説明

AppControllerでコンポーネントを設定したら、親クラスであるControllerで設定されている Session の設定が消えてしまったというのが、その問題です。

オブジェクト指向における一般的な継承に加え、CakePHP は、コントローラで使うコンポーネントやヘルパーといった特別なアトリビュートで、少し気の利いた動作をします。AppController の変数配列が、子のコントローラクラスの配列とマージされるのです。

AppController とアプリケーションの各コントローラの変数のうち、CakePHP がマージするものは次の通りです。

$components
$helpers
$uses

http://book.cakephp.org/ja/view/957/The-App-Controller

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') {
$normal = Set::normalize($this->{$var});
$app = Set::normalize($appVars[$var]);
if ($app !== $normal) {
$this->{$var} = Set::merge($app, $normal);
}

http://github.com/cakephp/cakephp/blob/master/cake/libs/controller/controller.php#L460

実際にマージするロジックはというと、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. MAMP 1.7.2
  2. PHP 5.2.6
  3. CakePHP 1.3.0

*1:もしくは環境のせい?

*2:UsersControllerとか

*3:オーバーライドだと思ってたけどこんな記事を見かけました。ほんとかな?「スーパークラスで定義されているクラスメソッド、メンバ変数をサブクラスで再定義することは隠蔽と言われ、オーバーライドとは区別されます。」http://www.javaroad.jp/java_class8.htm