Анализ PHP Clourse (класс закрытия)

задняя часть PHP pandas PhpStorm

0x00 Предисловие

Замыкания — это функции, которые инкапсулируют окружающее состояние во время создания. Даже если среда, в которой находится замыкание, больше не существует, состояние, инкапсулированное в замыкании, все равно существует.

Все замыкания в PHPClourseОбъект, созданный классом, то есть замыкание, ничем не отличается от любого другого объекта PHP. И у объекта должны быть свои методы и свойства.Эта статья суммирует основное использование иClourseРоль методов класса.

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,useТакие переменные нельзя передавать в:superglobals, $это или то же имя, что и у параметра.

Также используя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 по умолчанию используется передача по ссылке, поэтому работайте внутри замыкания.useПри прохождении объектов требуется особая осторожность. Пример выглядит следующим образом:

<?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 Класс закрытия

Докажите, что замыкания — это просто объекты класса Clourse.

<?php
$clourse = function () {
    echo 'hello clourse';
};

if (is_object($clourse)) {
    echo get_class($clourse);
}
// 输出 Closure

Приведенный выше код выведет Closure, доказывающий, что замыкание — это просто нормальноеClosureобъект класса.

Сводка класса Close

мы можем начать сОфициальное руководство по PHPСм. соответствующую информацию о классе замыкания, вот что я видел в локальной документации PhpStorm.ClourseРезюме класса.

/**
 * 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) {}
}

во-первыхClourseкласс какfinalкласс, то есть он не будет унаследован, за которым следует его конструктор__constructустановлен наprivateто есть не может пройтиnewКлючевое слово создает экземпляр объекта замыкания.Эти две точки гарантируют, что замыкание может быть передано только черезfunction (...) use(...) {...}Этот синтаксис создает экземпляр .

Почему замыкания могут выполняться как функции?

Из сводки класса выше мы видим, чтоClourseкласс реализует__invokeметод, который объясняется в официальном руководстве по PHP следующим образом:

При попытке вызвать объект способом, вызывающим функцию,__invoke()Метод будет вызван автоматически.

Вот почему замыкания могут выполняться как функции.

Привязывает указанный объект $this к области класса

В фреймворках, позволяющих использовать замыкающую маршрутизацию (таких как Slim), мы можем увидеть следующее написание:

$app->get('/test', function () {
    echo $this->request->getMethod();
});

На самом деле может использоваться в закрытии$this? это$thisНа какой объект он указывает?

пройти черезbindToиbindметоды могут быть привязаны$thisи функции с областью действия класса, примеры следующие:

<?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иbindметоды указаны$newscopeпараметр,$newscopeПараметр по умолчанию равенstaticТо есть область действия класса не изменяется.$newscopeПараметр принимает имя класса или объект и изменяет область класса закрытия на указанную область класса, в это времяPandasКатегория$numДоступ к свойствам можно получить с помощью замыканий.

Однократная привязка $this объекта и области действия класса и выполнение (PHP7)

bindToиbindКаждый раз, когда метод указывает новый объект и область класса, исходное замыкание должно быть скопировано, а затем должно быть возвращено новое замыкание, что неудобно, когда объект привязки необходимо изменять несколько раз, поэтому PHP7 предоставляет новый метод.callОн может временно привязать замыкание к объекту (область действия класса также изменяется в соответствии с классом, которому принадлежит объект) и выполнить его. Пример выглядит следующим образом:

<?php

class Pandas {
    protected $num = 1;
}

$pandas = new Pandas();

$add = function ($num) {
    $this->num += $num;
    echo $this->num . PHP_EOL;
};

$add->call($pandas, 5);
// 输出 6

Возможность вызова для закрытия (PHP7.1)

в PHP7.1Closureкласс существуетfromCallableметод можетcallableЗначение типа преобразуется в замыкание, пример такой:

<?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 Сводка

Для получения дополнительной информации см.Класс закрытияианонимная функция, потому что класс Closure китайской версии официального руководства по PHP не обновлялся, поэтому нетcallиfromCallableДля содержания метода рекомендуется прочитать английскую версию (ㄒoㄒ).

Анализ PHP Clourse (Closure Class) - My Blog Original