Синтаксис callable-выражений как объектов первого класса

Синтаксис callable-выражений как объектов первого класса, которые разрешается передавать как аргумент, возвращать из функций или присваивать переменным, представили в PHP 8.1.0 как способ, который создаёт анонимные функции из callable-выражений. Новый синтаксис вытесняет предыдущий callable-синтаксис со строками и массивами. Преимущество нового синтаксиса состоит в доступности для статического анализа и в том, что новый синтаксис наследует область видимости переменных той точки, в которой callable-выражение получили.

Синтаксис CallableExpr(...) создаёт объект Closure из выражения, доступного для вызова, где CallableExpr — элемент синтаксиса, который принимает выражение, доступное для прямого вызова в терминах PHP-грамматики:

Пример #1 Простой пример синтаксиса callable-выражения как объекта первого класса

<?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,
// и с этого момента будет проверяться видимость.

class Foo1
{
public function
getPrivateMethod()
{
// Callable-выражение унаследует область видимости переменных,
// в которой получат выражение
return $this->privateMethod(...); // Значение возврата идентично значению вызова
// Closure::fromCallable([$this, 'privateMethod']);
}

private function
privateMethod()
{
echo
__METHOD__, "\n";
}
}

$foo1 = new Foo1();
$privateMethod = $foo1->getPrivateMethod();
$privateMethod(); // Foo1::privateMethod

?>

Замечание:

Синтаксисом наподобие new Foo(...) нельзя создать объект, поскольку PHP не считает синтаксис new Foo() вызовом.

Замечание:

Синтаксис, который создаёт объекты первого класса в виде callable-выражений, нельзя комбинировать с null-безопасным оператором. Каждая из следующих строк вызывает ошибку времени компиляции:

<?php

$obj
?->method(...);
$obj?->prop->method(...);

?>