Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 jpm 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>
187 mathias 24
 * @copyright  2004-2008 Greg Beaver
25
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
94 jpm 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>
187 mathias 134
 * @version    1.10.1
94 jpm 135
 * @package    PEAR_ErrorStack
136
 * @category   Debugging
187 mathias 137
 * @copyright  2004-2008 Greg Beaver
138
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
94 jpm 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;
187 mathias 195
 
94 jpm 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.
187 mathias 200
     *
94 jpm 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();
187 mathias 207
 
94 jpm 208
    /**
209
     * PEAR::Log object for logging errors
210
     * @var false|Log
211
     * @access protected
212
     */
213
    var $_logger = false;
187 mathias 214
 
94 jpm 215
    /**
216
     * Error messages - designed to be overridden
217
     * @var array
218
     * @abstract
219
     */
220
    var $_errorMsgs = array();
187 mathias 221
 
94 jpm 222
    /**
223
     * Set up a new error stack
187 mathias 224
     *
94 jpm 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
     */
187 mathias 231
    function __construct($package, $msgCallback = false, $contextCallback = false,
94 jpm 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
187 mathias 251
     *
94 jpm 252
     * @return PEAR_ErrorStack
253
     */
187 mathias 254
    public static function &singleton(
255
        $package, $msgCallback = false, $contextCallback = false,
256
        $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack'
257
    ) {
94 jpm 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
     */
187 mathias 300
    public static function setDefaultLogger(&$log)
94 jpm 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;
187 mathias 306
        }
94 jpm 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
     */
187 mathias 360
    public static function setDefaultCallback($callback = false, $package = false)
94 jpm 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
     */
187 mathias 433
    public static function staticPushCallback($cb)
94 jpm 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
     */
187 mathias 443
    public static function staticPopCallback()
94 jpm 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
     */
187 mathias 603
    public static function staticPush(
604
        $package, $code, $level = 'error', $params = array(),
605
        $msg = false, $repackage = false, $backtrace = false
606
    ) {
94 jpm 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
     */
187 mathias 749
    public static function staticHasErrors($package = false, $level = false)
94 jpm 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
187 mathias 773
     *
94 jpm 774
     * @return array
775
     */
187 mathias 776
    public static function staticGetErrors(
777
        $purge = false, $level = false, $merge = false,
778
        $sortfunc = array('PEAR_ErrorStack', '_sortErrors')
779
    ) {
94 jpm 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
     */
187 mathias 804
    public static function _sortErrors($a, $b)
94 jpm 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
     */
187 mathias 828
    public static function getFileLine($code, $params, $backtrace = null)
94 jpm 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'], '(') &&
187 mathias 854
            	  preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
94 jpm 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
187 mathias 900
     *
94 jpm 901
     * @return string
902
     */
187 mathias 903
    public static function getErrorMessage(&$stack, $err, $template = false)
94 jpm 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
?>