私は、PHPでPythonスタイルのデコレータを実装するためのビジョンとベストプラクティスを共有することにしました。
ルアーとして、右の画像で使用する小さな例。 出力(デコレータ自体のロジックを実装した後):
ログ:b()を呼び出す int(42)
実装はC拡張の形式で行われ、PHP自体を再構築する必要はありません。 ただし、ダウンロードできないホスティングサイトでは開始されません。
現時点では、コードはベータ版です(必要な機能はすべて記述されていますが、おそらくバグとメモリリークがあります:))。 そのままです。 開発に協力したいという要望があれば、 githubでコミットを受け入れてうれしいです。
簡単な使用例:
<?php function double($func) { return function() use($func) { return 2*call_user_func_array($func, func_get_args()); }; } @double function a() { return 21; } var_dump(a()); /* : int(42) */
デコレータは、常に関数を返す関数です。 外部関数は、最初のパラメーターとして代替関数を受け入れます。 pythonとは異なり、パラメーター付きのデコレーターは、関数を返す関数を返す関数としては記述されません...追加のパラメーターは、置き換えられた関数の後に単に渡されます。
<?php function add($func, $v=0) { return function() use($func, $v) { return $v+call_user_func_array($func, func_get_args()); }; } @add(1) function a() { return 1; } var_dump(a()); /* : int(2) */
デコレータは組み合わせることができます:
<?php function dec($func, $p='[]') { return function() use($func, $p) { $s = call_user_func_array($func, func_get_args()); return $p[0].$s.$p[1]; }; } @dec function a() { return 'I'; } var_dump(a()); @dec('{}') function b() { return 'am'; } var_dump(b()); @dec @dec('()') @dec('{}') function c() { return 'here'; } var_dump(c()); /* : string(3) "[I]" string(4) "{am}" string(10) "[({here})]" */
さらに、これらは逆の順序で実行されます。
@A
@B
@C
関数X
になります
A(B(C(X(...))))
パラメーターの数とそのタイプは任意であり、計算の怠lazさは一般に手を縛ります。
<?php class Logger { const INFO = 'INFO'; public static function log($func, $text='', $level=self::INFO, $prefix='') { return function() use($func, $text, $level, $prefix) { printf("%s%s: %s\n", $prefix, $level, $text); return call_user_func_array($func, func_get_args()); }; } } @Logger::log('calling a()', Logger::INFO, date('Ymd H:i:s').': ') function a() { return 'Hello'; } var_dump(a()); /* : 2013-05-24 14:22:23: INFO: calling a() string(5) "Hello" */
さらに、デコレータの名前は、デコレータを記述するときではなく、呼び出し時に宣言された関数および静的メソッドである必要があります。 そして一般的に、あなたは実験することができます:
<?php class Arr { public static function map($func, $cb) { return function() use($func, $cb) { $v = call_user_func_array($func, func_get_args()); return array_map($cb, $v); }; } } class Foo { /* */ @Arr::map(function($i){return -$i;}) /** * * * * @return array */ public function bar() { return range(1, 3); } } $foo = new Foo(); print_r($foo->bar()); /* : Array ( [0] => -1 [1] => -2 [2] => -3 ) */
まあ、誰もが自分のタスクのコンテキストでもっと面白いものを思いつくことができると確信しています。
技術的な問題
現時点では、デコレータを使用してコードを実行するときのサポートを確認しました:
- cat file.php | php
- php file.php
- 必要/含める
- 評価する
たぶん他の何かが欠けています。
既知のバグ/機能から(機能について説明できます。バグはすぐに修正します):
- デコレータにパラメータがある場合、開始「(」はデコレータの名前と同じ行にある必要があります。
- コードの変更により、__ FUNCTION__と__METHOD__は関連性を失います。 最終値を持つ行の定数の置換を修正することは可能ですが、そのような解決策の正確さはわかりません。
- __LINE__は常に一致する必要がありますが、デコレータパラメータの複数行の記述の場合はまだ解決されていません。
- デコレータの説明の構文エラーの場合、ファイル名と行番号が正しくない例外基本クラスの例外がスローされます。
- ロシア語のgithubのコード内のコメント。 私の書かれた英語のレベルは、書かれていることを恥じないほど十分ではありません。 一時的に、そして誰かが良い翻訳でコミットを送っても、それは素晴らしいことです!
- まともなIDEは、理解できない構文を誓います。 少なくとも誓わないようにPHPStormをトレーニングする方法はありますか?
PS調査で「非常に詳細が必要」という十分なオプションが選択された場合、この拡張機能のSishnaの実装の詳細な説明を含む投稿を書くことができます。