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







