¿Debería usar assert en mi código PHP?

87

Un compañero de trabajo ha agregado el comando assert varias veces dentro de nuestras bibliotecas en lugares donde habría usado una declaración if y lanzado una excepción. (Nunca había oído hablar de la afirmación antes de esto). Aquí hay un ejemplo de cómo lo usó:

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

Yo habría hecho:

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

Después de leer los documentos de PHP sobre aserción , parece que se recomienda que se asegure de que la aserción esté activa y agregue un controlador antes de usar la aserción. No puedo encontrar un lugar donde haya hecho esto.

Entonces, mi pregunta es, ¿usar aserción es una buena idea dado lo anterior y debería usarlo con más frecuencia en lugar de if y excepciones?

Otra nota, estamos planeando usar estas bibliotecas en una variedad de proyectos y servidores, incluidos proyectos de los que es posible que ni siquiera seamos parte (las bibliotecas son de código abierto). ¿Esto hace alguna diferencia en el uso de assert?

Darryl Hein
fuente
¿Es realmente 'isset(la línea de código con assert)? ¿No solo isset(sin las comillas simples ')?
Peter Mortensen

Respuestas:

79

La regla general que se aplica en la mayoría de los lenguajes (todo lo que sé vagamente) es que an assertse usa para afirmar que una condición siempre es verdadera, mientras que an ifes apropiado si es concebible que a veces fallará.

En este caso, yo diría que assertes apropiado (basado en mi débil comprensión de la situación) porque recordsdebe siempre ser establecido antes de llamar al método dado. Por lo tanto, no establecer el récord sería un error en el programa en lugar de una condición de tiempo de ejecución. Aquí, el assertestá ayudando a asegurar (con las pruebas adecuadas) que no hay una posible ruta de ejecución del programa que pueda causar que el código que está siendo protegido con el assertsea ​​llamado sin recordshaber sido configurado.

La ventaja de usar asserten lugar de ifes que assertgeneralmente se puede desactivar en el código de producción, lo que reduce los gastos generales. El tipo de situaciones con las que se maneja mejor ifposiblemente podría ocurrir durante el tiempo de ejecución en el sistema de producción y, por lo tanto, no se pierde nada al no poder apagarlas.

aaronasterling
fuente
4
Para agregar a esto, es posible que no desee deshabilitar las afirmaciones en su código de producción, porque ayudan a asegurarse de que esas condiciones de "esto nunca debería suceder" permanezcan así. Puede ser mejor dejar que su aplicación se detenga a partir de una aserción que permitir que sus usuarios sigan una ruta de ejecución que no debería existir.
Derekerdmann
2
@derekerdmann: Cierto. Para algunas afirmaciones, puede ser suficiente registrarlas (en producción) o imprimir la advertencia (en el entorno de desarrollo). Pero dado que a menudo afirma que también protege el código relevante para la seguridad, también podría habilitarlo assert_options(ASSERT_BAIL). De todos modos, es más rápido que el manual si / lanza soluciones.
mario
4
@derekerdmann No estaría de acuerdo con esto (en el contexto del uso de assert () en php). Esta es una gran brecha de vulnerabilidad, ya que assert () trata todos los argumentos de cadena como código PHP, por lo que es (teóricamente) posible inyectar y ejecutar código arbitrario. En mi humilde opinión, las afirmaciones deben desactivarse en la producción
Vitaliy Lebedev
2
@VitaliyLebedev si no quiere ser susceptible a la inyección, no pase cadenas para afirmar.
Damon Snyder
9
Llegó tarde a la fiesta, pero PHP.net afirma: "Las afirmaciones deben usarse solo como una función de depuración".
Koen.
25

Piense en afirmaciones como "comentarios de poder". En lugar de un comentario como:

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

utilizar:

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

Los significados son exactamente los mismos y están destinados a la misma audiencia exacta, pero el primer comentario se olvida o ignora fácilmente (sin importar cuántos signos de exclamación), mientras que el "comentario de poder" no solo está disponible para que los humanos lo lean y comprendan, También se prueba constantemente en la máquina durante el desarrollo y no se ignorará si configura un buen manejo de aserción en el código y en los hábitos de trabajo.

Visto de esta manera, las afirmaciones son un concepto completamente diferente que si (error) ... y las excepciones, y pueden coexistir.

Sí, debería comentar su código, y sí, debería utilizar "comentarios de poder" (afirma) siempre que sea posible.

DaveWalley
fuente
¿Qué pasa si en el desarrollo, al probar, siempre pasa una buena condición a la aserción, pero en producción si la aserción está desactivada, algún usuario pasa otra condición en la que no pensó durante la prueba? O de lo contrario, debe mantener la aserción siempre activa, pero ¿no es lo mismo que escribir su propio cheque?
Darius.V
Entonces su programa fallará. Corríjalo correctamente con declaraciones if y las funciones de manejo de errores de su lenguaje y entorno de desarrollo. afirma que puede descubrir problemas, hay mejores formas de solucionar problemas.
DaveWalley
Tenga en cuenta que, a partir de PHP 7.2, el paso de una cadena a la aserción para su evaluación ha quedado obsoleto. Triste, porque parecía bastante útil.
Jannie Theunissen
16

Depende totalmente de su estrategia de desarrollo. La mayoría de los desarrolladores desconocen assert()y utilizan las pruebas unitarias posteriores. Pero los esquemas de prueba proactivos e integrados a veces pueden ser ventajosos.

Afirmar es útil porque se puede activar y desactivar. No agota el rendimiento si no se define tal controlador de aserción. Su colega no tiene uno, y debe diseñar algún código que lo habilite temporalmente en el entorno de desarrollo (si E_NOTICE / E_WARNINGs están activados, también debería estar el controlador de aserción). Lo uso ocasionalmente cuando mi código no puede soportar tipos de variables mixtas; normalmente no me dedico a la escritura estricta en un PHP de tipo débil, pero hay casos de uso aleatorios:

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

Lo que, por ejemplo, compensaría la falta de especificadores de tipo string $a, array $b. PHP5.4 los admitirá, pero no los comprobará.

mario
fuente
¿Qué significa "php 5.4 los tendrá pero no los comprobará"?
Kzqai
1
PHP 5.4 tiene, admite y comprueba afirma.
DaveWalley
7

Assert no es un sustituto de los me gusta ifo las excepciones de control de flujo normales , porque solo está destinado a ser utilizado para depurar durante el desarrollo.

Mark Snidovich
fuente
6

Una nota importante sobre la aserción en PHP anterior al 7. A diferencia de otros lenguajes con una construcción de aserción, PHP no descarta las declaraciones de aserción por completo - las trata como una función (hacer un debug_backtrace () en una función llamada por una aserción). Apagar afirmaciones parece simplemente conectar la función para que no haga nada en el motor. Tenga en cuenta que se puede hacer que PHP 7 emule este comportamiento estableciendo zend.assertions en 0 en lugar de los valores más normales de 1 (activado) o -1 (desactivado).

El problema surge en que la aserción acepta cualquier argumento, pero si el argumento no es una cadena, la aserción obtiene los resultados de la expresión, ya sea que la aserción esté activada o desactivada. Puede verificar esto con el siguiente bloque 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 la intención de afirmar, esto es un error, y uno de larga data, ya que ha estado en el lenguaje desde que se introdujo aseverar en PHP 4.

Las cadenas pasadas para afirmar se evalúan, con todas las implicaciones de rendimiento y los peligros que vienen con eso, pero es la única forma de hacer que las declaraciones de aserción funcionen como deberían en PHP (este comportamiento es obsoleto en PHP 7.2).

EDITAR: Cambiado arriba para notar cambios en PHP 7 y 7.2

Michael Morris
fuente
1
En PHP 7 hay / habrá una zend.assertionsconfiguración ini para apagar completamente assert().
Kontrollfreak
Esa es una excelente noticia, pero según la documentación allí, parece que un parche para PHPUnit está ordenado, agregando un controlador de devolución de llamada de aserción para lanzar AssertionException cuando las aserciones fallan en PHP 5.x. De esta manera, las pruebas unitarias pueden usar la anotación @expectedException AssertionException independientemente de si se ejecutan en PHP 5.xo 7.
Michael Morris
3

Assert solo debe usarse en desarrollo, ya que es útil para depurar. Entonces, si lo desea, puede usarlos para desarrollar su sitio web, pero debe usar excepciones para un sitio web activo.

Kyle
fuente
7
Pero uno todavía tendrá las afirmaciones en el código. Simplemente no estarán activos en un entorno de producción.
aaronasterling
1
Los mantendría en producción y ajustaría mi controlador de errores en consecuencia.
Daniel W.
3

No, su compañero de trabajo no debería usarlo como un controlador de errores de propósito general. Según el manual:

Las afirmaciones deben usarse solo como una función de depuración. Puede usarlos para verificaciones de cordura que prueban condiciones que siempre deberían ser VERDADERAS y que indican algunos errores de programación en caso contrario o para verificar la presencia de ciertas características como funciones de extensión o ciertos límites y características del sistema.

Las afirmaciones no deben usarse para operaciones normales en tiempo de ejecución como verificaciones de parámetros de entrada. Como regla general, su código siempre debería poder funcionar correctamente si la verificación de afirmaciones no está activada.

Si está familiarizado con los conjuntos de pruebas automatizados, el verbo "afirmar" se usa generalmente para verificar el resultado de algún método o función. Por ejemplo:

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.');

Su compañero de trabajo no debería usarlo como un controlador de errores de propósito general y, en este caso, como una verificación de entrada. Parece que es posible que algún usuario de su biblioteca no configure el campo de registros.

Decano o
fuente
3

Su compañero de trabajo realmente está intentando aplicar el diseño por contrato (DbC) del lenguaje Eiffel y basado en el libro: Construcción de software orientado a objetos, 2da edición.

La aserción, tal como la usó, sería la {P} -parte de la lógica de Hoare o el triple de Hoare: {P} C {Q}, donde {P} es la precondición (ion) sy {Q} son las afirmaciones posteriores a la condición.

Tomaría nota crítica de los consejos dados sobre la función de aserción en PHP que tiene errores. No quieres usar código con errores. Lo que realmente quiere es que los creadores de PHP corrijan el error en la aserción. Hasta que lo hagan, puede utilizar la aserción, pero utilícela teniendo en cuenta su actual estado defectuoso.

Además, si la función de aserción tiene errores, le sugiero que no la use en el código de producción. Sin embargo, le recomiendo que lo use en el desarrollo y en el código de prueba cuando sea apropiado.

Finalmente, si realiza un estudio de diseño por contrato, encontrará que el uso de aserciones booleanas a la luz de la herencia clásica orientada a objetos tiene consecuencias, es decir, nunca debe debilitar una condición previa ni debilitar una condición posterior. Hacerlo podría ser peligroso para los objetos descendientes polimórficos que interactúan entre sí. Hasta que comprenda lo que eso significa, ¡lo dejaría en paz!

Además, recomiendo encarecidamente que los creadores de PHP realicen un estudio completo del diseño por contrato e intenten ponerlo en PHP lo antes posible. Entonces, todos podemos beneficiarnos de tener un compilador / intérprete compatible con DbC, que manejaría los problemas señalados en las respuestas (arriba):

  1. Un compilador consciente de diseño por contrato implementado correctamente (con suerte) estaría libre de errores (a diferencia de la afirmación actual de PHP).
  2. ¡Un compilador consciente de diseño por contrato correctamente implementado manejaría los matices de la gestión de la lógica de aserción polimórfica por usted en lugar de atormentar su cerebro por el asunto!

NOTA: Incluso el uso de una ifdeclaración -como sustituto de la aserción (condición previa) sufrirá consecuencias nefastas si se usa para fortalecer una condición previa o debilitar una condición posterior. Para comprender lo que eso significa, ¡necesitará estudiar diseño por contrato para saberlo! :-)

Feliz estudiando y aprendiendo.

Larry
fuente