First class callable syntax
1. Introdução à First Class Callable Syntax
A First Class Callable Syntax (sintaxe de callable de primeira classe) foi introduzida no PHP 8.1 como uma forma elegante e concisa de criar closures a partir de funções existentes. Antes dessa feature, criar um callable a partir de uma função exigia escrever closures anônimas verbosas ou usar strings com nomes de funções, o que dificultava a legibilidade e a inferência de tipos.
A motivação principal é reduzir a verbosidade e melhorar a legibilidade do código. Em vez de escrever:
$closure = function($s) { return strlen($s); };
Podemos simplesmente escrever:
$closure = strlen(...);
Essa sintaxe transforma qualquer função ou método em um objeto Closure de primeira classe, permitindo que seja passado como argumento, armazenado em variáveis ou usado em funções de ordem superior sem a necessidade de wrappers desnecessários.
2. Sintaxe Básica e Exemplos
A sintaxe básica consiste em adicionar (...) após o nome da função ou método. Isso cria um callable que pode ser invocado posteriormente.
Exemplo com função nomeada:
$strlen = strlen(...);
echo $strlen('Hello World'); // 11
Comparação com closure tradicional:
// Antes (PHP < 8.1)
$strlenOld = function($s) { return strlen($s); };
// Depois (PHP 8.1+)
$strlenNew = strlen(...);
Callables a partir de métodos estáticos:
class MathUtils {
public static function square(int $n): int {
return $n * $n;
}
}
$square = MathUtils::square(...);
echo $square(5); // 25
Callables a partir de métodos de instância:
class Greeter {
public function greet(string $name): string {
return "Hello, $name!";
}
}
$greeter = new Greeter();
$greetMethod = $greeter->greet(...);
echo $greetMethod('Alice'); // Hello, Alice!
Callables a partir de funções anônimas:
$double = function(int $x): int { return $x * 2; };
$doubleCallable = $double(...);
echo $doubleCallable(10); // 20
3. Funcionamento Interno e Tipos
Quando você usa a sintaxe strlen(...), o PHP internamente cria um objeto da classe Closure. Essa closure mantém uma referência à função original e pode ser invocada como qualquer outra closure.
Verificação de tipo:
$callable = strlen(...);
var_dump($callable instanceof Closure); // bool(true)
var_dump(is_callable($callable)); // bool(true)
Inferência de assinatura:
O PHP infere automaticamente os parâmetros e o tipo de retorno da closure a partir da função original:
$trim = trim(...);
$reflection = new ReflectionFunction($trim);
echo $reflection->getNumberOfParameters(); // 2 (string, characters)
Exemplo com call_user_func:
$upper = strtoupper(...);
echo call_user_func($upper, 'php'); // PHP
4. Uso em Funções de Ordem Superior
A First Class Callable Syntax brilha especialmente quando usada com funções de ordem superior como array_map, array_filter e array_reduce.
Exemplo com array_map:
$nomes = ['alice', 'bob', 'charlie'];
// Sintaxe tradicional
$resultadoOld = array_map(function($n) { return strtoupper($n); }, $nomes);
// Com First Class Callable Syntax
$resultadoNew = array_map(strtoupper(...), $nomes);
print_r($resultadoNew);
// Array ( [0] => ALICE [1] => BOB [2] => CHARLIE )
Exemplo com array_filter:
$numeros = [1, 2, 3, 4, 5, 6];
// Filtrar números pares
$pares = array_filter($numeros, function($n) { return $n % 2 === 0; });
// Usando uma função auxiliar
function isPar(int $n): bool {
return $n % 2 === 0;
}
$pares = array_filter($numeros, isPar(...));
print_r($pares); // Array ( [1] => 2 [3] => 4 [5] => 6 )
Exemplo com array_reduce:
$valores = [10, 20, 30];
function soma(int $acc, int $val): int {
return $acc + $val;
}
$total = array_reduce($valores, soma(...), 0);
echo $total; // 60
Pipeline de processamento de dados:
$dados = [' alice ', 'BOB', ' CHARLIE '];
$processados = array_map(
strtolower(...),
array_map(
trim(...),
$dados
)
);
print_r($processados);
// Array ( [0] => alice [1] => bob [2] => charlie )
5. Limitações e Casos de Borda
Restrições com $this:
A sintaxe não pode ser usada diretamente com $this->method(...) dentro de um contexto de objeto. Você precisa primeiro armazenar o método em uma variável:
class UserService {
public function formatName(string $name): string {
return ucfirst(strtolower($name));
}
public function processNames(array $names): array {
// Isso NÃO funciona:
// return array_map($this->formatName(...), $names);
// Isso funciona:
$formatter = $this->formatName(...);
return array_map($formatter, $names);
}
}
Callables com parâmetros variádicos:
function somarTodos(int ...$numeros): int {
return array_sum($numeros);
}
$soma = somarTodos(...);
echo $soma(1, 2, 3, 4); // 10
Comportamento com funções internas vs funções definidas pelo usuário:
A sintaxe funciona tanto com funções internas do PHP quanto com funções definidas pelo usuário, mas há pequenas diferenças de performance:
// Função interna
$internal = array_map(...);
// Função definida pelo usuário
function meuMap(callable $fn, array $arr): array {
return array_map($fn, $arr);
}
$userDefined = meuMap(...);
Diferenças entre $fn(...) e Closure::fromCallable('fn'):
// Sintaxe First Class Callable
$callable1 = strlen(...);
// Equivalente explícito
$callable2 = Closure::fromCallable('strlen');
var_dump($callable1 == $callable2); // bool(true)
A principal diferença é que Closure::fromCallable() aceita strings e arrays como callable, enquanto a sintaxe (...) só funciona com identificadores de função/método.
6. Comparação com Alternativas
Closures tradicionais:
// Antes: verboso
$resultado = array_map(function($s) { return strtoupper($s); }, $array);
// Depois: conciso
$resultado = array_map(strtoupper(...), $array);
Sintaxe de string com nomes de função:
// String - sem type safety
$resultado = array_map('strtoupper', $array);
// First Class - com type safety
$resultado = array_map(strtoupper(...), $array);
Arrays de callable:
class Logger {
public function log(string $msg): void { echo $msg; }
}
$logger = new Logger();
// Array tradicional
$callable = [$logger, 'log'];
// First Class Callable Syntax
$callable = $logger->log(...);
Closure::fromCallable() explícito:
// Explícito
$callable = Closure::fromCallable('strtoupper');
// First Class Callable Syntax
$callable = strtoupper(...);
7. Boas Práticas e Aplicações no Mundo Real
Quando usar:
- Funções de ordem superior: Sempre que precisar passar uma função como argumento para
array_map,array_filter, etc. - Event listeners: Em sistemas de eventos, para criar handlers concisos.
- Composição de funções: Para criar pipelines de processamento de dados.
- Refatoração: Substituir closures anônimas simples que apenas chamam uma função.
Refatoração de código legado:
// Antes
$usuarios = array_map(function($user) {
return strtolower($user['email']);
}, $users);
// Depois
function extractEmail(array $user): string {
return strtolower($user['email']);
}
$usuarios = array_map(extractEmail(...), $users);
Exemplo em framework moderno (Laravel-style):
$pedidos = collect($pedidos)
->filter(fn($p) => $p['status'] === 'pendente')
->map(fn($p) => $p['total'])
->map(fn($t) => $t * 1.1) // imposto 10%
->map(fn($t) => number_format($t, 2));
// Com First Class Callable Syntax
function aplicarImposto(float $valor): float {
return $valor * 1.1;
}
function formatarMoeda(float $valor): string {
return number_format($valor, 2);
}
$pedidos = collect($pedidos)
->filter(fn($p) => $p['status'] === 'pendente')
->map(fn($p) => $p['total'])
->map(aplicarImposto(...))
->map(formatarMoeda(...));
Performance:
A First Class Callable Syntax tem performance comparável a Closure::fromCallable() e é geralmente mais rápida que closures anônimas que apenas chamam funções, pois elimina a sobrecarga da closure wrapper.
8. Conclusão e Referências
A First Class Callable Syntax é uma adição elegante ao PHP 8.1+ que melhora significativamente a legibilidade e concisão do código. Ela permite tratar funções como cidadãs de primeira classe de forma mais natural, reduzindo a verbosidade desnecessária e mantendo a segurança de tipos.
Os benefícios principais incluem:
- Redução de boilerplate
- Melhor legibilidade em funções de ordem superior
- Type safety mantido
- Performance competitiva
Em conjunto com outros recursos do PHP 8.1+ como Fibers, readonly properties e atributos nomeados, a First Class Callable Syntax contribui para um PHP mais moderno e expressivo.
Referências
- PHP Manual: First class callable syntax — Documentação oficial com exemplos e especificação completa da sintaxe.
- PHP 8.1: First-class callable syntax - PHP.Watch — Artigo detalhado com exemplos práticos e casos de uso.
- Stitcher.io: First class callable syntax in PHP 8.1 — Tutorial com explicações claras e comparações com alternativas.
- PHP RFC: First-class callable syntax — RFC original com discussão técnica e motivações por trás da implementação.
- The PHP Foundation: What's New in PHP 8.1 — Visão geral das novidades do PHP 8.1, incluindo a First Class Callable Syntax.
- Laravel News: PHP 8.1 First Class Callable Syntax — Exemplos práticos de uso em projetos Laravel e PHP moderno.