Variadic functions e spread operator
1. Introdução às Funções Variádicas
Funções variádicas são funções capazes de aceitar um número variável de argumentos. Em PHP, esse recurso resolve um problema clássico: como criar funções flexíveis que processam quantidades imprevisíveis de parâmetros sem depender de arrays explícitos ou soluções improvisadas.
Antes do PHP 5.6, a única maneira de lidar com argumentos variáveis era usando func_get_args(), func_num_args() e func_get_arg(). Essas funções internas funcionavam, mas exigiam código mais verboso e não ofereciam type hinting. O operador ... (splat operator) mudou esse cenário, trazendo uma sintaxe limpa e integrada ao sistema de tipos do PHP.
2. Sintaxe Básica com ...$args
A declaração de um parâmetro variádico é feita prefixando o nome do parâmetro com três pontos:
function soma(...$numeros) {
return array_sum($numeros);
}
echo soma(1, 2, 3, 4, 5); // 15
echo soma(10, 20); // 30
echo soma(); // 0 (array vazio)
Quando a função é chamada, todos os argumentos passados são automaticamente empacotados em um array. O parâmetro $numeros se torna um array contendo cada argumento na ordem em que foi fornecido.
Regra fundamental: o parâmetro variádico deve ser sempre o último na assinatura da função. Colocá-lo em outra posição gera um erro fatal.
// ERRADO: Parse error
function exemplo(...$args, $extra) { }
// CORRETO
function exemplo($fixo, ...$variavel) { }
3. Tipagem e Type Hinting em Parâmetros Variádicos
Um dos grandes avanços do operador ... é a possibilidade de aplicar type hints diretamente ao parâmetro variádico:
function media(float ...$valores): float {
if (empty($valores)) {
return 0.0;
}
return array_sum($valores) / count($valores);
}
echo media(10.5, 20.3, 30.1); // 20.3
echo media(1, 2, 3); // 2.0 (int convertido para float)
// echo media("a", "b"); // TypeError: Argument must be float
A tipagem garante que todos os argumentos passados sejam do tipo especificado. O PHP aplica a coerção normalmente (se em modo estrito) ou converte conforme as regras de tipo. Isso elimina a necessidade de validações manuais dentro da função.
Limitação: o tipo é único para todos os argumentos. Não é possível definir tipos diferentes para argumentos variádicos — para isso, ainda é necessário usar arrays ou objetos específicos.
4. Spread Operator em Chamadas de Funções
O operador ... também funciona no sentido inverso: desempacotar arrays ou objetos Traversable em argumentos individuais ao chamar funções.
function cumprimentar($saudacao, $nome, $pontuacao = ".") {
return "$saudacao, $nome$pontuacao";
}
$dados = ["Olá", "Maria", "!"];
echo cumprimentar(...$dados); // "Olá, Maria!"
Exemplos práticos com funções nativas:
// array_merge com spread
$parte1 = [1, 2, 3];
$parte2 = [4, 5, 6];
$parte3 = [7, 8, 9];
$tudo = array_merge(...[$parte1, $parte2, $parte3]);
// Resultado: [1, 2, 3, 4, 5, 6, 7, 8, 9]
// sprintf com argumentos dinâmicos
$formato = "Nome: %s, Idade: %d, Cidade: %s";
$valores = ["João", 30, "São Paulo"];
echo sprintf($formato, ...$valores);
// "Nome: João, Idade: 30, Cidade: São Paulo"
5. Combinação de Argumentos Fixos e Variádicos
É comum combinar parâmetros obrigatórios com o variádico para criar APIs expressivas:
function log($nivel, ...$mensagens) {
$timestamp = date('Y-m-d H:i:s');
foreach ($mensagens as $msg) {
echo "[$timestamp][$nivel] $msg\n";
}
}
log('INFO', 'Sistema iniciado', 'Conexão ok', 'Usuário logado');
// [2025-01-15 10:30:00][INFO] Sistema iniciado
// [2025-01-15 10:30:00][INFO] Conexão ok
// [2025-01-15 10:30:00][INFO] Usuário logado
Também é possível usar valores padrão antes do variádico:
function config($chave, $valor = null, ...$opcoes) {
$resultado = ['chave' => $chave, 'valor' => $valor];
if (!empty($opcoes)) {
$resultado['extra'] = $opcoes;
}
return $resultado;
}
print_r(config('timeout', 30, 'cache', 'ssl'));
// Array ( [chave] => timeout [valor] => 30 [extra] => Array ( [0] => cache [1] => ssl ) )
A ordem correta é sempre: parâmetros fixos → parâmetros com valor padrão → parâmetro variádico.
6. Spread Operator em Declarações de Array
Desde o PHP 7.4, é possível usar o spread operator dentro de arrays literais:
$numeros1 = [1, 2, 3];
$numeros2 = [4, 5, 6];
$combinado = [0, ...$numeros1, ...$numeros2, 7];
// Resultado: [0, 1, 2, 3, 4, 5, 6, 7]
Isso funciona com qualquer Traversable:
function gerarPares($limite) {
for ($i = 2; $i <= $limite; $i += 2) {
yield $i;
}
}
$lista = [1, ...gerarPares(10), 11];
// [1, 2, 4, 6, 8, 10, 11]
Cuidados com arrays associativos: arrays com chaves string podem sobrescrever valores se houver chaves duplicadas. Arrays numéricos são reindexados automaticamente:
$a = ['x' => 10, 'y' => 20];
$b = ['y' => 30, 'z' => 40];
$c = [...$a, ...$b];
// ['x' => 10, 'y' => 30, 'z' => 40] (y foi sobrescrito)
7. Funções Variádicas com Referências (&)
Parâmetros variádicos também podem ser passados por referência, permitindo modificar múltiplas variáveis simultaneamente:
function incrementar(&...$numeros) {
foreach ($numeros as &$n) {
$n++;
}
}
$a = 1;
$b = 2;
$c = 3;
incrementar($a, $b, $c);
echo "$a, $b, $c"; // 2, 3, 4
Limitações: não é possível misturar parâmetros por valor e por referência no mesmo variádico. Apenas variáveis podem ser passadas (não literais ou expressões). Use com moderação, pois referências podem tornar o código menos previsível.
8. Casos de Uso Avançados e Boas Práticas
Funções de callback flexíveis:
function aplicar(callable $fn, ...$args) {
return $fn(...$args);
}
$resultado = aplicar(function($a, $b) {
return $a * $b;
}, 5, 3);
echo $resultado; // 15
Middlewares e pipelines:
function pipeline($valor, callable ...$etapas) {
foreach ($etapas as $etapa) {
$valor = $etapa($valor);
}
return $valor;
}
$resultado = pipeline(5,
fn($n) => $n * 2,
fn($n) => $n + 1,
fn($n) => $n ** 2
);
echo $resultado; // 121
Quando evitar:
- Se o número de argumentos for sempre pequeno e fixo, parâmetros nomeados são mais legíveis.
- Em APIs públicas, considere se arrays nomeados (com chaves descritivas) não seriam mais autoexplicativos.
- O empacotamento/desempacotamento tem custo mínimo, mas em loops muito apertados (milhares de iterações) o func_get_args() pode ser marginalmente mais rápido.
Performance: em versões modernas do PHP (8.x), o operador ... é geralmente tão rápido quanto func_get_args() para a maioria dos casos de uso. A diferença é irrelevante em aplicações reais.
Referências
- PHP Manual: Function arguments - Variable-length argument lists — Documentação oficial sobre parâmetros variádicos com
.... - PHP Manual: Argument unpacking — Seção do manual sobre o spread operator em chamadas de função.
- PHP Manual: Spread operator in array expression — Documentação sobre desempacotamento de arrays com spread operator.
- PHP Watch: Variadic functions in PHP — Artigo técnico detalhado com exemplos práticos e boas práticas.
- Stitcher.io: Variadic arguments in PHP — Tutorial completo sobre funções variádicas e spread operator, incluindo casos de uso avançados.
- PHP.net RFC: Variadic functions — RFC original que introduziu o operador
...no PHP 5.6, com discussões técnicas.