0x00 はじめに
クロージャとは、作成時に周囲の状態をカプセル化する関数を指します。クロージャが配置されている環境が存在しなくなっても、クロージャにカプセル化された状態は依然として存在します。
PHP のすべてのクロージャは、Clouse クラスによってインスタンス化されたオブジェクトです。つまり、クロージャは他の PHP オブジェクトと何ら変わりません。オブジェクトにはそのメソッドとプロパティが必要です。この記事では、PHP におけるクロージャの基本的な使用法と Clouse クラス メソッドの役割を要約します。 [推奨: PHP ビデオ チュートリアル ]
0x01 クロージャの基本的な使用法
クロージャの最も基本的な使用法を見てみましょう:
<?php $hello = function ($word) { return 'hello ' . $word; }; echo $hello('world'); // 输出 hello world
ねえ、このコードの最も直感的な感じは、関数を $hello 変数に割り当て、それを $hello 経由で直接呼び出すことです。ただし、このクロージャは親スコープから変数を継承しません (つまり、周囲の状態をカプセル化します) use キーワードを通じてクロージャの親スコープから変数を継承できます。例は次のとおりです。
<?php $name = 'panda'; $hello = function () use ($name) { return 'hello ' . $name; }; echo $hello(); // 输出 hello panda
PHP 7.1 以降、スーパーグローバル、$this、またはパラメーターと同じ名前を持つ変数を渡すことはできません。
さらに、 use キーワードを使用すると、親スコープの変数が値によってクロージャに渡されます。つまり、クロージャが作成されると、外部変数が変更されたとしても、クロージャに渡される値には影響しません(つまり、クロージャが配置されている環境が存在しなくても、カプセル化された状態は影響を受けません)。閉鎖はまだ存在します)。例は次のとおりです:
<?php $name = 'panda'; $hello = function () use ($name) { return 'hello ' . $name; }; $name = 'cat'; echo $hello(); // 输出 hello panda
変数への参照を渡すと、クロージャが外部変数の値を変更できるようになります。例は次のとおりです:
<?php $name = 'panda'; $changeName = function () use (&$name) { $name = 'cat'; }; $changeName(); echo $name; // 输出 cat
注: オブジェクトを渡すときPHP では、デフォルトでは参照によって渡されるため、クロージャ内でパッケージ内で使用によって渡されたオブジェクトを操作する場合には特別な注意を払う必要があります。例は次のとおりです。
<?php class Dog { public $name = 'Wang Cai'; } $dog = new Dog(); $changeName = function () use ($dog) { $dog->name = 'Lai Fu'; }; $changeName(); echo $dog->name; // 输出 Lai Fu
0x02 Closure class
クロージャが単なる Closure クラス オブジェクトであることを証明します
<?php $clourse = function () { echo 'hello clourse'; }; if (is_object($clourse)) { echo get_class($clourse); } // 输出 Closure
上記のコードは出力します。 Closure。クロージャが単なる通常の Closure クラス オブジェクトであることを証明します。
Clourse クラスの概要
クロージャ クラスの関連情報は、PHP 公式マニュアルで確認できます。 PhpStorm のローカル ドキュメント。
/** * Class used to represent anonymous functions. * <p>Anonymous functions, implemented in PHP 5.3, yield objects of this type. * This fact used to be considered an implementation detail, but it can now be relied upon. * Starting with PHP 5.4, this class has methods that allow further control of the anonymous function after it has been created. * <p>Besides the methods listed here, this class also has an __invoke method. * This is for consistency with other classes that implement calling magic, as this method is not used for calling the function. * @link http://www.php.net/manual/en/class.closure.php */ final class Closure { /** * This method exists only to disallow instantiation of the Closure class. * Objects of this class are created in the fashion described on the anonymous functions page. * @link http://www.php.net/manual/en/closure.construct.php */ private function __construct() { } /** * This is for consistency with other classes that implement calling magic, * as this method is not used for calling the function. * @param mixed $_ [optional] * @return mixed * @link http://www.php.net/manual/en/class.closure.php */ public function __invoke(...$_) { } /** * Duplicates the closure with a new bound object and class scope * @link http://www.php.net/manual/en/closure.bindto.php * @param object $newthis The object to which the given anonymous function should be bound, or NULL for the closure to be unbound. * @param mixed $newscope The class scope to which associate the closure is to be associated, or 'static' to keep the current one. * If an object is given, the type of the object will be used instead. * This determines the visibility of protected and private methods of the bound object. * @return Closure Returns the newly created Closure object or FALSE on failure */ function bindTo($newthis, $newscope = 'static') { } /** * This method is a static version of Closure::bindTo(). * See the documentation of that method for more information. * @static * @link http://www.php.net/manual/en/closure.bind.php * @param Closure $closure The anonymous functions to bind. * @param object $newthis The object to which the given anonymous function should be bound, or NULL for the closure to be unbound. * @param mixed $newscope The class scope to which associate the closure is to be associated, or 'static' to keep the current one. * If an object is given, the type of the object will be used instead. * This determines the visibility of protected and private methods of the bound object. * @return Closure Returns the newly created Closure object or FALSE on failure */ static function bind(Closure $closure, $newthis, $newscope = 'static') { } /** * Temporarily binds the closure to newthis, and calls it with any given parameters. * @link http://php.net/manual/en/closure.call.php * @param object $newThis The object to bind the closure to for the duration of the call. * @param mixed $parameters [optional] Zero or more parameters, which will be given as parameters to the closure. * @return mixed * @since 7.0 */ function call ($newThis, ...$parameters) {} /** * @param callable $callable * @return Closure * @since 7.1 */ public static function fromCallable (callable $callable) {} }
まず第一に、Clouse クラスは最終クラスであるため、継承できません。次に、そのコンストラクター __construct が private に設定されているため、new キーワードを使用してクロージャー オブジェクトをインスタンス化できません。これら 2 つの保証は、クロージャが構文関数 (...) use(...) {...} を通じてのみインスタンス化できることを保証します。
クロージャはなぜ関数として実行できるのでしょうか?
上記のクラス概要から、Clouse クラスが __invoke メソッドを実装していることがわかります。このメソッドは、PHP 公式マニュアルで次のように説明されています。 function オブジェクトが呼び出されるとき、__invoke() メソッドが自動的に呼び出されます。
これが、クロージャを関数として実行できる理由です。
指定された $this オブジェクトとクラス スコープをバインドしますクロージャ ルーティングの使用を許可するフレームワーク (Slim など) では、次のような記述が見られます。
$app->get('/test', function () { echo $this->request->getMethod(); });
$this をクロージャで使用できますか?この $this はどのオブジェクトを指しているのでしょうか?
$this とクラス スコープをバインドする機能は、bindTo メソッドとバインド メソッドを通じて実現できます。例は次のとおりです:
<?php class Pandas { public $num = 1; } $pandas = new Pandas(); $add = function () { echo ++$this->num . PHP_EOL; }; $newAdd1 = $add->bindTo($pandas); $newAdd1(); // 输出 2 $newAdd2 = Closure::bind($add, $pandas); $newAdd2(); // 输出 3
上記の例は、指定されたオブジェクトをクロージャ $ this としてバインドします。ですが、クラススコープを指定しませんでした。そのため、Pandas クラスの $num プロパティを protected または private に書き換えると、致命的なエラーがスローされます。
Fatal error: Uncaught Error: Cannot access protected property Pandas::$num
バインドされたオブジェクトの非パブリック プロパティまたはメソッドにアクセスする必要がある場合、クラス スコープを指定する必要があります。例は次のとおりです:
<?php class Pandas { protected $num = 1; } $pandas = new Pandas(); $add = function () { echo ++$this->num . PHP_EOL; }; $newAdd1 = $add->bindTo($pandas, $pandas); $newAdd1(); // 输出 2 $newAdd2 = Closure::bind($add, $pandas, 'Pandas'); $newAdd2(); // 输出 3
ここでは、両方のbindTo メソッドとバインド メソッドでは、$newscope.パラメータを指定します。$newscope パラメータのデフォルトは static であり、クラス スコープは変更されません。 $newscope パラメータはクラス名またはオブジェクトを受け入れ、クロージャのクラス スコープを指定されたクラス スコープに変更します。このとき、クロージャは Pandas クラスの $num プロパティにアクセスできます。
$this オブジェクトとクラス スコープを 1 回バインドして実行する (PHP7)新しいオブジェクトとクラス スコープが指定されるたびに、bindTo メソッドと binding メソッドが実行されます。元のクロージャをコピーしてから新しいクロージャを返すと、バインドされたオブジェクトを何度も変更する必要があると面倒になるため、PHP7 ではクロージャをオブジェクトに一時的にバインドできる新しいメソッド呼び出しが提供されます (クラス スコープも変更されます)。オブジェクトが属するクラスに追加) して実行されます。例は次のとおりです。
<?php class Pandas { protected $num = 1; } $pandas = new Pandas(); $add = function ($num) { $this->num += $num; echo $this->num . PHP_EOL; }; $add->call($pandas, 5); // 输出 6Callable to Closure (PHP7.1)
PHP7.1 では、Closure クラスに呼び出し可能な型を変換できる fromCallable メソッドがあります。
<?php class Foo { protected $num = 1; public static function hello(string $bar) { echo 'hello ' . $bar; } } $hello = Closure::fromCallable(['Foo', 'hello']); $hello('world');
この記述方法は非常にクールです。結局のところ、call_user_func 関数で呼び出すよりも、クロージャーを介して呼び出す方がはるかに楽しいです^_^。
0x03 概要詳細については、PHP 公式マニュアルの Closure クラスの中国語版がまだ公開されていないため、Closure クラスと無名関数を参照してください。更新されていますが、call メソッドと fromCallable メソッドはありません。内容については、英語版を読むことをお勧めします (ㄒoㄒ)
以上がPHP クロージャと Clouse クラス メソッドの機能を分析するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。