Exception.php 14 KB

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