Синтаксис создания первоклассных callable-значений
Синтаксис преобразования callable-выражений в объекты первого класса, которые возможно
передавать как аргумент, возвращать из функций или присваивать переменным,
представили в PHP 8.1.0 как способ, который
создаёт анонимные функции
из callable-выражений.
Новый синтаксис вытесняет предыдущий callable-синтаксис со строками и массивами.
Преимущество нового синтаксиса состоит в доступности для статического анализа
и наследовании новым синтаксисом области видимости переменных в точке
получения callable-выражения.
Синтаксис CallableExpr(...) создаёт объект
Closure из выражения, доступного для вызова,
где CallableExpr — элемент синтаксиса, который принимает выражение,
доступное для прямого вызова в терминах PHP-грамматики:
Пример #1 Пример создания первоклассных вызываемых значений синтаксисом с многоточием
<?php
class Foo
{
public function method() {}
public static function staticmethod() {}
public function __invoke() {}
}
$obj = new Foo();
$classStr = 'Foo';
$methodStr = 'method';
$staticmethodStr = 'staticmethod';
$f1 = strlen(...);
$f2 = $obj(...); // Вызов объекта как функции
$f3 = $obj->method(...);
$f4 = $obj->$methodStr(...);
$f5 = Foo::staticmethod(...);
$f6 = $classStr::$staticmethodStr(...);
// Традиционный синтаксис callable-выражений со строками и массивами
$f7 = 'strlen'(...);
$f8 = [$obj, 'method'](...);
$f9 = [Foo::class, 'staticmethod'](...);
Замечание:
Оператор ... — часть синтаксиса, а не пропуск.
Выражение CallableExpr(...) и метод Closure::fromCallable()
семантически идентичны. Поэтому в отличие от callable-синтаксиса
со строками и массивами, синтаксис CallableExpr(...)
учитывает область видимости контекста, в котором создаёт замыкание:
Пример #2
Сравнение области видимости синтаксиса CallableExpr(...)
и традиционного callable-синтаксиса
<?php
class Foo
{
public function getPrivateMethod()
{
return [$this, 'privateMethod'];
}
private function privateMethod()
{
echo __METHOD__, "\n";
}
}
$foo = new Foo();
$privateMethod = $foo->getPrivateMethod();
$privateMethod();
// Fatal error: Call to private method Foo::privateMethod() from global scope
// Причина фатальной ошибки состоит в вызове замыкания вне класса Foo,
// при том что видимость метода проверяется в контексте вызова, а не определения
Замечание:
Синтаксисом наподобие new Foo(...) нельзя создать объект,
поскольку синтаксис new Foo() не относится к вызовам.
Замечание:
Синтаксис, который создаёт объекты первого класса из callable-выражений,
нельзя комбинировать
с null-безопасным оператором.
Каждая из следующих строк вызывает ошибку времени компиляции: