Exception.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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. private static $_observers = array();
  101. private static $_uniqueid = 0;
  102. protected $cause;
  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. * @throws PEAR_Exception
  118. */
  119. public function __construct($message, $p2 = null, $p3 = null)
  120. {
  121. if (is_int($p2)) {
  122. $code = $p2;
  123. $this->cause = null;
  124. } elseif (is_object($p2) || is_array($p2)) {
  125. // using is_object allows both Exception and PEAR_Error
  126. if (is_object($p2) && !($p2 instanceof Exception)) {
  127. if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
  128. throw new PEAR_Exception('exception cause must be Exception, ' .
  129. 'array, or PEAR_Error');
  130. }
  131. }
  132. $code = $p3;
  133. if (is_array($p2) && isset($p2['message'])) {
  134. // fix potential problem of passing in a single warning
  135. $p2 = array($p2);
  136. }
  137. $this->cause = $p2;
  138. } else {
  139. $code = null;
  140. $this->cause = null;
  141. }
  142. parent::__construct($message, $code);
  143. $this->signal();
  144. }
  145. private function signal()
  146. {
  147. foreach (self::$_observers as $func) {
  148. if (is_callable($func)) {
  149. call_user_func($func, $this);
  150. continue;
  151. }
  152. settype($func, 'array');
  153. switch ($func[0]) {
  154. case self::OBSERVER_PRINT :
  155. $f = (isset($func[1])) ? $func[1] : '%s';
  156. printf($f, $this->getMessage());
  157. break;
  158. case self::OBSERVER_TRIGGER :
  159. $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
  160. trigger_error($this->getMessage(), $f);
  161. break;
  162. case self::OBSERVER_DIE :
  163. $f = (isset($func[1])) ? $func[1] : '%s';
  164. die(printf($f, $this->getMessage()));
  165. break;
  166. default:
  167. trigger_error('invalid observer type', E_USER_WARNING);
  168. }
  169. }
  170. }
  171. /**
  172. * @param mixed $callback - A valid php callback, see php func is_callable()
  173. * - A PEAR_Exception::OBSERVER_* constant
  174. * - An array(const PEAR_Exception::OBSERVER_*,
  175. * mixed $options)
  176. * @param string $label The name of the observer. Use this if you want
  177. * to remove it later with removeObserver()
  178. */
  179. public static function addObserver($callback, $label = 'default')
  180. {
  181. self::$_observers[$label] = $callback;
  182. }
  183. public static function removeObserver($label = 'default')
  184. {
  185. unset(self::$_observers[$label]);
  186. }
  187. /**
  188. * @return int unique identifier for an observer
  189. */
  190. public static function getUniqueId()
  191. {
  192. return self::$_uniqueid++;
  193. }
  194. /**
  195. * Return specific error information that can be used for more detailed
  196. * error messages or translation.
  197. *
  198. * This method may be overridden in child exception classes in order
  199. * to add functionality not present in PEAR_Exception and is a placeholder
  200. * to define API
  201. *
  202. * The returned array must be an associative array of parameter => value like so:
  203. * <pre>
  204. * array('name' => $name, 'context' => array(...))
  205. * </pre>
  206. * @return array
  207. */
  208. public function getErrorData()
  209. {
  210. return array();
  211. }
  212. /**
  213. * Returns the exception that caused this exception to be thrown
  214. * @access public
  215. * @return Exception|array The context of the exception
  216. */
  217. public function getCause()
  218. {
  219. return $this->cause;
  220. }
  221. public function getErrorClass()
  222. {
  223. $trace = $this->getTraceSafe();
  224. return $trace[0]['class'];
  225. }
  226. public function getErrorMethod()
  227. {
  228. $trace = $this->getTraceSafe();
  229. return $trace[0]['function'];
  230. }
  231. public function __toString()
  232. {
  233. if (isset($_SERVER['REQUEST_URI'])) {
  234. return $this->toHtml();
  235. }
  236. return $this->toText();
  237. }
  238. public function toHtml()
  239. {
  240. $trace = $this->getTraceSafe();
  241. $causes = array();
  242. $this->getCauseMessage($causes);
  243. $html = '<table style="border: 1px" cellspacing="0">' . "\n";
  244. foreach ($causes as $i => $cause) {
  245. $html .= '<tr><td colspan="3" style="background: #ff9999">'
  246. . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
  247. . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
  248. . 'on line <b>' . $cause['line'] . '</b>'
  249. . "</td></tr>\n";
  250. }
  251. $html .= '<tr><td colspan="3" style="background-color: #aaaaaa; text-align: center; font-weight: bold;">Exception trace</td></tr>' . "\n"
  252. . '<tr><td style="text-align: center; background: #cccccc; width:20px; font-weight: bold;">#</td>'
  253. . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Function</td>'
  254. . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Location</td></tr>' . "\n";
  255. foreach ($trace as $k => $v) {
  256. $html .= '<tr><td style="text-align: center;">' . $k . '</td>'
  257. . '<td>';
  258. if (!empty($v['class'])) {
  259. $html .= $v['class'] . $v['type'];
  260. }
  261. $html .= $v['function'];
  262. $args = array();
  263. if (!empty($v['args'])) {
  264. foreach ($v['args'] as $arg) {
  265. if (is_null($arg)) $args[] = 'null';
  266. elseif (is_array($arg)) $args[] = 'Array';
  267. elseif (is_object($arg)) $args[] = 'Object(' . get_class($arg) . ')';
  268. elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
  269. elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
  270. else {
  271. $arg = (string)$arg;
  272. $str = htmlspecialchars(substr($arg, 0, 16));
  273. if (strlen($arg) > 16) $str .= '&hellip;';
  274. $args[] = "'" . $str . "'";
  275. }
  276. }
  277. }
  278. $html .= '(' . implode(', ', $args) . ')'
  279. . '</td>'
  280. . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
  281. . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
  282. . '</td></tr>' . "\n";
  283. }
  284. $html .= '<tr><td style="text-align: center;">' . ($k + 1) . '</td>'
  285. . '<td>{main}</td>'
  286. . '<td>&nbsp;</td></tr>' . "\n"
  287. . '</table>';
  288. return $html;
  289. }
  290. /**
  291. * Function must be public to call on caused exceptions
  292. * @param array
  293. */
  294. public function getCauseMessage(&$causes)
  295. {
  296. $trace = $this->getTraceSafe();
  297. $cause = array('class' => get_class($this),
  298. 'message' => $this->message,
  299. 'file' => 'unknown',
  300. 'line' => 'unknown');
  301. if (isset($trace[0])) {
  302. if (isset($trace[0]['file'])) {
  303. $cause['file'] = $trace[0]['file'];
  304. $cause['line'] = $trace[0]['line'];
  305. }
  306. }
  307. $causes[] = $cause;
  308. if ($this->cause instanceof PEAR_Exception) {
  309. $this->cause->getCauseMessage($causes);
  310. } elseif ($this->cause instanceof Exception) {
  311. $causes[] = array('class' => get_class($this->cause),
  312. 'message' => $this->cause->getMessage(),
  313. 'file' => $this->cause->getFile(),
  314. 'line' => $this->cause->getLine());
  315. } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
  316. $causes[] = array('class' => get_class($this->cause),
  317. 'message' => $this->cause->getMessage(),
  318. 'file' => 'unknown',
  319. 'line' => 'unknown');
  320. } elseif (is_array($this->cause)) {
  321. foreach ($this->cause as $cause) {
  322. if ($cause instanceof PEAR_Exception) {
  323. $cause->getCauseMessage($causes);
  324. } elseif ($cause instanceof Exception) {
  325. $causes[] = array('class' => get_class($cause),
  326. 'message' => $cause->getMessage(),
  327. 'file' => $cause->getFile(),
  328. 'line' => $cause->getLine());
  329. } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
  330. $causes[] = array('class' => get_class($cause),
  331. 'message' => $cause->getMessage(),
  332. 'file' => 'unknown',
  333. 'line' => 'unknown');
  334. } elseif (is_array($cause) && isset($cause['message'])) {
  335. // PEAR_ErrorStack warning
  336. $causes[] = array(
  337. 'class' => $cause['package'],
  338. 'message' => $cause['message'],
  339. 'file' => isset($cause['context']['file']) ?
  340. $cause['context']['file'] :
  341. 'unknown',
  342. 'line' => isset($cause['context']['line']) ?
  343. $cause['context']['line'] :
  344. 'unknown',
  345. );
  346. }
  347. }
  348. }
  349. }
  350. public function getTraceSafe()
  351. {
  352. if (!isset($this->_trace)) {
  353. $this->_trace = $this->getTrace();
  354. if (empty($this->_trace)) {
  355. $backtrace = debug_backtrace();
  356. $this->_trace = array($backtrace[count($backtrace) - 1]);
  357. }
  358. }
  359. return $this->_trace;
  360. }
  361. public function toText()
  362. {
  363. $causes = array();
  364. $this->getCauseMessage($causes);
  365. $causeMsg = '';
  366. foreach ($causes as $i => $cause) {
  367. $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
  368. . $cause['message'] . ' in ' . $cause['file']
  369. . ' on line ' . $cause['line'] . "\n";
  370. }
  371. return $causeMsg . $this->getTraceAsString();
  372. }
  373. }