_handlers = array();
$this->_fatal_handler = false;
$this->_postpone_mask = 0;
$this->_postponed_errors = array();
set_error_handler('ErrorManager_errorHandler');
}
/**
* Get mask indicating which errors are currently being postponed.
* @access public
* @return int The current postponed error mask.
*/
function getPostponedErrorMask() {
return $this->_postpone_mask;
}
/**
* Set mask indicating which errors to postpone.
*
* The default value of the postpone mask is zero (no errors postponed.)
*
* When you set this mask, any queue errors which do not match tne new
* mask are reported.
*
* @access public
* @param $newmask int The new value for the mask.
*/
function setPostponedErrorMask($newmask) {
$this->_postpone_mask = $newmask;
PrintXML($this->_flush_errors($newmask));
}
/**
* Report any queued error messages.
* @access public
*/
function flushPostponedErrors() {
PrintXML($this->_flush_errors());
}
/**
* Get postponed errors, formatted as HTML.
*
* This also flushes the postponed error queue.
*
* @return object HTML describing any queued errors (or false, if none).
*/
function getPostponedErrorsAsHTML() {
$flushed = $this->_flush_errors();
if ($flushed->isEmpty())
return false;
$html = HTML::div(array('class' => 'errors'),
HTML::h4("PHP Warnings"));
$html->pushContent($flushed);
return $html;
}
/**
* Push a custom error handler on the handler stack.
*
* Sometimes one is performing an operation where one expects
* certain errors or warnings. In this case, one might not want
* these errors reported in the normal manner. Installing a custom
* error handler via this method allows one to intercept such
* errors.
*
* An error handler installed via this method should be either a
* function or an object method taking one argument: a PhpError
* object.
*
* The error handler should return either:
*
* - False
- If it has not handled the error. In this case,
* error processing will proceed as if the handler
* had never been called: the error will be passed
* to the next handler in the stack, or the
* default handler, if there are no more handlers
* in the stack.
*
*
- True
- If the handler has handled the error. If the
* error was a non-fatal one, no further processing
* will be done. If it was a fatal error, the
* ErrorManager will still terminate the PHP
* process (see setFatalHandler.)
*
*
- A PhpError object
- The error is not considered
* handled, and will be passed on to
* the next handler(s) in the stack
* (or the default handler). The
* returned PhpError need not be the
* same as the one passed to the
* handler. This allows the handler to
* "adjust" the error message.
*
* @access public
* @param $handler WikiCallback Handler to call.
*/
function pushErrorHandler($handler) {
array_unshift($this->_handlers, $handler);
}
/**
* Pop an error handler off the handler stack.
* @access public
*/
function popErrorHandler() {
return array_shift($this->_handlers);
}
/**
* Set a termination handler.
*
* This handler will be called upon fatal errors. The handler
* gets passed one argument: a PhpError object describing the
* fatal error.
*
* @access public
* @param $handler WikiCallback Callback to call on fatal errors.
*/
function setFatalHandler($handler) {
$this->_fatal_handler = $handler;
}
/**
* Handle an error.
*
* The error is passed through any registered error handlers, and
* then either reported or postponed.
*
* @access public
* @param $error object A PhpError object.
*/
function handleError($error) {
static $in_handler;
if (!empty($in_handler)) {
$msg = $error->_getDetail();
$msg->unshiftContent(HTML::h2(fmt("%s: error while handling error:",
"ErrorManager")));
$msg->printXML();
return;
}
$in_handler = true;
foreach ($this->_handlers as $handler) {
$result = $handler->call($error);
if (!$result) {
continue; // Handler did not handle error.
}
elseif (is_object($result)) {
// handler filtered the result. Still should pass to
// the rest of the chain.
if ($error->isFatal()) {
// Don't let handlers make fatal errors non-fatal.
$result->errno = $error->errno;
}
$error = $result;
}
else {
// Handler handled error.
if (!$error->isFatal()) {
$in_handler = false;
return;
}
break;
}
}
// Error was either fatal, or was not handled by a handler.
// Handle it ourself.
if ($error->isFatal()) {
$this->_die($error);
}
else if (($error->errno & error_reporting()) != 0) {
if (($error->errno & $this->_postpone_mask) != 0) {
$this->_postponed_errors[] = $error;
}
else {
$error->printXML();
}
}
$in_handler = false;
}
/**
* @access private
*/
function _die($error) {
$error->printXML();
PrintXML($this->_flush_errors());
if ($this->_fatal_handler)
$this->_fatal_handler->call($error);
exit -1;
}
/**
* @access private
*/
function _flush_errors($keep_mask = 0) {
$errors = &$this->_postponed_errors;
$flushed = HTML();
foreach ($errors as $key => $error) {
if (($error->errno & $keep_mask) != 0)
continue;
unset($errors[$key]);
$flushed->pushContent($error);
}
return $flushed;
}
}
/**
* Global error handler for class ErrorManager.
*
* This is necessary since PHP's set_error_handler() does not allow
* one to set an object method as a handler.
*
* @access private
*/
function ErrorManager_errorHandler($errno, $errstr, $errfile, $errline)
{
global $ErrorManager;
$error = new PhpError($errno, $errstr, $errfile, $errline);
$ErrorManager->handleError($error);
}
/**
* A class representing a PHP error report.
*
* @see The PHP documentation for set_error_handler at
* http://php.net/manual/en/function.set-error-handler.php .
*/
class PhpError {
/**
* The PHP errno
*/
var $errno;
/**
* The PHP error message.
*/
var $errstr;
/**
* The source file where the error occurred.
*/
var $errfile;
/**
* The line number (in $this->errfile) where the error occured.
*/
var $errline;
/**
* Construct a new PhpError.
* @param $errno int
* @param $errstr string
* @param $errfile string
* @param $errline int
*/
function PhpError($errno, $errstr, $errfile, $errline) {
$this->errno = $errno;
$this->errstr = $errstr;
$this->errfile = $errfile;
$this->errline = $errline;
}
/**
* Determine whether this is a fatal error.
* @return boolean True if this is a fatal error.
*/
function isFatal() {
return ($this->errno & (EM_WARNING_ERRORS|EM_NOTICE_ERRORS)) == 0;
}
/**
* Determine whether this is a warning level error.
* @return boolean
*/
function isWarning() {
return ($this->errno & EM_WARNING_ERRORS) != 0;
}
/**
* Determine whether this is a notice level error.
* @return boolean
*/
function isNotice() {
return ($this->errno & EM_NOTICE_ERRORS) != 0;
}
/**
* Get a printable, HTML, message detailing this error.
* @return object The detailed error message.
*/
function _getDetail() {
if ($this->isNotice())
$what = 'Notice';
else if ($this->isWarning())
$what = 'Warning';
else
$what = 'Fatal';
$errfile = ereg_replace('^' . getcwd() . '/', '', $this->errfile);
$lines = explode("\n", $this->errstr);
$msg = sprintf("%s:%d: %s[%d]: %s",
$errfile, $this->errline,
$what, $this->errno,
array_shift($lines));
$html = HTML::div(array('class' => 'error'), HTML::p($msg));
if ($lines) {
$list = HTML::ul();
foreach ($lines as $line)
$list->pushContent(HTML::li($line));
$html->pushContent($list);
}
return $html;
}
/**
* Print an HTMLified version of this error.
* @see asXML()
*/
function printXML() {
PrintXML($this->_getDetail());
}
/**
* Print an HTMLified version of this error.
*/
function asXML() {
return AsXML($this->_getDetail());
}
}
if (!isset($GLOBALS['ErrorManager'])) {
$GLOBALS['ErrorManager'] = new ErrorManager;
}
// (c-file-style: "gnu")
// Local Variables:
// mode: php
// tab-width: 8
// c-basic-offset: 4
// c-hanging-comment-ender-p: nil
// indent-tabs-mode: nil
// End:
?>