Jan 14

Tal como comentaba en el post anterior voy a explicar como capturar los fatal errors del PHP para poder tratarlos en la medida de lo posible. Según este mensaje de la lista de desarrollo del PHP (fijaros en los autores) los fatal errors nunca podrán ser capturados en el userland, por ejemplo con un handler de errores personalizado, ya que en estos casos el intérprete es probable que quede en un estado inestable y es necesario de una terminación rápida y limpia.

Podemos conseguir un buen comportamiento ante los fatal error configurando adecuadamente las directivas implicadas. Sólo editando el php.ini podemos conseguir que se muestre un mensaje personalizado por pantalla, que se guarde en archivo o no, etc. Para más detalles podéis consultar el capítulo sobre el tema en el manual del PHP. Lo que aquí se describe es una forma alternativa que nos permite tenerlo controlado desde nuestro código PHP. Como requisitos hemos de tener configurado el PHP para que nos muestre errores por pantalla mediante la directiva de configuración display_errors.

La idea es sencilla, se trata de capturar todo el output buffer de nuestra aplicación y antes de enviarlo al usuario parsearlo en busca de la palabra “error” en negrita seguida de dos puntos con un mensaje y un salto de línea. El fallo es que, lógicamente, si nosotros sacamos por pantalla un string que cumpla lo anterior lo detectaremos como un fatal error del intérprete… aunque es muy difícil que tengamos una cadena de texto que cumpla estas condiciones a no ser que sean errores de nuestra aplicación, en ese caso siempre les podemos cambiar el texto o también podemos configurar la directiva error_prepend_string con una key identificable y modificar el código aquí incluido.

El código es el siguiente:

[php] function fatal_error_handler($buffer) {
if (ereg(“(error:)(.+)(/”,””,$regs[2]);
error_log($err,1,’tu.email@de.alertas.com’);
return ‘Mensaje de error personalizado para el usuario’;
}
return $buffer;
}
ob_start(“fatal_error_handler”);
funcion_que_no_existe();
ob_end_flush(); [/php]

Entre lo aquí descrito y el handler de errores detallado en el post anterior deberíamos tener cualquier error del PHP bajo control. Existen otros errores que no vamos a poder controlar nunca como son todos los errores de sintaxis que provocan parse errors, por ejemplo que falte un “;”, estos normalmente se detectan en la etapa de desarrollo y no se espera que sucedan en producción… y si suceden sólo nos queda tener correctamente configuradas las directivas pertinentes del PHP.

Tagged with:
Jan 13

Para conseguir que los errores de PHP lancen exceptions en lugar del clásico mensaje por pantalla y/o entrada en el log, dependiendo de las directivas de configuración usadas, es necesario redefinir el handler de errores del intérprete. Esto es muy útil si estamos programando con PHP5 una aplicación orientada a objetos usando excepciones personalizadas para tratar los errores de nuestro código. En esta situación sólo nos falta capturar los errores del PHP y convertirlos en excepciones para conseguir tratar de la misma forma cualquier error que se produzca ya sea de nuestro código o del intérprete.

El código necesario es el siguiente:

[php] class MyException extends Exception {
public $file;
public $line;
public function errorHandler($errno, $errstr, $errfile, $errline) {
$e = new self();
$e->message = $errstr;
$e->code = $errno;
$e->file = $errfile;
$e->line = $errline;
throw $e;
}
}
set_error_handler(array(‘MyException’, ‘errorHandler’), E_ALL); [/php]

Si ya dispones de una clase exception personalizada puedes simplemente añadirle el método errorHandler y lanzar una de tus excepciones ante un error del PHP. Es necesario colocar este código antes de que haya empezado la ejecución de tu aplicación, una buena técnica es crear un script y hacer que la directiva auto_prepend_file apunte a su ubicación… en general yo encuentro cómodo redefinir todos los handlers necesarios en el auto_prepend_file (por ejemplo nuestros handlers de sesiones, errores y excepciones).

Tener en cuenta que en el anterior código PHP fijo el nivel de errores a E_ALL, esto provoca que se lancen exceptions con los E_NOTICE. Si para ti es demasiado que se lance una excepción porque no has inicializado una variable puedes sustituir la línea con la llamada a set_error_handler por:

[php] set_error_handler(array(‘MyException’, ‘errorHandler’), E_ALL ^ E_NOTICE); [/php]

Puedes consultar más información acerca de los niveles de error del PHP en la documentación de la función error_reporting, también puedes pegarle un vistazo al post Controlar el Script PHP: set_time_limit, memory_limit y error_reporting publicado en SyntaxError.es, un blog amigo dentro de la red SmallSquid.com. Es interesante también consultar la documentación de set_error_handler para más información acerca de los handlers de errores.

Podéis comprobar que el handler funciona con el siguiente código de test:

[php] try {
$a + 2;
print ‘aquí no llegamos ya que $a no está inicializada’;
} catch (Exception $e) {
print ‘Excepción capturada! ‘.”\n”;
print ‘Code: ‘.$e->getCode().”\n”;
print ‘Message: ‘.$e->getMessage().”\n”;
print ‘Line: ‘.$e->getLine().”\n”;
print ‘File: ‘.$e->getFile().”\n”;
} [/php]

A pesar de redefinir el handler de errores los fatal errors nunca podrán ser capturados mediante nuestro error handler ya que el intérprete se queda en un estado inestable y necesita de una terminación rápida y limpia. En un siguiente post explicaré como tratar estos casos.

Tagged with:
preload preload preload