Devo usar assert no meu código PHP?

87

Um colega de trabalho adicionou o comando assert algumas vezes em nossas bibliotecas em locais onde eu teria usado uma instrução if e lançado uma exceção. (Eu nunca tinha ouvido falar de assert antes disso.) Aqui está um exemplo de como ele o usou:

assert('isset($this->records); /* Records must be set before this is called. */');

Eu teria feito:

if (!isset($this->records)) {
    throw new Exception('Records must be set before this is called');
}

Ao ler a documentação do PHP sobre assert , parece que é recomendado que você certifique-se de que assert esteja ativo e adicione um manipulador antes de usar assert. Não consigo encontrar um lugar onde ele tenha feito isso.

Então, minha pergunta é, usar assert é uma boa ideia dada a situação acima e devo usá-lo com mais frequência em vez de exceções if e?

Outra observação, estamos planejando usar essas bibliotecas em uma variedade de projetos e servidores, incluindo projetos dos quais podemos nem mesmo fazer parte (as bibliotecas são de código aberto). Isso faz alguma diferença no uso de assert?

Darryl Hein
fonte
É realmente 'isset(a linha de código com assert)? Não apenas isset(sem as aspas simples ')?
Peter Mortensen

Respostas:

79

A regra prática aplicável na maioria das línguas (tudo que eu sei vagamente) é que an asserté usado para afirmar que uma condição é sempre verdadeira, enquanto an ifé apropriado se for concebível que às vezes falhe.

Nesse caso, eu diria que asserté apropriado (com base em meu fraco entendimento da situação) porque semprerecords deve ser definido antes de o método fornecido ser chamado. Portanto, uma falha em definir o registro seria um bug no programa, e não uma condição de tempo de execução. Aqui, o está ajudando a garantir (com testes adequados) que não há um caminho de execução de programa possível que possa fazer com que o código que está sendo protegido com o seja chamado sem ter sido definido.assertassertrecords

A vantagem de usar assertem oposição a ifé que assertgeralmente pode ser desativado no código de produção, reduzindo assim a sobrecarga. O tipo de situação que é mais bem tratada ifpoderia ocorrer durante o tempo de execução no sistema de produção e, portanto, nada é perdido por não ser capaz de desligá-los.

Aaronasterling
fonte
4
Para adicionar a isso, você pode não querer desabilitar asserções em seu código de produção, porque elas ajudam a garantir que as condições "isso nunca deveria acontecer" permaneçam assim. Pode ser melhor deixar seu aplicativo parar de uma declaração do que permitir que seus usuários continuem seguindo um caminho de execução que não deveria existir.
derekerdmann
2
@derekerdmann: Verdadeiro. Para algumas assertivas, pode ser suficiente registrá-las (na produção) ou imprimir o aviso (no ambiente de desenvolvimento). Mas, como muitas vezes as afirmações também protegem o código de segurança relevante, você também pode habilitar assert_options(ASSERT_BAIL). É mais rápido do que as soluções manuais if / throw de qualquer maneira.
mario
4
@derekerdmann Eu discordo disso (no contexto do uso de assert () no php). Esta é uma grande lacuna de vulnerabilidade, já que assert () trata todos os argumentos de string como código PHP, portanto, é (teoricamente) possível injetar e executar código arbitrário. IMHO, afirmações devem ser desligadas na produção
Vitaliy Lebedev
2
@VitaliyLebedev se você não quer ser suscetível a injeção, não passe strings para afirmar.
Damon Snyder
9
Atrasado para a festa, mas PHP.net afirma: "As afirmações devem ser usadas apenas como um recurso de depuração."
Koen.
25

Pense em afirmações como "comentários poderosos". Em vez de um comentário como:

// Note to developers: the parameter "a" should always be a number!!!

usar:

assert('is_numeric(a) /* The parameter "a" should always be a number. */');

Os significados são exatamente os mesmos e destinam-se ao mesmo público, mas o primeiro comentário é facilmente esquecido ou ignorado (não importa quantos pontos de exclamação), enquanto o "comentário poderoso" não está disponível apenas para humanos lerem e entenderem, ele também é constantemente testado por máquina durante o desenvolvimento e não será ignorado se você configurar um bom manuseio de assert no código e nos hábitos de trabalho.

Visto dessa forma, afirmações são um conceito completamente diferente de if (erro) ... e exceções, e podem coexistir.

Sim, você deve comentar seu código e, sim, você deve usar "comentários poderosos" (afirmações) sempre que possível.

DaveWalley
fonte
e se no desenvolvimento durante o teste você sempre passar boa condição para o assert, mas na produção se o assert estiver desativado - algum usuário passa em outra condição na qual você não pensou ao testar? Ou então você precisa manter a assert sempre ativada, mas então não é o mesmo que preencher seu próprio cheque?
Darius.V
Então seu programa irá falhar. Corrija-o adequadamente com instruções if e os recursos de tratamento de erros de sua linguagem e ambiente de desenvolvimento. afirma pode descobrir problemas, existem maneiras melhores de resolver problemas.
DaveWalley
Note que a partir do PHP 7.2, passar uma string para assert para avaliação tornou-se obsoleto. Triste, porque parecia muito prático.
Jannie Theunissen
16

Depende totalmente da sua estratégia de desenvolvimento. A maioria dos desenvolvedores desconhece assert()e usa o teste de unidade downstream. Mas esquemas de teste proativos e integrados às vezes podem ser vantajosos.

assert é útil, porque pode ser habilitado e desabilitado. Ele não drena o desempenho se nenhum manipulador de asserção for definido. Seu colega não tem um, e você deve criar algum código que o habilite temporariamente no ambiente de desenvolvimento (se E_NOTICE / E_WARNINGs estiverem ativados, então deve estar o manipulador de asserção). Eu o uso ocasionalmente onde meu código não tolera tipos de variáveis ​​misturados - eu normalmente não me envolvo na digitação estrita em um PHP de digitação fraca, mas há casos de uso aleatórios:

 function xyz($a, $b) {
     assert(is_string($a));
     assert(is_array($b));

O que, por exemplo, compensaria a falta de especificadores de tipo string $a, array $b. PHP5.4 irá suportá-los, mas não verificará.

mario
fonte
O que significa "php 5.4 will have them but not check"?
Kzqai
1
PHP 5.4 tem, suporta e verifica declarações.
DaveWalley
7

Assert não é um substituto para o controle de fluxo normal, como ifou exceções, porque se destina apenas a ser usado para depuração durante o desenvolvimento.

Mark Snidovich
fonte
6

Uma nota importante sobre assert no PHP anterior a 7. Diferente de outras linguagens com uma construção assert, o PHP não joga declarações assert completamente - ele o trata como uma função (faça um debug_backtrace () em uma função chamada por um assertion). Desligar assert parece apenas fazer a função hotwire em não fazer nada no motor. Note que o PHP 7 pode ser feito para emular este comportamento definindo zend.assertions para 0 ao invés dos valores mais normais de 1 (ligado) ou -1 (desligado).

O problema surge em que assert aceitará qualquer argumento - mas se o argumento não for uma string, então assert obtém os resultados da expressão se assert estiver ativado ou desativado. Você pode verificar isso com o seguinte bloco de código.

<?php
  function foo($a) { 
    echo $a . "\n"; 
    return TRUE;
  }
  assert_options(ASSERT_ACTIVE, FALSE);

  assert( foo('You will see me.'));
  assert('foo(\'You will not see me.\')');

  assert_options(ASSERT_ACTIVE, TRUE);

  assert( foo('Now you will see'));
  assert('foo(\'both of us.\')');

Dada a intenção de assert, este é um bug, e um bug antigo desde que está na linguagem desde que assert foi introduzido no PHP 4.

As strings passadas para assert são avaliadas, com todas as implicações de desempenho e riscos que vêm com isso, mas é a única maneira de fazer as declarações assert funcionarem da maneira que deveriam no PHP (este comportamento foi descontinuado no PHP 7.2).

EDIT: Alterado acima para observar as alterações no PHP 7 e 7.2

Michael Morris
fonte
1
No PHP 7 existe / haverá a zend.assertionsconfiguração inicial para desligar completamente assert().
Kontrollfreak
Essa é uma excelente notícia - mas com base na documentação lá, parece que um patch para o PHPUnit está pronto, adicionando um manipulador de retorno de chamada de declaração para lançar AssertionException quando as declarações falham no PHP 5.x. Desta forma, os testes de unidade podem usar a anotação @expectedException AssertionException independentemente de serem executados no PHP 5.x ou 7.
Michael Morris
3

Assert só deve ser usado no desenvolvimento, pois é útil para depuração. Portanto, se você quiser, pode usá-los para desenvolver seu site, mas deve usar exceções para um site ativo.

Kyle
fonte
7
Mas um ainda terá as afirmações no código. Eles simplesmente não estarão ativos em um ambiente de produção.
aaronasterling
1
Eu os manteria em produção e ajustaria meu gerenciador de erros de acordo.
Daniel W.
3

Não, seu colega de trabalho não deve usá-lo como um manipulador de erros de propósito geral. De acordo com o manual:

As asserções devem ser usadas apenas como um recurso de depuração. Você pode usá-los para verificações de integridade que testam as condições que devem sempre ser VERDADEIRAS e que indicam alguns erros de programação, caso contrário, ou para verificar a presença de certos recursos como funções de extensão ou certos limites e recursos do sistema.

Asserções não devem ser usadas para operações normais de tempo de execução, como verificações de parâmetros de entrada. Como regra geral, seu código deve sempre funcionar corretamente se a verificação de asserção não estiver ativada.

Se você estiver familiarizado com conjuntos de testes automatizados, o verbo "assert" geralmente é usado para verificar a saída de algum método ou função. Por exemplo:

function add($a, $b) {
    return $a + $b;
}

assert(add(2,2) == 5, 'Two and two is four, dummy!');
assert(is_numeric(add(2,2)), 'Output of this function to only return numeric values.');

Seu colega de trabalho não deve usá-lo como um manipulador de erros de propósito geral e, neste caso, como uma verificação de entrada. Parece que é possível que o campo de registros não seja definido por algum usuário de sua biblioteca.

Dean ou
fonte
3

Seu colega de trabalho está realmente tentando aplicar design by contract (DbC) da linguagem Eiffel e baseado no livro: Object Oriented Software Construction, 2nd Edition.

A afirmação, como ele a usou, seria a parte {P} da lógica de Hoare ou triplo de Hoare: {P} C {Q}, onde {P} é a afirmação de pré-condição (íons) e {Q} são a afirmação (íon) de pós-condição.

Eu tomaria nota crítica dos conselhos dados sobre o recurso assert no PHP com bugs. Você não quer usar código com bugs. O que você realmente quer são os criadores de PHP para consertar o bug no assert. Até que o façam, você pode usar o assert, mas use-o ciente de seu estado atual de erros.

Além disso, se o recurso assert tiver erros, sugiro que você não o use no código de produção. No entanto, eu recomendo que você o use no desenvolvimento e teste de código quando apropriado.

Finalmente - se você fizer um estudo de design por contrato, descobrirá que há consequências em usar asserções booleanas à luz da herança clássica orientada a objetos - isto é - você nunca deve enfraquecer uma pré-condição, nem enfraquecer uma pós-condição. Fazer isso pode ser perigoso para seus objetos descendentes polimórficos interagindo uns com os outros. Até você entender o que isso significa - eu deixaria isso pra lá!

Além disso, eu recomendo fortemente que os criadores do PHP façam um estudo abrangente de design por contrato e tentem colocá-lo no PHP o mais rápido possível! Então, todos nós podemos nos beneficiar de ter um compilador / interpretador compatível com DbC, que lidaria com os problemas observados nas respostas (acima):

  1. Um compilador com reconhecimento de design por contrato devidamente implementado (com sorte) estaria livre de bugs (ao contrário da declaração atual do PHP).
  2. Um compilador com reconhecimento de projeto por contrato implementado de maneira adequada lidaria com as nuances do gerenciamento de lógica de asserção polimórfica para você, em vez de quebrar sua cabeça com o assunto!

NOTA: Até mesmo o uso de uma ifinstrução -como um substituto para a afirmação (pré-condição) sofrerá consequências terríveis se usado para fortalecer uma pré-condição ou enfraquecer uma pós-condição. Para entender o que isso significa, você precisará estudar design por contrato para saber! :-)

Feliz estudando e aprendendo.

Larry
fonte