1 <?php rcs_id('$Id: ErrorManager.php,v 1.18 2004-02-08 06:42:58 rurban 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);
11 assert_options (ASSERT_CALLBACK, 'wiki_assert_handler');
13 function wiki_assert_handler ($file, $line, $code) {
14 ErrorManager_errorHandler( $code, sprintf("<br />%s:%s: %s: Assertion failed <br />", $file, $line, $code), $file, $line);
18 * A class which allows custom handling of PHP errors.
20 * This is a singleton class. There should only be one instance
21 * of it --- you can access the one instance via $GLOBALS['ErrorManager'].
30 * As this is a singleton class, you should never call this.
33 function ErrorManager() {
34 $this->_handlers = array();
35 $this->_fatal_handler = false;
36 $this->_postpone_mask = 0;
37 $this->_postponed_errors = array();
39 set_error_handler('ErrorManager_errorHandler');
43 * Get mask indicating which errors are currently being postponed.
45 * @return int The current postponed error mask.
47 function getPostponedErrorMask() {
48 return $this->_postpone_mask;
52 * Set mask indicating which errors to postpone.
54 * The default value of the postpone mask is zero (no errors postponed.)
56 * When you set this mask, any queue errors which do not match tne new
60 * @param $newmask int The new value for the mask.
62 function setPostponedErrorMask($newmask) {
63 $this->_postpone_mask = $newmask;
64 PrintXML($this->_flush_errors($newmask));
68 * Report any queued error messages.
71 function flushPostponedErrors() {
72 PrintXML($this->_flush_errors());
76 * Get postponed errors, formatted as HTML.
78 * This also flushes the postponed error queue.
80 * @return object HTML describing any queued errors (or false, if none).
82 function getPostponedErrorsAsHTML() {
83 $flushed = $this->_flush_errors();
84 if ($flushed->isEmpty())
86 $html = HTML::div(array('class' => 'errors'),
87 HTML::h4("PHP Warnings"));
88 $html->pushContent($flushed);
93 * Push a custom error handler on the handler stack.
95 * Sometimes one is performing an operation where one expects
96 * certain errors or warnings. In this case, one might not want
97 * these errors reported in the normal manner. Installing a custom
98 * error handler via this method allows one to intercept such
101 * An error handler installed via this method should be either a
102 * function or an object method taking one argument: a PhpError
105 * The error handler should return either:
107 * <dt> False <dd> If it has not handled the error. In this case,
108 * error processing will proceed as if the handler
109 * had never been called: the error will be passed
110 * to the next handler in the stack, or the
111 * default handler, if there are no more handlers
114 * <dt> True <dd> If the handler has handled the error. If the
115 * error was a non-fatal one, no further processing
116 * will be done. If it was a fatal error, the
117 * ErrorManager will still terminate the PHP
118 * process (see setFatalHandler.)
120 * <dt> A PhpError object <dd> The error is not considered
121 * handled, and will be passed on to
122 * the next handler(s) in the stack
123 * (or the default handler). The
124 * returned PhpError need not be the
125 * same as the one passed to the
126 * handler. This allows the handler to
127 * "adjust" the error message.
130 * @param $handler WikiCallback Handler to call.
132 function pushErrorHandler($handler) {
133 array_unshift($this->_handlers, $handler);
137 * Pop an error handler off the handler stack.
140 function popErrorHandler() {
141 return array_shift($this->_handlers);
145 * Set a termination handler.
147 * This handler will be called upon fatal errors. The handler
148 * gets passed one argument: a PhpError object describing the
152 * @param $handler WikiCallback Callback to call on fatal errors.
154 function setFatalHandler($handler) {
155 $this->_fatal_handler = $handler;
161 * The error is passed through any registered error handlers, and
162 * then either reported or postponed.
165 * @param $error object A PhpError object.
167 function handleError($error) {
170 if (!empty($in_handler)) {
171 $msg = $error->_getDetail();
172 $msg->unshiftContent(HTML::h2(fmt("%s: error while handling error:",
179 foreach ($this->_handlers as $handler) {
180 $result = $handler->call($error);
182 continue; // Handler did not handle error.
184 elseif (is_object($result)) {
185 // handler filtered the result. Still should pass to
186 // the rest of the chain.
187 if ($error->isFatal()) {
188 // Don't let handlers make fatal errors non-fatal.
189 $result->errno = $error->errno;
194 // Handler handled error.
195 if (!$error->isFatal()) {
203 // Error was either fatal, or was not handled by a handler.
204 // Handle it ourself.
205 if ($error->isFatal()) {
208 else if (($error->errno & error_reporting()) != 0) {
209 if (($error->errno & $this->_postpone_mask) != 0) {
210 $this->_postponed_errors[] = $error;
219 function warning($msg, $errno=E_USER_NOTICE) {
220 $this->handleError(new PhpWikiError($errno, $msg));
226 function _die($error) {
228 PrintXML($this->_flush_errors());
229 if ($this->_fatal_handler)
230 $this->_fatal_handler->call($error);
237 function _flush_errors($keep_mask = 0) {
238 $errors = &$this->_postponed_errors;
240 foreach ($errors as $key => $error) {
241 if (($error->errno & $keep_mask) != 0)
243 unset($errors[$key]);
244 $flushed->pushContent($error);
251 * Global error handler for class ErrorManager.
253 * This is necessary since PHP's set_error_handler() does not allow
254 * one to set an object method as a handler.
258 function ErrorManager_errorHandler($errno, $errstr, $errfile, $errline)
260 if (!isset($GLOBALS['ErrorManager'])) {
261 $GLOBALS['ErrorManager'] = new ErrorManager;
264 $error = new PhpError($errno, $errstr, $errfile, $errline);
265 $GLOBALS['ErrorManager']->handleError($error);
270 * A class representing a PHP error report.
272 * @see The PHP documentation for set_error_handler at
273 * http://php.net/manual/en/function.set-error-handler.php .
282 * The PHP error message.
287 * The source file where the error occurred.
292 * The line number (in $this->errfile) where the error occured.
297 * Construct a new PhpError.
299 * @param $errstr string
300 * @param $errfile string
301 * @param $errline int
303 function PhpError($errno, $errstr, $errfile, $errline) {
304 $this->errno = $errno;
305 $this->errstr = $errstr;
306 $this->errfile = $errfile;
307 $this->errline = $errline;
311 * Determine whether this is a fatal error.
312 * @return boolean True if this is a fatal error.
315 return ($this->errno & (EM_WARNING_ERRORS|EM_NOTICE_ERRORS)) == 0;
319 * Determine whether this is a warning level error.
322 function isWarning() {
323 return ($this->errno & EM_WARNING_ERRORS) != 0;
327 * Determine whether this is a notice level error.
330 function isNotice() {
331 return ($this->errno & EM_NOTICE_ERRORS) != 0;
335 * Get a printable, HTML, message detailing this error.
336 * @return object The detailed error message.
338 function _getDetail() {
339 if ($this->isNotice())
341 else if ($this->isWarning())
346 $errfile = ereg_replace('^' . getcwd() . '/', '', $this->errfile);
347 $lines = explode("\n", $this->errstr);
349 $msg = sprintf("%s:%d: %s[%d]: %s",
350 $errfile, $this->errline,
352 array_shift($lines));
354 $html = HTML::div(array('class' => 'error'), HTML::p($msg));
358 foreach ($lines as $line)
359 $list->pushContent(HTML::li($line));
360 $html->pushContent($list);
367 * Print an HTMLified version of this error.
370 function printXML() {
371 PrintXML($this->_getDetail());
375 * Return an HTMLified version of this error.
378 return AsXML($this->_getDetail());
382 * Return a plain-text version of this error.
384 function asString() {
385 return AsString($this->_getDetail());
390 * A class representing a PhpWiki warning.
392 * This is essentially the same as a PhpError, except that the
393 * error message is quieter: no source line, etc...
395 class PhpWikiError extends PhpError {
397 * Construct a new PhpError.
399 * @param $errstr string
401 function PhpWikiError($errno, $errstr) {
402 $this->PhpError($errno, $errstr, '?', '?');
405 function _getDetail() {
406 if ($this->isNotice())
408 else if ($this->isWarning())
413 return HTML::div(array('class' => 'error'), HTML::p("$what: $this->errstr"));
418 if (!isset($GLOBALS['ErrorManager'])) {
419 $GLOBALS['ErrorManager'] = new ErrorManager;
423 // (c-file-style: "gnu")
428 // c-hanging-comment-ender-p: nil
429 // indent-tabs-mode: nil