]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/ErrorManager.php
Found more strings needing gettext _() and sprintf. Error strings containing function...
[SourceForge/phpwiki.git] / lib / ErrorManager.php
1 <?php rcs_id('$Id: ErrorManager.php,v 1.5 2001-12-28 09:53:06 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 certain errors
73      * or warnings.  In this case, one might not want these errors reported
74      * in the normal manner.  Installing a custom error handler via this method
75      * allows one to intercept such errors.
76      *
77      * An error handler installed via this method should be either a
78      * function or an object method taking one argument: a PhpError object.
79      *
80      * The error handler should return either:
81      * <dl>
82      * <dt> False <dd> If it has not handled the error.  In this case, error
83      *       processing will proceed as if the handler had never been called:
84      *       the error will be passed to the next handler in the stack,
85      *       or the default handler, if there are no more handlers in the stack.
86      * <dt> True  <dd> If the handler has handled the error.  If the error was
87      *        a non-fatal one, no further processing will be done.
88      *        If it was a fatal error, the ErrorManager will still
89      *        terminate the PHP process (see setFatalHandler.)
90      * <dt> A PhpError object
91      *   <dd> The error is not considered
92      *        handled, and will be passed on to the next handler(s) in the stack
93      *        (or the default handler).
94      *        The returned PhpError need not be the same as the one passed to the
95      *        handler.  This allows the handler to "adjust" the error message.
96      *
97      * @access public
98      * @param $handler WikiCallback  Handler to call.
99      */
100     function pushErrorHandler($handler) {
101         array_unshift($this->_handlers, $handler);
102     }
103
104     /**
105      * Pop an error handler off the handler stack.
106      * @access public
107      */
108     function popErrorHandler() {
109         return array_shift($this->_handlers);
110     }
111
112     /**
113      * Set a termination handler.
114      *
115      * This handler will be called upon fatal errors.  The handler gets passed
116      * one argument: a PhpError object describing the fatal error.
117      *
118      * @access public
119      * @param $handler WikiCallback  Callback to call on fatal errors.
120      */
121     function setFatalHandler($handler) {
122         $this->_fatal_handler = $handler;
123     }
124
125     /**
126      * Handle an error.
127      *
128      * The error is passed through any registered error handlers,
129      * and then either reported or postponed.
130      *
131      * @access public
132      * @param $error object A PhpError object.
133      */
134     function handleError($error) {
135         static $in_handler;
136         
137         if (!empty($in_handler)) {
138             echo "<p>ErrorManager: "._("error while handling error:")."</p>\n";
139             echo $error->printError();
140             return;
141         }
142         $in_handler = true;
143         
144         foreach ($this->_handlers as $handler) {
145             $result = $handler->call($error);
146             if (!$result) {
147                 continue;       // Handler did not handle error.
148             }
149             elseif (is_object($result)) {
150                 // handler filtered the result.  Still should pass to the
151                 // rest of the chain.
152                 if ($error->isFatal()) {
153                     // Don't let handlers make fatal errors non-fatal.
154                     $result->errno = $error->errno;
155                 }
156                 $error = $result;
157             }
158             else {
159                 // Handler handled error.
160                 if (!$error->isFatal()) {
161                     $in_handler = false;
162                     return;
163                 }
164                 break;
165             }
166         }
167
168         // Error was either fatal, or was not handled by a handler.
169         // Handle it ourself.
170         if ($error->isFatal()) {
171             $this->_die($error);
172         }
173         else if (($error->errno & error_reporting()) != 0) {
174             if (($error->errno & $this->_postpone_mask) != 0) {
175                 $this->_postponed_errors[] = $error;
176             }
177             else {
178                 $error->printError();
179             }
180         }
181         $in_handler = false;
182     }
183
184     /**
185      * @access private
186      */
187     function _die($error) {
188         $error->printError();
189         $this->_flush_errors();
190         if ($this->_fatal_handler)
191             $this->_fatal_handler->call($error);
192         exit -1;
193     }
194         
195     /**
196      * @access private
197      */
198     function _flush_errors($keep_mask = 0) {
199         $errors = &$this->_postponed_errors;
200         foreach ($errors as $key => $error) {
201             if (($error->errno & $keep_mask) != 0)
202                 continue;
203             unset($errors[$key]);
204             $error->printError();
205         }
206     }
207 }
208
209 /**
210  * Global error handler for class ErrorManager.
211  *
212  * This is necessary since PHP's set_error_handler() does not allow one to
213  * set an object method as a handler.
214  * 
215  * @access private
216  */
217 function ErrorManager_errorHandler($errno, $errstr, $errfile, $errline) 
218 {
219     global $ErrorManager;
220     $error = new PhpError($errno, $errstr, $errfile, $errline);
221     $ErrorManager->handleError($error);
222 }
223
224
225 /**
226  * A class representing a PHP error report.
227  *
228  * @see The PHP documentation for set_error_handler at
229  *      http://php.net/manual/en/function.set-error-handler.php .
230  */
231 class PhpError {
232     /**
233      * The PHP errno
234      */
235     var $errno;
236
237     /**
238      * The PHP error message.
239      */
240     var $errstr;
241
242     /**
243      * The source file where the error occurred.
244      */
245     var $errfile;
246
247     /**
248      * The line number (in $this->errfile) where the error occured.
249      */
250     var $errline;
251
252     /**
253      * Construct a new PhpError.
254      * @param $errno int
255      * @param $errstr string
256      * @param $errfile string
257      * @param $errline int
258      */
259     function PhpError($errno, $errstr, $errfile, $errline) {
260         $this->errno = $errno;
261         $this->errstr = $errstr;
262         $this->errfile = $errfile;
263         $this->errline = $errline;
264     }
265
266     /**
267      * Determine whether this is a fatal error.
268      * @return boolean True if this is a fatal error.
269      */
270     function isFatal() {
271         return ($this->errno & (EM_WARNING_ERRORS|EM_NOTICE_ERRORS)) == 0;
272     }
273
274     /**
275      * Determine whether this is a warning level error.
276      * @return boolean
277      */
278     function isWarning() {
279         return ($this->errno & EM_WARNING_ERRORS) != 0;
280     }
281
282     /**
283      * Determine whether this is a notice level error.
284      * @return boolean
285      */
286     function isNotice() {
287         return ($this->errno & EM_NOTICE_ERRORS) != 0;
288     }
289
290     /**
291      * Get a printable, HTML, message detailing this error.
292      * @return string The detailed error message.
293      */
294     function getDetail() {
295         if ($this->isNotice())
296             $what = 'Notice';
297         else if ($this->isWarning())
298             $what = 'Warning';
299         else
300             $what = 'Fatal';
301
302         $errfile = ereg_replace('^' . getcwd() . '/', '', $this->errfile);
303
304         $lines = explode("\n", $this->errstr);
305         $errstr = htmlspecialchars(array_shift($lines));
306         foreach ($lines as $key => $line)
307             $lines[$key] = "<li>" . htmlspecialchars($line) . "</li>";
308         if ($lines)
309             $errstr .= "<ul>\n" . join("\n", $lines) . "\n</ul>";
310         
311         return sprintf("<p class='error'>%s:%d: %s[%d]: %s</p>\n",
312                        htmlspecialchars($errfile),
313                        $this->errline, $what, $this->errno,
314                        $errstr);
315     }
316
317     /**
318      * Print an HTMLified version of this error.
319      * @see getDetail
320      */
321     function printError() {
322         echo $this->getDetail();
323     }
324 }
325
326 if (!isset($GLOBALS['ErrorManager'])) {
327     $GLOBALS['ErrorManager'] = new ErrorManager;
328 }
329
330 // (c-file-style: "gnu")
331 // Local Variables:
332 // mode: php
333 // tab-width: 8
334 // c-basic-offset: 4
335 // c-hanging-comment-ender-p: nil
336 // indent-tabs-mode: nil
337 // End:   
338 ?>