クロージャ
クロージャと匿名関数は PHP 5.3.0 以降に登場しました。これは私のお気に入りで最もよく使われる PHP 機能です。これらの名前を聞いたときは非常に不安でしたが(少なくとも初めて聞いたときはそう思いました)、実際には非常にわかりやすいものです。これらは、すべての PHP 開発者がツールボックスに入れておくべき最も便利なツールです。
関数として、クロージャは作成時に外部状態をカプセル化します。クロージャが最初に作成された環境がもう存在しない場合でも、カプセル化された状態はクロージャ内に残ります。これは理解するのが難しい概念ですが、一度理解できれば、人生の新たな章が始まったように感じます。
匿名関数は、実際には名前のない関数です。匿名関数は、変数に割り当てて、他のすべての PHP オブジェクトと同様にコード内で渡すことができます。ただし、これは関数であることに変わりはないため、呼び出してパラメータを渡すことができます。匿名関数の最大の用途は、関数またはメソッドのコールバックとして使用することです。
クロージャと匿名関数は理論的には異なる概念です。ただし、PHP ではこれらを同じものとみなします。したがって、クロージャと言うときは、匿名関数を指すこともあり、その逆も同様です。
PHP のクロージャと匿名関数は構文的には関数と同じですが、混同しないでください。これらは実際には関数に見せかけたオブジェクトです。 PHP クロージャまたは無名関数の型を出力チェックすると、両方とも Closure クラスのインスタンスであることがわかります。クロージャは、文字列や整数と同じくらい重要なデータ型とみなすことができます。
Create
PHP のクロージャと関数が非常に似ていることは誰もが知っています。例 2-19 のような PHP クロージャを作成しても、驚くことはありません。
例 2-19 単純なクロージャー
<?php$closure = function ($name) { return sprintf('Hello %s', $name);};echo $closure("Josh");// 输出 --> "Hello Josh"
とても簡単です。例2-19では、Closureオブジェクトを作成し、それを変数$closureに割り当てます。これは標準の PHP 関数のように見えます。同じ構文を使用し、パラメータを受け取り、値を返します。しかし、名前はありません。
$closure はクロージャであり、Closure クロージャ オブジェクトはすべて _invoke() マジック メソッドを実装しているため、$closure 変数を呼び出すことができます。変数名の後に () 記号のペアが続く場合、PHP は自動的に __invoke() メソッドを見つけて呼び出します。
私は通常、PHP のクロージャ オブジェクトを関数やメソッドのコールバックとして使用します。多くの PHP 関数は、array_map() や preg_replace_callback() などのコールバック関数を使用します。これは、PHP 匿名関数用に特別に作られた機能のようなものです。他の値と同様に、クロージャはパラメータとして他の PHP 関数に渡すことができることに注意してください。例 2-10 では、array_map() 関数のコールバック パラメータとしてクロージャ オブジェクトを使用します。
例 2-20 array_map Closure
<?php$numbersPlusOne = array_map(function ($number) { return $number +1;}, [1,2,3]);print_r($numbersPlusOne);// 输出 --> [2,3,4]
<?php// 具名回调的实现function incrementNumber ($number) { return $number + 1;}// 具名回调的使用$numberPlusOne = array_map('incrementNumber', [1,2,3]);print_r($numberPlusOne);
添付ステータス
これまで、名前のない (匿名とも呼ばれる) 関数をコールバックとして使用する方法を説明してきました。 PHP クロージャーを使用して状態をアタッチしてカプセル化する方法を調べてみましょう。 PHP クロージャは、JavaScript のようにアプリケーションの状態を自動的にクロージャにカプセル化しないため、JavaScript 開発者は PHP クロージャに混乱する可能性があります。代わりに、クロージャ オブジェクトの bintTo() メソッドまたは use キーワードを呼び出して、状態を PHP クロージャに手動でアタッチする必要があります。
通常、use キーワードを使用してクロージャ状態を付加します。これを例として取り上げます (例 2-21)。 use キーワードを使用して変数をクロージャにアタッチすると、アタッチされた変数の値は、変数がクロージャにアタッチされた時点の値と同じままになります。
例 2-21 use キーワードを使用してクロージャ状態を付加する
<?phpfunction enclosePerson($name) { return function ($doCommand) use ($name) { return sprintf('%s, %s', $name, $doCommand); };}// 将字符串"Clay"封装进闭包$clay = enclosePerson('Clay');// 调用闭包echo $clay('get me sweet tea!');// 输出 --> "Clay, get me sweet tea!"
use キーワードを使用して、複数のパラメータをクロージャに渡すことができます。通常、PHP 関数またはメソッドのパラメーターを使用するのと同じように、パラメーターを区切るにはカンマを使用します。
别忘了PHP闭包都是对象。每个闭包的实例都有其内部状态,我们可以像其他PHP对象那样使用$this关键词来获取这些状态。一个闭包对象的默认状态相当无趣,它包含了一个魔术方法__invoke()和一个bindTo()方法,仅此而已。
不过bindTo()方法却可以带领我们去发掘出一些有趣的实现。这个方法允许我们将闭包对象的内部状态绑定到另一个对象上。bindTo()方法的第二个参数相当关键,它可以指定闭包需要绑定到的对象的类。这样我们就可以在闭包中获取绑定后的对象中protected和private的成员变量了。
你会发现bindTo()方法经常被一些PHP框架用来将路由地址映射到匿名回调函数上。这些框架将一个匿名函数绑定到应用程序对象上。你可以在匿名函数中使用$this关键词来引用应用程序对象,就像例子 2-22中所示
例子 2-22 使用bindTo方法附着闭包状态
<?phpclass App{ protected $routes = array(); protected $responseStatus = '200 OK'; protected $responseContentType = 'text/html'; protected $responseBody = 'Hello world'; public function addRoute($routePath, $routeCallback) { $this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__); } public function dispatch($currentPath) { foreach ($this->routes as $routePath => $callback) { if ($routePath === $currentPath) { $callback(); } } header('HTTP/1.1 ' . $this->responseStatus); header('Content-type: ' . $this->responseContentType); header('Content-length: ' . mb_strlen($this->responseBody)); echo $this->responseBody; }}
<?php$app = new App();$app->addRoute('/users/josh', function () { $this->responseContentType = 'application/json;charset=utf8'; $this->responseBody = '{"name": "Josh"}';});$app->dispatch('/users/josh');