PHP5.3無名関数の中身はClosureクラスぽいので自分でクラスから作ってみる

in

PHP5.3無名関数シリーズ1はこちら

下岡さんのClosureクラスはfinalらしいと言うエントリーを見て、無名関数は実際は内部finalクラスClosureと言うもので表現されているらしいと知りました。

Class [  final class Closure ] {
  - Constants [0] {}
  - Static properties [0] {}
  - Static methods [0] {}
  - Properties [0] {}
  - Methods [1] {
    Method [  public method __invoke ] {
    }
  }

リフレクションクラスの存在を忘れていました。先日のエントリーでのcurry関数のパラメーター数推定もリフレクションクラスを使った方がすっきり書けそうでした。

めげていても仕方がないのでよく見てみると、__invoke関数はpublicらしいので直接呼び出してみましょう。

$f = function () {echo 3;};
$f(); // 3
$f->__invoke(); // 3

無事動くようです!
では自分でクラスを定義した場合は?

class PseudoClosure {
	public function __invoke(){
		echo 3;
	}
}
$p = new PseudoClosure();
$p->__invoke(); // 3
$p(); // 3

Closureクラスではないにもかかわらず、これまた動きました!
__invokeメソッドはPHP5.3から追加されるマジックメソッド、と言う扱いのようです。

しかしながら、__callメソッド経由では動作しませんでした。

class PseudoClosure {
	public function __call($name, array $arguments){
		echo 3;
	}
}
$p = new PseudoClosure();
$p->__invoke(); // 3
$p(); // Fatal error: Function name must be a string

コールバック関数として設定しても動くようです。

$fruits = array("d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple");
class PseudoClosure {
	public function __invoke(){
	    echo func_get_arg(0), ". ", func_get_arg(1), "
\n"; } } $p = new PseudoClosure(); array_walk($fruits, $p); // ok!

こうしてみると、新たに導入されたのは__invoke()であって、無名関数形式の表記は構文糖衣(シンタックスシュガー)のような気もしてきますね。

では、下記のような無名関数の処理をPseudoClosureクラスで書き直すとどんな感じになるでしょうか。

$i = 5;
$f = function ($j) use ($i) {
	echo $j * $i;
};
$f(2); // 10

useは事前に設定するのでコンストラクタに当てはめてみれば簡単ですね!

class PseudoClosure {
	private $use = array();
	public function __construct($i){
		$this->use['i'] = $i;
	}
	public function __invoke($j){
		echo $j * $this->use['i'];
	}
}
$p = new PseudoClosure(5);
$p(2); // 10

変数名の部分や、引数を参照にする場合などに書き方はそれぞれかわってきそうですが、基本的には同じようなものだとわかると思います。

まぁ無名関数形式の方が書きやすいのでこんなことをして意味があるのかわかりませんが、無理やり使い方を考えると、例えば継承ができたりします。

function f($j, $g){
	$g($j);
}
class PseudoClosure {
	protected $use = array();
	public function __construct($i){
		$this->use['i'] = $i;
	}
	public function __invoke($j){
		echo $j * $this->use['i'];
	}
}
class ConcretePseudoClosure extends PseudoClosure {
	public function __construct($i){
		parent::__construct($i);
	}
	public function __invoke($j){
		parent::__invoke($j);
		echo ":", ($j + $this->use['i']);
	}
}
$p = new ConcretePseudoClosure(5);
f(2, $p); // 10, 7

面白いですね無名関数!

Trackback URL for this post:

http://nonn-et-twk.net/twk/trackback/226
0