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