Синтаксис callable-функций как объектов первого класса представили в PHP 8.1.0 как способ, которым анонимные функции создают из callable-объектов. Он заменяет существующий синтаксис вызываемых объектов со строками и массивами. Преимущество синтаксиса состоит в том, что он доступен для статического анализа и использует область видимости точки, в которой получили callable-объект.
Синтаксис CallableExpr(...)
создаёт объект Closure из callable-объекта.
Часть 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(...)
) нельзя, поскольку синтаксисnew Foo()
не признаётся вызовом.
Замечание:
Синтаксис callable-объектов первого класса нельзя комбинировать с оператором Nullsafe. Оба следующих результата приводят к ошибке времени компиляции:
<?php
$obj?->method(...);
$obj?->prop->method(...);
?>