無名関数がかなり使いやすくなりました。高階関数のような記述もできます。
たとえば、これの結果は
<?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!