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