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ㄒ).