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
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
3
/**
4
 * PEAR_Exception
5
 *
6
 * PHP versions 4 and 5
7
 *
8
 * @category   pear
9
 * @package    PEAR
10
 * @author     Tomas V. V. Cox <cox@idecnet.com>
11
 * @author     Hans Lellelid <hans@velum.net>
12
 * @author     Bertrand Mansion <bmansion@mamasam.com>
13
 * @author     Greg Beaver <cellog@php.net>
187 mathias 14
 * @copyright  1997-2009 The Authors
15
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
94 jpm 16
 * @link       http://pear.php.net/package/PEAR
17
 * @since      File available since Release 1.3.3
18
 */
19
 
20
 
21
/**
22
 * Base PEAR_Exception Class
23
 *
24
 * 1) Features:
25
 *
26
 * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
27
 * - Definable triggers, shot when exceptions occur
28
 * - Pretty and informative error messages
29
 * - Added more context info available (like class, method or cause)
30
 * - cause can be a PEAR_Exception or an array of mixed
31
 *   PEAR_Exceptions/PEAR_ErrorStack warnings
32
 * - callbacks for specific exception classes and their children
33
 *
34
 * 2) Ideas:
35
 *
36
 * - Maybe a way to define a 'template' for the output
37
 *
38
 * 3) Inherited properties from PHP Exception Class:
39
 *
40
 * protected $message
41
 * protected $code
42
 * protected $line
43
 * protected $file
44
 * private   $trace
45
 *
46
 * 4) Inherited methods from PHP Exception Class:
47
 *
48
 * __clone
49
 * __construct
50
 * getMessage
51
 * getCode
52
 * getFile
53
 * getLine
54
 * getTraceSafe
55
 * getTraceSafeAsString
56
 * __toString
57
 *
58
 * 5) Usage example
59
 *
60
 * <code>
61
 *  require_once 'PEAR/Exception.php';
62
 *
63
 *  class Test {
64
 *     function foo() {
65
 *         throw new PEAR_Exception('Error Message', ERROR_CODE);
66
 *     }
67
 *  }
68
 *
69
 *  function myLogger($pear_exception) {
70
 *     echo $pear_exception->getMessage();
71
 *  }
72
 *  // each time a exception is thrown the 'myLogger' will be called
73
 *  // (its use is completely optional)
74
 *  PEAR_Exception::addObserver('myLogger');
75
 *  $test = new Test;
76
 *  try {
77
 *     $test->foo();
78
 *  } catch (PEAR_Exception $e) {
79
 *     print $e;
80
 *  }
81
 * </code>
82
 *
83
 * @category   pear
84
 * @package    PEAR
85
 * @author     Tomas V.V.Cox <cox@idecnet.com>
86
 * @author     Hans Lellelid <hans@velum.net>
87
 * @author     Bertrand Mansion <bmansion@mamasam.com>
88
 * @author     Greg Beaver <cellog@php.net>
187 mathias 89
 * @copyright  1997-2009 The Authors
90
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
91
 * @version    Release: 1.10.1
94 jpm 92
 * @link       http://pear.php.net/package/PEAR
93
 * @since      Class available since Release 1.3.3
94
 *
95
 */
96
class PEAR_Exception extends Exception
97
{
98
    const OBSERVER_PRINT = -2;
99
    const OBSERVER_TRIGGER = -4;
100
    const OBSERVER_DIE = -8;
101
    protected $cause;
102
    private static $_observers = array();
103
    private static $_uniqueid = 0;
104
    private $_trace;
105
 
106
    /**
107
     * Supported signatures:
108
     *  - PEAR_Exception(string $message);
109
     *  - PEAR_Exception(string $message, int $code);
110
     *  - PEAR_Exception(string $message, Exception $cause);
111
     *  - PEAR_Exception(string $message, Exception $cause, int $code);
112
     *  - PEAR_Exception(string $message, PEAR_Error $cause);
113
     *  - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
114
     *  - PEAR_Exception(string $message, array $causes);
115
     *  - PEAR_Exception(string $message, array $causes, int $code);
116
     * @param string exception message
117
     * @param int|Exception|PEAR_Error|array|null exception cause
118
     * @param int|null exception code or null
119
     */
120
    public function __construct($message, $p2 = null, $p3 = null)
121
    {
122
        if (is_int($p2)) {
123
            $code = $p2;
124
            $this->cause = null;
125
        } elseif (is_object($p2) || is_array($p2)) {
126
            // using is_object allows both Exception and PEAR_Error
127
            if (is_object($p2) && !($p2 instanceof Exception)) {
128
                if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
129
                    throw new PEAR_Exception('exception cause must be Exception, ' .
130
                        'array, or PEAR_Error');
131
                }
132
            }
133
            $code = $p3;
134
            if (is_array($p2) && isset($p2['message'])) {
135
                // fix potential problem of passing in a single warning
136
                $p2 = array($p2);
137
            }
138
            $this->cause = $p2;
139
        } else {
140
            $code = null;
141
            $this->cause = null;
142
        }
143
        parent::__construct($message, $code);
144
        $this->signal();
145
    }
146
 
147
    /**
148
     * @param mixed $callback  - A valid php callback, see php func is_callable()
149
     *                         - A PEAR_Exception::OBSERVER_* constant
150
     *                         - An array(const PEAR_Exception::OBSERVER_*,
151
     *                           mixed $options)
152
     * @param string $label    The name of the observer. Use this if you want
153
     *                         to remove it later with removeObserver()
154
     */
155
    public static function addObserver($callback, $label = 'default')
156
    {
157
        self::$_observers[$label] = $callback;
158
    }
159
 
160
    public static function removeObserver($label = 'default')
161
    {
162
        unset(self::$_observers[$label]);
163
    }
164
 
165
    /**
166
     * @return int unique identifier for an observer
167
     */
168
    public static function getUniqueId()
169
    {
170
        return self::$_uniqueid++;
171
    }
172
 
173
    private function signal()
174
    {
175
        foreach (self::$_observers as $func) {
176
            if (is_callable($func)) {
177
                call_user_func($func, $this);
178
                continue;
179
            }
180
            settype($func, 'array');
181
            switch ($func[0]) {
182
                case self::OBSERVER_PRINT :
183
                    $f = (isset($func[1])) ? $func[1] : '%s';
184
                    printf($f, $this->getMessage());
185
                    break;
186
                case self::OBSERVER_TRIGGER :
187
                    $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
188
                    trigger_error($this->getMessage(), $f);
189
                    break;
190
                case self::OBSERVER_DIE :
191
                    $f = (isset($func[1])) ? $func[1] : '%s';
192
                    die(printf($f, $this->getMessage()));
193
                    break;
194
                default:
195
                    trigger_error('invalid observer type', E_USER_WARNING);
196
            }
197
        }
198
    }
199
 
200
    /**
201
     * Return specific error information that can be used for more detailed
202
     * error messages or translation.
203
     *
204
     * This method may be overridden in child exception classes in order
205
     * to add functionality not present in PEAR_Exception and is a placeholder
206
     * to define API
207
     *
208
     * The returned array must be an associative array of parameter => value like so:
209
     * <pre>
210
     * array('name' => $name, 'context' => array(...))
211
     * </pre>
212
     * @return array
213
     */
214
    public function getErrorData()
215
    {
216
        return array();
217
    }
218
 
219
    /**
220
     * Returns the exception that caused this exception to be thrown
221
     * @access public
222
     * @return Exception|array The context of the exception
223
     */
224
    public function getCause()
225
    {
226
        return $this->cause;
227
    }
228
 
229
    /**
230
     * Function must be public to call on caused exceptions
231
     * @param array
232
     */
233
    public function getCauseMessage(&$causes)
234
    {
235
        $trace = $this->getTraceSafe();
236
        $cause = array('class'   => get_class($this),
237
                       'message' => $this->message,
238
                       'file' => 'unknown',
239
                       'line' => 'unknown');
240
        if (isset($trace[0])) {
241
            if (isset($trace[0]['file'])) {
242
                $cause['file'] = $trace[0]['file'];
243
                $cause['line'] = $trace[0]['line'];
244
            }
245
        }
246
        $causes[] = $cause;
247
        if ($this->cause instanceof PEAR_Exception) {
248
            $this->cause->getCauseMessage($causes);
249
        } elseif ($this->cause instanceof Exception) {
250
            $causes[] = array('class'   => get_class($this->cause),
251
                              'message' => $this->cause->getMessage(),
252
                              'file' => $this->cause->getFile(),
253
                              'line' => $this->cause->getLine());
254
        } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
255
            $causes[] = array('class' => get_class($this->cause),
187 mathias 256
                              'message' => $this->cause->getMessage(),
257
                              'file' => 'unknown',
258
                              'line' => 'unknown');
94 jpm 259
        } elseif (is_array($this->cause)) {
260
            foreach ($this->cause as $cause) {
261
                if ($cause instanceof PEAR_Exception) {
262
                    $cause->getCauseMessage($causes);
263
                } elseif ($cause instanceof Exception) {
264
                    $causes[] = array('class'   => get_class($cause),
265
                                   'message' => $cause->getMessage(),
266
                                   'file' => $cause->getFile(),
267
                                   'line' => $cause->getLine());
268
                } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
269
                    $causes[] = array('class' => get_class($cause),
187 mathias 270
                                      'message' => $cause->getMessage(),
271
                                      'file' => 'unknown',
272
                                      'line' => 'unknown');
94 jpm 273
                } elseif (is_array($cause) && isset($cause['message'])) {
274
                    // PEAR_ErrorStack warning
275
                    $causes[] = array(
276
                        'class' => $cause['package'],
277
                        'message' => $cause['message'],
278
                        'file' => isset($cause['context']['file']) ?
279
                                            $cause['context']['file'] :
280
                                            'unknown',
281
                        'line' => isset($cause['context']['line']) ?
282
                                            $cause['context']['line'] :
283
                                            'unknown',
284
                    );
285
                }
286
            }
287
        }
288
    }
289
 
290
    public function getTraceSafe()
187 mathias 291
    {
94 jpm 292
        if (!isset($this->_trace)) {
293
            $this->_trace = $this->getTrace();
294
            if (empty($this->_trace)) {
295
                $backtrace = debug_backtrace();
296
                $this->_trace = array($backtrace[count($backtrace)-1]);
297
            }
298
        }
299
        return $this->_trace;
300
    }
301
 
302
    public function getErrorClass()
303
    {
304
        $trace = $this->getTraceSafe();
305
        return $trace[0]['class'];
306
    }
307
 
308
    public function getErrorMethod()
309
    {
310
        $trace = $this->getTraceSafe();
311
        return $trace[0]['function'];
312
    }
313
 
314
    public function __toString()
315
    {
316
        if (isset($_SERVER['REQUEST_URI'])) {
317
            return $this->toHtml();
318
        }
319
        return $this->toText();
320
    }
321
 
322
    public function toHtml()
323
    {
324
        $trace = $this->getTraceSafe();
325
        $causes = array();
326
        $this->getCauseMessage($causes);
187 mathias 327
        $html =  '<table style="border: 1px" cellspacing="0">' . "\n";
94 jpm 328
        foreach ($causes as $i => $cause) {
187 mathias 329
            $html .= '<tr><td colspan="3" style="background: #ff9999">'
94 jpm 330
               . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
331
               . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
332
               . 'on line <b>' . $cause['line'] . '</b>'
333
               . "</td></tr>\n";
334
        }
187 mathias 335
        $html .= '<tr><td colspan="3" style="background-color: #aaaaaa; text-align: center; font-weight: bold;">Exception trace</td></tr>' . "\n"
336
               . '<tr><td style="text-align: center; background: #cccccc; width:20px; font-weight: bold;">#</td>'
337
               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Function</td>'
338
               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Location</td></tr>' . "\n";
94 jpm 339
 
340
        foreach ($trace as $k => $v) {
187 mathias 341
            $html .= '<tr><td style="text-align: center;">' . $k . '</td>'
94 jpm 342
                   . '<td>';
343
            if (!empty($v['class'])) {
344
                $html .= $v['class'] . $v['type'];
345
            }
346
            $html .= $v['function'];
347
            $args = array();
348
            if (!empty($v['args'])) {
349
                foreach ($v['args'] as $arg) {
350
                    if (is_null($arg)) $args[] = 'null';
351
                    elseif (is_array($arg)) $args[] = 'Array';
352
                    elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
353
                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
354
                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
355
                    else {
356
                        $arg = (string)$arg;
357
                        $str = htmlspecialchars(substr($arg, 0, 16));
358
                        if (strlen($arg) > 16) $str .= '&hellip;';
359
                        $args[] = "'" . $str . "'";
360
                    }
361
                }
362
            }
363
            $html .= '(' . implode(', ',$args) . ')'
364
                   . '</td>'
365
                   . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
366
                   . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
367
                   . '</td></tr>' . "\n";
368
        }
187 mathias 369
        $html .= '<tr><td style="text-align: center;">' . ($k+1) . '</td>'
94 jpm 370
               . '<td>{main}</td>'
371
               . '<td>&nbsp;</td></tr>' . "\n"
372
               . '</table>';
373
        return $html;
374
    }
375
 
376
    public function toText()
377
    {
378
        $causes = array();
379
        $this->getCauseMessage($causes);
380
        $causeMsg = '';
381
        foreach ($causes as $i => $cause) {
382
            $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
383
                   . $cause['message'] . ' in ' . $cause['file']
384
                   . ' on line ' . $cause['line'] . "\n";
385
        }
386
        return $causeMsg . $this->getTraceAsString();
387
    }
187 mathias 388
}