Sintaxe de Callable de Primeira Classe
A sintaxe de callable de primeira classe é introduzida a partir do PHP 8.1.0, como uma maneira de criar funções anônimas a partir de callable. Ela substitui a sintaxe de callable existente usando strings e arrays. A vantagem dessa sintaxe é que ela é acessível à análise estática, e usa o escopo no ponto onde o callable é adquirido.
A sintaxe CallableExpr(...)
é usada para criar um objeto Closure a partir de callable. CallableExpr
aceita qualquer expressão que possa ser diretamente chamada na gramática do PHP:
Example #1 Sintaxe de callable de primeira classe simples
<?php
class Foo {
public function metodo() {}
public static function metodoestatico() {}
public function __invoke() {}
}
$obj = new Foo();
$strClasse = 'Foo';
$strMetodo = 'metodo';
$strMetodoestatico = 'metodoestatico';
$f1 = strlen(...);
$f2 = $obj(...); // objeto invocável
$f3 = $obj->metodo(...);
$f4 = $obj->$strMetodo(...);
$f5 = Foo::metodoestatico(...);
$f6 = $strClasse::$strMetodoestatico(...);
// Callable tradicional usando string, array
$f7 = 'strlen'(...);
$f8 = [$obj, 'metodo'](...);
$f9 = [Foo::class, 'metodoestatico'](...);
?>
Note:
As
...
são parte da sintaxe, e não uma omissão.
CallableExpr(...)
tem a mesma semântica que Closure::fromCallable().
Isto é, That is, ao contrário de callable usando string e array, CallableExpr(...)
respeita o escopo no ponto onde ela é criada:
Example #2 Comparação de escopo de CallableExpr(...)
e callable tradicional
<?php
class Foo {
public function obterMetodoPrivado() {
return [$this, 'metodoPrivado'];
}
private function metodoPrivado() {
echo __METHOD__, "\n";
}
}
$foo = new Foo;
$metodoPrivado = $foo->obterMetodoPrivado();
$metodoPrivado();
// Fatal error: Call to private method Foo::metodoPrivado() from global scope
// Isso acontece porque a chamada é realizada fora de Foo e a visibilidade será verificada a partir desse ponto.
class Foo1 {
public function obterMetodoPrivado() {
// Usa o escopo onde o callable é adquirido.
return $this->metodoPrivado(...); // Idêntico a Closure::fromCallable([$this, 'metodoPrivado']);
}
private function metodoPrivado() {
echo __METHOD__, "\n";
}
}
$foo1 = new Foo1;
$metodoPrivado = $foo1->obterMetodoPrivado();
$metodoPrivado(); // Foo1::metodoPrivado
?>
Note:
Criação de objetos por essa sintaxe (por exemplo
new Foo(...)
) não é suportada, porque a sintaxenew Foo()
não é considerada uma chamada.
Note:
A sintaxe de callable de primeira classe não pode ser combinada com o operador nullsafe. Ambos os resultados a seguir resultam em um erro de tempo de compilação:
<?php $obj?->metodo(...); $obj?->prop->metodo(...); ?>