php 5.4 リポジトリに舞い戻ってきたクロージャの $this サポート
php 5.3 beta の時は使えていたのに、stable リリース時には消えていた クロージャ( 無名関数 ) の $this サポート。
これが、php 5.4 のブランチで再びコミットされ、 php5.4 alpha1 で利用可能になっている。
とはいえ、ただそのまま移植されたわけではなく、新たな構文や、新たなメソッドを引き連れてきたらしい。
そこで、php5.4 alpha2 時点で使えるようになっているクロージャの機能をざっとまとめてみることにした。
$this サポート
これは、php5.3 beta にもあったように、クラス内で定義したクロージャの内部で $this が使えるようになるというもの。
$this は暗黙的に束縛されるので、特別なことは何もしなくて良い。
php 5.4 で利用可能な $this
<?php class C { private $x = 'hello'; public function getPrinter() { return function () { print $this->x; // private にアクセス可能! }; } } $c = new C(); $closure = $c->getPrinter(); $closure(); // print 'hello'
php 5.3 では、 $this が利用出来なかったため、一度 $self = $this してから use($self) するといった事も行われていたが、
これだと、public にしかアクセス出来なかった。
php 5.3 の残念な 疑似 $this
<?php class C { private $x = 'hello'; public function getPrinter() { $self = $this; return function () use($self) { print $self->x; // private にはアクセスできない。。。 }; } } $c = new C(); $closure = $c->getPrinter(); $closure(); // Fatal error: Cannot access private property C::$x
新たなクロージャ生成構文
従来の生成方法
function & (parameters) use (lexical vars) { body }
に加えて、
static function & (parameters) use (lexical vars) { body }
が利用可能となっている。
要は、 function の前に static が付けられるようになったというだけなんだけど、
これにより、明示的に $this を束縛しないクロージャが作れる。
当然、このクロージャの中では $this を利用できない。
利用出来ない分、メモリの消費を抑制できるということらしい。(クロージャが握ったままだと $this の先がいつまで経っても解放されないからね)
静的クロージャでは、$this は束縛されない
<?php class C { function make() { return static function () { var_dump($this); }; } } $c = new C(); $closure = $c->make(); $closure(); // NULL (Notice: Undefined variable: this)
bindTo メソッド / bind 静的メソッド
Closure クラスに、2つのメソッドが追加された。
bindTo で、$this を別のオブジェクトに束縛し直したクロージャを得られる。
bindTo
<?php class A { private $class_name = 'A'; public function get() { return function() { echo "\$this is {$this->class_name}\n"; }; } } class B { private $class_name = 'B'; } $a = new A(); $b = new B(); $closure_a = $a->get(); $closure_b = $closure_a->bindTo($b); $closure_a(); // $this is A $closure_b(); // $this is B
Closure::bind を使えば、任意のクロージャをつくって、その $this に任意のオブジェクトを束縛できる。
Closure::bind
<?php class A { private $val = 'private in A'; } $closure = Closure::bind(new A(), function(){ echo $this->val, "\n"; }); $closure(); // private in A
うーん。便利だけど、これは良いのかなぁ……?
クラスの内部構造さえ分かれば、外部から弄り放題じゃ。。。