PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/BaseException.php

http://github.com/atk4/atk4
PHP | 495 lines | 266 code | 63 blank | 166 comment | 32 complexity | 0953ce3ff807e85965fc60a005e56bfe MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * BaseException is parent of all exceptions in Agile Toolkit which
  4. * are meant to be for informational purposes. There are also some
  5. * exceptions (StopInit) which are used for data-flow.
  6. */
  7. class BaseException extends Exception
  8. {
  9. // Exception defines it's methods as "final", which is complete nonsence
  10. // and incorrect behavor in my opinion. Therefore I need to re-declare
  11. // it's class and re-define the methods so I could extend my own methods
  12. // in my classes.
  13. /** @var array Backtrace array */
  14. public $my_backtrace;
  15. /** @var int Backtrace shift */
  16. public $shift = 0;
  17. /** @var string Classname of exception */
  18. public $name;
  19. /** @var string|int error code */
  20. public $code;
  21. /** @var array Array with more info */
  22. public $more_info = array();
  23. /** @var string Plain text recommendation on how the problem can be solved */
  24. public $recommendation;
  25. /** @var array Array of available actions */
  26. public $actions = array();
  27. /** @var Exception Link to another exception which caused this one */
  28. public $by_exception = null;
  29. /** @var AbstractObject Link to object into which we added this object */
  30. public $owner;
  31. /** @var App_CLI Always points to current Application */
  32. public $app;
  33. /**
  34. * @deprecated 4.3.0 Left for compatibility with ATK 4.2 and lower, use ->app instead
  35. */
  36. public $api;
  37. /**
  38. * Initialization.
  39. */
  40. public function init()
  41. {
  42. }
  43. /**
  44. * On class construct.
  45. *
  46. * @param string $msg Error message
  47. * @param string|int $code Error code
  48. */
  49. public function __construct($msg, $code = 0)
  50. {
  51. parent::__construct($msg, $code);
  52. $this->collectBasicData($code);
  53. }
  54. /**
  55. * Collect basic data of exception.
  56. *
  57. * @param string|int $code Error code
  58. */
  59. public function collectBasicData($code)
  60. {
  61. $this->name = get_class($this);
  62. $this->my_backtrace = debug_backtrace();
  63. array_shift($this->my_backtrace);
  64. array_shift($this->my_backtrace);
  65. }
  66. /**
  67. * Call this to add additional information to the exception you are about
  68. * to throw.
  69. *
  70. * @param string $key
  71. * @param mixed $value
  72. *
  73. * @return $this
  74. */
  75. public function addMoreInfo($key, $value)
  76. {
  77. $this->more_info[$key] = $value;
  78. return $this;
  79. }
  80. /**
  81. * Add reference to the object.
  82. * Do not call this directly, exception() method takes care of that.
  83. *
  84. * @param AbstractObject $obj
  85. */
  86. public function addThis($obj)
  87. {
  88. return $this->addMoreInfo('Raised by object', $obj);
  89. }
  90. /**
  91. * Set error code
  92. *
  93. * @param int $code
  94. */
  95. public function setCode(int $code)
  96. {
  97. $this->code = $code;
  98. }
  99. /**
  100. * Records another exception as a cause of your current exception.
  101. * Wrapping one exception inside another helps you to track problems
  102. * better.
  103. *
  104. * @param Exception $e
  105. *
  106. * @return $this
  107. */
  108. public function by(Exception $e)
  109. {
  110. $this->by_exception = $e;
  111. return $this;
  112. }
  113. /**
  114. * Actions will be displayed as links on the exception page allowing viewer
  115. * to perform additional debug functions.
  116. * addAction('show info',array('info'=>true)) will result in link to &info=1.
  117. *
  118. * @param string|array $key
  119. * @param string|array $descr
  120. *
  121. * @return $this
  122. */
  123. public function addAction($key, $descr)
  124. {
  125. if (is_array($key)) {
  126. $this->recommendation = (string) $descr;
  127. $this->actions = array_merge($this->actions, $key);
  128. return $this;
  129. }
  130. $this->actions[$key] = $descr;
  131. return $this;
  132. }
  133. /**
  134. * Return collected backtrace info.
  135. *
  136. * @return array
  137. */
  138. public function getMyTrace()
  139. {
  140. return $this->my_backtrace;
  141. }
  142. /**
  143. * Return filename from backtrace log.
  144. *
  145. * @return string
  146. */
  147. public function getMyFile()
  148. {
  149. return $this->my_backtrace[2]['file'];
  150. }
  151. /**
  152. * Return line number from backtract log.
  153. *
  154. * @return string
  155. */
  156. public function getMyLine()
  157. {
  158. return $this->my_backtrace[2]['line'];
  159. }
  160. /**
  161. * Returns HTML representation of the exception.
  162. *
  163. * @return string
  164. */
  165. public function getHTML()
  166. {
  167. $e = $this;
  168. $o = '<div class="atk-layout">';
  169. $o .= $this->getHTMLHeader();
  170. $o .= $this->getHTMLSolution();
  171. //$o.=$this->getHTMLBody();
  172. $o .= '<div class="atk-layout-row"><div class="atk-wrapper atk-section-small">';
  173. if (isset($e->more_info)) {
  174. $o .= '<h3>Additional information:</h3>';
  175. $o .= $this->print_r($e->more_info, '<ul>', '</ul>', '<li>', '</li>', ' ');
  176. }
  177. if (method_exists($e, 'getMyFile')) {
  178. $o .= '<div class="atk-effect-info">'.$e->getMyFile().':'.$e->getMyLine().'</div>';
  179. }
  180. if (method_exists($e, 'getMyTrace')) {
  181. $o .= $this->backtrace(3, $e->getMyTrace());
  182. } else {
  183. $o .= $this->backtrace(@$e->shift, $e->getTrace());
  184. }
  185. if (isset($e->by_exception)) {
  186. $o .= '<h3>This error was triggered by the following error:</h3>';
  187. if ($e->by_exception instanceof self) {
  188. $o .= $e->by_exception->getHTML();
  189. } elseif ($e->by_exception instanceof Exception) {
  190. $o .= $e->by_exception->getMessage();
  191. }
  192. }
  193. $o .= '</div></div>';
  194. return $o;
  195. }
  196. /**
  197. * @return string
  198. */
  199. public function getHeader()
  200. {
  201. return get_class($this).': '.htmlspecialchars($this->getMessage()).
  202. ($this->getCode() ? ' [code: '.$this->getCode().']' : '');
  203. }
  204. /**
  205. * @return string
  206. */
  207. public function getHTMLHeader()
  208. {
  209. return
  210. "<div class='atk-layout-row atk-effect-danger atk-swatch-red'>".
  211. "<div class='atk-wrapper atk-section-small atk-align-center'><h2>".
  212. $this->getHeader().
  213. "</h2>\n".
  214. '</div></div>';
  215. }
  216. /**
  217. * @return array
  218. */
  219. public function getSolution()
  220. {
  221. return $this->actions;
  222. }
  223. /**
  224. * @return string
  225. */
  226. public function getHTMLSolution()
  227. {
  228. $solution = $this->getSolution();
  229. if (empty($solution)) {
  230. return '';
  231. }
  232. return
  233. "<div class='atk-layout-row atk-effect-info'>".
  234. "<div class='atk-wrapper atk-section-small atk-swatch-white atk-align-center'>".
  235. "<h3>".$this->recommendation."</h3>".
  236. $this->getHTMLActions().
  237. '</div></div>';
  238. }
  239. /**
  240. * @return string
  241. */
  242. public function getHTMLActions()
  243. {
  244. $o = '';
  245. foreach ($this->actions as $label => $url) {
  246. $o .= "<a href='".$url."' class='atk-button atk-swatch-yellow'>".$label."</a>\n";
  247. }
  248. return $o;
  249. }
  250. /**
  251. * Utility.
  252. *
  253. * @param array|object|string $key
  254. * @param string $gs
  255. * @param string $ge
  256. * @param string $ls
  257. * @param string $le
  258. * @param string $ind
  259. *
  260. * @return null|string
  261. */
  262. public function print_r($key, $gs, $ge, $ls, $le, $ind = ' ')
  263. {
  264. $o = '';
  265. if (strlen($ind) > 3) {
  266. return;
  267. }
  268. if (is_array($key)) {
  269. $o = $gs;
  270. foreach ($key as $a => $b) {
  271. $o .= $ind.$ls.$a.': '.$this->print_r($b, $gs, $ge, $ls, $le, $ind.' ').$le;
  272. }
  273. $o .= $ge;
  274. } elseif (is_object($key)) {
  275. $o .= 'Object '.get_class($key);
  276. } else {
  277. $o .= $gs ? htmlspecialchars($key) : $key;
  278. }
  279. return $o;
  280. }
  281. /**
  282. * Classes define a DOC constant which points to a on-line resource
  283. * containing documentation for given class. This method will
  284. * return full URL for the specified object.
  285. *
  286. * @param AbstractObject $o
  287. *
  288. * @return bool|string
  289. */
  290. public function getDocURL($o)
  291. {
  292. if (!is_object($o)) {
  293. return false;
  294. }
  295. if (!$o instanceof AbstractObject) {
  296. return false;
  297. }
  298. /*$refl = new ReflectionClass($o);
  299. $parent = $refl->getParentClass();
  300. if($parent) {
  301. // check to make sure property is overriden in child
  302. $const = $parent->getConstants();
  303. var_Dump($const);
  304. if ($const['DOC'] == $o::DOC) return false;
  305. }
  306. */
  307. $url = $o::DOC;
  308. if (substr($url, 0, 4) != 'http') {
  309. return 'http://book.agiletoolkit.org/'.$url.'.html';
  310. }
  311. return $url;
  312. }
  313. /**
  314. * @param int $sh
  315. * @param array $backtrace
  316. *
  317. * @return string
  318. */
  319. public function backtrace($sh = null, $backtrace = null)
  320. {
  321. $output = '<div class="atk-box-small atk-table atk-table-zebra">';
  322. $output .= "<table>\n";
  323. $output .= "<tr><th align='right'>File</th><th>Object Name</th><th>Stack Trace</th><th>Help</th></tr>";
  324. if (!isset($backtrace)) {
  325. $backtrace = debug_backtrace();
  326. }
  327. $sh -= 2;
  328. $n = 0;
  329. foreach ($backtrace as $bt) {
  330. ++$n;
  331. $args = '';
  332. if (!isset($bt['args'])) {
  333. continue;
  334. }
  335. foreach ($bt['args'] as $a) {
  336. if (!empty($args)) {
  337. $args .= ', ';
  338. }
  339. switch (gettype($a)) {
  340. case 'integer':
  341. case 'double':
  342. $args .= $a;
  343. break;
  344. case 'string':
  345. $a = htmlspecialchars(substr($a, 0, 128)).((strlen($a) > 128) ? '...' : '');
  346. $args .= "\"$a\"";
  347. break;
  348. case 'array':
  349. $args .= 'Array('.count($a).')';
  350. break;
  351. case 'object':
  352. $args .= 'Object('.get_class($a).')';
  353. break;
  354. case 'resource':
  355. $args .= 'Resource('.strstr((string) $a, '#').')';
  356. break;
  357. case 'boolean':
  358. $args .= $a ? 'True' : 'False';
  359. break;
  360. case 'NULL':
  361. $args .= 'Null';
  362. break;
  363. default:
  364. $args .= 'Unknown';
  365. }
  366. }
  367. if (($sh == null && strpos($bt['file'], '/atk4/lib/') === false)
  368. || (!is_int($sh) && $bt['function'] == $sh)
  369. ) {
  370. $sh = $n;
  371. }
  372. $doc = $this->getDocURL($bt['object']);
  373. if ($doc) {
  374. $doc .= '#'.get_class($bt['object']).'::'.$bt['function'];
  375. }
  376. $output .= '<tr><td valign=top align=right class=atk-effect-'.
  377. ($sh == $n ? 'danger' : 'info').'>'.htmlspecialchars(dirname($bt['file'])).'/'.
  378. '<b>'.htmlspecialchars(basename($bt['file'])).'</b>';
  379. $output .= ":{$bt['line']}</font>&nbsp;</td>";
  380. $name = (!isset($bt['object']->name)) ? get_class($bt['object']) : $bt['object']->name;
  381. if ($name) {
  382. $output .= '<td>'.$name.'</td>';
  383. } else {
  384. $output .= '<td></td>';
  385. }
  386. $output .= '<td valign=top class=atk-effect-'.($sh == $n ? 'danger' : 'success').'>'.
  387. get_class($bt['object'])."{$bt['type']}<b>{$bt['function']}</b>($args)</td>";
  388. if ($doc) {
  389. $output .= "<td><a href='".$doc."' target='_blank'><i class='icon-book'></i></a></td>";
  390. } else {
  391. $output .= '<td>&nbsp;</td>';
  392. }
  393. $output .= '</tr>';
  394. }
  395. $output .= "</table></div>\n";
  396. return $output;
  397. }
  398. /**
  399. * Returns Textual representation of the exception.
  400. *
  401. * @return string
  402. */
  403. public function getText()
  404. {
  405. $more_info = $this->print_r($this->more_info, '[', ']', '', ',', ' ');
  406. $text = get_class($this).': '.$this->getMessage().' ('.$more_info.')'.
  407. ' in '.$this->getMyFile().':'.$this->getMyLine();
  408. return $text;
  409. }
  410. /**
  411. * Redefine this function to add additional HTML output.
  412. *
  413. * @return string
  414. */
  415. public function getDetailedHTML()
  416. {
  417. return '';
  418. }
  419. /**
  420. * Undocumented.
  421. *
  422. * @todo Check this method, looks something useless. Optionally used only in Logger class.
  423. *
  424. * @return string
  425. */
  426. public function getAdditionalMessage()
  427. {
  428. return $this->recommendation;
  429. }
  430. }