Sintaxis de llamadas de retorno de primera clase

La sintaxis de llamadas de retorno de primera clase fue introducida desde PHP 8.1.0, como una forma de creación de funciones anónimas a partir de una llamada de retorno. Reemplaza la sintaxis de invocación existente que utiliza cadenas y arrays. La ventaja de esta sintaxis es que es accesible para el análisis estático y utiliza el alcance en el punto donde se adquiere la invocación.

La sintaxis CallableExpr(...) es usada para crear un objeto Closure a partir de una llamada de retorno. CallableExpr acepta cualquier expersión que pueda ser llamada en la sintaxis de PHP.

Ejemplo #1 Sintaxis de llamadas de retorno de primera clase

<?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(...); // Objeto invocable
$f3 = $obj->method(...);
$f4 = $obj->$methodStr(...);
$f5 = Foo::staticmethod(...);
$f6 = $classStr::$staticmethodStr(...);

// traditional callable using string, array
$f7 = 'strlen'(...);
$f8 = [$obj, 'method'](...);
$f9 = [Foo::class, 'staticmethod'](...);
?>

Nota:

El ... es parte de la sintaxis, y no una omisión.

CallableExpr(...) tiene la misma sintaxis que Closure::fromCallable(). Es decir, a diferencia de las llamadas que utilizan cadenas y arrays, CallableExpr(...) respeta el ámbito en el punto donde se crea:

Ejemplo #2 Comparación del ámbito de CallableExpr(...) y una llamada de retorno tradicional

<?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
// Esto es porque la llamada es realizada fuera de Foo y la visibilidad es comprobada en ese momento.

class Foo1 {
public function
getPrivateMethod() {
// Usa el ámbito donde la llamada de retorno es creada.
return $this->privateMethod(...); // Identico a Closure::fromCallable([$this, 'privateMethod']);
}

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

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

Nota:

La creación de objetos mediante esta sintaxis (por ejemplo, new Foo(...)) no está soportada, porque la sintaxis new Foo() no se considera una llamada.

Nota:

La sintaxis de llamada de retorno de primera clase no se puede combinar con el operador nullsafe. Ambos de los siguientes resultan en un error en tiempo de compilación:

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