無名関数がかなり使いやすくなりました。高階関数のような記述もできます。
たとえば、これの結果は
<?php function($func){ $func('Hello, Anonymous World!'); }(function(){ return function($str){ echo $str, PHP_EOL; }; }());
こうなります。
Hello, Anonymous World!
激しく使えるので、英語の壁を乗り越えてphp.internalsに投げたくなってきました。
すべての変更点は以下の通りです。
- 同じ無名関数を返すステートメントを複数回実行できない問題を修正。
- 無名関数の生成はコンパイル時の1回だけなのでループ中に書いてもcreate_functionのようにメモリを浪費しない。
- 無名関数を直接コールできるように改良。
- JavaScriptの無名関数のように使える。
- スコープを汚染せずにコードが実行可能。
- 無名関数名 (おかしな表現ではある) を"anonymous#<serial>" に変更。
- 関数名に"#"を含むので通常の関数名と衝突しない。
- Callable Objectをコールバック関数として使用するための __callback__ 擬似型へのキャストをサポート。
パッチのダウンロード:
- php-5.2.5-rsky-071128a.patch 無名関数と角括弧の配列リテラル
- php-5.2.5-rsky-071128b.patch ↑↓両方
- php-5.2.5-rsky-071128c.patch Callable Object
無名関数と角括弧の配列リテラルの使用例:
<?php // 角括弧の配列リテラル var_dump([0, 1, 2], ['foo': 'orange', 'bar': 'apple', 'baz': 'lemon']); // 三項演算子や角括弧構文とも衝突しない var_dump((1 == 2) ? 1 : 2, $array = [(1 == 2) ? 1 : 2 : 0, 1 : 2], [$array[1], $array[2]]); // 無名関数を作成・実行 $func = function($n =1){ return implode(' ', array_fill(0, $n, __FUNCTION__)); }; var_dump($func, $func(2)); ReflectionFunction::export($func); // 無名関数でソート $array = str_split('0120-APPLE-1'); print_r($array); usort($array, function($a, $b){ if (is_numeric($a) && is_numeric($b)) { return $a - $b; } elseif (is_string($a) && is_numeric($b)) { return -1; } elseif (is_numeric($a) && is_string($b)) { return 1; } return strcmp($b, $a); }); print_r($array); // 無名関数を直接実行 function(){ printf("%d: %s\n", __LINE__, __FUNCTION__); }(); // もちろん引数もとれる function($n){ for ($i = 0; $i < $n; $i++) { printf("%d: %s\n", __LINE__, __FUNCTION__); } }(3); // 無名関数内で宣言された関数は外側のスコープに影響しない var_dump(isset($i)); // もちろん値を返すこともできる $result = function(){ return sprintf('%d: %s', __LINE__, __FUNCTION__); }(); var_dump($result); // 直でもOK var_dump(function(){ return sprintf('%d: %s', __LINE__, __FUNCTION__); }()); // ループでも使える for ($i = 0; $i < 3; $i++) { function(){ printf("%d: %s\n", __LINE__, __FUNCTION__); }(); function(){ printf("%d: %s\n", __LINE__, __FUNCTION__); }(); } // ネストもOK function($func){ $func('Hello, Anonymous World!'); }(function(){ return function($str){ echo $str, PHP_EOL; }; }()); // 無名関数は実際には anonymous# + 連番 の関数名で定義される $i = 1; while (function_exists("anonymous#$i")) { $i++; } var_dump($i); ReflectionFunction::export(sprintf("anonymous#%d", $i - 1));
結果:
array(3) { [0]=> int(0) [1]=> int(1) [2]=> int(2) } array(3) { ["foo"]=> string(6) "orange" ["bar"]=> string(5) "apple" ["baz"]=> string(5) "lemon" } int(2) array(2) { [2]=> int(0) [1]=> int(2) } array(2) { [0]=> int(2) [1]=> int(0) } string(11) "anonymous#1" string(23) "anonymous#1 anonymous#1" Function [ <user> function anonymous#1 ] { @@ /example.php 11 - 13 - Parameters [1] { Parameter #0 [ <optional> $n = 1 ] } } Array ( [0] => 0 [1] => 1 [2] => 2 [3] => 0 [4] => - [5] => A [6] => P [7] => P [8] => L [9] => E [10] => - [11] => 1 ) Array ( [0] => P [1] => P [2] => L [3] => E [4] => A [5] => - [6] => - [7] => 0 [8] => 0 [9] => 1 [10] => 1 [11] => 2 ) 36: anonymous#3 42: anonymous#4 42: anonymous#4 42: anonymous#4 bool(false) string(15) "51: anonymous#5" string(15) "57: anonymous#6" 63: anonymous#7 66: anonymous#8 63: anonymous#7 66: anonymous#8 63: anonymous#7 66: anonymous#8 Hello, Anonymous World! int(12) Function [ <user> function anonymous#11 ] { @@ /example.php 75 - 77 - Parameters [1] { Parameter #0 [ <required> $str ] } }
Callable Objectの使用例:
<?php // 関数のように使えるオブジェクト class Proc implements Callable { public function call() { echo "Hello, World!\n"; } } $proc = new Proc(); $proc(); // call_user_func等に使うには__callback__擬似型へキャストするか // array($proc, 'call') としなければならない call_user_func((__callback__)$proc); // 比較クラス class Compar implements Callable { public function call($a, $b) { return $a - $b; } } $array = range(1, 10); shuffle($array); print_r($array); usort($array, (__callback__)new Compar); print_r($array); // 置換クラス class Replacer implements Callable { public function call($matches) { return $matches[0] . ' ' . $matches[0]; } } echo preg_replace_callback('/\\w+/', (__callback__)new Replacer, "Hello, World!\n");
結果:
Hello, World! Hello, World! Array ( [0] => 9 [1] => 4 [2] => 8 [3] => 2 [4] => 5 [5] => 1 [6] => 6 [7] => 10 [8] => 3 [9] => 7 ) Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 [5] => 6 [6] => 7 [7] => 8 [8] => 9 [9] => 10 ) Hello Hello, World World!