1 <?php rcs_id('$Id: ErrorManager.php,v 1.13 2002-01-28 18:49:08 dairiki Exp $');
3 require_once('lib/HtmlElement.php');
5 define ('EM_FATAL_ERRORS',
6 E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR);
7 define ('EM_WARNING_ERRORS',
8 E_WARNING | E_CORE_WARNING | E_COMPILE_WARNING | E_USER_WARNING);
9 define ('EM_NOTICE_ERRORS', E_NOTICE | E_USER_NOTICE);
13 * A class which allows custom handling of PHP errors.
15 * This is a singleton class. There should only be one instance
16 * of it --- you can access the one instance via $GLOBALS['ErrorManager'].
25 * As this is a singleton class, you should never call this.
28 function ErrorManager() {
29 $this->_handlers = array();
30 $this->_fatal_handler = false;
31 $this->_postpone_mask = 0;
32 $this->_postponed_errors = array();
34 set_error_handler('ErrorManager_errorHandler');
38 * Get mask indicating which errors are currently being postponed.
40 * @return int The current postponed error mask.
42 function getPostponedErrorMask() {
43 return $this->_postpone_mask;
47 * Set mask indicating which errors to postpone.
49 * The default value of the postpone mask is zero (no errors postponed.)
51 * When you set this mask, any queue errors which do not match tne new
55 * @param $newmask int The new value for the mask.
57 function setPostponedErrorMask($newmask) {
58 $this->_postpone_mask = $newmask;
59 PrintXML($this->_flush_errors($newmask));
63 * Report any queued error messages.
66 function flushPostponedErrors() {
67 PrintXML($this->_flush_errors());
71 * Get postponed errors, formatted as HTML.
73 * This also flushes the postponed error queue.
75 * @return object HTML describing any queued errors (or false, if none).
77 function getPostponedErrorsAsHTML() {
78 $flushed = $this->_flush_errors();
79 if ($flushed->isEmpty())
81 $html = HTML::div(array('class' => 'errors'),
82 HTML::h4("PHP Warnings"));
83 $html->pushContent($flushed);
88 * Push a custom error handler on the handler stack.
90 * Sometimes one is performing an operation where one expects
91 * certain errors or warnings. In this case, one might not want
92 * these errors reported in the normal manner. Installing a custom
93 * error handler via this method allows one to intercept such
96 * An error handler installed via this method should be either a
97 * function or an object method taking one argument: a PhpError
100 * The error handler should return either:
102 * <dt> False <dd> If it has not handled the error. In this case,
103 * error processing will proceed as if the handler
104 * had never been called: the error will be passed
105 * to the next handler in the stack, or the
106 * default handler, if there are no more handlers
109 * <dt> True <dd> If the handler has handled the error. If the
110 * error was a non-fatal one, no further processing
111 * will be done. If it was a fatal error, the
112 * ErrorManager will still terminate the PHP
113 * process (see setFatalHandler.)
115 * <dt> A PhpError object <dd> The error is not considered
116 * handled, and will be passed on to
117 * the next handler(s) in the stack
118 * (or the default handler). The
119 * returned PhpError need not be the
120 * same as the one passed to the
121 * handler. This allows the handler to
122 * "adjust" the error message.
125 * @param $handler WikiCallback Handler to call.
127 function pushErrorHandler($handler) {
128 array_unshift($this->_handlers, $handler);
132 * Pop an error handler off the handler stack.
135 function popErrorHandler() {
136 return array_shift($this->_handlers);
140 * Set a termination handler.
142 * This handler will be called upon fatal errors. The handler
143 * gets passed one argument: a PhpError object describing the
147 * @param $handler WikiCallback Callback to call on fatal errors.
149 function setFatalHandler($handler) {
150 $this->_fatal_handler = $handler;
156 * The error is passed through any registered error handlers, and
157 * then either reported or postponed.
160 * @param $error object A PhpError object.
162 function handleError($error) {
165 if (!empty($in_handler)) {
166 $msg = $error->_getDetail();
167 $msg->unshiftContent(HTML::h2(fmt("%s: error while handling error:",
174 foreach ($this->_handlers as $handler) {
175 $result = $handler->call($error);
177 continue; // Handler did not handle error.
179 elseif (is_object($result)) {
180 // handler filtered the result. Still should pass to
181 // the rest of the chain.
182 if ($error->isFatal()) {
183 // Don't let handlers make fatal errors non-fatal.
184 $result->errno = $error->errno;
189 // Handler handled error.
190 if (!$error->isFatal()) {
198 // Error was either fatal, or was not handled by a handler.
199 // Handle it ourself.
200 if ($error->isFatal()) {
203 else if (($error->errno & error_reporting()) != 0) {
204 if (($error->errno & $this->_postpone_mask) != 0) {
205 $this->_postponed_errors[] = $error;
217 function _die($error) {
219 PrintXML($this->_flush_errors());
220 if ($this->_fatal_handler)
221 $this->_fatal_handler->call($error);
228 function _flush_errors($keep_mask = 0) {
229 $errors = &$this->_postponed_errors;
231 foreach ($errors as $key => $error) {
232 if (($error->errno & $keep_mask) != 0)
234 unset($errors[$key]);
235 $flushed->pushContent($error);
242 * Global error handler for class ErrorManager.
244 * This is necessary since PHP's set_error_handler() does not allow
245 * one to set an object method as a handler.
249 function ErrorManager_errorHandler($errno, $errstr, $errfile, $errline)
251 global $ErrorManager;
252 $error = new PhpError($errno, $errstr, $errfile, $errline);
253 $ErrorManager->handleError($error);
258 * A class representing a PHP error report.
260 * @see The PHP documentation for set_error_handler at
261 * http://php.net/manual/en/function.set-error-handler.php .
270 * The PHP error message.
275 * The source file where the error occurred.
280 * The line number (in $this->errfile) where the error occured.
285 * Construct a new PhpError.
287 * @param $errstr string
288 * @param $errfile string
289 * @param $errline int
291 function PhpError($errno, $errstr, $errfile, $errline) {
292 $this->errno = $errno;
293 $this->errstr = $errstr;
294 $this->errfile = $errfile;
295 $this->errline = $errline;
299 * Determine whether this is a fatal error.
300 * @return boolean True if this is a fatal error.
303 return ($this->errno & (EM_WARNING_ERRORS|EM_NOTICE_ERRORS)) == 0;
307 * Determine whether this is a warning level error.
310 function isWarning() {
311 return ($this->errno & EM_WARNING_ERRORS) != 0;
315 * Determine whether this is a notice level error.
318 function isNotice() {
319 return ($this->errno & EM_NOTICE_ERRORS) != 0;
323 * Get a printable, HTML, message detailing this error.
324 * @return object The detailed error message.
326 function _getDetail() {
327 if ($this->isNotice())
329 else if ($this->isWarning())
334 $errfile = ereg_replace('^' . getcwd() . '/', '', $this->errfile);
335 $lines = explode("\n", $this->errstr);
337 $msg = sprintf("%s:%d: %s[%d]: %s",
338 $errfile, $this->errline,
340 array_shift($lines));
342 $html = HTML::div(array('class' => 'error'), HTML::p($msg));
346 foreach ($lines as $line)
347 $list->pushContent(HTML::li($line));
348 $html->pushContent($list);
355 * Print an HTMLified version of this error.
358 function printXML() {
359 PrintXML($this->_getDetail());
363 * Print an HTMLified version of this error.
366 return AsXML($this->_getDetail());
370 if (!isset($GLOBALS['ErrorManager'])) {
371 $GLOBALS['ErrorManager'] = new ErrorManager;
375 // (c-file-style: "gnu")
380 // c-hanging-comment-ender-p: nil
381 // indent-tabs-mode: nil