俺のコントローラのprotectedがpublicなわけがない

俺のコントローラのprotectedがpublicなわけがないこんにちは。
最近、東北TECH道場(仙台道場)でAndroidの講師してます、GeNERACE CTOけんたろです。
今回は東北TECH道場で行なっている活動やハックについて書こうと思いましたが、弊社の仕事でCakePHP(v2.3.1)を使っていて気になったことについてメモします。

突然ですが・・・PHPのアクセス修飾子は4つあります

種類 アクセス範囲
public どこからでもアクセス可
protected 親子関係のあるクラスからアクセス可
private 記述したクラスからのみアクセス可
(なし) publicと同じ

例えば以下のようにコードを書いて実行した場合、

TestClass1.class.php

<?php
class TestClass1  {
        protected $test = 1;
}

TestClass2.class.php

<?php
require_once('TestClass1.class.php');
require_once('TestClass3.class.php');
class TestClass2 extends TestClass1{
        public function test1() {
                // TestClass1のprotected $test
                echo $this->test;
        }
        public function test2()
        {
                // TestClass3のtestメソッドにTestClass2を渡し、実行。
                $testClass3 = new TestClass3();
                $testClass3->test($this);
        }
}
$testClass2 = new TestClass2();
echo "\n";
$testClass2->test1();
echo "\n";
$testClass2->test2();
echo "\n";

TestClass3.class.php

<?php
class TestClass3 {
        public function test($testClass2) {
                // TestClass1のprotected $test
                echo $testClass2->test;
        }
}

当然だけど、こうなる。

ken@ken-ubuntu-vaio:~$ php TestClass2.class.php
1
#protectedなのでTestClass1を継承していないTestClass3ではアクセス出来ない。
PHP Fatal error:  Cannot access protected property TestClass2::$test in /home/ken/TestClass3.class.php on line 5

ところがCakePHPだと・・・

親子関係は
コントローラ側
Object->Controller->AppController->GameBaseController->GameAController
コンポーネント側
Object->Component->GameExecuteComponent

各ソースは以下、

<?php
// -GameBaseController
class GameBaseController extends AppController {
	protected $name = 'test';
}

// -GameAController
class GameAController extends GameBaseController {
	public $components = array('GameExecute');
	public function test() {
		$this->GameExecute->test($this);
	}
}

// -GameExecuteComponent
class GameExecuteComponent extends Component {
	public function test($controller) {
		var_dump($controller->name);
	}
}

gist

このように書き、GameAControllerのpublic function test実行をすると・・・

GameExecuteComponentでGameBaseControllerのprotected $nameにアクセス出来、var_dumpで’test’が表示されてしまいます。(protectedなのに・・・)

疑問に思い、知り合いのCakePHP界隈の方々が集まるコワーキングスペースに質問を投下してみたところ無事解決。
http://www.facebook.com/groups/322027887903083/permalink/377724812333390/

まさかの実装。
https://github.com/cakephp/cakephp/blob/2.2.8/lib/Cake/Controller/Controller.php#L404
こんなところにマジックメソッド__get()のオーバーロードが・・・!

怖くなって、他にも無いかとgrepしてみたところ、
コンポーネントモデルにも同じ実装が・・・。

img9fd5ec33zik7zj

まとめと対応

  • CakePHPにおいてprotectedはpublicと等価
  • CakePHPに抗う場合→正しいprotectedのルールに従い、アクセサを準備し、複数人で開発する場合には全員が合意の上でアクセサをちゃんと使う
  • CakePHPに従う場合→protectedをpublicに書き換える

何故こんな実装をしてるんだろう・・・?と考えた時、容易さや学習コストの為のトレードオフなのかなと感じました。

ご覧いただきありがとうございました。

One thought on “俺のコントローラのprotectedがpublicなわけがない

  1. Pingback: 俺のコントローラのprotectedがpublicなわけがない | GeNERACE labo : ちゅどん道中記

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です