|
|
(Next) constructor PEAR_ErrorStack::PEAR_ErrorStack |
||||
| |
|||||
|
|||||
Introducción al uso de PEAR_ErrorStack
Esta clase se encuentra disponible como parte del paquete PEAR. Características incluidas:
Completamente testada (unit-tested) y documentada
Brillantemente rápida - coloca a PEAR_Error fuera del tiesto
Errores específicos para los paquetes
Niveles de error (notice/warning/error/exception)
Los datos del contexto de error se guardan por separado del mensaje de error
Errores en cascada - pueden especificarse errores padre
L ageneración dinámica de mensajes de error permite la generación de mensajes de error múltiples y distintos para el mismo objeto de error
Callbacks sofisticados están disponibles para la generación de mensajes de error, generación del contexto del error, y funcionalidad de la gestión de errores, ver Mostrar el contexto del error, Generación personalizada de mensajes de error, y controlando la generación de errores
PEAR_ErrorStack implementa el manejo y lanzamiento de error usando un patrón de pila. Esto tiene ventajs tremendas sobre la implementación de PEAR_Error. PEAR_Error centraliza toda la creación y manejo de errores en el constructor del objeto PEAR_Error. Una vez que un objeto ha sido creado, toda la gestión debe haber sido completada, incluso cuando se comprueba el valor devuelto por un método, o a través de un único callback global. Además, es casi imposible determinar la fuente de un error, y como el bagaje de toda la clase base PEAR es muy abultado, cada creación de error se ve acompañada de métodos lentos.
<?php
// uso tradicional de PEAR_Error
require_once 'PEAR.php';
class myobj
{
// no hay modo de saber de dónde procede $err
function errorCallback($err)
{
$this->display($err->getMessage());
$this->log($err->getMessage());
}
function log($msg)
{
error_log($msg, 3, 'somefile.log')
}
function display($msg)
{
echo $msg . '<br />';
}
}
$myobj = new myobj;
// usiando un callback
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array(&$myobj, 'errorCallback'));
$ret = SomePackage::doSomething();
if (PEAR::isError($ret)) {
// realizar alguna manipulación - este error también se muestra y se gurada en los logs
}
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
$ret = SomePackage::doSomething();
if (PEAR::isError($ret)) {
// realizar alguna manipulación - este error no se muestra, ni se gurada en los logs
}
PEAR::popErrorHandling();
?>
La clase PEAR_ErrorStack se ha creado en conocimiento del paquete Log, puede diferenciar fácilmente e incluso automáticamente errores de re-empaquetamiento con poca o ninguna dificultad.
<?php
// Gestión de errores con PEAR_ErrorStack
require_once 'PEAR/ErrorStack.php';
require_once 'Log.php';
define('MYPACKAGE_ERROR_DBERROR', 1);
class myobj
{
var $_stack;
function myobj()
{
$this->_stack = &PEAR_ErrorStack::singleton('MyPackage');
}
function errorCallback($err)
{
switch($err['package']){
case 'MyPackage':
// le decimos a la pila de error que sólo "loguee" el error
// este no será colocado en la pila
return PEAR_ERRORSTACK_LOG;
break;
case 'InternalDbPackage':
// re-empaquetamos estos errores como correspondientes a errores de nuestro paquete
// para empleo final del usuario
$this->_stack->push(MYPACKAGE_ERROR_DBERROR, 'error',
array('dbmessage' => $err['message'],
'dbcode' => $err['code'],
'We are having Connection problems, please' .
'try again in a few moments'),
'', $err); // incluir el eror tal como ha sido re-empaquetado
// le decimos a la DB interna de la pila de error que ignore este error,
// como si nunca se hubiese producido
return PEAR_ERRORSTACK_IGNORE;
break;
} // switch
}
}
$myobj = &new myobj;
// separamos las pilas de error para mi paquete, y el paquete interno DB
$dbstack = &PEAR_ErrorStack::singleton('InternalDbPackage');
$mystack = &PEAR_ErrorStack::singleton('MyPackage');
// fijamso un fichero de log empleando PEAR::Log
$log = &Log::Factory('file', 'somefile.log', 'MyPackage error log');
$mystack->setLogger($log);
// fijamos un log por defecto para emplearlo con todas las pilas de error
PEAR_ErrorStack::setDefaultLogger($log);
// los errores devueltos por MyPackage son "logueados"
$ret = SomePackage::doSomething();
// Notar que $ret no necesita ser comprobado para ninguna condición de error - los errores están
// completamente separados del código
if ($dbstack->hasErrors()) {
var_dump($dbstack->getErrors();
}
// usa el callback
$dbstack->setCallback(array(&$myobj, 'errorCallback'));
// cualquiera de los errores de db son transparentemente reempaquetados ahora como
// errores de MyPackage amigables con el usuario
$ret = SomePackage::doSomething();
?>
�Por qué escribir una nueva rutina de gestión de errores cuando existePEAR_Error? Hay varios problemas con PEAR_Error. Si bien un mensaje de error está presente en una clase de error, procesar este mensaje de error automaticamente es excesivamente difícil para las computadoras. Además, el mensaje de error no puede traducirse fácilmente una vez ha sido colocado en PEAR_Error. No existe tampoco una utilidad estándar para guardar datos relacionados con los errores en la clase de error. Por encima de los impedimentos relacionados con mensajes de error, no hay ninguna manera de determinar automáticamente de qué paquete proviene un objeto PEAR_Error, o la gravedad de un error. Errores fatales tienen el mismo aspecto que los que no lo son tanto.
EL defecto más grande del objeto PEAR_Error es el diseño de un sólotipo de error. Cada objeto PEAR_Error e ssimplemente un objeto PEAR_Error. NO existe diferenciación basada en la severidad de un error, o su origen. El único modo de determinar la severidad es usar PEAR_ERROR_TRIGGER y las constantes E_USER_NOTICE/E_USER_WARNING/E_USER_ERROR de PHP http://www.php.net/trigger_error. Pero el uso de esta funcionalidad no justifica 900 líneas de código, simplemente porque ¡trigger_error() se encuentra dentro del propio PHP!
Ahora, para empezar a usar tu nuevo tipo de objetos de error creados, cambia todas tus llamadas aPEAR::raiseError() o PEAR::throwError() coo estas...
<?php
require_once 'PEAR.php';
// modo antiguo:
$error_specific_info = 'bad';
$e = PEAR::raiseError("error message - very " . $error_specific_info .
" way to do things", MYPACKAGE_ERROR_FOO);
// otro modo antiguo:
$e = PEAR::throwError("error message - very " . $error_specific_info .
" way to do things", MYPACKAGE_ERROR_FOO);
?>
...por algo como esto:
<?php
require_once 'PEAR/ErrorStack.php';
// new way
// versión 1: acceso a instancias de la pila
$stack = &PEAR_ErrorStack::singleton('MyPackage');
$stack->push(MYPACKAGE_ERROR_DBERROR, 'error',
array('query' => $query, 'dsn' => $dsn),
'Critical Database Error: Contact Administrator immediately');
// versión 2: acceso estático a singleton (ligeramente más lentos)
PEAR_ErrorStack::staticPush('MyPackage', MYPACKAGE_ERROR_DBERROR, 'error',
array('query' => $query, 'dsn' => $dsn),
'Critical Database Error: Contact Administrator immediately');
?>
Para uso básico, esto es todo lo que se necesita para el uso del paquete PEAR_ErrorStack en lugar de PEAR_Error.
En algunos casos, puedes querer personalizar la gestión de errores. Por ejemplo, para muchas excepciones, es útil incluir el archivo, número de línea, y contexto de clase/función en orden a trazar un error. Está disponible una opción por defecto que será suficiente para la mayoría de los casos, y que es PEAR_ErrorStack::getFileLine().
No todos los errores de los paquetes se producen en el archivo fuente de PHP. Por ejemplo, errores en la compilación de motores de plantillas pueden producirse en los archivos fuente de las plantillas. Los errores de las bases de datos pueden ocurrir en el texto de una sentencia, o internamente al servidor de la base de datos. Los errores relacionados con paquetes de Internet pueden ocurrir en otro servidor. Todo esta información puede incluirse en un mensaje de error usando un callback que grabe el contexto.
<?php
require_once 'PEAR/ErrorStack.php';
class DatabaseClass
{
var $_dbError;
var $_dbErrorMsg;
var $_dbQuery;
var $_dbPos;
/**
* Grabando el contexto para el paquete Database
* @param integer Código de Error
* @param array parámetros de error pasados a {@link PEAR_ErrorStack::push()}
* @param array Salida de debug_backtrace() (no empleado en este callback)
*/
function getErrorContext($code, $params, $backtrace)
{
$context = array(
'errorcode' => $this->_dbError,
'errormsg' => $this->_dbErrorMsg,
'query' => $this->_dbQuery,
'pos' => $this->_dbPos,
);
return $context;
}
}
$db = new DatabaseClass;
PEAR_ErrorStack::staticSetContextCallback('Database', array(&$db, 'getErrorContext'));
?>
La información del contexto es formateada para ser fácilmente procesada por un aaplicación externa. Si quieres que la información del contexto esté en el error, el callback del mensaje de error debería emplearse para añadir la información en un formato legible por humanos al mensaje de error, tal y como se describe en la siguiente sección.
Hay tres métodos de PEAR_ErrorStack diseñados para el uso en la generación de mensajes de error eficientemente. para usarlos, debes hacer una de las dos cosas siguientes:
Llamar a PEAR_ErrorStack::setErrorMessageTemplate(), y fijar un mapeo de array de códigos de error a plantillas de mensajes de error, como sigue:
<?php
define('ERROR_ONE', 1);
define('ERROR_TWO', 2);
define('ERROR_THREE', 3);
define('ERROR_FOUR', 4);
require_once 'PEAR/ErrorStack.php';
$stack = &PEAR_ErrorStack::singleton('mypackage');
$messages = array(
ERROR_ONE => 'The gronk number %num% dropped a %thing%',
ERROR_TWO => 'The %list% items were missing',
ERROR_THREE => 'I like chocolate, how about %you%?',
ERROR_FOUR => 'and a %partridge% in a pear %tree%',
);
$stack->setErrorMessageTemplate($messages);
?>
La sustitución se hace empleando http://www.php.net/str_replace, y es muy simple. Basicamente, si un nombre de variable se encierra en signos de tanto por ciento (%), será reemplazada con el valor pasado en el array asociativo. Si un array
<?php
array('varname' => 'value');
?>
Además, si los valores son objetos, los métodos buscarán un método llamado "__toString()" y si lo encuentran, lo usarán para convertir el objeto en una cadena. Si se pasa un error de cadenas, éstas se unirán por comas.
<?php
array('varname' => array('first', 'second', 'third'));
// se convertirá en 'first, second, third'
?>
Llamar PEAR_ErrorStack::setMessageCallback(), y fijar un mensaje de error personalizado generando la función o método. Esta es probablemente la mejor opción para la mayoría de situaciones complejas, dado que permite a los usuarios sobreescribir o incluso extender el callback de error exsitente usando PEAR_ErrorStack::getMessageCallback(). Por ejemplo:
<?php
require_once 'PEAR/ErrorStack.php';
class foo
{
var $_oldcallback;
function callback(&$stack, $err)
{
$message = call_user_func_array($this->_oldcallback, array(&$stack, $err));
$message .= "File " . $err['context']['file'];
return $message;
}
}
$a = new foo;
$stack = &PEAR_ErrorStack::singleton('otherpackage');
$a->_oldcallback = $stack->getMessageCallback('otherpackage');
$stack->setMessageCallback(array(&$a, 'callback'));
?>
Extender PEAR_ErrorStack con tu propia clase, y sobreescribir PEAR_ErrorStack::getErrorMessageTemplate() o PEAR_ErrorStack::getErrorMessage(). Para garantizar qu eesta clase será utilizada por otros paquetes/aplicaciones, usa este código justo después de la declaración de la clase:
<?php
PEAR_ErrorStack::singleton('mypackage', false, null, 'MyPEAR_ErrorStack');
?>
Hay muchos escenarios en que un control detallado sobre la generación de error es absolutamtne deseable. Un callback genérico para manejo de error significa que cada error lanzado será manejado por el mismo callback de error. Aunque PEAR_ErrorStack está diseñado para operar con callbacks independientes para cada paquete, la gestión genérica de error es posible a través del método PEAR_ErrorStack::staticPushCallback(). Esto no es diferente del modo de error de PEAR_Error, PEAR_ERROR_CALLBACK.
La autéacute;ntica fuerza de PEAR_ErrorStack procede del propio callback. El callback de PEAR_Error no tiene actualmente ningún efecto en el mensaje de error - toda la gestión de errores debe producirse en el méacute;todo empleado como callcack o en la propia función. El callback de PEAR_ErrorStack puede tener influencia sobre el error por medio del uso de tres constantes:
PEAR_ERRORSTACK_IGNORE informa a la pila para que ignore el error, como si nunca hubiese ocurrido. EL error no será ni "logueado, ni puesto en la pila. Será, no obstante, devuelto por PEAR_ErrorStack::push()
PEAR_ERRORSTACK_PUSH informa a la pila para que coloque el error en la pila de error, pero no lo loguee.
PEAR_ERRORSTACK_LOG informa a la pila para que no ponga al error en al pila de errores, y simplemente lo loguee.
<?php
define('ERROR_CODE_ONE',1);
define('ERROR_CODE_TWO',2);
define('ERROR_CODE_THREE',3);
require_once 'PEAR/ErrorStack.php';
require_once 'Log.php';
function somecallback($err)
{
switch($err['code']){
case ERROR_CODE_ONE:
return PEAR_ERRORSTACK_IGNORE;
break;
case ERROR_CODE_TWO:
return PEAR_ERRORSTACK_PUSH;
break;
case ERROR_CODE_THREE:
return PEAR_ERRORSTACK_LOG;
break;
} // switch
}
$log = &Log::factory('display');
$stack = &PEAR_ErrorStack::singleton('mypackage');
$stack->setLogger($log);
$stack->pushCallback('somecallback');
$stack->push(ERROR_CODE_ONE);
$stack->push(ERROR_CODE_TWO);
$stack->push(ERROR_CODE_THREE);
var_dump(PEAR_ErrorStack::staticGetErrors());
// simular PEAR_ERROR_CALLBACK, con un callback específico para mi paquete
// cualquier otro paquete sólo logueará los errores, sólamente los errores de mi paquete
// serán colocados en la pila de error, como condición
class myclass {
function acallback($err)
{
return PEAR_ERRORSTACK_LOG;
}
}
$stack2 = PEAR_ErrorStack::singleton('anotherpackage');
$stack3 = &PEAR_ErrorStack::singleton('thirdpackage');
PEAR_ErrorStack::setDefaultCallback(array('myclass', 'acallback'));
?>
El uso más obvio para un callback de error envuelve un escenario común en muchas aplicaciones de nivel de usuario que usan paquetes de nivel de sistema. Si escribes un Content Management System (CMS) con el paquete PEAR DB, es normalmente una mala idea mostrar errores de nivel de la base de datos cuando un usuario hace click en un link para añadir un mensaje a un forum. PEAR_ErrorStack puede emplearse para re-empaquetar este errror como un eror de MyPackage.
<?php
define('MYPACKAGE_ERROR_DBDOWN',1);
require_once 'PEAR/ErrorStack.php';
function repackage($err)
{
if ($err['package'] == 'DB') {
$mystack = &PEAR_ErrorStack::singleton('mypackage');
$mystack->push(MYPACKAGE_ERROR_DBDOWN, 'error', array('olderror' => $err));
// ignora el error de la DB, pero guárdalo en los errores de mi paquete, para logueado
return PEAR_ERRORSTACK_IGNORE;
}
}
?>
Una de las mayores dificultades de uso de PEAR_Error envuelve al méacute;todo PEAR::expectError(). Con errores normales de PHP, es posible silenciarlos usando el operador @ como sigue:
<?php
@file_get_contents();
?>
La emulación de este comportamiento con PEAR_ErrorStack es simple.
<?php
define('ERROR_CODE_SOMETHING', 1);
require_once 'PEAR/ErrorStack.php';
function silence($err)
{
// ignora todos los errores
return PEAR_ERRORSTACK_IGNORE;
}
$stack = &PEAR_ErrorStack::singleton('mypackage');
$stack->pushCallback('silence');
$stack->push(ERROR_CODE_SOMETHING);
?>
PEAR_ErrorStack puede ir un paso más allá en este sentido, y sólo loguear errores o sólo poner errores en la pila de error, usando las otras dos constantes. Finalmente, los errores particulares pueden ser singularizados, y todos los demás ignorados.
<?php
define('SOMEPACKAGE_ERROR_THING', 1);
require_once 'PEAR/ErrorStack.php';
function silenceSome($err)
{
if ($err['package'] != 'somepackage') {
// ignora todos los errores de otros paquetes
return PEAR_ERROR_IGNORE;
}
if ($err['code'] != SOMEPACKAGE_ERROR_THING) {
// ignora todos los demás códigos de error
return PEAR_ERRORSTACK_IGNORE;
}
}
$stack = &PEAR_ErrorStack::singleton('mypackage');
$stack->pushCallback('silenceSome');
$stack->push(ERROR_CODE_SOMETHING);
?>
PEAR_ErrorStack puede programarse para lanzar automáticamente un PEAR_Error usando PEAR::raiseError(), simplemente pasaléacute; verdadero al modo de compatibilidad con PEAR_Error como sigue:
<?php
require_once 'PEAR/ErrorStack.php';
$stack = &PEAR_ErrorStack::singleton('mypackage', false, null, 'PEAR_ErrorStack', true);
?>
PEAR_ErrorStack devolverá una excepción automáticamente en PHP 5. Puedes fijar el nombre de la clase de excepción que será devuelta empleando el código siguiente:
<?php
require_once 'PEAR/ErrorStack.php';
$stack = &PEAR_ErrorStack::singleton('mypackage', false, null, 'PEAR_ErrorStack', false, 'MyException');
?>
|
|
(Next) constructor PEAR_ErrorStack::PEAR_ErrorStack |
||||||||
| |
|||||||||
|
|||||||||