annotate vendor/pear/pear_exception/PEAR/Exception.php @ 19:fa3358dc1485 tip

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