]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/ErrorManager.php
Rename functional for PearDB backend
[SourceForge/phpwiki.git] / lib / ErrorManager.php
1 <?php rcs_id('$Id: ErrorManager.php,v 1.18 2004-02-08 06:42:58 rurban Exp $');
2
3 require_once('lib/HtmlElement.php');
4
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);
10
11 assert_options (ASSERT_CALLBACK, 'wiki_assert_handler');
12
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);
15 }
16
17 /**
18  * A class which allows custom handling of PHP errors.
19  *
20  * This is a singleton class. There should only be one instance
21  * of it --- you can access the one instance via $GLOBALS['ErrorManager'].
22  *
23  * FIXME: more docs.
24  */ 
25 class ErrorManager 
26 {
27     /**
28      * Constructor.
29      *
30      * As this is a singleton class, you should never call this.
31      * @access private
32      */
33     function ErrorManager() {
34         $this->_handlers = array();
35         $this->_fatal_handler = false;
36         $this->_postpone_mask = 0;
37         $this->_postponed_errors = array();
38
39         set_error_handler('ErrorManager_errorHandler');
40     }
41
42     /**
43      * Get mask indicating which errors are currently being postponed.
44      * @access public
45      * @return int The current postponed error mask.
46      */
47     function getPostponedErrorMask() {
48         return $this->_postpone_mask;
49     }
50
51     /**
52      * Set mask indicating which errors to postpone.
53      *
54      * The default value of the postpone mask is zero (no errors postponed.)
55      *
56      * When you set this mask, any queue errors which do not match tne new
57      * mask are reported.
58      *
59      * @access public
60      * @param $newmask int The new value for the mask.
61      */
62     function setPostponedErrorMask($newmask) {
63         $this->_postpone_mask = $newmask;
64         PrintXML($this->_flush_errors($newmask));
65     }
66
67     /**
68      * Report any queued error messages.
69      * @access public
70      */
71     function flushPostponedErrors() {
72         PrintXML($this->_flush_errors());
73     }
74
75     /**
76      * Get postponed errors, formatted as HTML.
77      *
78      * This also flushes the postponed error queue.
79      *
80      * @return object HTML describing any queued errors (or false, if none). 
81      */
82     function getPostponedErrorsAsHTML() {
83         $flushed = $this->_flush_errors();
84         if ($flushed->isEmpty())
85             return false;
86         $html = HTML::div(array('class' => 'errors'),
87                           HTML::h4("PHP Warnings"));
88         $html->pushContent($flushed);
89         return $html;
90     }
91     
92     /**
93      * Push a custom error handler on the handler stack.
94      *
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
99      * errors.
100      *
101      * An error handler installed via this method should be either a
102      * function or an object method taking one argument: a PhpError
103      * object.
104      *
105      * The error handler should return either:
106      * <dl>
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
112      *                 in the stack.
113      *
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.)
119      *
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.
128      * </dl>
129      * @access public
130      * @param $handler WikiCallback  Handler to call.
131      */
132     function pushErrorHandler($handler) {
133         array_unshift($this->_handlers, $handler);
134     }
135
136     /**
137      * Pop an error handler off the handler stack.
138      * @access public
139      */
140     function popErrorHandler() {
141         return array_shift($this->_handlers);
142     }
143
144     /**
145      * Set a termination handler.
146      *
147      * This handler will be called upon fatal errors. The handler
148      * gets passed one argument: a PhpError object describing the
149      * fatal error.
150      *
151      * @access public
152      * @param $handler WikiCallback  Callback to call on fatal errors.
153      */
154     function setFatalHandler($handler) {
155         $this->_fatal_handler = $handler;
156     }
157
158     /**
159      * Handle an error.
160      *
161      * The error is passed through any registered error handlers, and
162      * then either reported or postponed.
163      *
164      * @access public
165      * @param $error object A PhpError object.
166      */
167     function handleError($error) {
168         static $in_handler;
169
170         if (!empty($in_handler)) {
171             $msg = $error->_getDetail();
172             $msg->unshiftContent(HTML::h2(fmt("%s: error while handling error:",
173                                               "ErrorManager")));
174             $msg->printXML();
175             return;
176         }
177         $in_handler = true;
178
179         foreach ($this->_handlers as $handler) {
180             $result = $handler->call($error);
181             if (!$result) {
182                 continue;       // Handler did not handle error.
183             }
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;
190                 }
191                 $error = $result;
192             }
193             else {
194                 // Handler handled error.
195                 if (!$error->isFatal()) {
196                     $in_handler = false;
197                     return;
198                 }
199                 break;
200             }
201         }
202
203         // Error was either fatal, or was not handled by a handler.
204         // Handle it ourself.
205         if ($error->isFatal()) {
206             $this->_die($error);
207         }
208         else if (($error->errno & error_reporting()) != 0) {
209             if  (($error->errno & $this->_postpone_mask) != 0) {
210                 $this->_postponed_errors[] = $error;
211             }
212             else {
213                 $error->printXML();
214             }
215         }
216         $in_handler = false;
217     }
218
219     function warning($msg, $errno=E_USER_NOTICE) {
220         $this->handleError(new PhpWikiError($errno, $msg));
221     }
222     
223     /**
224      * @access private
225      */
226     function _die($error) {
227         $error->printXML();
228         PrintXML($this->_flush_errors());
229         if ($this->_fatal_handler)
230             $this->_fatal_handler->call($error);
231         exit -1;
232     }
233
234     /**
235      * @access private
236      */
237     function _flush_errors($keep_mask = 0) {
238         $errors = &$this->_postponed_errors;
239         $flushed = HTML();
240         foreach ($errors as $key => $error) {
241             if (($error->errno & $keep_mask) != 0)
242                 continue;
243             unset($errors[$key]);
244             $flushed->pushContent($error);
245         }
246         return $flushed;
247     }
248 }
249
250 /**
251  * Global error handler for class ErrorManager.
252  *
253  * This is necessary since PHP's set_error_handler() does not allow
254  * one to set an object method as a handler.
255  * 
256  * @access private
257  */
258 function ErrorManager_errorHandler($errno, $errstr, $errfile, $errline) 
259 {
260     if (!isset($GLOBALS['ErrorManager'])) {
261       $GLOBALS['ErrorManager'] = new ErrorManager;
262     }
263         
264     $error = new PhpError($errno, $errstr, $errfile, $errline);
265     $GLOBALS['ErrorManager']->handleError($error);
266 }
267
268
269 /**
270  * A class representing a PHP error report.
271  *
272  * @see The PHP documentation for set_error_handler at
273  *      http://php.net/manual/en/function.set-error-handler.php .
274  */
275 class PhpError {
276     /**
277      * The PHP errno
278      */
279     var $errno;
280
281     /**
282      * The PHP error message.
283      */
284     var $errstr;
285
286     /**
287      * The source file where the error occurred.
288      */
289     var $errfile;
290
291     /**
292      * The line number (in $this->errfile) where the error occured.
293      */
294     var $errline;
295
296     /**
297      * Construct a new PhpError.
298      * @param $errno   int
299      * @param $errstr  string
300      * @param $errfile string
301      * @param $errline int
302      */
303     function PhpError($errno, $errstr, $errfile, $errline) {
304         $this->errno   = $errno;
305         $this->errstr  = $errstr;
306         $this->errfile = $errfile;
307         $this->errline = $errline;
308     }
309
310     /**
311      * Determine whether this is a fatal error.
312      * @return boolean True if this is a fatal error.
313      */
314     function isFatal() {
315         return ($this->errno & (EM_WARNING_ERRORS|EM_NOTICE_ERRORS)) == 0;
316     }
317
318     /**
319      * Determine whether this is a warning level error.
320      * @return boolean
321      */
322     function isWarning() {
323         return ($this->errno & EM_WARNING_ERRORS) != 0;
324     }
325
326     /**
327      * Determine whether this is a notice level error.
328      * @return boolean
329      */
330     function isNotice() {
331         return ($this->errno & EM_NOTICE_ERRORS) != 0;
332     }
333
334     /**
335      * Get a printable, HTML, message detailing this error.
336      * @return object The detailed error message.
337      */
338     function _getDetail() {
339         if ($this->isNotice())
340             $what = 'Notice';
341         else if ($this->isWarning())
342             $what = 'Warning';
343         else
344             $what = 'Fatal';
345
346         $errfile = ereg_replace('^' . getcwd() . '/', '', $this->errfile);
347         $lines = explode("\n", $this->errstr);
348
349         $msg = sprintf("%s:%d: %s[%d]: %s",
350                        $errfile, $this->errline,
351                        $what, $this->errno,
352                        array_shift($lines));
353         
354         $html = HTML::div(array('class' => 'error'), HTML::p($msg));
355         
356         if ($lines) {
357             $list = HTML::ul();
358             foreach ($lines as $line)
359                 $list->pushContent(HTML::li($line));
360             $html->pushContent($list);
361         }
362         
363         return $html;
364     }
365
366     /**
367      * Print an HTMLified version of this error.
368      * @see asXML()
369      */
370     function printXML() {
371         PrintXML($this->_getDetail());
372     }
373
374     /**
375      * Return an HTMLified version of this error.
376      */
377     function asXML() {
378         return AsXML($this->_getDetail());
379     }
380
381     /**
382      * Return a plain-text version of this error.
383      */
384     function asString() {
385         return AsString($this->_getDetail());
386     }
387 }
388
389 /**
390  * A class representing a PhpWiki warning.
391  *
392  * This is essentially the same as a PhpError, except that the
393  * error message is quieter: no source line, etc...
394  */
395 class PhpWikiError extends PhpError {
396     /**
397      * Construct a new PhpError.
398      * @param $errno   int
399      * @param $errstr  string
400      */
401     function PhpWikiError($errno, $errstr) {
402         $this->PhpError($errno, $errstr, '?', '?');
403     }
404
405     function _getDetail() {
406         if ($this->isNotice())
407             $what = 'Notice';
408         else if ($this->isWarning())
409             $what = 'Warning';
410         else
411             $what = 'Fatal';
412
413         return HTML::div(array('class' => 'error'), HTML::p("$what: $this->errstr"));
414     }
415 }
416
417
418 if (!isset($GLOBALS['ErrorManager'])) {
419     $GLOBALS['ErrorManager'] = new ErrorManager;
420 }
421
422
423 // (c-file-style: "gnu")
424 // Local Variables:
425 // mode: php
426 // tab-width: 8
427 // c-basic-offset: 4
428 // c-hanging-comment-ender-p: nil
429 // indent-tabs-mode: nil
430 // End:
431 ?>