Dev (Back & Front)ARTIGO

Potencial sintaxe para funções variádicas no PHP 5.6

Funções variádicas já são possíveis em PHP e têm sido ao longo 4.x e 5.x na forma de func_get_args(), o que é bastante grave.

Um incrível RFC apareceu no outro dia: Sintaxe para funções variádicas, desenvolvida pela Nikita Popov. Eu o li e adorei, mas eu tinha o Google para ver o que uma função variádica era. Isso é o que acontece quando você se ensina a codificar. Você sabe como fazer as coisas, mas não conhece nenhuma das palavras.

Funções variádicas já são possíveis em PHP e têm sido ao longo 4.x e 5.x na forma de func_get_args(), o que é bastante grave. É usado para as funções em que você deseja ter um número ilimitado de funções como:

max(11, 2, 4, 6, 1);
sum(1, 5, 6);

Atualmente, para implementar uma função como essa, você faria:

function sum()
{
    return array_sum(func_get_args());
}

echo sum(1, 4, 12, 20);

O RFC proposto permite que você escreva o seguinte:

function sum(...$nums)
{
    return array_sum($nums);
}

echo sum(1, 4, 12, 20);

Esse exemplo bem trivial mostra a diferença entre as duas abordagens, mas algumas pessoas estão hesitantes sobre o quão útil isso é. Vamos considerar algumas coisas aqui.

Pró: Legibilidade

Imagine uma função que possui 50 linhas, e na linha 45 o desenvolvedor está usando uma func_get_args(). Você tem que perceber e saber que é esperado que você passe um monte de argumento… A nova sintaxe variádica tornaria incrivelmente óbvio o que está acontecendo na declaração da função, que é de onde os argumentos vêm.

Corri para encontrar uma pasta no Pyro para ver o que surgiu primeiro, e a resposta é:

    public function orX($x = null)
    {
        return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args());
    }

O Doctrine parece bêbado aqui. Ele aceita um parâmetro chamado $x, que é opcional, então usa func_get_args( ) de qualquer maneira o que significa dane-se o $x.

ou:

    public function orX(...$x)
    {
        return new CompositeExpression(CompositeExpression::TYPE_OR, $x);
    }

Vou levar isso por favor. Limpo, código auto-documentável.

Pró: Documentação

Eu sou um grande fã de DockBlocks. Ter um código bem documentado significa que você pode executar geradores de API, usar autocompletar de IDE, receber avisos sobre o tipo de retorno de confrontos direto em seu editor etc., razão pela qual eu me ofereci para coordenar o novo PSR PHPDoc com a equipe phpDocumentor. Simplificando: isso é incrível.

Claro que poderíamos adicionar uma sintaxe de documento @param *variadic ou algo assim, mas seria estranho e não misturaria com a outra sintaxe muito bem. A nova sintaxe o tornaria super fácil:

     /**
     * @param mixed ...$x
     *
     * @return CompositeExpression
     */
    public function orX(...$x)
    {
        return new CompositeExpression(CompositeExpression::TYPE_OR, $x);
    }

Obrigado!

Pró: Indução de Tipo

Indução de Tipo agora é como um corte de cabelo semipronto. Algumas brigas na equipe principal sobre como lidar com Indução de Tipo forte/fraca para int, float, string etc. leva à confusão e significa que o recurso é restrito a Indução de Tipo para um array, callable, ou um nome de classe/interface. Claro que não é possível fazer Indução de Tipo para qualquer tipo de valor [ainda], mas o fato é que Indução de Tipo existe e deveríamos ser capaz de usá-la.

     /**
     * Favorite one or more statuses.
     *
     * @param string $screenName
     * @param Twitter\Status ...$statuses
     *
     * @return array[League\Twitter\User]
     */
    public function favoriteStatus($screenName, Twitter\Status ...$statuses)
    {

Isso é incrível , eu posso especificar o tipo exato, em vez de ter que permitir que um array e então verificar instâncias em um loop ou algo assim. Mais rápido, mais óbvio, melhor para todos.

Pró: Menos perda de tempo

Embora não seja o aspecto mais importante, mesmo que você não ache que definir isso na assinatura da função seja importante, você pode evitar este tipo horrendo de perda de tempo:

        public function tryMethod()
    {
        $args = func_get_args();
        $method = $args[0];
        unset($args[0]);
        $args = array_values($args);

    try {
        return call\_user\_func\_array([$this, $method], $args);
    } catch (\Exception $e) {
        return false;
    }
}

Isso se torna:

    public function tryMethod($method, ...$args)
    {
        try {
            return call_user_func_array([$this, $method], $args);
        } catch (\Exception $e) {
            return false;
        }
    }

Por que bagunçar tudo quando você precisa?

Pró: Mantenha-se atualizado

C#, Ruby e Python, bem como muitos outros. Embora as características de cópia possam não ser o melhor caminho para a inovação, isso mostra que há prioridade para a funcionalidade.

PHP pode não estar usando exatamente o mesmo operador que os outros, mas PHP é um pillagin pirata, e piratas pillagin têm que levar o que puderem.

Contra: Trollolololol?

Eu joguei essa RFC no Reddit para ver se alguém poderia me dar algumas desvantagens e começaram umas trollagens épicas por alguns caras do /r/lolphp, que pareciam estar perdidos num sub-reddit errado.

Um cara estava reclamando sobre desempenho, mas não há razão para que isso tenha qualquer efeito sobre o desempenho. Adicionar um token extra para PHP não vai fazê-lo explodir, e fazer acusações aleatórias sobre coisas não lhe tornam inteligente.

Contra: desempacotando argumento

Um contra válido surgiu na lista interna do PHP e algumas vezes nos comentários, que com essa nova sintaxe você não pode passar o valor variádico de forma adequada.

Bem, você também não consegue fazer isso agora. Para reutilizar o mesmo exemplo, você tem que fazer algo assim:

    public function tryMethod($method, ...$args)
    {
        try {
            return call_user_func_array([$this, $method], $args);
        } catch (\Exception $e) {
            return false;
        }
    }

Para isso, Nikita tem outra RFC para o desempacotamento de argumento usando o que é conhecido como um operador splat.

Tomando esse mesmo exemplo, aplicar o novo operador splat torna tudo muito mais agradável:

    public function tryMethod($method, ...$args)
    {
        try {
            return $this->$method(...$args);
        } catch (\Exception $e) {
            return false;
        }
    }

É só tomar os argumentos e empurrá-los para o método da mesma forma que eles entraram.

Os exemplos no RFC também são muito úteis:

call_user_func_array([$db, 'query'], array_merge(array($query), $params));
// or
$db->query($query, ...$params);

Qual você prefere para escrever?

Não se preocupa com sintaxe bonita? Ok, bem, é mais rápido também . Por volta de 3.5x -4x mais rápido.

Resumo

Nenhum desses RFCs está dizendo que você tem que usá-lo, ou que variádicas são sempre boas, ou que o uso de um array de valores como parâmetro está errado. É simplesmente sobre arrumar drasticamente a funcionalidade existente no PHP.

Alguém no Reddit apontou isso é como namespaces. Usuários PHP os implementaram de uma forma meio hacky: (“Vamos todos concordar que underscores significa um namespace … ok ?”), então a linguagem ratificou o uso com a sintaxe, e agora temos um separador real de namespace e a funcionalidade use. É o mesmo. As pessoas já estão usando variádicas, então, em vez de ser uma confusão desagradável, essa sintaxe o arruma.

Use os argumentos para explicar para as pessoas por que essa funcionalidade é ótima. Se você conhece alguém na equipe principal que está tendo dificuldades com esse recurso (e tenho visto alguns comentários realmente bizarros por aí), então talvez isso possa ajudá-lo a explicar-lhes porque é que devemos tê-lo.

PHP deve ser autorizado a ter coisas boas. Que tal nomear parâmetros?

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://philsturgeon.co.uk/blog/2013/08/potential-variadic-function-syntax-for-php-56

Programa em PHP desde os 12 anos. Gosta de explorar novas tecnologias, testar novas metodologias e construir coisas maiores e melhores. Atualmente, trabalha na Ride, uma empresa de transporte solidário.

Ver perfil