Initial repo created
[timetracker.git] / WEB-INF / lib / pear / PEAR / ErrorStack.php
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 ?>