Aserciones en PHP

Con este artículo estrenamos una serie para mostrar buenas prácticas y novedades de las últimas versiones de uno de los lenguajes que más usamos en VorticeSoft, PHP.

En esta ocasión vamos a hablar de una característica que ha recibido una merecida atención en la versión 7, la función assert. Esta función está presente de una u otra forma en otros muchos lenguajes, como Java, C# o Javascript. En PHP está disponible desde la versión 5. Aunque con ciertas diferencias.

Quizás la más importante novedad es que ha pasado de ser una función a una construcción del lenguaje. Esto ha posibilitado algunas posibilidades interesantes que veremos después.

Pero, para los que no lo conozcan, vamos a empezar con un ejemplo simple de su uso.

<?php

    echo “Paso 1\n”;

    assert(1 == 1);

    echo “Paso 2\n”;

    assert(1 == 2);

    echo “Paso 3\n”;

?>

La salida de este ejemplo sería algo como esto:

Paso 1

Paso 2

PHP Warning:  assert(): assert(1 == 2) failed in /home/moises/devel/pruebas/assert1.php on line 5

PHP Stack trace:

PHP   1. {main}() /home/moises/devel/pruebas/assert1.php:0

PHP   2. assert() /home/moises/devel/pruebas/assert1.php:5

Paso 3

Como se puede ver nos alerta de la asertación que ha fallado. Indicándonos dónde ha fallado y la prueba que hemos ejecutado.

switch ($palo) {
   case ROMBOS:
       /* ... */
   break;

   case DIAMANTES:
       /* ... */
   break;

   case CORAZONES:
       /* ... */
   break;

   case ESPADAS:
       /* ... */
   break;

   default:
       assert (false, "Se ha indicado un palo erróneo: {$palo}");
}

En este otro ejemplo vemos que assert puede tener dos parámetros. El primero acepta cualquier expresión que devuelva un booleano. El segundo acepta una cadena, como el ejemplo, o una excepción. En este caso se usa simplemente false porque si llega ahí es que hemos cometido un error. En otros casos se puede llamar a una función que compruebe la condición.

En este ejemplo particular assert mostraría el error indicado en caso de que lo que se pase al switch no sea válido. ¿Por qué usar entonces assert en vez de mostrar el error simplemente? Lo primero que hay que tener en cuenta es para qué está diseñado. Su uso debería limitarse exclusivamente como herramienta de depuración. Para detectar problemas en el código durante el desarrollo. Nunca como herramientas de detección de errores. Para eso existen las excepciones.

Una de las posibilidades que nos ofrece PHP al usar esta funcionalidad es deshabilitar la ejecución según el valor de una directiva en el archivo php.ini. Al deshabilitarse no solo se evita que se muestre el mensaje en caso de encontrar un error, sino que puede indicarse que el compilador no produzca el código correspondiente a su uso. Esto hace que no necesitemos mantener un código diferente para producción ni que disminuya el rendimiento por su uso. Las directivas que habría que modificar (y sus parámetros) serían los siguientes:

zend.assertions1genera y ejecuta el código(modo de desarrollo)
 0genera el código pero no lo ejecuta
 -1no genera ningún código(modo de producción)
assert.exception1cuando la aserción falla lanza el objeto indicado como excepción o lanza un nuevo objeto AssertionError con el mensaje indicado si lo que se pasa es una cadena de texto
 0usa o genera una excepción como en el caso anterior. Pero en vez de lanzarla genera un mensaje de advertencia. Este formato es compatible con que se hacía en PHP 5

 

Más opciones

Como he comentado antes el primer parámetro puede ser cualquier expresión que se pueda evaluar como verdadero o falso. Además, por compatibilidad puede ser una cadena. En ese caso se evaluará la cadena y su resultado será el usado por assert. Sin embargo esta forma no debería usarse porque tiene más problemas que ventajas. La más inmediata es que en una cadena de texto podemos cometer errores. Errores de los que no nos daremos cuenta hasta que se ejecute. La misma operativa puede alcanzarse ahora con una expresión como se muestra en este otro ejemplo.

public function setResponseCode($code) {

    assert($code < 550 && $code > 100, "Se ha indicado un código de respuesta no válido: {$code}");

    $this->code = $code;
}

Por último decir que aunque no se prohíbe la función llamada por assert no debería modificar el estado de la aplicación. De forma más genérica hay que tener en cuenta que nuestra aplicación debe funcionar exactamente igual si se elimina la llamada a assert.