1 | <?php |
---|
2 | /** |
---|
3 | * Error Stack Implementation |
---|
4 | * |
---|
5 | * This is an incredibly simple implementation of a very complex error handling |
---|
6 | * facility. It contains the ability |
---|
7 | * to track multiple errors from multiple packages simultaneously. In addition, |
---|
8 | * it can track errors of many levels, save data along with the error, context |
---|
9 | * information such as the exact file, line number, class and function that |
---|
10 | * generated the error, and if necessary, it can raise a traditional PEAR_Error. |
---|
11 | * It has built-in support for PEAR::Log, to log errors as they occur |
---|
12 | * |
---|
13 | * Since version 0.2alpha, it is also possible to selectively ignore errors, |
---|
14 | * through the use of an error callback, see {@link pushCallback()} |
---|
15 | * |
---|
16 | * Since version 0.3alpha, it is possible to specify the exception class |
---|
17 | * returned from {@link push()} |
---|
18 | * |
---|
19 | * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can |
---|
20 | * still be done quite handily in an error callback or by manipulating the returned array |
---|
21 | * @category Debugging |
---|
22 | * @package PEAR_ErrorStack |
---|
23 | * @author Greg Beaver <cellog@php.net> |
---|
24 | * @copyright 2004-2008 Greg Beaver |
---|
25 | * @license http://opensource.org/licenses/bsd-license.php New BSD License |
---|
26 | * @version CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $ |
---|
27 | * @link http://pear.php.net/package/PEAR_ErrorStack |
---|
28 | */ |
---|
29 | |
---|
30 | /** |
---|
31 | * Singleton storage |
---|
32 | * |
---|
33 | * Format: |
---|
34 | * <pre> |
---|
35 | * array( |
---|
36 | * 'package1' => PEAR_ErrorStack object, |
---|
37 | * 'package2' => PEAR_ErrorStack object, |
---|
38 | * ... |
---|
39 | * ) |
---|
40 | * </pre> |
---|
41 | * @access private |
---|
42 | * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] |
---|
43 | */ |
---|
44 | $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array(); |
---|
45 | |
---|
46 | /** |
---|
47 | * Global error callback (default) |
---|
48 | * |
---|
49 | * This is only used if set to non-false. * is the default callback for |
---|
50 | * all packages, whereas specific packages may set a default callback |
---|
51 | * for all instances, regardless of whether they are a singleton or not. |
---|
52 | * |
---|
53 | * To exclude non-singletons, only set the local callback for the singleton |
---|
54 | * @see PEAR_ErrorStack::setDefaultCallback() |
---|
55 | * @access private |
---|
56 | * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] |
---|
57 | */ |
---|
58 | $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array( |
---|
59 | '*' => false, |
---|
60 | ); |
---|
61 | |
---|
62 | /** |
---|
63 | * Global Log object (default) |
---|
64 | * |
---|
65 | * This is only used if set to non-false. Use to set a default log object for |
---|
66 | * all stacks, regardless of instantiation order or location |
---|
67 | * @see PEAR_ErrorStack::setDefaultLogger() |
---|
68 | * @access private |
---|
69 | * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] |
---|
70 | */ |
---|
71 | $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false; |
---|
72 | |
---|
73 | /** |
---|
74 | * Global Overriding Callback |
---|
75 | * |
---|
76 | * This callback will override any error callbacks that specific loggers have set. |
---|
77 | * Use with EXTREME caution |
---|
78 | * @see PEAR_ErrorStack::staticPushCallback() |
---|
79 | * @access private |
---|
80 | * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] |
---|
81 | */ |
---|
82 | $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); |
---|
83 | |
---|
84 | /**#@+ |
---|
85 | * One of four possible return values from the error Callback |
---|
86 | * @see PEAR_ErrorStack::_errorCallback() |
---|
87 | */ |
---|
88 | /** |
---|
89 | * If this is returned, then the error will be both pushed onto the stack |
---|
90 | * and logged. |
---|
91 | */ |
---|
92 | define('PEAR_ERRORSTACK_PUSHANDLOG', 1); |
---|
93 | /** |
---|
94 | * If this is returned, then the error will only be pushed onto the stack, |
---|
95 | * and not logged. |
---|
96 | */ |
---|
97 | define('PEAR_ERRORSTACK_PUSH', 2); |
---|
98 | /** |
---|
99 | * If this is returned, then the error will only be logged, but not pushed |
---|
100 | * onto the error stack. |
---|
101 | */ |
---|
102 | define('PEAR_ERRORSTACK_LOG', 3); |
---|
103 | /** |
---|
104 | * If this is returned, then the error is completely ignored. |
---|
105 | */ |
---|
106 | define('PEAR_ERRORSTACK_IGNORE', 4); |
---|
107 | /** |
---|
108 | * If this is returned, then the error is logged and die() is called. |
---|
109 | */ |
---|
110 | define('PEAR_ERRORSTACK_DIE', 5); |
---|
111 | /**#@-*/ |
---|
112 | |
---|
113 | /** |
---|
114 | * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in |
---|
115 | * the singleton method. |
---|
116 | */ |
---|
117 | define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); |
---|
118 | |
---|
119 | /** |
---|
120 | * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} |
---|
121 | * that has no __toString() method |
---|
122 | */ |
---|
123 | define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); |
---|
124 | /** |
---|
125 | * Error Stack Implementation |
---|
126 | * |
---|
127 | * Usage: |
---|
128 | * <code> |
---|
129 | * // global error stack |
---|
130 | * $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); |
---|
131 | * // local error stack |
---|
132 | * $local_stack = new PEAR_ErrorStack('MyPackage'); |
---|
133 | * </code> |
---|
134 | * @author Greg Beaver <cellog@php.net> |
---|
135 | * @version 1.9.4 |
---|
136 | * @package PEAR_ErrorStack |
---|
137 | * @category Debugging |
---|
138 | * @copyright 2004-2008 Greg Beaver |
---|
139 | * @license http://opensource.org/licenses/bsd-license.php New BSD License |
---|
140 | * @version CVS: $Id: ErrorStack.php 313023 2011-07-06 19:17:11Z dufuz $ |
---|
141 | * @link http://pear.php.net/package/PEAR_ErrorStack |
---|
142 | */ |
---|
143 | class PEAR_ErrorStack { |
---|
144 | /** |
---|
145 | * Errors are stored in the order that they are pushed on the stack. |
---|
146 | * @since 0.4alpha Errors are no longer organized by error level. |
---|
147 | * This renders pop() nearly unusable, and levels could be more easily |
---|
148 | * handled in a callback anyway |
---|
149 | * @var array |
---|
150 | * @access private |
---|
151 | */ |
---|
152 | var $_errors = array(); |
---|
153 | |
---|
154 | /** |
---|
155 | * Storage of errors by level. |
---|
156 | * |
---|
157 | * Allows easy retrieval and deletion of only errors from a particular level |
---|
158 | * @since PEAR 1.4.0dev |
---|
159 | * @var array |
---|
160 | * @access private |
---|
161 | */ |
---|
162 | var $_errorsByLevel = array(); |
---|
163 | |
---|
164 | /** |
---|
165 | * Package name this error stack represents |
---|
166 | * @var string |
---|
167 | * @access protected |
---|
168 | */ |
---|
169 | var $_package; |
---|
170 | |
---|
171 | /** |
---|
172 | * Determines whether a PEAR_Error is thrown upon every error addition |
---|
173 | * @var boolean |
---|
174 | * @access private |
---|
175 | */ |
---|
176 | var $_compat = false; |
---|
177 | |
---|
178 | /** |
---|
179 | * If set to a valid callback, this will be used to generate the error |
---|
180 | * message from the error code, otherwise the message passed in will be |
---|
181 | * used |
---|
182 | * @var false|string|array |
---|
183 | * @access private |
---|
184 | */ |
---|
185 | var $_msgCallback = false; |
---|
186 | |
---|
187 | /** |
---|
188 | * If set to a valid callback, this will be used to generate the error |
---|
189 | * context for an error. For PHP-related errors, this will be a file |
---|
190 | * and line number as retrieved from debug_backtrace(), but can be |
---|
191 | * customized for other purposes. The error might actually be in a separate |
---|
192 | * configuration file, or in a database query. |
---|
193 | * @var false|string|array |
---|
194 | * @access protected |
---|
195 | */ |
---|
196 | var $_contextCallback = false; |
---|
197 | |
---|
198 | /** |
---|
199 | * If set to a valid callback, this will be called every time an error |
---|
200 | * is pushed onto the stack. The return value will be used to determine |
---|
201 | * whether to allow an error to be pushed or logged. |
---|
202 | * |
---|
203 | * The return value must be one an PEAR_ERRORSTACK_* constant |
---|
204 | * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG |
---|
205 | * @var false|string|array |
---|
206 | * @access protected |
---|
207 | */ |
---|
208 | var $_errorCallback = array(); |
---|
209 | |
---|
210 | /** |
---|
211 | * PEAR::Log object for logging errors |
---|
212 | * @var false|Log |
---|
213 | * @access protected |
---|
214 | */ |
---|
215 | var $_logger = false; |
---|
216 | |
---|
217 | /** |
---|
218 | * Error messages - designed to be overridden |
---|
219 | * @var array |
---|
220 | * @abstract |
---|
221 | */ |
---|
222 | var $_errorMsgs = array(); |
---|
223 | |
---|
224 | /** |
---|
225 | * Set up a new error stack |
---|
226 | * |
---|
227 | * @param string $package name of the package this error stack represents |
---|
228 | * @param callback $msgCallback callback used for error message generation |
---|
229 | * @param callback $contextCallback callback used for context generation, |
---|
230 | * defaults to {@link getFileLine()} |
---|
231 | * @param boolean $throwPEAR_Error |
---|
232 | */ |
---|
233 | function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false, |
---|
234 | $throwPEAR_Error = false) |
---|
235 | { |
---|
236 | $this->_package = $package; |
---|
237 | $this->setMessageCallback($msgCallback); |
---|
238 | $this->setContextCallback($contextCallback); |
---|
239 | $this->_compat = $throwPEAR_Error; |
---|
240 | } |
---|
241 | |
---|
242 | /** |
---|
243 | * Return a single error stack for this package. |
---|
244 | * |
---|
245 | * Note that all parameters are ignored if the stack for package $package |
---|
246 | * has already been instantiated |
---|
247 | * @param string $package name of the package this error stack represents |
---|
248 | * @param callback $msgCallback callback used for error message generation |
---|
249 | * @param callback $contextCallback callback used for context generation, |
---|
250 | * defaults to {@link getFileLine()} |
---|
251 | * @param boolean $throwPEAR_Error |
---|
252 | * @param string $stackClass class to instantiate |
---|
253 | * @static |
---|
254 | * @return PEAR_ErrorStack |
---|
255 | */ |
---|
256 | function &singleton($package, $msgCallback = false, $contextCallback = false, |
---|
257 | $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack') |
---|
258 | { |
---|
259 | if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { |
---|
260 | return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; |
---|
261 | } |
---|
262 | if (!class_exists($stackClass)) { |
---|
263 | if (function_exists('debug_backtrace')) { |
---|
264 | $trace = debug_backtrace(); |
---|
265 | } |
---|
266 | PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, |
---|
267 | 'exception', array('stackclass' => $stackClass), |
---|
268 | 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', |
---|
269 | false, $trace); |
---|
270 | } |
---|
271 | $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] = |
---|
272 | new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); |
---|
273 | |
---|
274 | return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; |
---|
275 | } |
---|
276 | |
---|
277 | /** |
---|
278 | * Internal error handler for PEAR_ErrorStack class |
---|
279 | * |
---|
280 | * Dies if the error is an exception (and would have died anyway) |
---|
281 | * @access private |
---|
282 | */ |
---|
283 | function _handleError($err) |
---|
284 | { |
---|
285 | if ($err['level'] == 'exception') { |
---|
286 | $message = $err['message']; |
---|
287 | if (isset($_SERVER['REQUEST_URI'])) { |
---|
288 | echo '<br />'; |
---|
289 | } else { |
---|
290 | echo "\n"; |
---|
291 | } |
---|
292 | var_dump($err['context']); |
---|
293 | die($message); |
---|
294 | } |
---|
295 | } |
---|
296 | |
---|
297 | /** |
---|
298 | * Set up a PEAR::Log object for all error stacks that don't have one |
---|
299 | * @param Log $log |
---|
300 | * @static |
---|
301 | */ |
---|
302 | function setDefaultLogger(&$log) |
---|
303 | { |
---|
304 | if (is_object($log) && method_exists($log, 'log') ) { |
---|
305 | $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; |
---|
306 | } elseif (is_callable($log)) { |
---|
307 | $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; |
---|
308 | } |
---|
309 | } |
---|
310 | |
---|
311 | /** |
---|
312 | * Set up a PEAR::Log object for this error stack |
---|
313 | * @param Log $log |
---|
314 | */ |
---|
315 | function setLogger(&$log) |
---|
316 | { |
---|
317 | if (is_object($log) && method_exists($log, 'log') ) { |
---|
318 | $this->_logger = &$log; |
---|
319 | } elseif (is_callable($log)) { |
---|
320 | $this->_logger = &$log; |
---|
321 | } |
---|
322 | } |
---|
323 | |
---|
324 | /** |
---|
325 | * Set an error code => error message mapping callback |
---|
326 | * |
---|
327 | * This method sets the callback that can be used to generate error |
---|
328 | * messages for any instance |
---|
329 | * @param array|string Callback function/method |
---|
330 | */ |
---|
331 | function setMessageCallback($msgCallback) |
---|
332 | { |
---|
333 | if (!$msgCallback) { |
---|
334 | $this->_msgCallback = array(&$this, 'getErrorMessage'); |
---|
335 | } else { |
---|
336 | if (is_callable($msgCallback)) { |
---|
337 | $this->_msgCallback = $msgCallback; |
---|
338 | } |
---|
339 | } |
---|
340 | } |
---|
341 | |
---|
342 | /** |
---|
343 | * Get an error code => error message mapping callback |
---|
344 | * |
---|
345 | * This method returns the current callback that can be used to generate error |
---|
346 | * messages |
---|
347 | * @return array|string|false Callback function/method or false if none |
---|
348 | */ |
---|
349 | function getMessageCallback() |
---|
350 | { |
---|
351 | return $this->_msgCallback; |
---|
352 | } |
---|
353 | |
---|
354 | /** |
---|
355 | * Sets a default callback to be used by all error stacks |
---|
356 | * |
---|
357 | * This method sets the callback that can be used to generate error |
---|
358 | * messages for a singleton |
---|
359 | * @param array|string Callback function/method |
---|
360 | * @param string Package name, or false for all packages |
---|
361 | * @static |
---|
362 | */ |
---|
363 | function setDefaultCallback($callback = false, $package = false) |
---|
364 | { |
---|
365 | if (!is_callable($callback)) { |
---|
366 | $callback = false; |
---|
367 | } |
---|
368 | $package = $package ? $package : '*'; |
---|
369 | $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback; |
---|
370 | } |
---|
371 | |
---|
372 | /** |
---|
373 | * Set a callback that generates context information (location of error) for an error stack |
---|
374 | * |
---|
375 | * This method sets the callback that can be used to generate context |
---|
376 | * information for an error. Passing in NULL will disable context generation |
---|
377 | * and remove the expensive call to debug_backtrace() |
---|
378 | * @param array|string|null Callback function/method |
---|
379 | */ |
---|
380 | function setContextCallback($contextCallback) |
---|
381 | { |
---|
382 | if ($contextCallback === null) { |
---|
383 | return $this->_contextCallback = false; |
---|
384 | } |
---|
385 | if (!$contextCallback) { |
---|
386 | $this->_contextCallback = array(&$this, 'getFileLine'); |
---|
387 | } else { |
---|
388 | if (is_callable($contextCallback)) { |
---|
389 | $this->_contextCallback = $contextCallback; |
---|
390 | } |
---|
391 | } |
---|
392 | } |
---|
393 | |
---|
394 | /** |
---|
395 | * Set an error Callback |
---|
396 | * If set to a valid callback, this will be called every time an error |
---|
397 | * is pushed onto the stack. The return value will be used to determine |
---|
398 | * whether to allow an error to be pushed or logged. |
---|
399 | * |
---|
400 | * The return value must be one of the ERRORSTACK_* constants. |
---|
401 | * |
---|
402 | * This functionality can be used to emulate PEAR's pushErrorHandling, and |
---|
403 | * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of |
---|
404 | * the error stack or logging |
---|
405 | * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG |
---|
406 | * @see popCallback() |
---|
407 | * @param string|array $cb |
---|
408 | */ |
---|
409 | function pushCallback($cb) |
---|
410 | { |
---|
411 | array_push($this->_errorCallback, $cb); |
---|
412 | } |
---|
413 | |
---|
414 | /** |
---|
415 | * Remove a callback from the error callback stack |
---|
416 | * @see pushCallback() |
---|
417 | * @return array|string|false |
---|
418 | */ |
---|
419 | function popCallback() |
---|
420 | { |
---|
421 | if (!count($this->_errorCallback)) { |
---|
422 | return false; |
---|
423 | } |
---|
424 | return array_pop($this->_errorCallback); |
---|
425 | } |
---|
426 | |
---|
427 | /** |
---|
428 | * Set a temporary overriding error callback for every package error stack |
---|
429 | * |
---|
430 | * Use this to temporarily disable all existing callbacks (can be used |
---|
431 | * to emulate the @ operator, for instance) |
---|
432 | * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG |
---|
433 | * @see staticPopCallback(), pushCallback() |
---|
434 | * @param string|array $cb |
---|
435 | * @static |
---|
436 | */ |
---|
437 | function staticPushCallback($cb) |
---|
438 | { |
---|
439 | array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb); |
---|
440 | } |
---|
441 | |
---|
442 | /** |
---|
443 | * Remove a temporary overriding error callback |
---|
444 | * @see staticPushCallback() |
---|
445 | * @return array|string|false |
---|
446 | * @static |
---|
447 | */ |
---|
448 | function staticPopCallback() |
---|
449 | { |
---|
450 | $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']); |
---|
451 | if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) { |
---|
452 | $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); |
---|
453 | } |
---|
454 | return $ret; |
---|
455 | } |
---|
456 | |
---|
457 | /** |
---|
458 | * Add an error to the stack |
---|
459 | * |
---|
460 | * If the message generator exists, it is called with 2 parameters. |
---|
461 | * - the current Error Stack object |
---|
462 | * - an array that is in the same format as an error. Available indices |
---|
463 | * are 'code', 'package', 'time', 'params', 'level', and 'context' |
---|
464 | * |
---|
465 | * Next, if the error should contain context information, this is |
---|
466 | * handled by the context grabbing method. |
---|
467 | * Finally, the error is pushed onto the proper error stack |
---|
468 | * @param int $code Package-specific error code |
---|
469 | * @param string $level Error level. This is NOT spell-checked |
---|
470 | * @param array $params associative array of error parameters |
---|
471 | * @param string $msg Error message, or a portion of it if the message |
---|
472 | * is to be generated |
---|
473 | * @param array $repackage If this error re-packages an error pushed by |
---|
474 | * another package, place the array returned from |
---|
475 | * {@link pop()} in this parameter |
---|
476 | * @param array $backtrace Protected parameter: use this to pass in the |
---|
477 | * {@link debug_backtrace()} that should be used |
---|
478 | * to find error context |
---|
479 | * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also |
---|
480 | * thrown. If a PEAR_Error is returned, the userinfo |
---|
481 | * property is set to the following array: |
---|
482 | * |
---|
483 | * <code> |
---|
484 | * array( |
---|
485 | * 'code' => $code, |
---|
486 | * 'params' => $params, |
---|
487 | * 'package' => $this->_package, |
---|
488 | * 'level' => $level, |
---|
489 | * 'time' => time(), |
---|
490 | * 'context' => $context, |
---|
491 | * 'message' => $msg, |
---|
492 | * //['repackage' => $err] repackaged error array/Exception class |
---|
493 | * ); |
---|
494 | * </code> |
---|
495 | * |
---|
496 | * Normally, the previous array is returned. |
---|
497 | */ |
---|
498 | function push($code, $level = 'error', $params = array(), $msg = false, |
---|
499 | $repackage = false, $backtrace = false) |
---|
500 | { |
---|
501 | $context = false; |
---|
502 | // grab error context |
---|
503 | if ($this->_contextCallback) { |
---|
504 | if (!$backtrace) { |
---|
505 | $backtrace = debug_backtrace(); |
---|
506 | } |
---|
507 | $context = call_user_func($this->_contextCallback, $code, $params, $backtrace); |
---|
508 | } |
---|
509 | |
---|
510 | // save error |
---|
511 | $time = explode(' ', microtime()); |
---|
512 | $time = $time[1] + $time[0]; |
---|
513 | $err = array( |
---|
514 | 'code' => $code, |
---|
515 | 'params' => $params, |
---|
516 | 'package' => $this->_package, |
---|
517 | 'level' => $level, |
---|
518 | 'time' => $time, |
---|
519 | 'context' => $context, |
---|
520 | 'message' => $msg, |
---|
521 | ); |
---|
522 | |
---|
523 | if ($repackage) { |
---|
524 | $err['repackage'] = $repackage; |
---|
525 | } |
---|
526 | |
---|
527 | // set up the error message, if necessary |
---|
528 | if ($this->_msgCallback) { |
---|
529 | $msg = call_user_func_array($this->_msgCallback, |
---|
530 | array(&$this, $err)); |
---|
531 | $err['message'] = $msg; |
---|
532 | } |
---|
533 | $push = $log = true; |
---|
534 | $die = false; |
---|
535 | // try the overriding callback first |
---|
536 | $callback = $this->staticPopCallback(); |
---|
537 | if ($callback) { |
---|
538 | $this->staticPushCallback($callback); |
---|
539 | } |
---|
540 | if (!is_callable($callback)) { |
---|
541 | // try the local callback next |
---|
542 | $callback = $this->popCallback(); |
---|
543 | if (is_callable($callback)) { |
---|
544 | $this->pushCallback($callback); |
---|
545 | } else { |
---|
546 | // try the default callback |
---|
547 | $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ? |
---|
548 | $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] : |
---|
549 | $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*']; |
---|
550 | } |
---|
551 | } |
---|
552 | if (is_callable($callback)) { |
---|
553 | switch(call_user_func($callback, $err)){ |
---|
554 | case PEAR_ERRORSTACK_IGNORE: |
---|
555 | return $err; |
---|
556 | break; |
---|
557 | case PEAR_ERRORSTACK_PUSH: |
---|
558 | $log = false; |
---|
559 | break; |
---|
560 | case PEAR_ERRORSTACK_LOG: |
---|
561 | $push = false; |
---|
562 | break; |
---|
563 | case PEAR_ERRORSTACK_DIE: |
---|
564 | $die = true; |
---|
565 | break; |
---|
566 | // anything else returned has the same effect as pushandlog |
---|
567 | } |
---|
568 | } |
---|
569 | if ($push) { |
---|
570 | array_unshift($this->_errors, $err); |
---|
571 | if (!isset($this->_errorsByLevel[$err['level']])) { |
---|
572 | $this->_errorsByLevel[$err['level']] = array(); |
---|
573 | } |
---|
574 | $this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; |
---|
575 | } |
---|
576 | if ($log) { |
---|
577 | if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) { |
---|
578 | $this->_log($err); |
---|
579 | } |
---|
580 | } |
---|
581 | if ($die) { |
---|
582 | die(); |
---|
583 | } |
---|
584 | if ($this->_compat && $push) { |
---|
585 | return $this->raiseError($msg, $code, null, null, $err); |
---|
586 | } |
---|
587 | return $err; |
---|
588 | } |
---|
589 | |
---|
590 | /** |
---|
591 | * Static version of {@link push()} |
---|
592 | * |
---|
593 | * @param string $package Package name this error belongs to |
---|
594 | * @param int $code Package-specific error code |
---|
595 | * @param string $level Error level. This is NOT spell-checked |
---|
596 | * @param array $params associative array of error parameters |
---|
597 | * @param string $msg Error message, or a portion of it if the message |
---|
598 | * is to be generated |
---|
599 | * @param array $repackage If this error re-packages an error pushed by |
---|
600 | * another package, place the array returned from |
---|
601 | * {@link pop()} in this parameter |
---|
602 | * @param array $backtrace Protected parameter: use this to pass in the |
---|
603 | * {@link debug_backtrace()} that should be used |
---|
604 | * to find error context |
---|
605 | * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also |
---|
606 | * thrown. see docs for {@link push()} |
---|
607 | * @static |
---|
608 | */ |
---|
609 | function staticPush($package, $code, $level = 'error', $params = array(), |
---|
610 | $msg = false, $repackage = false, $backtrace = false) |
---|
611 | { |
---|
612 | $s = &PEAR_ErrorStack::singleton($package); |
---|
613 | if ($s->_contextCallback) { |
---|
614 | if (!$backtrace) { |
---|
615 | if (function_exists('debug_backtrace')) { |
---|
616 | $backtrace = debug_backtrace(); |
---|
617 | } |
---|
618 | } |
---|
619 | } |
---|
620 | return $s->push($code, $level, $params, $msg, $repackage, $backtrace); |
---|
621 | } |
---|
622 | |
---|
623 | /** |
---|
624 | * Log an error using PEAR::Log |
---|
625 | * @param array $err Error array |
---|
626 | * @param array $levels Error level => Log constant map |
---|
627 | * @access protected |
---|
628 | */ |
---|
629 | function _log($err) |
---|
630 | { |
---|
631 | if ($this->_logger) { |
---|
632 | $logger = &$this->_logger; |
---|
633 | } else { |
---|
634 | $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']; |
---|
635 | } |
---|
636 | if (is_a($logger, 'Log')) { |
---|
637 | $levels = array( |
---|
638 | 'exception' => PEAR_LOG_CRIT, |
---|
639 | 'alert' => PEAR_LOG_ALERT, |
---|
640 | 'critical' => PEAR_LOG_CRIT, |
---|
641 | 'error' => PEAR_LOG_ERR, |
---|
642 | 'warning' => PEAR_LOG_WARNING, |
---|
643 | 'notice' => PEAR_LOG_NOTICE, |
---|
644 | 'info' => PEAR_LOG_INFO, |
---|
645 | 'debug' => PEAR_LOG_DEBUG); |
---|
646 | if (isset($levels[$err['level']])) { |
---|
647 | $level = $levels[$err['level']]; |
---|
648 | } else { |
---|
649 | $level = PEAR_LOG_INFO; |
---|
650 | } |
---|
651 | $logger->log($err['message'], $level, $err); |
---|
652 | } else { // support non-standard logs |
---|
653 | call_user_func($logger, $err); |
---|
654 | } |
---|
655 | } |
---|
656 | |
---|
657 | |
---|
658 | /** |
---|
659 | * Pop an error off of the error stack |
---|
660 | * |
---|
661 | * @return false|array |
---|
662 | * @since 0.4alpha it is no longer possible to specify a specific error |
---|
663 | * level to return - the last error pushed will be returned, instead |
---|
664 | */ |
---|
665 | function pop() |
---|
666 | { |
---|
667 | $err = @array_shift($this->_errors); |
---|
668 | if (!is_null($err)) { |
---|
669 | @array_pop($this->_errorsByLevel[$err['level']]); |
---|
670 | if (!count($this->_errorsByLevel[$err['level']])) { |
---|
671 | unset($this->_errorsByLevel[$err['level']]); |
---|
672 | } |
---|
673 | } |
---|
674 | return $err; |
---|
675 | } |
---|
676 | |
---|
677 | /** |
---|
678 | * Pop an error off of the error stack, static method |
---|
679 | * |
---|
680 | * @param string package name |
---|
681 | * @return boolean |
---|
682 | * @since PEAR1.5.0a1 |
---|
683 | */ |
---|
684 | function staticPop($package) |
---|
685 | { |
---|
686 | if ($package) { |
---|
687 | if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { |
---|
688 | return false; |
---|
689 | } |
---|
690 | return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop(); |
---|
691 | } |
---|
692 | } |
---|
693 | |
---|
694 | /** |
---|
695 | * Determine whether there are any errors on the stack |
---|
696 | * @param string|array Level name. Use to determine if any errors |
---|
697 | * of level (string), or levels (array) have been pushed |
---|
698 | * @return boolean |
---|
699 | */ |
---|
700 | function hasErrors($level = false) |
---|
701 | { |
---|
702 | if ($level) { |
---|
703 | return isset($this->_errorsByLevel[$level]); |
---|
704 | } |
---|
705 | return count($this->_errors); |
---|
706 | } |
---|
707 | |
---|
708 | /** |
---|
709 | * Retrieve all errors since last purge |
---|
710 | * |
---|
711 | * @param boolean set in order to empty the error stack |
---|
712 | * @param string level name, to return only errors of a particular severity |
---|
713 | * @return array |
---|
714 | */ |
---|
715 | function getErrors($purge = false, $level = false) |
---|
716 | { |
---|
717 | if (!$purge) { |
---|
718 | if ($level) { |
---|
719 | if (!isset($this->_errorsByLevel[$level])) { |
---|
720 | return array(); |
---|
721 | } else { |
---|
722 | return $this->_errorsByLevel[$level]; |
---|
723 | } |
---|
724 | } else { |
---|
725 | return $this->_errors; |
---|
726 | } |
---|
727 | } |
---|
728 | if ($level) { |
---|
729 | $ret = $this->_errorsByLevel[$level]; |
---|
730 | foreach ($this->_errorsByLevel[$level] as $i => $unused) { |
---|
731 | // entries are references to the $_errors array |
---|
732 | $this->_errorsByLevel[$level][$i] = false; |
---|
733 | } |
---|
734 | // array_filter removes all entries === false |
---|
735 | $this->_errors = array_filter($this->_errors); |
---|
736 | unset($this->_errorsByLevel[$level]); |
---|
737 | return $ret; |
---|
738 | } |
---|
739 | $ret = $this->_errors; |
---|
740 | $this->_errors = array(); |
---|
741 | $this->_errorsByLevel = array(); |
---|
742 | return $ret; |
---|
743 | } |
---|
744 | |
---|
745 | /** |
---|
746 | * Determine whether there are any errors on a single error stack, or on any error stack |
---|
747 | * |
---|
748 | * The optional parameter can be used to test the existence of any errors without the need of |
---|
749 | * singleton instantiation |
---|
750 | * @param string|false Package name to check for errors |
---|
751 | * @param string Level name to check for a particular severity |
---|
752 | * @return boolean |
---|
753 | * @static |
---|
754 | */ |
---|
755 | function staticHasErrors($package = false, $level = false) |
---|
756 | { |
---|
757 | if ($package) { |
---|
758 | if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { |
---|
759 | return false; |
---|
760 | } |
---|
761 | return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level); |
---|
762 | } |
---|
763 | foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { |
---|
764 | if ($obj->hasErrors($level)) { |
---|
765 | return true; |
---|
766 | } |
---|
767 | } |
---|
768 | return false; |
---|
769 | } |
---|
770 | |
---|
771 | /** |
---|
772 | * Get a list of all errors since last purge, organized by package |
---|
773 | * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be |
---|
774 | * @param boolean $purge Set to purge the error stack of existing errors |
---|
775 | * @param string $level Set to a level name in order to retrieve only errors of a particular level |
---|
776 | * @param boolean $merge Set to return a flat array, not organized by package |
---|
777 | * @param array $sortfunc Function used to sort a merged array - default |
---|
778 | * sorts by time, and should be good for most cases |
---|
779 | * @static |
---|
780 | * @return array |
---|
781 | */ |
---|
782 | function staticGetErrors($purge = false, $level = false, $merge = false, |
---|
783 | $sortfunc = array('PEAR_ErrorStack', '_sortErrors')) |
---|
784 | { |
---|
785 | $ret = array(); |
---|
786 | if (!is_callable($sortfunc)) { |
---|
787 | $sortfunc = array('PEAR_ErrorStack', '_sortErrors'); |
---|
788 | } |
---|
789 | foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { |
---|
790 | $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level); |
---|
791 | if ($test) { |
---|
792 | if ($merge) { |
---|
793 | $ret = array_merge($ret, $test); |
---|
794 | } else { |
---|
795 | $ret[$package] = $test; |
---|
796 | } |
---|
797 | } |
---|
798 | } |
---|
799 | if ($merge) { |
---|
800 | usort($ret, $sortfunc); |
---|
801 | } |
---|
802 | return $ret; |
---|
803 | } |
---|
804 | |
---|
805 | /** |
---|
806 | * Error sorting function, sorts by time |
---|
807 | * @access private |
---|
808 | */ |
---|
809 | function _sortErrors($a, $b) |
---|
810 | { |
---|
811 | if ($a['time'] == $b['time']) { |
---|
812 | return 0; |
---|
813 | } |
---|
814 | if ($a['time'] < $b['time']) { |
---|
815 | return 1; |
---|
816 | } |
---|
817 | return -1; |
---|
818 | } |
---|
819 | |
---|
820 | /** |
---|
821 | * Standard file/line number/function/class context callback |
---|
822 | * |
---|
823 | * This function uses a backtrace generated from {@link debug_backtrace()} |
---|
824 | * and so will not work at all in PHP < 4.3.0. The frame should |
---|
825 | * reference the frame that contains the source of the error. |
---|
826 | * @return array|false either array('file' => file, 'line' => line, |
---|
827 | * 'function' => function name, 'class' => class name) or |
---|
828 | * if this doesn't work, then false |
---|
829 | * @param unused |
---|
830 | * @param integer backtrace frame. |
---|
831 | * @param array Results of debug_backtrace() |
---|
832 | * @static |
---|
833 | */ |
---|
834 | function getFileLine($code, $params, $backtrace = null) |
---|
835 | { |
---|
836 | if ($backtrace === null) { |
---|
837 | return false; |
---|
838 | } |
---|
839 | $frame = 0; |
---|
840 | $functionframe = 1; |
---|
841 | if (!isset($backtrace[1])) { |
---|
842 | $functionframe = 0; |
---|
843 | } else { |
---|
844 | while (isset($backtrace[$functionframe]['function']) && |
---|
845 | $backtrace[$functionframe]['function'] == 'eval' && |
---|
846 | isset($backtrace[$functionframe + 1])) { |
---|
847 | $functionframe++; |
---|
848 | } |
---|
849 | } |
---|
850 | if (isset($backtrace[$frame])) { |
---|
851 | if (!isset($backtrace[$frame]['file'])) { |
---|
852 | $frame++; |
---|
853 | } |
---|
854 | $funcbacktrace = $backtrace[$functionframe]; |
---|
855 | $filebacktrace = $backtrace[$frame]; |
---|
856 | $ret = array('file' => $filebacktrace['file'], |
---|
857 | 'line' => $filebacktrace['line']); |
---|
858 | // rearrange for eval'd code or create function errors |
---|
859 | if (strpos($filebacktrace['file'], '(') && |
---|
860 | preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'], |
---|
861 | $matches)) { |
---|
862 | $ret['file'] = $matches[1]; |
---|
863 | $ret['line'] = $matches[2] + 0; |
---|
864 | } |
---|
865 | if (isset($funcbacktrace['function']) && isset($backtrace[1])) { |
---|
866 | if ($funcbacktrace['function'] != 'eval') { |
---|
867 | if ($funcbacktrace['function'] == '__lambda_func') { |
---|
868 | $ret['function'] = 'create_function() code'; |
---|
869 | } else { |
---|
870 | $ret['function'] = $funcbacktrace['function']; |
---|
871 | } |
---|
872 | } |
---|
873 | } |
---|
874 | if (isset($funcbacktrace['class']) && isset($backtrace[1])) { |
---|
875 | $ret['class'] = $funcbacktrace['class']; |
---|
876 | } |
---|
877 | return $ret; |
---|
878 | } |
---|
879 | return false; |
---|
880 | } |
---|
881 | |
---|
882 | /** |
---|
883 | * Standard error message generation callback |
---|
884 | * |
---|
885 | * This method may also be called by a custom error message generator |
---|
886 | * to fill in template values from the params array, simply |
---|
887 | * set the third parameter to the error message template string to use |
---|
888 | * |
---|
889 | * The special variable %__msg% is reserved: use it only to specify |
---|
890 | * where a message passed in by the user should be placed in the template, |
---|
891 | * like so: |
---|
892 | * |
---|
893 | * Error message: %msg% - internal error |
---|
894 | * |
---|
895 | * If the message passed like so: |
---|
896 | * |
---|
897 | * <code> |
---|
898 | * $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); |
---|
899 | * </code> |
---|
900 | * |
---|
901 | * The returned error message will be "Error message: server error 500 - |
---|
902 | * internal error" |
---|
903 | * @param PEAR_ErrorStack |
---|
904 | * @param array |
---|
905 | * @param string|false Pre-generated error message template |
---|
906 | * @static |
---|
907 | * @return string |
---|
908 | */ |
---|
909 | function getErrorMessage(&$stack, $err, $template = false) |
---|
910 | { |
---|
911 | if ($template) { |
---|
912 | $mainmsg = $template; |
---|
913 | } else { |
---|
914 | $mainmsg = $stack->getErrorMessageTemplate($err['code']); |
---|
915 | } |
---|
916 | $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); |
---|
917 | if (is_array($err['params']) && count($err['params'])) { |
---|
918 | foreach ($err['params'] as $name => $val) { |
---|
919 | if (is_array($val)) { |
---|
920 | // @ is needed in case $val is a multi-dimensional array |
---|
921 | $val = @implode(', ', $val); |
---|
922 | } |
---|
923 | if (is_object($val)) { |
---|
924 | if (method_exists($val, '__toString')) { |
---|
925 | $val = $val->__toString(); |
---|
926 | } else { |
---|
927 | PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, |
---|
928 | 'warning', array('obj' => get_class($val)), |
---|
929 | 'object %obj% passed into getErrorMessage, but has no __toString() method'); |
---|
930 | $val = 'Object'; |
---|
931 | } |
---|
932 | } |
---|
933 | $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); |
---|
934 | } |
---|
935 | } |
---|
936 | return $mainmsg; |
---|
937 | } |
---|
938 | |
---|
939 | /** |
---|
940 | * Standard Error Message Template generator from code |
---|
941 | * @return string |
---|
942 | */ |
---|
943 | function getErrorMessageTemplate($code) |
---|
944 | { |
---|
945 | if (!isset($this->_errorMsgs[$code])) { |
---|
946 | return '%__msg%'; |
---|
947 | } |
---|
948 | return $this->_errorMsgs[$code]; |
---|
949 | } |
---|
950 | |
---|
951 | /** |
---|
952 | * Set the Error Message Template array |
---|
953 | * |
---|
954 | * The array format must be: |
---|
955 | * <pre> |
---|
956 | * array(error code => 'message template',...) |
---|
957 | * </pre> |
---|
958 | * |
---|
959 | * Error message parameters passed into {@link push()} will be used as input |
---|
960 | * for the error message. If the template is 'message %foo% was %bar%', and the |
---|
961 | * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will |
---|
962 | * be 'message one was six' |
---|
963 | * @return string |
---|
964 | */ |
---|
965 | function setErrorMessageTemplate($template) |
---|
966 | { |
---|
967 | $this->_errorMsgs = $template; |
---|
968 | } |
---|
969 | |
---|
970 | |
---|
971 | /** |
---|
972 | * emulate PEAR::raiseError() |
---|
973 | * |
---|
974 | * @return PEAR_Error |
---|
975 | */ |
---|
976 | function raiseError() |
---|
977 | { |
---|
978 | require_once 'PEAR.php'; |
---|
979 | $args = func_get_args(); |
---|
980 | return call_user_func_array(array('PEAR', 'raiseError'), $args); |
---|
981 | } |
---|
982 | } |
---|
983 | $stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack'); |
---|
984 | $stack->pushCallback(array('PEAR_ErrorStack', '_handleError')); |
---|
985 | ?> |
---|