Bueno, digamos que tuve una noche "agitada" y mientras estaba comenzando a desarrollar unas clases que van a formar parte del NokTemplates2, descubrí que necesitaba algún tipo de "manejo de errores" para una aplicación. Dado que PHP4 aún no tiene soporte para Excepciones ( que si tendrá PHP5 ) se me dio por hacer un objeto que administre los errores, pero quería que esto se hiciera automáticamente, o sea, nada de estar indicando
$objError->error("algo salió mal"); sino que queria utilizar las funciones que nos da PHP actualmente para el manejo de los errores.
Así que leyendo entre algunas cosas que recordaba, y revisando en los comentarios de PHP.net, conseguí implementar un truquito para "atrapar" errores Fatales ( que a
set_error_handler se le escapan ).
Es así que terminé con una clase simple, que les comparto, para que la puedan estudiar, darme sus opiniones, sugerir mejoras, etc. El "Sistemita" está compuesto de 2 clases, AdminError y Contexto, este es el código de los mismos:
Código PHP:
class AdminError
{
function AdminError()
{
function adm_error($numero, $mensaje, $archivo, $linea, $contexto, $retorna=0)
{
$objContexto = new Contexto($numero, $mensaje, $archivo, $linea, $contexto);
if($retorna)
return $objContexto->leer();
else
print $objContexto->leer();
}
function errorFatal($buffer)
{
$buffer_temporal = $buffer;
$texto = strip_tags($buffer_temporal);
if(preg_match('/Fatal error: (.+) in (.+)? on line (\d+)/', $texto, $c))
return adm_error(E_USER_ERROR, $c[1], $c[2], $c[3], "", true);
return $buffer;
}
ob_start('errorFatal');
set_error_handler('adm_error');
}
}
/**
* Clase Contexto
* Devuelve el contexto de la linea de un archivo.
*
**/
class Contexto
{
/**
* Atributo
*
**/
var $_numero = "";
/**
* Atributo
*
**/
var $_mensaje = "";
/**
* Atributo
*
**/
var $_lineas = 5;
/**
* Constructor
* @access protected
*/
function Contexto($numero, $mensaje, $archivo, $linea, $contexto)
{
$this->_mensaje = "
<b>Error:</b> $mensaje<br><hr>
<b>Archivo:</b> $archivo<br><hr>
<b>Línea:</b> $linea<br><hr>
<b>Contexto del Código:</b><br><pre>".
$this->obtenerContexto($archivo, (int) $linea)."</pre><hr>";
}
/**
*
* @access public
* @return void
**/
function leer()
{
return $this->_mensaje;
}
/**
*
* @access public
* @return void
**/
function obtenerContexto($archivo, $linea)
{
if (!file_exists($archivo))
{
// Nos fijamos que el archivo exista
return "El contexto no puede mostrarse - ($archivo) no existe";
} elseif ((!is_int($linea)) OR ($linea <= 0)) {
// Verificamos que el numero de linea sea válido
return "El contexto no puede mostrarse - ($linea) es un número inválido de linea";
} else {
// leemos el codigo
$codigo = file( $archivo );
$lineas = count($codigo);
// calculamos los numeros de linea
$inicio = $linea - $this->_lineas;
$fin = $linea + $this->_lineas;
// verificaciones de seguridad
if ($inicio < 0) $inicio = 0;
if ($fin >= $lineas) $fin = $lineas;
$largo_fin= strlen($fin) + 2;
for ($i = $inicio-1; $i < $fin; $i++)
{
// marcamos la linea en cuestion.
$color=($i==$linea-1?"red":"black");
$salida[] = "<span style='background-color: lightgrey'>".($i+1).
str_repeat(" ", $largo_fin - strlen($i+1)).
"</span><span style='color: $color'>".
htmlentities($codigo[$i]).'</span>';
}
return trim(join("", $salida));
}
}
}
Un Scrip de ejemplo, para mostrar el úso de la clase, es el siguiente:
Código PHP:
<?php
// activamos todos los errores
error_reporting(E_ALL);
include('./clases/class.AdminError.php');
$error = new AdminError;
// Esto produce dos Notices
echo $data[constante_que_no_existe];
// Esto produce un Warning
$handler = "No soy un handler adecuado";
$data = fgets($handler, 23);
// Esto produce un Fatal Error
funcion_que_no_existe();
?>
Primero, si hay un Fatal Error, solo va a mostrar ESE Fatal Error hasta que sea arreglado. Luego, si el script solo contiene
Warnings o
Notices los muestra todos juntos, junto a la linea que provoca el error y ( por defecto ) las 5 lineas anteriores y posteriores a la linea "conflictiva".
Que queda por hacer? Ufff.. muchisimas cosas:
- Aún el objeto no atrapa errores del Tipo "Parse Error"
- No atrapa Fatal Errors provocados por las clases AdminError y Contexto
- Me gustaria que el coigo se mostrara como highlight_string, pero no puedo por ser una funcion "handler" de output_buffering.
- Como no puedo utilizar ob_start, no puedo mostrar los contenidos de la variable "$contexto" para dar una idea del estado de las variables al momento de creado el error.
- Algo más que se me ocurra en el camino.
Espero que no se hayan aburrido con un Post tan largo y específico. Espero que esta idea del trabajo con Errores les agrade, y sino, por favor, muestren sus técnicas para el trabajo de este tipo.
Saludos.