Синтаксис создания первоклассных 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,
// при том что видимость метода проверяется в контексте вызова, а не определения

<?php
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-выражений, нельзя комбинировать с null-безопасным оператором. Каждая из следующих строк вызывает ошибку времени компиляции:

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