PHP 5.4 ではクラスのインスタンスメソッド内で定義したクロージャーには $this が暗黙的に束縛されてしまうため、次のコードは 5.3 と 5.4 で動作が異なります。
<?php class AAA { public function getFunction() { // クロージャー! return function ($a, $b) { return $a + $b; }; } public function __destruct() { echo __METHOD__ . PHP_EOL; } } class BBB { public function hoge() { echo __METHOD__ . " BEGIN" . PHP_EOL; $aa = new AAA; $func = $aa->getFunction(); echo __METHOD__ . " END" . PHP_EOL; return $func; } public function fuga() { echo __METHOD__ . " BEGIN" . PHP_EOL; $func = $this->hoge(); $func(1, 2); echo __METHOD__ . " END" . PHP_EOL; } } echo "BEGIN" . PHP_EOL; $bb = new BBB; $bb->fuga(); echo "END" . PHP_EOL;
PHP 5.3.16 の実行結果
AAA::getFunction() が返すクロージャーに AAA のインスタンスが束縛されていないので、BBB::hoge() のスコープを抜けた時に AAA のデストラクタが呼ばれます。
BEGIN BBB::fuga BEGIN BBB::hoge BEGIN BBB::hoge END AAA::__destruct BBB::fuga END END
php 5.4.6 の実行結果
AAA::getFunction() が返すクロージャーに AAA のインスタンスが束縛されているため、BBB::hoge() のスコープを抜けてもまだクロージャーが生きているので AAA のデストラクタは呼ばれません。BBB::fuga() のスコープを抜けるとクロージャー($func)も消えるので AAA のデストラクタが呼ばれます。
BEGIN BBB::fuga BEGIN BBB::hoge BEGIN BBB::hoge END BBB::fuga END AAA::__destruct END
5.3 でも 5.4 でも同じ動作にする方法
PHP 5.4 でもクロージャーの定義に static をつければ $this は束縛されなくなりますが、もちろん PHP 5.3 でそんなことをしてもパースエラーになるだけです。
$this が束縛されるのはインスタンスメソッドの場合だけなので、$this を束縛したくない場合は静的メソッドでクロージャーを定義すると良さそうです。例えば↑の例では次のように書き換えれば 5.3 でも 5.4 でも同じ動きになります。
<?php class AAA { public function getFunction() { return self::getStaticFunction(); } public static function getStaticFunction() { return function ($a, $b) { return $a + $b; }; } public function __destruct() { echo __METHOD__ . PHP_EOL; } } class BBB { public function hoge() { echo __METHOD__ . " BEGIN" . PHP_EOL; $aa = new AAA; $func = $aa->getFunction(); echo __METHOD__ . " END" . PHP_EOL; return $func; } public function fuga() { echo __METHOD__ . " BEGIN" . PHP_EOL; $func = $this->hoge(); $func(1, 2); echo __METHOD__ . " END" . PHP_EOL; } } echo "BEGIN" . PHP_EOL; $bb = new BBB; $bb->fuga(); echo "END" . PHP_EOL;
滅多に $this が束縛されたら困るようなことは無いと思いますが・・・↓のようにのようにエラーハンドラにクロージャーを仕込んでデストラクタでエラーハンドラをリストアする処理が PHP 5.4 では意図通りに動かなくなったりしました。