980 lines
33 KiB
PHP
980 lines
33 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Error Stack Implementation
|
||
|
*
|
||
|
* This is an incredibly simple implementation of a very complex error handling
|
||
|
* facility. It contains the ability
|
||
|
* to track multiple errors from multiple packages simultaneously. In addition,
|
||
|
* it can track errors of many levels, save data along with the error, context
|
||
|
* information such as the exact file, line number, class and function that
|
||
|
* generated the error, and if necessary, it can raise a traditional PEAR_Error.
|
||
|
* It has built-in support for PEAR::Log, to log errors as they occur
|
||
|
*
|
||
|
* Since version 0.2alpha, it is also possible to selectively ignore errors,
|
||
|
* through the use of an error callback, see {@link pushCallback()}
|
||
|
*
|
||
|
* Since version 0.3alpha, it is possible to specify the exception class
|
||
|
* returned from {@link push()}
|
||
|
*
|
||
|
* Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can
|
||
|
* still be done quite handily in an error callback or by manipulating the returned array
|
||
|
* @category Debugging
|
||
|
* @package PEAR_ErrorStack
|
||
|
* @author Greg Beaver <cellog@php.net>
|
||
|
* @copyright 2004-2008 Greg Beaver
|
||
|
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
||
|
* @link http://pear.php.net/package/PEAR_ErrorStack
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Singleton storage
|
||
|
*
|
||
|
* Format:
|
||
|
* <pre>
|
||
|
* array(
|
||
|
* 'package1' => PEAR_ErrorStack object,
|
||
|
* 'package2' => PEAR_ErrorStack object,
|
||
|
* ...
|
||
|
* )
|
||
|
* </pre>
|
||
|
* @access private
|
||
|
* @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
|
||
|
*/
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
|
||
|
|
||
|
/**
|
||
|
* Global error callback (default)
|
||
|
*
|
||
|
* This is only used if set to non-false. * is the default callback for
|
||
|
* all packages, whereas specific packages may set a default callback
|
||
|
* for all instances, regardless of whether they are a singleton or not.
|
||
|
*
|
||
|
* To exclude non-singletons, only set the local callback for the singleton
|
||
|
* @see PEAR_ErrorStack::setDefaultCallback()
|
||
|
* @access private
|
||
|
* @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
|
||
|
*/
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
|
||
|
'*' => false,
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
* Global Log object (default)
|
||
|
*
|
||
|
* This is only used if set to non-false. Use to set a default log object for
|
||
|
* all stacks, regardless of instantiation order or location
|
||
|
* @see PEAR_ErrorStack::setDefaultLogger()
|
||
|
* @access private
|
||
|
* @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
|
||
|
*/
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
|
||
|
|
||
|
/**
|
||
|
* Global Overriding Callback
|
||
|
*
|
||
|
* This callback will override any error callbacks that specific loggers have set.
|
||
|
* Use with EXTREME caution
|
||
|
* @see PEAR_ErrorStack::staticPushCallback()
|
||
|
* @access private
|
||
|
* @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
|
||
|
*/
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
|
||
|
|
||
|
/**#@+
|
||
|
* One of four possible return values from the error Callback
|
||
|
* @see PEAR_ErrorStack::_errorCallback()
|
||
|
*/
|
||
|
/**
|
||
|
* If this is returned, then the error will be both pushed onto the stack
|
||
|
* and logged.
|
||
|
*/
|
||
|
define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
|
||
|
/**
|
||
|
* If this is returned, then the error will only be pushed onto the stack,
|
||
|
* and not logged.
|
||
|
*/
|
||
|
define('PEAR_ERRORSTACK_PUSH', 2);
|
||
|
/**
|
||
|
* If this is returned, then the error will only be logged, but not pushed
|
||
|
* onto the error stack.
|
||
|
*/
|
||
|
define('PEAR_ERRORSTACK_LOG', 3);
|
||
|
/**
|
||
|
* If this is returned, then the error is completely ignored.
|
||
|
*/
|
||
|
define('PEAR_ERRORSTACK_IGNORE', 4);
|
||
|
/**
|
||
|
* If this is returned, then the error is logged and die() is called.
|
||
|
*/
|
||
|
define('PEAR_ERRORSTACK_DIE', 5);
|
||
|
/**#@-*/
|
||
|
|
||
|
/**
|
||
|
* Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
|
||
|
* the singleton method.
|
||
|
*/
|
||
|
define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
|
||
|
|
||
|
/**
|
||
|
* Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
|
||
|
* that has no __toString() method
|
||
|
*/
|
||
|
define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
|
||
|
/**
|
||
|
* Error Stack Implementation
|
||
|
*
|
||
|
* Usage:
|
||
|
* <code>
|
||
|
* // global error stack
|
||
|
* $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
|
||
|
* // local error stack
|
||
|
* $local_stack = new PEAR_ErrorStack('MyPackage');
|
||
|
* </code>
|
||
|
* @author Greg Beaver <cellog@php.net>
|
||
|
* @version 1.10.13
|
||
|
* @package PEAR_ErrorStack
|
||
|
* @category Debugging
|
||
|
* @copyright 2004-2008 Greg Beaver
|
||
|
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
||
|
* @link http://pear.php.net/package/PEAR_ErrorStack
|
||
|
*/
|
||
|
class PEAR_ErrorStack {
|
||
|
/**
|
||
|
* Errors are stored in the order that they are pushed on the stack.
|
||
|
* @since 0.4alpha Errors are no longer organized by error level.
|
||
|
* This renders pop() nearly unusable, and levels could be more easily
|
||
|
* handled in a callback anyway
|
||
|
* @var array
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_errors = array();
|
||
|
|
||
|
/**
|
||
|
* Storage of errors by level.
|
||
|
*
|
||
|
* Allows easy retrieval and deletion of only errors from a particular level
|
||
|
* @since PEAR 1.4.0dev
|
||
|
* @var array
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_errorsByLevel = array();
|
||
|
|
||
|
/**
|
||
|
* Package name this error stack represents
|
||
|
* @var string
|
||
|
* @access protected
|
||
|
*/
|
||
|
var $_package;
|
||
|
|
||
|
/**
|
||
|
* Determines whether a PEAR_Error is thrown upon every error addition
|
||
|
* @var boolean
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_compat = false;
|
||
|
|
||
|
/**
|
||
|
* If set to a valid callback, this will be used to generate the error
|
||
|
* message from the error code, otherwise the message passed in will be
|
||
|
* used
|
||
|
* @var false|string|array
|
||
|
* @access private
|
||
|
*/
|
||
|
var $_msgCallback = false;
|
||
|
|
||
|
/**
|
||
|
* If set to a valid callback, this will be used to generate the error
|
||
|
* context for an error. For PHP-related errors, this will be a file
|
||
|
* and line number as retrieved from debug_backtrace(), but can be
|
||
|
* customized for other purposes. The error might actually be in a separate
|
||
|
* configuration file, or in a database query.
|
||
|
* @var false|string|array
|
||
|
* @access protected
|
||
|
*/
|
||
|
var $_contextCallback = false;
|
||
|
|
||
|
/**
|
||
|
* If set to a valid callback, this will be called every time an error
|
||
|
* is pushed onto the stack. The return value will be used to determine
|
||
|
* whether to allow an error to be pushed or logged.
|
||
|
*
|
||
|
* The return value must be one an PEAR_ERRORSTACK_* constant
|
||
|
* @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
|
||
|
* @var false|string|array
|
||
|
* @access protected
|
||
|
*/
|
||
|
var $_errorCallback = array();
|
||
|
|
||
|
/**
|
||
|
* PEAR::Log object for logging errors
|
||
|
* @var false|Log
|
||
|
* @access protected
|
||
|
*/
|
||
|
var $_logger = false;
|
||
|
|
||
|
/**
|
||
|
* Error messages - designed to be overridden
|
||
|
* @var array
|
||
|
* @abstract
|
||
|
*/
|
||
|
var $_errorMsgs = array();
|
||
|
|
||
|
/**
|
||
|
* Set up a new error stack
|
||
|
*
|
||
|
* @param string $package name of the package this error stack represents
|
||
|
* @param callback $msgCallback callback used for error message generation
|
||
|
* @param callback $contextCallback callback used for context generation,
|
||
|
* defaults to {@link getFileLine()}
|
||
|
* @param boolean $throwPEAR_Error
|
||
|
*/
|
||
|
function __construct($package, $msgCallback = false, $contextCallback = false,
|
||
|
$throwPEAR_Error = false)
|
||
|
{
|
||
|
$this->_package = $package;
|
||
|
$this->setMessageCallback($msgCallback);
|
||
|
$this->setContextCallback($contextCallback);
|
||
|
$this->_compat = $throwPEAR_Error;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return a single error stack for this package.
|
||
|
*
|
||
|
* Note that all parameters are ignored if the stack for package $package
|
||
|
* has already been instantiated
|
||
|
* @param string $package name of the package this error stack represents
|
||
|
* @param callback $msgCallback callback used for error message generation
|
||
|
* @param callback $contextCallback callback used for context generation,
|
||
|
* defaults to {@link getFileLine()}
|
||
|
* @param boolean $throwPEAR_Error
|
||
|
* @param string $stackClass class to instantiate
|
||
|
*
|
||
|
* @return PEAR_ErrorStack
|
||
|
*/
|
||
|
public static function &singleton(
|
||
|
$package, $msgCallback = false, $contextCallback = false,
|
||
|
$throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack'
|
||
|
) {
|
||
|
if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
|
||
|
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
|
||
|
}
|
||
|
if (!class_exists($stackClass)) {
|
||
|
if (function_exists('debug_backtrace')) {
|
||
|
$trace = debug_backtrace();
|
||
|
}
|
||
|
PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
|
||
|
'exception', array('stackclass' => $stackClass),
|
||
|
'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
|
||
|
false, $trace);
|
||
|
}
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
|
||
|
new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
|
||
|
|
||
|
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Internal error handler for PEAR_ErrorStack class
|
||
|
*
|
||
|
* Dies if the error is an exception (and would have died anyway)
|
||
|
* @access private
|
||
|
*/
|
||
|
function _handleError($err)
|
||
|
{
|
||
|
if ($err['level'] == 'exception') {
|
||
|
$message = $err['message'];
|
||
|
if (isset($_SERVER['REQUEST_URI'])) {
|
||
|
echo '<br />';
|
||
|
} else {
|
||
|
echo "\n";
|
||
|
}
|
||
|
var_dump($err['context']);
|
||
|
die($message);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set up a PEAR::Log object for all error stacks that don't have one
|
||
|
* @param Log $log
|
||
|
*/
|
||
|
public static function setDefaultLogger(&$log)
|
||
|
{
|
||
|
if (is_object($log) && method_exists($log, 'log') ) {
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
|
||
|
} elseif (is_callable($log)) {
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set up a PEAR::Log object for this error stack
|
||
|
* @param Log $log
|
||
|
*/
|
||
|
function setLogger(&$log)
|
||
|
{
|
||
|
if (is_object($log) && method_exists($log, 'log') ) {
|
||
|
$this->_logger = &$log;
|
||
|
} elseif (is_callable($log)) {
|
||
|
$this->_logger = &$log;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set an error code => error message mapping callback
|
||
|
*
|
||
|
* This method sets the callback that can be used to generate error
|
||
|
* messages for any instance
|
||
|
* @param array|string Callback function/method
|
||
|
*/
|
||
|
function setMessageCallback($msgCallback)
|
||
|
{
|
||
|
if (!$msgCallback) {
|
||
|
$this->_msgCallback = array(&$this, 'getErrorMessage');
|
||
|
} else {
|
||
|
if (is_callable($msgCallback)) {
|
||
|
$this->_msgCallback = $msgCallback;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get an error code => error message mapping callback
|
||
|
*
|
||
|
* This method returns the current callback that can be used to generate error
|
||
|
* messages
|
||
|
* @return array|string|false Callback function/method or false if none
|
||
|
*/
|
||
|
function getMessageCallback()
|
||
|
{
|
||
|
return $this->_msgCallback;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets a default callback to be used by all error stacks
|
||
|
*
|
||
|
* This method sets the callback that can be used to generate error
|
||
|
* messages for a singleton
|
||
|
* @param array|string Callback function/method
|
||
|
* @param string Package name, or false for all packages
|
||
|
*/
|
||
|
public static function setDefaultCallback($callback = false, $package = false)
|
||
|
{
|
||
|
if (!is_callable($callback)) {
|
||
|
$callback = false;
|
||
|
}
|
||
|
$package = $package ? $package : '*';
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a callback that generates context information (location of error) for an error stack
|
||
|
*
|
||
|
* This method sets the callback that can be used to generate context
|
||
|
* information for an error. Passing in NULL will disable context generation
|
||
|
* and remove the expensive call to debug_backtrace()
|
||
|
* @param array|string|null Callback function/method
|
||
|
*/
|
||
|
function setContextCallback($contextCallback)
|
||
|
{
|
||
|
if ($contextCallback === null) {
|
||
|
return $this->_contextCallback = false;
|
||
|
}
|
||
|
if (!$contextCallback) {
|
||
|
$this->_contextCallback = array(&$this, 'getFileLine');
|
||
|
} else {
|
||
|
if (is_callable($contextCallback)) {
|
||
|
$this->_contextCallback = $contextCallback;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set an error Callback
|
||
|
* If set to a valid callback, this will be called every time an error
|
||
|
* is pushed onto the stack. The return value will be used to determine
|
||
|
* whether to allow an error to be pushed or logged.
|
||
|
*
|
||
|
* The return value must be one of the ERRORSTACK_* constants.
|
||
|
*
|
||
|
* This functionality can be used to emulate PEAR's pushErrorHandling, and
|
||
|
* the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
|
||
|
* the error stack or logging
|
||
|
* @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
|
||
|
* @see popCallback()
|
||
|
* @param string|array $cb
|
||
|
*/
|
||
|
function pushCallback($cb)
|
||
|
{
|
||
|
array_push($this->_errorCallback, $cb);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove a callback from the error callback stack
|
||
|
* @see pushCallback()
|
||
|
* @return array|string|false
|
||
|
*/
|
||
|
function popCallback()
|
||
|
{
|
||
|
if (!count($this->_errorCallback)) {
|
||
|
return false;
|
||
|
}
|
||
|
return array_pop($this->_errorCallback);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set a temporary overriding error callback for every package error stack
|
||
|
*
|
||
|
* Use this to temporarily disable all existing callbacks (can be used
|
||
|
* to emulate the @ operator, for instance)
|
||
|
* @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
|
||
|
* @see staticPopCallback(), pushCallback()
|
||
|
* @param string|array $cb
|
||
|
*/
|
||
|
public static function staticPushCallback($cb)
|
||
|
{
|
||
|
array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove a temporary overriding error callback
|
||
|
* @see staticPushCallback()
|
||
|
* @return array|string|false
|
||
|
*/
|
||
|
public static function staticPopCallback()
|
||
|
{
|
||
|
$ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
|
||
|
if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
|
||
|
}
|
||
|
return $ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add an error to the stack
|
||
|
*
|
||
|
* If the message generator exists, it is called with 2 parameters.
|
||
|
* - the current Error Stack object
|
||
|
* - an array that is in the same format as an error. Available indices
|
||
|
* are 'code', 'package', 'time', 'params', 'level', and 'context'
|
||
|
*
|
||
|
* Next, if the error should contain context information, this is
|
||
|
* handled by the context grabbing method.
|
||
|
* Finally, the error is pushed onto the proper error stack
|
||
|
* @param int $code Package-specific error code
|
||
|
* @param string $level Error level. This is NOT spell-checked
|
||
|
* @param array $params associative array of error parameters
|
||
|
* @param string $msg Error message, or a portion of it if the message
|
||
|
* is to be generated
|
||
|
* @param array $repackage If this error re-packages an error pushed by
|
||
|
* another package, place the array returned from
|
||
|
* {@link pop()} in this parameter
|
||
|
* @param array $backtrace Protected parameter: use this to pass in the
|
||
|
* {@link debug_backtrace()} that should be used
|
||
|
* to find error context
|
||
|
* @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
|
||
|
* thrown. If a PEAR_Error is returned, the userinfo
|
||
|
* property is set to the following array:
|
||
|
*
|
||
|
* <code>
|
||
|
* array(
|
||
|
* 'code' => $code,
|
||
|
* 'params' => $params,
|
||
|
* 'package' => $this->_package,
|
||
|
* 'level' => $level,
|
||
|
* 'time' => time(),
|
||
|
* 'context' => $context,
|
||
|
* 'message' => $msg,
|
||
|
* //['repackage' => $err] repackaged error array/Exception class
|
||
|
* );
|
||
|
* </code>
|
||
|
*
|
||
|
* Normally, the previous array is returned.
|
||
|
*/
|
||
|
function push($code, $level = 'error', $params = array(), $msg = false,
|
||
|
$repackage = false, $backtrace = false)
|
||
|
{
|
||
|
$context = false;
|
||
|
// grab error context
|
||
|
if ($this->_contextCallback) {
|
||
|
if (!$backtrace) {
|
||
|
$backtrace = debug_backtrace();
|
||
|
}
|
||
|
$context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
|
||
|
}
|
||
|
|
||
|
// save error
|
||
|
$time = explode(' ', microtime());
|
||
|
$time = $time[1] + $time[0];
|
||
|
$err = array(
|
||
|
'code' => $code,
|
||
|
'params' => $params,
|
||
|
'package' => $this->_package,
|
||
|
'level' => $level,
|
||
|
'time' => $time,
|
||
|
'context' => $context,
|
||
|
'message' => $msg,
|
||
|
);
|
||
|
|
||
|
if ($repackage) {
|
||
|
$err['repackage'] = $repackage;
|
||
|
}
|
||
|
|
||
|
// set up the error message, if necessary
|
||
|
if ($this->_msgCallback) {
|
||
|
$msg = call_user_func_array($this->_msgCallback,
|
||
|
array(&$this, $err));
|
||
|
$err['message'] = $msg;
|
||
|
}
|
||
|
$push = $log = true;
|
||
|
$die = false;
|
||
|
// try the overriding callback first
|
||
|
$callback = $this->staticPopCallback();
|
||
|
if ($callback) {
|
||
|
$this->staticPushCallback($callback);
|
||
|
}
|
||
|
if (!is_callable($callback)) {
|
||
|
// try the local callback next
|
||
|
$callback = $this->popCallback();
|
||
|
if (is_callable($callback)) {
|
||
|
$this->pushCallback($callback);
|
||
|
} else {
|
||
|
// try the default callback
|
||
|
$callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
|
||
|
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
|
||
|
}
|
||
|
}
|
||
|
if (is_callable($callback)) {
|
||
|
switch(call_user_func($callback, $err)){
|
||
|
case PEAR_ERRORSTACK_IGNORE:
|
||
|
return $err;
|
||
|
break;
|
||
|
case PEAR_ERRORSTACK_PUSH:
|
||
|
$log = false;
|
||
|
break;
|
||
|
case PEAR_ERRORSTACK_LOG:
|
||
|
$push = false;
|
||
|
break;
|
||
|
case PEAR_ERRORSTACK_DIE:
|
||
|
$die = true;
|
||
|
break;
|
||
|
// anything else returned has the same effect as pushandlog
|
||
|
}
|
||
|
}
|
||
|
if ($push) {
|
||
|
array_unshift($this->_errors, $err);
|
||
|
if (!isset($this->_errorsByLevel[$err['level']])) {
|
||
|
$this->_errorsByLevel[$err['level']] = array();
|
||
|
}
|
||
|
$this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
|
||
|
}
|
||
|
if ($log) {
|
||
|
if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
|
||
|
$this->_log($err);
|
||
|
}
|
||
|
}
|
||
|
if ($die) {
|
||
|
die();
|
||
|
}
|
||
|
if ($this->_compat && $push) {
|
||
|
return $this->raiseError($msg, $code, null, null, $err);
|
||
|
}
|
||
|
return $err;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Static version of {@link push()}
|
||
|
*
|
||
|
* @param string $package Package name this error belongs to
|
||
|
* @param int $code Package-specific error code
|
||
|
* @param string $level Error level. This is NOT spell-checked
|
||
|
* @param array $params associative array of error parameters
|
||
|
* @param string $msg Error message, or a portion of it if the message
|
||
|
* is to be generated
|
||
|
* @param array $repackage If this error re-packages an error pushed by
|
||
|
* another package, place the array returned from
|
||
|
* {@link pop()} in this parameter
|
||
|
* @param array $backtrace Protected parameter: use this to pass in the
|
||
|
* {@link debug_backtrace()} that should be used
|
||
|
* to find error context
|
||
|
* @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
|
||
|
* thrown. see docs for {@link push()}
|
||
|
*/
|
||
|
public static function staticPush(
|
||
|
$package, $code, $level = 'error', $params = array(),
|
||
|
$msg = false, $repackage = false, $backtrace = false
|
||
|
) {
|
||
|
$s = &PEAR_ErrorStack::singleton($package);
|
||
|
if ($s->_contextCallback) {
|
||
|
if (!$backtrace) {
|
||
|
if (function_exists('debug_backtrace')) {
|
||
|
$backtrace = debug_backtrace();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Log an error using PEAR::Log
|
||
|
* @param array $err Error array
|
||
|
* @param array $levels Error level => Log constant map
|
||
|
* @access protected
|
||
|
*/
|
||
|
function _log($err)
|
||
|
{
|
||
|
if ($this->_logger) {
|
||
|
$logger = &$this->_logger;
|
||
|
} else {
|
||
|
$logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
|
||
|
}
|
||
|
if (is_a($logger, 'Log')) {
|
||
|
$levels = array(
|
||
|
'exception' => PEAR_LOG_CRIT,
|
||
|
'alert' => PEAR_LOG_ALERT,
|
||
|
'critical' => PEAR_LOG_CRIT,
|
||
|
'error' => PEAR_LOG_ERR,
|
||
|
'warning' => PEAR_LOG_WARNING,
|
||
|
'notice' => PEAR_LOG_NOTICE,
|
||
|
'info' => PEAR_LOG_INFO,
|
||
|
'debug' => PEAR_LOG_DEBUG);
|
||
|
if (isset($levels[$err['level']])) {
|
||
|
$level = $levels[$err['level']];
|
||
|
} else {
|
||
|
$level = PEAR_LOG_INFO;
|
||
|
}
|
||
|
$logger->log($err['message'], $level, $err);
|
||
|
} else { // support non-standard logs
|
||
|
call_user_func($logger, $err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Pop an error off of the error stack
|
||
|
*
|
||
|
* @return false|array
|
||
|
* @since 0.4alpha it is no longer possible to specify a specific error
|
||
|
* level to return - the last error pushed will be returned, instead
|
||
|
*/
|
||
|
function pop()
|
||
|
{
|
||
|
$err = @array_shift($this->_errors);
|
||
|
if (!is_null($err)) {
|
||
|
@array_pop($this->_errorsByLevel[$err['level']]);
|
||
|
if (!count($this->_errorsByLevel[$err['level']])) {
|
||
|
unset($this->_errorsByLevel[$err['level']]);
|
||
|
}
|
||
|
}
|
||
|
return $err;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Pop an error off of the error stack, static method
|
||
|
*
|
||
|
* @param string package name
|
||
|
* @return boolean
|
||
|
* @since PEAR1.5.0a1
|
||
|
*/
|
||
|
static function staticPop($package)
|
||
|
{
|
||
|
if ($package) {
|
||
|
if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
|
||
|
return false;
|
||
|
}
|
||
|
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Determine whether there are any errors on the stack
|
||
|
* @param string|array Level name. Use to determine if any errors
|
||
|
* of level (string), or levels (array) have been pushed
|
||
|
* @return boolean
|
||
|
*/
|
||
|
function hasErrors($level = false)
|
||
|
{
|
||
|
if ($level) {
|
||
|
return isset($this->_errorsByLevel[$level]);
|
||
|
}
|
||
|
return count($this->_errors);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve all errors since last purge
|
||
|
*
|
||
|
* @param boolean set in order to empty the error stack
|
||
|
* @param string level name, to return only errors of a particular severity
|
||
|
* @return array
|
||
|
*/
|
||
|
function getErrors($purge = false, $level = false)
|
||
|
{
|
||
|
if (!$purge) {
|
||
|
if ($level) {
|
||
|
if (!isset($this->_errorsByLevel[$level])) {
|
||
|
return array();
|
||
|
} else {
|
||
|
return $this->_errorsByLevel[$level];
|
||
|
}
|
||
|
} else {
|
||
|
return $this->_errors;
|
||
|
}
|
||
|
}
|
||
|
if ($level) {
|
||
|
$ret = $this->_errorsByLevel[$level];
|
||
|
foreach ($this->_errorsByLevel[$level] as $i => $unused) {
|
||
|
// entries are references to the $_errors array
|
||
|
$this->_errorsByLevel[$level][$i] = false;
|
||
|
}
|
||
|
// array_filter removes all entries === false
|
||
|
$this->_errors = array_filter($this->_errors);
|
||
|
unset($this->_errorsByLevel[$level]);
|
||
|
return $ret;
|
||
|
}
|
||
|
$ret = $this->_errors;
|
||
|
$this->_errors = array();
|
||
|
$this->_errorsByLevel = array();
|
||
|
return $ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Determine whether there are any errors on a single error stack, or on any error stack
|
||
|
*
|
||
|
* The optional parameter can be used to test the existence of any errors without the need of
|
||
|
* singleton instantiation
|
||
|
* @param string|false Package name to check for errors
|
||
|
* @param string Level name to check for a particular severity
|
||
|
* @return boolean
|
||
|
*/
|
||
|
public static function staticHasErrors($package = false, $level = false)
|
||
|
{
|
||
|
if ($package) {
|
||
|
if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
|
||
|
return false;
|
||
|
}
|
||
|
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
|
||
|
}
|
||
|
foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
|
||
|
if ($obj->hasErrors($level)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a list of all errors since last purge, organized by package
|
||
|
* @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
|
||
|
* @param boolean $purge Set to purge the error stack of existing errors
|
||
|
* @param string $level Set to a level name in order to retrieve only errors of a particular level
|
||
|
* @param boolean $merge Set to return a flat array, not organized by package
|
||
|
* @param array $sortfunc Function used to sort a merged array - default
|
||
|
* sorts by time, and should be good for most cases
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public static function staticGetErrors(
|
||
|
$purge = false, $level = false, $merge = false,
|
||
|
$sortfunc = array('PEAR_ErrorStack', '_sortErrors')
|
||
|
) {
|
||
|
$ret = array();
|
||
|
if (!is_callable($sortfunc)) {
|
||
|
$sortfunc = array('PEAR_ErrorStack', '_sortErrors');
|
||
|
}
|
||
|
foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
|
||
|
$test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
|
||
|
if ($test) {
|
||
|
if ($merge) {
|
||
|
$ret = array_merge($ret, $test);
|
||
|
} else {
|
||
|
$ret[$package] = $test;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ($merge) {
|
||
|
usort($ret, $sortfunc);
|
||
|
}
|
||
|
return $ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Error sorting function, sorts by time
|
||
|
* @access private
|
||
|
*/
|
||
|
public static function _sortErrors($a, $b)
|
||
|
{
|
||
|
if ($a['time'] == $b['time']) {
|
||
|
return 0;
|
||
|
}
|
||
|
if ($a['time'] < $b['time']) {
|
||
|
return 1;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Standard file/line number/function/class context callback
|
||
|
*
|
||
|
* This function uses a backtrace generated from {@link debug_backtrace()}
|
||
|
* and so will not work at all in PHP < 4.3.0. The frame should
|
||
|
* reference the frame that contains the source of the error.
|
||
|
* @return array|false either array('file' => file, 'line' => line,
|
||
|
* 'function' => function name, 'class' => class name) or
|
||
|
* if this doesn't work, then false
|
||
|
* @param unused
|
||
|
* @param integer backtrace frame.
|
||
|
* @param array Results of debug_backtrace()
|
||
|
*/
|
||
|
public static function getFileLine($code, $params, $backtrace = null)
|
||
|
{
|
||
|
if ($backtrace === null) {
|
||
|
return false;
|
||
|
}
|
||
|
$frame = 0;
|
||
|
$functionframe = 1;
|
||
|
if (!isset($backtrace[1])) {
|
||
|
$functionframe = 0;
|
||
|
} else {
|
||
|
while (isset($backtrace[$functionframe]['function']) &&
|
||
|
$backtrace[$functionframe]['function'] == 'eval' &&
|
||
|
isset($backtrace[$functionframe + 1])) {
|
||
|
$functionframe++;
|
||
|
}
|
||
|
}
|
||
|
if (isset($backtrace[$frame])) {
|
||
|
if (!isset($backtrace[$frame]['file'])) {
|
||
|
$frame++;
|
||
|
}
|
||
|
$funcbacktrace = $backtrace[$functionframe];
|
||
|
$filebacktrace = $backtrace[$frame];
|
||
|
$ret = array('file' => $filebacktrace['file'],
|
||
|
'line' => $filebacktrace['line']);
|
||
|
// rearrange for eval'd code or create function errors
|
||
|
if (strpos($filebacktrace['file'], '(') &&
|
||
|
preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
|
||
|
$matches)) {
|
||
|
$ret['file'] = $matches[1];
|
||
|
$ret['line'] = $matches[2] + 0;
|
||
|
}
|
||
|
if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
|
||
|
if ($funcbacktrace['function'] != 'eval') {
|
||
|
if ($funcbacktrace['function'] == '__lambda_func') {
|
||
|
$ret['function'] = 'create_function() code';
|
||
|
} else {
|
||
|
$ret['function'] = $funcbacktrace['function'];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
|
||
|
$ret['class'] = $funcbacktrace['class'];
|
||
|
}
|
||
|
return $ret;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Standard error message generation callback
|
||
|
*
|
||
|
* This method may also be called by a custom error message generator
|
||
|
* to fill in template values from the params array, simply
|
||
|
* set the third parameter to the error message template string to use
|
||
|
*
|
||
|
* The special variable %__msg% is reserved: use it only to specify
|
||
|
* where a message passed in by the user should be placed in the template,
|
||
|
* like so:
|
||
|
*
|
||
|
* Error message: %msg% - internal error
|
||
|
*
|
||
|
* If the message passed like so:
|
||
|
*
|
||
|
* <code>
|
||
|
* $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
|
||
|
* </code>
|
||
|
*
|
||
|
* The returned error message will be "Error message: server error 500 -
|
||
|
* internal error"
|
||
|
* @param PEAR_ErrorStack
|
||
|
* @param array
|
||
|
* @param string|false Pre-generated error message template
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function getErrorMessage(&$stack, $err, $template = false)
|
||
|
{
|
||
|
if ($template) {
|
||
|
$mainmsg = $template;
|
||
|
} else {
|
||
|
$mainmsg = $stack->getErrorMessageTemplate($err['code']);
|
||
|
}
|
||
|
$mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
|
||
|
if (is_array($err['params']) && count($err['params'])) {
|
||
|
foreach ($err['params'] as $name => $val) {
|
||
|
if (is_array($val)) {
|
||
|
// @ is needed in case $val is a multi-dimensional array
|
||
|
$val = @implode(', ', $val);
|
||
|
}
|
||
|
if (is_object($val)) {
|
||
|
if (method_exists($val, '__toString')) {
|
||
|
$val = $val->__toString();
|
||
|
} else {
|
||
|
PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
|
||
|
'warning', array('obj' => get_class($val)),
|
||
|
'object %obj% passed into getErrorMessage, but has no __toString() method');
|
||
|
$val = 'Object';
|
||
|
}
|
||
|
}
|
||
|
$mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
|
||
|
}
|
||
|
}
|
||
|
return $mainmsg;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Standard Error Message Template generator from code
|
||
|
* @return string
|
||
|
*/
|
||
|
function getErrorMessageTemplate($code)
|
||
|
{
|
||
|
if (!isset($this->_errorMsgs[$code])) {
|
||
|
return '%__msg%';
|
||
|
}
|
||
|
return $this->_errorMsgs[$code];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the Error Message Template array
|
||
|
*
|
||
|
* The array format must be:
|
||
|
* <pre>
|
||
|
* array(error code => 'message template',...)
|
||
|
* </pre>
|
||
|
*
|
||
|
* Error message parameters passed into {@link push()} will be used as input
|
||
|
* for the error message. If the template is 'message %foo% was %bar%', and the
|
||
|
* parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
|
||
|
* be 'message one was six'
|
||
|
* @return string
|
||
|
*/
|
||
|
function setErrorMessageTemplate($template)
|
||
|
{
|
||
|
$this->_errorMsgs = $template;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* emulate PEAR::raiseError()
|
||
|
*
|
||
|
* @return PEAR_Error
|
||
|
*/
|
||
|
function raiseError()
|
||
|
{
|
||
|
require_once 'PEAR.php';
|
||
|
$args = func_get_args();
|
||
|
return call_user_func_array(array('PEAR', 'raiseError'), $args);
|
||
|
}
|
||
|
}
|
||
|
$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
|
||
|
$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
|
||
|
?>
|