PHP で クロージャ さっきまでの無し

http://d.hatena.ne.jp/am11op/20070702/1183376369
↑前エントリがただの劣化コピーだってことに電車の中で気が付いた。

てことで、勝手にリベンジ。


その前に、closure の定義自体はとりあえず置いておいて、
自分が作ろうとしてる(欲しい)のは何なのか。

  • 外側の変数を参照できる
  • 使うその場で定義できる


↓ていうか、これ。
http://itpro.nikkeibp.co.jp/article/COLUMN/20050930/221971/?ST=oss


これができないのは、ただの callback 関数なんじゃないかと思うわけです。


で作ったのがこれ。

/*
 * closure を実現するための class
 */
class Closure {
    var $_args;
    var $_func;
    /**
     * @private
     */
    function Closure($func, $args) {
        $this->_args = $args;
        $func = ereg_replace("^function\([^\)]*\) *\{", "", $func);
        $func = ereg_replace("\}$", "", $func);
        $this->_func = $func;
    }
    
    /**
     * @param $func string closure
     * @param $arg1 $mixed first object/var which is used in $func
     * @param $arg2 $mixed second object/var which is used in $func
     * ...
     */
    function bind() {
        $args = func_get_args();
        return new Closure(array_shift($args), $args);
    }

    /**
     * use this method to call closure 
     */    
    function call() {
        $func = create_function('$args', $this->_func);
        $func($this->_args);
    }
    
    
}

// 以下、使用例
class Test{
    function sayTest($int=0){
        return 'this is a test.';
    }
}
$obj = new Test;

function piyo($bool, $obj) {
    if ($bool) {
        $obj['onSuccess']->call();
    } else {
        $obj['onFailure']->call();
    }
}

$hoge = "you know, ";

$arr = array(
        'onSuccess'=>Closure::bind("function(\$args){echo '$hoge'.\$args[0]->sayTest();}", &$obj), 
        'onFailure'=>Closure::bind("function(\$args){echo 'fail!';"), 
);

echo piyo(true, $arr); // you know, this is a test.
echo piyo(false, $arr); // fail!
  • Closure::bind の第一引数に closure を渡す
    • closure 内の instance は全て $args とする
  • $args に対応する instance を第二引数以降に渡す
  • closure はダブルクォーテーションでくくる場合は、$args の前に「\」を付ける必要がある
  • closure の頭に「function(\$args){」、尻に「}」が付いているが、これは closure であることを明示するためである
    • どっちみち ereg_replace で削除されるので、はっきり言って必要ない


「\」がウザい場合は、クロージャをシングルクォーテーションで囲って、
普通の変数も引数に渡してクロージャ内から $args[n] として参照すればよいです。


ちょっとは closure ぽくなったでしょか。

PHP で クロージャ

※これ失敗でした。↓にもちょっとマシなやつ書いてます
http://d.hatena.ne.jp/am11op/20070702/1183392610

CakePHP 使ってたら、ものすごく closure 使いたくなった。
ググったら途中までやってる方達がいたので、
それパクって作ってみた。


↓途中までやってる方達

http://blog.xole.net/article.php?id=419
http://p0t.jp/mt/archives/2007/04/1byte.html

要は create_function を使えばよいわけだ。

closure.php

function closure($str) {
	$str = ereg_replace("^function\(\) *\{", "", $str);
	$str = ereg_replace("\}$", "", $str);


	$func =create_function('', $str);
	$func();
}

function hoge($str, $func='') {
	echo $str;
	if ($func) {
		closure($func);
	}
}

echo hoge('こんにちわ。', "function() {echo 'クロージャだよ!';}");


実行結果

こんにちわ。クロージャだよ!


ものすごく使う気にならないのはなんでだ。

PHP で クロージャ失敗

http://d.hatena.ne.jp/am11op/20070702/1183376369
↑前エントリのクロージャもどきがものすごく使いたくならない理由がわかった。

function closure($str) {
	$str = ereg_replace("^function\(\) *\{", "", $str);
	$str = ereg_replace("\}$", "", $str);


	$func =create_function('', $str);
	$func();
}

class Test{
	function sayTest(){
		echo 'test';
	}
}
$obj = new Test;

function piyo($bool, $obj='') {
	if ($bool) {
		closure($obj['onSuccess']);
	} else {
		closure($obj['onFailure']);
	}
}

$arr1 = array(
		'onSuccess'=>'echo "OK!";',
		'onFailure'=>'echo "NG!";',
);

echo piyo(true, $arr1);	// OK!
echo piyo(false, $arr1);	// NG!

$arr2 = array(
		'onSuccess'=>'function(){echo $obj->sayTest();}', // この時点で syntax error
);

echo piyo(true, $arr2);

class とか instance 使った時点で閉じた空間じゃなくなるので、
こういうものはいくらそれっぽく作っても、
結局 closure とは呼べないだろうと思った。

結論
失敗例を増やしただけ