ホームページ > バックエンド開発 > PHPチュートリアル > [モダン PHP] 第 2 章 新機能 5 終わりに

[モダン PHP] 第 2 章 新機能 5 終わりに

WBOY
リリース: 2016-06-23 13:28:22
オリジナル
860 人が閲覧しました

クロージャ


クロージャと匿名関数は 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 開発者には名前付き関数を作成し、その関数名をパラメータとして渡す以外に選択肢がなかったことを思い出してください。これは実行が少し遅くなります。最も重要なことは、コールバックの実装と使用が分離されていることです。昔ながらの PHP 開発者は次のコードを使用します:


<?php// 具名回调的实现function incrementNumber ($number) {    return $number + 1;}// 具名回调的使用$numberPlusOne = array_map('incrementNumber', [1,2,3]);print_r($numberPlusOne);
ログイン後にコピー

上記のコードは通常どおり実行されますが、例 2-20 ほど簡潔ではありません。コールバックとして、incrementNumber() という名前の個別の 1 回限りの関数は必要ありません。クロージャをコールバックとして使用すると、よりクリーンで読みやすいコードが作成されます。


添付ステータス


これまで、名前のない (匿名とも呼ばれる) 関数をコールバックとして使用する方法を説明してきました。 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!"
ログイン後にコピー

例 2-21 では、名前付き関数 closeperson() 関数は $name パラメータを受け取り、$name をカプセル化する値を返します。パラメータ。クロージャが最終的に closePerson() 関数のスコープを離れる場合でも、返されたクロージャ オブジェクト $clay は、クロージャにアタッチされたときの $name パラメータの値を保持します。つまり、$name 変数はクロージャ内にまだ存在しています。


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;    }}
ログイン後にコピー

注意addRoute方法。它接收一个路由地址(例如 /users/josh)和一个路由的回调。dispatch()方法接收一个当前HTTP请求地址并调用对应的路由回调。神奇的地方在第11行,我们将路由的回调绑定给了当前App类的实例。这样我们就可以创造一个可以操作App实例状态的回调函数了:

<?php$app = new App();$app->addRoute('/users/josh', function () {    $this->responseContentType = 'application/json;charset=utf8';    $this->responseBody = '{"name": "Josh"}';});$app->dispatch('/users/josh');
ログイン後にコピー


ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート