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