Named arguments: chamando funções com clareza

1. O que são Named Arguments e por que surgiram?

Antes do PHP 8.0, chamar funções com múltiplos parâmetros opcionais era um pesadelo. Você precisava lembrar a ordem exata dos parâmetros e frequentemente passava null para parâmetros intermediários que não queria modificar. Isso gerava código confuso e propenso a erros.

// A velha forma: ordem importa, muitos nulls
function configurarSistema($host, $porta = 3306, $usuario = 'root', $senha = '', $ssl = false, $timeout = 30) {
    // ...
}

// Chamada confusa: o que é true? SSL? Debug? Cache?
configurarSistema('localhost', 3306, 'admin', '123', true, 60);

Named arguments, introduzidos no PHP 8.0 através da RFC "Named Arguments", resolveram esse problema permitindo que você especifique o nome do parâmetro na chamada da função.

// Nova forma: clara e auto-documentada
configurarSistema(
    host: 'localhost',
    usuario: 'admin',
    ssl: true,
    timeout: 60
);

2. Sintaxe e regras fundamentais

A sintaxe é simples: use nomeDoParametro: valor separado por vírgulas.

function criarUsuario($nome, $email, $ativo = true, $tipo = 'comum', $notificar = false) {
    // lógica...
}

// Ordem arbitrária - funciona!
criarUsuario(
    email: 'joao@email.com',
    nome: 'João Silva',
    tipo: 'admin',
    notificar: true
);

Regras importantes:
- Argumentos posicionais devem vir antes dos nomeados
- Não é possível misturar no mesmo parâmetro (ex: $nome: 'João' posicional não funciona)
- O nome deve corresponder exatamente ao parâmetro da função

// Válido: posicionais primeiro, depois nomeados
criarUsuario('Maria', 'maria@email.com', tipo: 'moderador');

// Inválido: nomeado antes de posicional
criarUsuario(email: 'maria@email.com', 'Maria'); // Erro!

3. Vantagens práticas na legibilidade do código

O maior benefício é eliminar parâmetros "mágicos" que exigem consulta à documentação:

// Antes - o que significa cada booleano?
$query->where('status', '=', 'ativo')
      ->orderBy('nome', 'asc')
      ->limit(10, 20);

// Depois - auto-documentado
$query->where(
    coluna: 'status',
    operador: '=',
    valor: 'ativo'
)->orderBy(
    coluna: 'nome',
    direcao: 'asc'
)->limit(
    offset: 10,
    quantidade: 20
);

Para funções com parâmetros do mesmo tipo, os named arguments evitam trocas acidentais:

function calcularDistancia($x1, $y1, $x2, $y2) {
    return sqrt(($x2 - $x1)**2 + ($y2 - $y1)**2);
}

// Antes: fácil trocar x2 com y1
calcularDistancia(0, 0, 3, 4);

// Depois: impossível errar
calcularDistancia(x1: 0, y1: 0, x2: 3, y2: 4);

4. Trabalhando com parâmetros opcionais e valores padrão

A capacidade de pular parâmetros intermediários é revolucionária:

function criarConexao(
    $driver = 'mysql',
    $host = 'localhost',
    $porta = 3306,
    $banco = 'test',
    $usuario = 'root',
    $senha = '',
    $charset = 'utf8mb4',
    $collation = 'utf8mb4_unicode_ci'
) {
    // ...
}

// Antes: precisava passar null para tudo
criarConexao('mysql', 'localhost', 3306, 'meubanco', 'admin', null, null, null);

// Depois: especifique apenas o necessário
criarConexao(
    banco: 'meubanco',
    usuario: 'admin',
    charset: 'utf8'
);

Comparado com a abordagem antiga de arrays associativos:

// Abordagem antiga com array
criarConexaoArray([
    'banco' => 'meubanco',
    'usuario' => 'admin'
]);

// Named arguments é mais elegante e com type hinting preservado
criarConexao(banco: 'meubanco', usuario: 'admin');

5. Named arguments com variadic functions e spread operator

Com funções variádicas, os named arguments têm limitações importantes:

function somar(...$numeros) {
    return array_sum($numeros);
}

// Isso funciona - spread operator
$args = [1, 2, 3];
somar(...$args);

// Isso NÃO funciona - named argument não pode ser capturado por variadic
somar(numeros: [1, 2, 3]); // Erro! 'numeros' não é um parâmetro nomeável

Exemplo prático com compact():

function registrarLog($mensagem, $nivel = 'info', $contexto = []) {
    // ...
}

$usuario = 'João';
$acao = 'login';
$ip = '192.168.1.1';

// Spread com named arguments
registrarLog(
    mensagem: 'Usuário realizou ação',
    contexto: compact('usuario', 'acao', 'ip')
);

6. Limitações e cuidados importantes

Callbacks dinâmicos: Named arguments não funcionam com call_user_func_array() de forma direta:

function processar($nome, $idade) {
    echo "$nome tem $idade anos";
}

// Isso funciona
call_user_func_array('processar', ['João', 30]);

// Isso NÃO funciona
call_user_func_array('processar', ['nome' => 'João', 'idade' => 30]); // Erro!

Funções internas: Muitas funções built-in do PHP não suportam named arguments porque seus parâmetros não têm nomes definidos:

// Isso funciona
array_map('strtoupper', ['a', 'b']);

// Isso NÃO funciona
array_map(callback: 'strtoupper', array: ['a', 'b']); // Erro!

Performance: Em loops intensivos, o overhead de named arguments é mínimo (~5-10%), mas pode ser relevante:

// Em loops críticos, prefira posicional
for ($i = 0; $i < 1000000; $i++) {
    funcaoPesada(valor: $i, opcao: true); // Mais lento
    funcaoPesada($i, true); // Mais rápido
}

7. Casos de uso reais e boas práticas

Laravel Query Builder:

User::query()
    ->where(column: 'status', operator: '=', value: 'ativo')
    ->orderBy(column: 'criado_em', direction: 'desc')
    ->take(value: 10)
    ->get();

Symfony Forms:

$form->add(
    name: 'email',
    type: EmailType::class,
    options: [
        'required' => true,
        'label' => 'E-mail'
    ]
);

Testes unitários com PHPUnit:

$mock = $this->createMock(UsuarioRepository::class);
$mock->method('find')
     ->with(id: 42, includeDeleted: false)
     ->willReturn($usuario);

Quando usar:
- Funções com 4+ parâmetros
- Parâmetros booleanos (evite true/false soltos)
- Parâmetros opcionais no meio da lista
- APIs públicas de bibliotecas

Quando evitar:
- Funções com 2-3 parâmetros óbvios (somar($a, $b))
- Loops extremamente críticos de performance
- Callbacks dinâmicos com call_user_func()

8. Exemplo completo: refatorando uma função complexa

Vamos refatorar uma função típica de criação de usuário:

// Versão original - péssima legibilidade
function criarUsuario(
    $nome,
    $email,
    $ativo = true,
    $verificado = false,
    $tipo = 'comum',
    $plano = null,
    $notificar = true,
    $roles = ['usuario']
) {
    // lógica complexa...
}

// Chamada original - o que cada parâmetro significa?
criarUsuario(
    'João Silva',
    'joao@email.com',
    true,   // ativo?
    false,  // verificado?
    'admin', // tipo
    null,   // plano?
    true    // notificar?
);

Refatoração com named arguments:

// Agora a chamada é auto-documentada
criarUsuario(
    nome: 'João Silva',
    email: 'joao@email.com',
    tipo: 'admin',
    notificar: true,
    roles: ['admin', 'editor']
);

// Manutenção facilitada - adicionar/remover parâmetros não quebra chamadas
criarUsuario(
    nome: 'Maria Santos',
    email: 'maria@email.com',
    tipo: 'moderador',
    // Note: não preciso especificar ativo, verificado, plano
);

Benefícios no longo prazo:
1. Menos bugs: Trocar parâmetros de posição é impossível
2. Documentação viva: O código se explica
3. Refatoração segura: Adicionar novos parâmetros opcionais não quebra nada
4. Produtividade: IDE mostra os nomes dos parâmetros, facilitando chamadas complexas

Named arguments transformaram a forma como escrevemos PHP, especialmente em APIs públicas e funções de configuração. Use-os com sabedoria e seu código será mais claro, seguro e fácil de manter.

Referências