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

うーん。便利だけど、これは良いのかなぁ……?
クラスの内部構造さえ分かれば、外部から弄り放題じゃ。。。

最後に

とりあえず、見つけたクロージャの機能はこれくらい。

まだ alpha なので、この後また変更される可能性もあるし、あるいはまた消えてしまうかも知れない。
php5.3 で採用が見送られた理由もよく分からないし。。。



検証日:2011/07/27
対象: php5.4aplha2 (CentOS 5.6)