unreal4u's Personal Network Because my reality… is just your virtuality

29Dic/09Off

Manejar errores en PHP

Algunas consideraciones

Hasta el momento hemos visto cómo mostrar correctamente un error de forma bonita, pero lo que no hemos hecho es saber cómo capturarlo.
En PHP existen diversas formas para hacer esto, dependiendo siempre de la manera que se haga. Si, por ejemplo, quisiéramos abrir un archivo, tenemos dos posibilidades: la primera es simplemente abrirlo y preguntar si se trata de un puntero a archivo válido o la otra es primero saber si existe, y después abrirlo.

// Forma NO recomendada.
if (!$fp = @fopen('archivo.txt','r')) $err[] = 34;
else {
  // ejecutar las demás acciones
}

// Forma recomendada
if(file_exists('archivo.txt')) {
  $fp = fopen('archivo.txt','r');
  // Otras acciones
}
else $err[] = 34;

Sin embargo, este es un solo caso de apertura de archivo y nada más. Aunque ojo: ¡También es posible que este archivo tenga problemas de permisos, por lo cual $fp seguirá siendo un puntero inválido!

PHP en realidad tiene dos formas de mostrar errores: primero están los errores que generan un error en cuanto se compilan, y también están los errores que nosotros podemos cometer como programadores.
Lo primero tiene una fácil solución que se llama php.ini.

Este archivo contiene todas las directivas de configuración del ambiente completo. ¿Qué les puedo aconsejar? Cuando trabajen en un servidor de desarrollo, activen cualquier tipo de error que pudiera surgir, mediante las siguientes directivas:

expose_php = On
error_reporting = E_ALL
display_errors = On
display_startup_errors = On
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
track_errors = On
html_errors = On

De esa forma, se puede evitar en gran medida cualquier tipo de error que pudiera ocurrir en forma de compilación.

Luego, cuando traspasen el resultado al sitio final, simplemente se desactiva el mostrario de errores. Mediante esta técnica, y por experiencia propia, se puede eliminar hasta el 80% de los posibles errores que pudieran producirse.
Sin embargo, aquí es donde empieza lo bueno: ¿Cómo capturamos los errores que no han podido ser capturados antes? Suponiendo que ya no existen errores de compilación, esta pregunta tiene una fácil respuesta que implica la función trigger_error(), set_error_handler() y una pequeña función hecha por nosotros.

La primera función que veremos es trigger_error().
Esta función acepta dos parámetros y devuelve un booleano:
bool trigger_error ( string $mensaje [, int $tipo = E_USER_NOTICE ] )
Donde:
$mensaje es el mensaje que le queremos mostrar al usuario.
$tipo es el tipo de error que se elevará, que puede ser del tipo: E_USER_ERROR, E_USER_WARNING o (predeterminadamente) E_USER_NOTICE.

Supongamos el siguiente código:

<?php
trigger_error('este es un error!',E_USER_ERROR);
?>

Ahora bien, si ejecutamos ese archivo, nos dará un error feo muy al estilo de PHP, lo cual es justamente todo lo contrario de lo que queremos, ya que muestra demasiada información que no queremos mostrar.

Para solucionar esto, podemos hacer uso de set_error_handler().

Esta función, acepta dos parámetros, aunque siempre la he ocupado con uno solo (El segundo argumento limita el tipo de error a solo algunos, predeterminadamente, toma todos los errores). Ese único parámetro es el nombre de la función a ejecutarse cuando ocurre un error. ¿Para qué sirve esto? Básicamente, sirve para no pasar por el generador de errores de PHP, sino que tener uno hecho por nosotros. De esta manera, podemos hacer lo que queramos con el error sin que salgan errores feos de parte de PHP.

Supongamos el siguiente código:

// Esto será lo primero que debemos invocar en nuestros archivos
function dramas($errno, $errstr, $errfile, $errline, $errctx) {
    mail('su-email@ejemplo.com',
         'Error en la página',
         'Ocurrió un error N° '.$errno.' que dice: '.$errstr.' en el archivo '.$errfile.' en la línea '.$errline
    );
    die('Ocurrió un error!');
    return true;
}
set_error_handler("dramas");

// Finalmente, en nuestra aplicación, generamos un error (archivo no encontrado)
include('archivo-que-no-existe.php');

Con el código de arriba, ocurren hartas cosas interesantes:

  1. Primero definimos que nuestro error_handler sea la función llamada dramas().
  2. Esta función, lo primero que hará al recibir un error, será mandarnos un mail con detalles acerca del error.
  3. Enseguida, para la ejecución del script con un mensaje trivial.
  4. Luego, retorna true para que de esta manera, se ignore el handler de errores de PHP.

Los resultados? En la página:
2009-12-29-u4u-001

Y en nuestro mail:
2009-12-29-u4u-000

Por último, se pueden hacer muchísimas cosas más, ya que en vez de enviar un mail, se podría guardar a un archivo XML o bien guardar los errores en la base de datos, pero la idea general se entiende. También se puede logear la hora, la IP del visitante y un montón de otras cosas más haciendo buen uso de estas dos funciones.

Ahora bien, combinando nuestro pequeño código con la función trigger_error() y también aplicando todo lo aprendido hasta el momento podemos armar errores mucho más completos y fáciles de entender. Un pequeño ejemplo:

function error_handler($errno, $errstr, $errfile, $errline, $errctx) {
    switch($errno) {
      CASE 256: // alias de E_USER_ERROR
                die('Error fatal producido por el usuario!');
                break;
      CASE 512: // alias de E_USER_WARNING
                $err[] = 33;
                break;
      CASE 1024: // alias de E_USER_NOTICE
                $err[] = 77;
                break;
      DEFAULT: // cualquier otro tipo de error
              mail('su-email@ejemplo.com',
                   'Error en la página',
                   'Ocurrió un error N° '.$errno.'
              );
              die('Ocurrió un error!');
              break;
    }
    return true;
}
set_error_handler("error_handler");

// Supongamos que el usuario haya ingresado como año 2004:
$usuario_ingresa = '2004';

// Y el acceso a la página es sólo para mayores de 18 años:
$edad = date('Y') - $usuario_ingresa;
if ($edad < 18) trigger_error('asdf',E_USER_NOTICE);

// Por último, ya al mostrar la página, incluimos los errores:
include('includes/errores.php');

Con todo lo que se ha aprendido, ya podremos implementar una buena forma de capturar errores sin que les salga mensajes feos al usuario.

¿Te gustó este artículo?

¡Considera suscribirte a nuestro feed!

Sobre Camilo Sperberg

Es Ingeniero Informático especializado en Linux y PHP (Es la primera persona en certificarse en PHP5.3 en Chile). En su tiempo libre le gusta estudiar nuevas técnicas de programación y escribir. Además, es amigo de todo ser viviente y cree que la tecnocracia es la mejor forma de política.
Archivado en: Mundo Web, PHP, 2,663 vistas Comments Off