Exception.php 12 KB

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