Closures e funções anônimas
Esta aula aborda closures e funções anônimas em PHP, incluindo sintaxe, captura de variáveis com 'use', arrow functions e callables. O conteúdo é prático e progressivo, com exemplos e exercícios para fixação.
Em PHP, funções anônimas (também chamadas de closures) são funções sem nome que podem ser armazenadas em variáveis, passadas como argumentos ou retornadas por outras funções. Elas são fundamentais para programação funcional, callbacks e operações em arrays. Nesta aula, exploraremos a criação, uso e nuances das closures, além das modernas arrow functions introduzidas no PHP 7.4.
Entender closures é essencial para trabalhar com bibliotecas como Laravel (ex.: rotas, middlewares) e para escrever código mais expressivo e reutilizável.
Funções anônimas
Uma função anônima é definida sem um nome e pode ser atribuída a uma variável ou passada diretamente como argumento. A sintaxe básica é: function($param) { ... }. Elas herdam o escopo do local onde são definidas, mas precisam de use para capturar variáveis externas.
Exemplo básico:
$saudacao = function($nome) {
return "Olá, $nome!";
};
echo $saudacao('Ana'); // Olá, Ana!Funções anônimas são frequentemente usadas com array_map, array_filter e outras funções de array. Exemplo:
$numeros = [1, 2, 3, 4, 5];
$dobrados = array_map(function($n) {
return $n * 2;
}, $numeros);
print_r($dobrados); // [2, 4, 6, 8, 10]use (captura)
Por padrão, funções anônimas não podem acessar variáveis do escopo pai. Para isso, usamos a palavra-chave use após a lista de parâmetros, especificando quais variáveis capturar. A captura pode ser por valor (padrão) ou por referência (com &).
Exemplo de captura por valor:
$mensagem = 'Bem-vindo';
$saudacao = function($nome) use ($mensagem) {
return "$mensagem, $nome!";
};
echo $saudacao('Carlos'); // Bem-vindo, Carlos!Captura por referência permite modificar a variável externa:
$contador = 0;
$incrementar = function() use (&$contador) {
$contador++;
};
$incrementar();
$incrementar();
echo $contador; // 2Importante: variáveis capturadas por valor são copiadas no momento da definição da closure, não no momento da execução. Já por referência, a closure mantém uma ligação com a variável original.
Arrow functions (fn)
Arrow functions, introduzidas no PHP 7.4, são uma sintaxe mais concisa para funções anônimas. Usam a palavra-chave fn e não precisam de use para capturar variáveis do escopo pai – elas automaticamente capturam por valor (implicitamente). Além disso, arrow functions sempre retornam o valor da expressão, sem necessidade de return explícito.
Sintaxe: fn($param) => expressão;
Exemplo:
$numeros = [1, 2, 3, 4, 5];
$dobrados = array_map(fn($n) => $n * 2, $numeros);
print_r($dobrados); // [2, 4, 6, 8, 10]Captura implícita:
$fator = 3;
$multiplicar = fn($n) => $n * $fator;
echo $multiplicar(5); // 15Limitações: arrow functions só podem conter uma única expressão (não podem ter múltiplas instruções). Também não permitem captura por referência – se precisar, use closure tradicional.
Callable
O tipo callable em PHP representa qualquer coisa que possa ser chamada como função: funções internas, funções definidas pelo usuário, métodos de classe, closures e objetos que implementam __invoke. Funções que aceitam callables como parâmetro (ex.: array_map, usort) permitem alta flexibilidade.
Exemplo com função nomeada:
function quadrado($n) {
return $n * $n;
}
$quadrados = array_map('quadrado', [1, 2, 3]);
print_r($quadrados); // [1, 4, 9]Exemplo com método estático:
class Utils {
public static function cubo($n) {
return $n * $n * $n;
}
}
$cubos = array_map(['Utils', 'cubo'], [1, 2, 3]);
print_r($cubos); // [1, 8, 27]Exemplo com objeto que implementa __invoke:
class Multiplicador {
private $fator;
public function __construct($fator) {
$this->fator = $fator;
}
public function __invoke($n) {
return $n * $this->fator;
}
}
$duplicar = new Multiplicador(2);
echo $duplicar(5); // 10Para verificar se algo é callable, use is_callable(). Para chamar um callable, use call_user_func() ou call_user_func_array().
Boas práticas
- Prefira arrow functions para closures simples de uma linha, pois são mais legíveis.
- Evite capturar grandes variáveis por referência para não causar efeitos colaterais inesperados.
- Use type hints nos parâmetros e retorno das closures sempre que possível (PHP 7.1+ permite tipagem em closures).
- Em callbacks complexos, considere extrair para uma função nomeada para facilitar testes e reuso.
Referências
Exercícios
Crie uma função anônima que receba dois números e retorne a soma. Atribua-a a uma variável e chame-a com os valores 5 e 7.
✓ Resposta:$soma = function($a, $b) { return $a + $b; }; echo $soma(5, 7); // 12Escreva uma closure que capture uma variável $prefixo por valor e uma variável $sufixo por referência. A closure deve concatenar prefixo + texto + sufixo. Modifique o sufixo após definir a closure e veja o efeito.
✓ Resposta:$prefixo = '['; $sufixo = ']'; $formatar = function($texto) use ($prefixo, &$sufixo) { return $prefixo . $texto . $sufixo; }; echo $formatar('teste'); // [teste] $sufixo = ')'; echo $formatar('teste'); // [teste)Use uma arrow function com array_map para converter um array de strings para maiúsculas.
✓ Resposta:$palavras = ['php', 'closure', 'arrow']; $maiusculas = array_map(fn($p) => strtoupper($p), $palavras); print_r($maiusculas); // ['PHP', 'CLOSURE', 'ARROW']Crie uma função que aceite um callable e um array, e aplique o callable a cada elemento, retornando um novo array. Teste com uma função anônima que dobra o valor.
✓ Resposta:function aplicarCallable(callable $fn, array $arr): array { $resultado = []; foreach ($arr as $item) { $resultado[] = $fn($item); } return $resultado; } $dobro = function($n) { return $n * 2; }; print_r(aplicarCallable($dobro, [1, 2, 3])); // [2, 4, 6]Implemente uma classe 'Multiplicador' com __invoke que receba um fator no construtor e, ao ser chamada, multiplique o argumento pelo fator. Crie uma instância com fator 3 e use-a em array_map.
✓ Resposta:class Multiplicador { private $fator; public function __construct($fator) { $this->fator = $fator; } public function __invoke($n) { return $n * $this->fator; } } $tresVezes = new Multiplicador(3); $resultado = array_map($tresVezes, [1, 2, 3]); print_r($resultado); // [3, 6, 9]