Funções Variádicas

Artigo que apresenta a sintaxe do PHP para declaração de funções variádicas e explica sua utilidade.

Introdução

Uma das novidades do PHP 5.6 é o suporte a uma nova sintaxe para declaração de funções variádicas. Para quem não sabe, funções variádicas (variadic functions) são aquelas que podem receber um número indeterminado de parâmetros. Neste artigo veremos o que são funções variádicas e qual é a nova sintaxe para declará-las a partir do PHP 5.6.

O que são funções variádicas?

Uma função variádica é uma função que permite a passagem de um número indeterminado de parâmetros. Normalmente estas funções tem alguns parâmetros fixos e, a partir de determinado parâmetro, este número é variável. Um exemplo de função variádica do próprio PHP é a função printf. Ela obrigatoriamente recebe o primeiro parâmetro, que representa o formato da string que ela vai imprimir (este formato pode possuir slots para serem exibidos de forma formatada). A quantidade de parâmetros passados após o primeiro é que é variável, e depende do valor do formato passado no primeiro parâmetro. Veja alguns exemplos:

// 1 parametro de formato + 0 parametros auxiliares
printf('Meu nome é Rubens');

// 1 parametro de formato + 1 parametro auxiliar
printf('Meu nome é %s', $nome);

// 1 parametro de formato + 2 parametros auxiliares
printf('Meu nome é %s e tenho %d anos', $nome, $idade);

Observe que o "formato" passado como primeiro parâmetro para a função pode possuir slots para serem preenchidos com valores e formatados apropriadamente. Estes valores são informados através dos parâmetros passados após o primeiro, e a quantidade varia de acordo com a quantidade de slots disponíveis no formato.

Você pode estar se perguntando: se a quantidade de valores é indefinida, por que não passa um array para a função? Na verdade o PHP até possui uma função assim, que é a vprintf. Ela sempre recebe dois parâmetros: o primeiro representando o formato e o segundo é um array com os valores que serão usados nos slots do formato. Veja, abaixo, sua utilização:

// 1 parametro de formato + 0 valores no array
vprintf('Meu nome é Rubens', array());

// 1 parametro de formato + 1 valor no array
vprintf('Meu nome é %s', array($nome));

// 1 parametro de formato + 2 valores no array
$params = array();
$params[] = 'Rubens';
$params[] = 29;
vprintf('Meu nome é %s e tenho %d anos', $params);

A diferença entre as duas funções é simplesmente a usabilidade. Normalmente se utiliza printf quando você já tem os valores separadamente e prontos para passá-los para função de formatação. Já o vprintf é mais indicado quando você quer preencher um vetor, modificá-lo, e passar todos os dados para serem formatados de uma só vez.

Como declarar funções variádicas no PHP?

Em 2011, escrevi o artigo Funções com número indefinido de parâmetros em PHP, que mostrava como criar funções variádicas em versões do PHP anteriores à 5.6.

Naquele artigo, coloquei como exemplo uma função media, que calculava a média dos parâmetros informados. Para criar a mesma função usando a nova sintaxe, ela ficaria assim:

function media(...$valores) {
    if (emtpy($valores)) {
        $media = 0;
    } else {
        $soma = array_sum($valores);
        $total = count($valores);
        $media = $soma / $total;
    }
    return $media;
}

A diferença é que, agora, informamos a partir de qual parâmetro a função pode receber um número indefinido de parâmetros. Isso é feito usando três pontos (reticências) antes do nome do parâmetro considerado variádico. Este parâmetro variádico receberá um array com os parâmetros passados a partir daquele ponto. Com a especificação de um nome para os parâmetros variádicos, conseguimos manipulá-los de forma mais legível, sem a necessidade de capturar pedaços do array devolvido por func_get_args, que serve para pegar TODOS os parâmetros informados para a função (e não apenas os variádicos).

Exemplo de utilização da função:

$x = media();           // $valores = array()
$x = media(1);          // $valores = array(1)
$x = media(1, 2, 3);    // $valores = array(1, 2, 3)

Ou seja, o funcionamento da função continua o mesmo, mas a função está mais legível. Note que, na função do artigo anterior, não sabíamos que a função media recebia parâmetros só de olhar para sua declaração. Isso poderia ser feito através de documentação da função, com comentários, mas agora, com a nova sintaxe, isso fica explícito na assinatura da função.

Veja um outro exemplo que mostra uma função variádica que possui um parâmetro obrigatório e, em seguida, como seria feito antes do PHP 5.6:

// Nova sintaxe
function formatar_lista_datas($formato, ...$tempos) {
    $retorno = '<ul>';
    foreach ($tempos as $tempo) {
        $retorno .= '<li>' . strftime($formato, $tempo) . '</li>';
    }
    $retorno .= '</ul>';
    return $retorno;
}

// Forma antiga
function formatar_lista_datas($formato) {
    $tempos = array_slice(func_get_args(), 1);
    $retorno = '<ul>';
    foreach ($tempos as $tempo) {
        $retorno .= '<li>' . strftime($formato, $tempo) . '</li>';
    }
    $retorno .= '</ul>';
    return $retorno;
}

Note que para pegar os parâmetros posteriores ao primeiro, precisamos extrair uma porção do valor de func_get_args. Caso incluíssemos outro parâmetro obrigatório, precisaríamos ajustar a posição da porção que é extraída.

Observações

A declaração de "ponto variádico" (reticências) só pode ser aplicado em um único parâmetro por função, e este parâmetro precisa obrigatoriamente ser o último.

O parâmetro variádico também pode ser declarado como referência da seguinte forma:

function teste($a, $b, &...$c) {

Neste caso, o símbolo de referência (&) é colocado antes das reticências e ao manipular o valor de $c, também modificamos a variável que foi passada para a função teste. Este comportamento não era possível antes da criação da nova sintaxe, portanto é bem vindo.

Outra observação é que os parâmetros variádicos não podem receber valores default, como ocorre com os parâmetros convencionais. Logo, a afirmação de que "não pode haver parâmetro sem valor default após um parâmetro que possui valor default" não é mais verdadeira e pode ser ajustada para "após um parâmetro com valor default pode existir um parâmetro variádico, mas não pode haver outros parâmetros convencionais sem valores default.".

E a última observação é que, com a chegada da nova sintaxe, as classes de Reflexão (Reflection) também receberam novos métodos para determinar se a função é variádica e se um parâmetro é variádico (ReflectionFunction::isVariadic e ReflectionParameter::isVariadic respectivamente).

5 comentários

Anônimo disse...

Muito bom camarada, me visite qualquer dia desses

http://olamundophp.wordpress.com/