]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/ErrorManager.php
Removed htmlspecialchars to allow urls in error messages (see text2png plugin for...
[SourceForge/phpwiki.git] / lib / ErrorManager.php
1 <?php rcs_id('$Id: ErrorManager.php,v 1.7 2002-01-10 04:03:12 carstenklapp Exp $');
2
3
4 define ('EM_FATAL_ERRORS',
5         E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR);
6 define ('EM_WARNING_ERRORS',
7         E_WARNING | E_CORE_WARNING | E_COMPILE_WARNING | E_USER_WARNING);
8 define ('EM_NOTICE_ERRORS', E_NOTICE | E_USER_NOTICE);
9
10
11 /**
12  * A class which allows custom handling of PHP errors.
13  *
14  * This is a singleton class. There should only be one instance
15  * of it --- you can access the one instance via $GLOBALS['ErrorManager'].
16  *
17  * FIXME: more docs.
18  */ 
19 class ErrorManager 
20 {
21     /**
22      * Constructor.
23      *
24      * As this is a singleton class, you should never call this.
25      * @access private
26      */
27     function ErrorManager() {
28         $this->_handlers = array();
29         $this->_fatal_handler = false;
30         $this->_postpone_mask = 0;
31         $this->_postponed_errors = array();
32
33         set_error_handler('ErrorManager_errorHandler');
34     }
35
36     /**
37      * Get mask indicating which errors are currently being postponed.
38      * @access public
39      * @return int The current postponed error mask.
40      */
41     function getPostponedErrorMask() {
42         return $this->_postpone_mask;
43     }
44
45     /**
46      * Set mask indicating which errors to postpone.
47      *
48      * The default value of the postpone mask is zero (no errors postponed.)
49      *
50      * When you set this mask, any queue errors which do not match tne new
51      * mask are reported.
52      *
53      * @access public
54      * @param $newmask int The new value for the mask.
55      */
56     function setPostponedErrorMask($newmask) {
57         $this->_postpone_mask = $newmask;
58         $this->_flush_errors($newmask);
59     }
60
61     /**
62      * Report any queued error messages.
63      * @access public
64      */
65     function flushPostponedErrors() {
66         $this->_flush_errors();
67     }
68
69     /**
70      * Push a custom error handler on the handler stack.
71      *
72      * Sometimes one is performing an operation where one expects
73      * certain errors or warnings. In this case, one might not want
74      * these errors reported in the normal manner. Installing a custom
75      * error handler via this method allows one to intercept such
76      * errors.
77      *
78      * An error handler installed via this method should be either a
79      * function or an object method taking one argument: a PhpError
80      * object.
81      *
82      * The error handler should return either:
83      * <dl>
84      * <dt> False <dd> If it has not handled the error. In this case,
85      *                 error processing will proceed as if the handler
86      *                 had never been called: the error will be passed
87      *                 to the next handler in the stack, or the
88      *                 default handler, if there are no more handlers
89      *                 in the stack.
90      *
91      * <dt> True <dd> If the handler has handled the error. If the
92      *                error was a non-fatal one, no further processing
93      *                will be done. If it was a fatal error, the
94      *                ErrorManager will still terminate the PHP
95      *                process (see setFatalHandler.)
96      *
97      * <dt> A PhpError object <dd> The error is not considered
98      *                             handled, and will be passed on to
99      *                             the next handler(s) in the stack
100      *                             (or the default handler). The
101      *                             returned PhpError need not be the
102      *                             same as the one passed to the
103      *                             handler. This allows the handler to
104      *                             "adjust" the error message.
105      * </dl>
106      * @access public
107      * @param $handler WikiCallback  Handler to call.
108      */
109     function pushErrorHandler($handler) {
110         array_unshift($this->_handlers, $handler);
111     }
112
113     /**
114      * Pop an error handler off the handler stack.
115      * @access public
116      */
117     function popErrorHandler() {
118         return array_shift($this->_handlers);
119     }
120
121     /**
122      * Set a termination handler.
123      *
124      * This handler will be called upon fatal errors. The handler
125      * gets passed one argument: a PhpError object describing the
126      * fatal error.
127      *
128      * @access public
129      * @param $handler WikiCallback  Callback to call on fatal errors.
130      */
131     function setFatalHandler($handler) {
132         $this->_fatal_handler = $handler;
133     }
134
135     /**
136      * Handle an error.
137      *
138      * The error is passed through any registered error handlers, and
139      * then either reported or postponed.
140      *
141      * @access public
142      * @param $error object A PhpError object.
143      */
144     function handleError($error) {
145         static $in_handler;
146
147         if (!empty($in_handler)) {
148             echo "<p>ErrorManager: "._("error while handling error:")."</p>\n";
149             echo $error->printError();
150             return;
151         }
152         $in_handler = true;
153
154         foreach ($this->_handlers as $handler) {
155             $result = $handler->call($error);
156             if (!$result) {
157                 continue;       // Handler did not handle error.
158             }
159             elseif (is_object($result)) {
160                 // handler filtered the result. Still should pass to
161                 // the rest of the chain.
162                 if ($error->isFatal()) {
163                     // Don't let handlers make fatal errors non-fatal.
164                     $result->errno = $error->errno;
165                 }
166                 $error = $result;
167             }
168             else {
169                 // Handler handled error.
170                 if (!$error->isFatal()) {
171                     $in_handler = false;
172                     return;
173                 }
174                 break;
175             }
176         }
177
178         // Error was either fatal, or was not handled by a handler.
179         // Handle it ourself.
180         if ($error->isFatal()) {
181             $this->_die($error);
182         }
183         else if (($error->errno & error_reporting()) != 0) {
184             if  (($error->errno & $this->_postpone_mask) != 0) {
185                 $this->_postponed_errors[] = $error;
186             }
187             else {
188                 $error->printError();
189             }
190         }
191         $in_handler = false;
192     }
193
194     /**
195      * @access private
196      */
197     function _die($error) {
198         $error->printError();
199         $this->_flush_errors();
200         if ($this->_fatal_handler)
201             $this->_fatal_handler->call($error);
202         exit -1;
203     }
204
205     /**
206      * @access private
207      */
208     function _flush_errors($keep_mask = 0) {
209         $errors = &$this->_postponed_errors;
210         foreach ($errors as $key => $error) {
211             if (($error->errno & $keep_mask) != 0)
212                 continue;
213             unset($errors[$key]);
214             $error->printError();
215         }
216     }
217 }
218
219 /**
220  * Global error handler for class ErrorManager.
221  *
222  * This is necessary since PHP's set_error_handler() does not allow
223  * one to set an object method as a handler.
224  * 
225  * @access private
226  */
227 function ErrorManager_errorHandler($errno, $errstr, $errfile, $errline) 
228 {
229     global $ErrorManager;
230     $error = new PhpError($errno, $errstr, $errfile, $errline);
231     $ErrorManager->handleError($error);
232 }
233
234
235 /**
236  * A class representing a PHP error report.
237  *
238  * @see The PHP documentation for set_error_handler at
239  *      http://php.net/manual/en/function.set-error-handler.php .
240  */
241 class PhpError {
242     /**
243      * The PHP errno
244      */
245     var $errno;
246
247     /**
248      * The PHP error message.
249      */
250     var $errstr;
251
252     /**
253      * The source file where the error occurred.
254      */
255     var $errfile;
256
257     /**
258      * The line number (in $this->errfile) where the error occured.
259      */
260     var $errline;
261
262     /**
263      * Construct a new PhpError.
264      * @param $errno   int
265      * @param $errstr  string
266      * @param $errfile string
267      * @param $errline int
268      */
269     function PhpError($errno, $errstr, $errfile, $errline) {
270         $this->errno   = $errno;
271         $this->errstr  = $errstr;
272         $this->errfile = $errfile;
273         $this->errline = $errline;
274     }
275
276     /**
277      * Determine whether this is a fatal error.
278      * @return boolean True if this is a fatal error.
279      */
280     function isFatal() {
281         return ($this->errno & (EM_WARNING_ERRORS|EM_NOTICE_ERRORS)) == 0;
282     }
283
284     /**
285      * Determine whether this is a warning level error.
286      * @return boolean
287      */
288     function isWarning() {
289         return ($this->errno & EM_WARNING_ERRORS) != 0;
290     }
291
292     /**
293      * Determine whether this is a notice level error.
294      * @return boolean
295      */
296     function isNotice() {
297         return ($this->errno & EM_NOTICE_ERRORS) != 0;
298     }
299
300     /**
301      * Get a printable, HTML, message detailing this error.
302      * @return string The detailed error message.
303      */
304     function getDetail() {
305         if ($this->isNotice())
306             $what = 'Notice';
307         else if ($this->isWarning())
308             $what = 'Warning';
309         else
310             $what = 'Fatal';
311
312         $errfile = ereg_replace('^' . getcwd() . '/', '', $this->errfile);
313
314         $lines = explode("\n", $this->errstr);
315         //$errstr = htmlspecialchars(array_shift($lines));
316         // Quick hack: by removing htmlspecialchars urls can be used
317         // in error messages. How will this affect other error
318         // messages?
319         $errstr = array_shift($lines);
320         foreach ($lines as $key => $line)
321             $lines[$key] = "<li>" . htmlspecialchars($line) . "</li>";
322         if ($lines)
323             $errstr .= "<ul>\n" . join("\n", $lines) . "\n</ul>";
324         
325         return sprintf("<p class='error'>%s:%d: %s[%d]: %s</p>\n",
326                        htmlspecialchars($errfile),
327                        $this->errline, $what, $this->errno,
328                        $errstr);
329     }
330
331     /**
332      * Print an HTMLified version of this error.
333      * @see getDetail
334      */
335     function printError() {
336         echo $this->getDetail();
337     }
338 }
339
340 if (!isset($GLOBALS['ErrorManager'])) {
341     $GLOBALS['ErrorManager'] = new ErrorManager;
342 }
343
344 // (c-file-style: "gnu")
345 // Local Variables:
346 // mode: php
347 // tab-width: 8
348 // c-basic-offset: 4
349 // c-hanging-comment-ender-p: nil
350 // indent-tabs-mode: nil
351 // End:
352 ?>