## Синтаксис создания первоклассных 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(...);

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open