1 <?php rcs_id('$Id: ErrorManager.php,v 1.22 2004-05-12 10:49:54 rurban Exp $');
3 require_once(dirname(__FILE__).'/HtmlElement.php');
4 if (isset($GLOBALS['ErrorManager'])) return;
6 define ('EM_FATAL_ERRORS',
7 E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR);
8 define ('EM_WARNING_ERRORS',
9 E_WARNING | E_CORE_WARNING | E_COMPILE_WARNING | E_USER_WARNING);
10 define ('EM_NOTICE_ERRORS', E_NOTICE | E_USER_NOTICE);
12 assert_options (ASSERT_CALLBACK, 'wiki_assert_handler');
14 function wiki_assert_handler ($file, $line, $code) {
15 ErrorManager_errorHandler( $code, sprintf("<br />%s:%s: %s: Assertion failed <br />", $file, $line, $code), $file, $line);
19 * A class which allows custom handling of PHP errors.
21 * This is a singleton class. There should only be one instance
22 * of it --- you can access the one instance via $GLOBALS['ErrorManager'].
31 * As this is a singleton class, you should never call this.
34 function ErrorManager() {
35 $this->_handlers = array();
36 $this->_fatal_handler = false;
37 $this->_postpone_mask = 0;
38 $this->_postponed_errors = array();
40 set_error_handler('ErrorManager_errorHandler');
44 * Get mask indicating which errors are currently being postponed.
46 * @return int The current postponed error mask.
48 function getPostponedErrorMask() {
49 return $this->_postpone_mask;
53 * Set mask indicating which errors to postpone.
55 * The default value of the postpone mask is zero (no errors postponed.)
57 * When you set this mask, any queue errors which do not match the new
61 * @param $newmask int The new value for the mask.
63 function setPostponedErrorMask($newmask) {
64 $this->_postpone_mask = $newmask;
65 if (function_exists('PrintXML'))
66 PrintXML($this->_flush_errors($newmask));
68 echo($this->_flush_errors($newmask));
73 * Report any queued error messages.
76 function flushPostponedErrors() {
77 if (function_exists('PrintXML'))
78 PrintXML($this->_flush_errors());
80 echo $this->_flush_errors();
84 * Get postponed errors, formatted as HTML.
86 * This also flushes the postponed error queue.
88 * @return object HTML describing any queued errors (or false, if none).
90 function getPostponedErrorsAsHTML() {
91 $flushed = $this->_flush_errors();
92 if ($flushed->isEmpty())
94 $html = HTML::div(array('class' => 'errors'),
95 HTML::h4("PHP Warnings"));
96 $html->pushContent($flushed);
101 * Push a custom error handler on the handler stack.
103 * Sometimes one is performing an operation where one expects
104 * certain errors or warnings. In this case, one might not want
105 * these errors reported in the normal manner. Installing a custom
106 * error handler via this method allows one to intercept such
109 * An error handler installed via this method should be either a
110 * function or an object method taking one argument: a PhpError
113 * The error handler should return either:
115 * <dt> False <dd> If it has not handled the error. In this case,
116 * error processing will proceed as if the handler
117 * had never been called: the error will be passed
118 * to the next handler in the stack, or the
119 * default handler, if there are no more handlers
122 * <dt> True <dd> If the handler has handled the error. If the
123 * error was a non-fatal one, no further processing
124 * will be done. If it was a fatal error, the
125 * ErrorManager will still terminate the PHP
126 * process (see setFatalHandler.)
128 * <dt> A PhpError object <dd> The error is not considered
129 * handled, and will be passed on to
130 * the next handler(s) in the stack
131 * (or the default handler). The
132 * returned PhpError need not be the
133 * same as the one passed to the
134 * handler. This allows the handler to
135 * "adjust" the error message.
138 * @param $handler WikiCallback Handler to call.
140 function pushErrorHandler($handler) {
141 array_unshift($this->_handlers, $handler);
145 * Pop an error handler off the handler stack.
148 function popErrorHandler() {
149 return array_shift($this->_handlers);
153 * Set a termination handler.
155 * This handler will be called upon fatal errors. The handler
156 * gets passed one argument: a PhpError object describing the
160 * @param $handler WikiCallback Callback to call on fatal errors.
162 function setFatalHandler($handler) {
163 $this->_fatal_handler = $handler;
169 * The error is passed through any registered error handlers, and
170 * then either reported or postponed.
173 * @param $error object A PhpError object.
175 function handleError($error) {
178 if (!empty($in_handler)) {
179 $msg = $error->_getDetail();
180 $msg->unshiftContent(HTML::h2(fmt("%s: error while handling error:",
187 foreach ($this->_handlers as $handler) {
188 $result = $handler->call($error);
190 continue; // Handler did not handle error.
192 elseif (is_object($result)) {
193 // handler filtered the result. Still should pass to
194 // the rest of the chain.
195 if ($error->isFatal()) {
196 // Don't let handlers make fatal errors non-fatal.
197 $result->errno = $error->errno;
202 // Handler handled error.
203 if (!$error->isFatal()) {
211 // Error was either fatal, or was not handled by a handler.
212 // Handle it ourself.
213 if ($error->isFatal()) {
216 else if (($error->errno & error_reporting()) != 0) {
217 if (($error->errno & $this->_postpone_mask) != 0) {
218 $this->_postponed_errors[] = $error;
227 function warning($msg, $errno=E_USER_NOTICE) {
228 $this->handleError(new PhpWikiError($errno, $msg));
234 function _die($error) {
236 PrintXML($this->_flush_errors());
237 if ($this->_fatal_handler)
238 $this->_fatal_handler->call($error);
245 function _flush_errors($keep_mask = 0) {
246 $errors = &$this->_postponed_errors;
248 foreach ($errors as $key => $error) {
249 if (($error->errno & $keep_mask) != 0)
251 unset($errors[$key]);
252 $flushed->pushContent($error);
259 * Global error handler for class ErrorManager.
261 * This is necessary since PHP's set_error_handler() does not allow
262 * one to set an object method as a handler.
266 function ErrorManager_errorHandler($errno, $errstr, $errfile, $errline)
268 if (!isset($GLOBALS['ErrorManager'])) {
269 $GLOBALS['ErrorManager'] = new ErrorManager;
272 $error = new PhpError($errno, $errstr, $errfile, $errline);
273 $GLOBALS['ErrorManager']->handleError($error);
278 * A class representing a PHP error report.
280 * @see The PHP documentation for set_error_handler at
281 * http://php.net/manual/en/function.set-error-handler.php .
290 * The PHP error message.
295 * The source file where the error occurred.
300 * The line number (in $this->errfile) where the error occured.
305 * Construct a new PhpError.
307 * @param $errstr string
308 * @param $errfile string
309 * @param $errline int
311 function PhpError($errno, $errstr, $errfile, $errline) {
312 $this->errno = $errno;
313 $this->errstr = $errstr;
314 $this->errfile = $errfile;
315 $this->errline = $errline;
319 * Determine whether this is a fatal error.
320 * @return boolean True if this is a fatal error.
323 return ($this->errno & (EM_WARNING_ERRORS|EM_NOTICE_ERRORS)) == 0;
327 * Determine whether this is a warning level error.
330 function isWarning() {
331 return ($this->errno & EM_WARNING_ERRORS) != 0;
335 * Determine whether this is a notice level error.
338 function isNotice() {
339 return ($this->errno & EM_NOTICE_ERRORS) != 0;
343 * Get a printable, HTML, message detailing this error.
344 * @return object The detailed error message.
346 function _getDetail() {
347 if ($this->isNotice())
349 else if ($this->isWarning())
354 $errfile = ereg_replace('^' . getcwd() . '/', '', $this->errfile);
355 $lines = explode("\n", $this->errstr);
357 $msg = sprintf("%s:%d: %s[%d]: %s",
358 $errfile, $this->errline,
360 array_shift($lines));
362 $html = HTML::div(array('class' => 'error'), HTML::p($msg));
366 foreach ($lines as $line)
367 $list->pushContent(HTML::li($line));
368 $html->pushContent($list);
375 * Print an HTMLified version of this error.
378 function printXML() {
379 PrintXML($this->_getDetail());
383 * Return an HTMLified version of this error.
386 return AsXML($this->_getDetail());
390 * Return a plain-text version of this error.
392 function asString() {
393 return AsString($this->_getDetail());
398 * A class representing a PhpWiki warning.
400 * This is essentially the same as a PhpError, except that the
401 * error message is quieter: no source line, etc...
403 class PhpWikiError extends PhpError {
405 * Construct a new PhpError.
407 * @param $errstr string
409 function PhpWikiError($errno, $errstr) {
410 $this->PhpError($errno, $errstr, '?', '?');
413 function _getDetail() {
414 if ($this->isNotice())
416 else if ($this->isWarning())
421 return HTML::div(array('class' => 'error'), HTML::p("$what: $this->errstr"));
426 if (!isset($GLOBALS['ErrorManager'])) {
427 $GLOBALS['ErrorManager'] = new ErrorManager;
431 // (c-file-style: "gnu")
436 // c-hanging-comment-ender-p: nil
437 // indent-tabs-mode: nil