platform/classes/Q/Exception.php - Q Platform
Show:

File: platform/classes/Q/Exception.php

  1. <?php
  2.  
  3. /**
  4. * @module Q
  5. */
  6. class Q_Exception extends Exception
  7. {
  8. /**
  9. * Represents a complex exception thrown in Q. It may contain more details.
  10. * @class Q_Exception
  11. * @constructor
  12. * @extends Exception
  13. * @param {array} [$params=array()] Array of parameters for the exception.
  14. * Used in the exception message, etc.
  15. * To access them later, call $e->params()
  16. * You can also provide a string here, which will
  17. * then be the exception message.
  18. * @param {array} [$inputFields=array()] Array of names of input fields to which the exception applies.
  19. * @param {array} [$code=null] Optionally pass the error code here to override the default
  20. */
  21. function __construct(
  22. $params = array(),
  23. $inputFields = array(),
  24. $code = null,
  25. $file = null,
  26. $line = null,
  27. $trace = null,
  28. $traceAsString = null)
  29. {
  30. if (is_string($inputFields)) {
  31. $inputFields = array($inputFields);
  32. }
  33. $this->inputFields = $inputFields;
  34. if (isset($file)) {
  35. $this->file = $file;
  36. }
  37. if (isset($line)) {
  38. $this->line = $line;
  39. }
  40. if (isset($trace)) {
  41. $this->trace = $trace;
  42. }
  43. if (isset($traceAsString)) {
  44. $this->traceAsString = $traceAsString;
  45. }
  46. if (is_string($params)) {
  47. parent::__construct($params, is_numeric($code) ? $code : -1);
  48. if (isset($code)) {
  49. $this->code = $code; // Q_Exception allows non-numeric codes
  50. }
  51. return;
  52. }
  53. $this->params = is_array($params) ? $params : array();
  54.  
  55. $className = get_class($this);
  56. $message = isset(self::$messages[$className])
  57. ? Q::interpolate(self::$messages[$className], $this->params)
  58. : $className;
  59. $code = isset($code) ? $code :
  60. (isset(self::$codes[$className]) ? self::$codes[$className] : 1);
  61. parent::__construct($message, $code);
  62. }
  63. /**
  64. * Construct a Q_Exception object from an Exception.
  65. * @method $exception
  66. * @param $exception
  67. * @return {Q_Exception}
  68. */
  69. static function fromException($exception)
  70. {
  71. $result = new Q_Exception();
  72. $fields = get_object_vars($exception);
  73. foreach ($fields as $k => $v) {
  74. $result->$k = $v;
  75. }
  76. return $result;
  77. }
  78. /**
  79. * @method __get
  80. * @param {string} $param
  81. * @return {mixed}
  82. */
  83. function __get($param)
  84. {
  85. return isset($this->params[$param])
  86. ? $this->params[$param]
  87. : null;
  88. }
  89. /**
  90. * @method __set
  91. * @param {string} $param
  92. * @param {mixed} $value
  93. */
  94. function __set($param, $value) {
  95. $this->params[$param] = $value;
  96. }
  97. /**
  98. * Returns the array of parameters the exception was created with.
  99. * @method params
  100. * @return {array}
  101. */
  102. function params()
  103. {
  104. return $this->params;
  105. }
  106. /**
  107. * Returns the array of names of input fields the exception applies to.
  108. * @method inputFields
  109. * @return {array}
  110. */
  111. function inputFields()
  112. {
  113. return $this->inputFields;
  114. }
  115. /**
  116. * Registers a new exception class that extends Q_Exception
  117. * @method add
  118. * @static
  119. * @param {string} $className The name of the exception class.
  120. * @param {string} $message The description of the error. Will be eval()-ed before rendering,
  121. * so it can include references to parameters, such as $my_param.
  122. * @param {array} [$rethrowDestClasses=array()] The name of the class that should handle this exception,
  123. * @param {string} [$baseClassName=null] Here you can pass the name of different base class than Q_Exception
  124. * should it be thrown. Almost all catch() blocks in your code should use
  125. * `Q_Exception::rethrow($e, __CLASS__)` as the first statement,
  126. * if the exception might have to be re-thrown further down the stack.
  127. */
  128. static function add(
  129. $className,
  130. $message,
  131. $rethrowDestClasses = array(),
  132. $baseClassName = null)
  133. {
  134. if (is_string($rethrowDestClasses)) {
  135. $baseClassName = $rethrowDestClasses;
  136. $rethrowDestClasses = array();
  137. }
  138. static $exception_code = 10000;
  139. ++$exception_code; // TODO: improve this somehow
  140. self::$codes[$className] = $exception_code;
  141. self::$messages[$className] = $message;
  142. self::$rethrowDestClasses[$className] = $rethrowDestClasses;
  143. if (is_array($baseClassName)) {
  144. $rethrowDestClasses = $baseClassName;
  145. $baseClassName = null;
  146. }
  147. if (isset($baseClassName)) {
  148. self::$baseClasses[$className] = isset($baseClass) ? $baseClass : 'Q_Exception';
  149. }
  150. }
  151. /**
  152. * Use in your catch() blocks if you think the exception
  153. * might have to be thrown further down the stack.
  154. * @method rethrow
  155. * @static
  156. * @param {Exception} $exception The exception that was thrown. It is analyzed for
  157. * whether it should be re-thrown.
  158. * @param {string} $currentClass If the $rethrowDestClasses was specified in Q_Exception::add
  159. * when creating this exception's class, and it does not contain
  160. * $currentClass, this function throws the exception again.
  161. */
  162. static function rethrow(
  163. $exception,
  164. $currentClass)
  165. {
  166. if (!is_callable(array($exception, 'rethrowDestClasses'))) {
  167. return false;
  168. }
  169.  
  170. $rdc = $exception->rethrowDestClasses();
  171. if ($rdc and !in_array($currentClass, $rdc)) {
  172. throw $exception;
  173. }
  174. }
  175. /**
  176. * Returns an array of classes to rethrow to, if any.
  177. * @method rethrowDestClasses
  178. * @return {array}
  179. */
  180. function rethrowDestClasses()
  181. {
  182. $className = get_class($this);
  183. if (isset(self::$rethrowDestClasses[$className])) {
  184. return self::$rethrowDestClasses[$className];
  185. }
  186. return array();
  187. }
  188. /**
  189. * Returns the trace array, can be overridden. Use this in your exception reporting.
  190. * This is the default implementation.
  191. * @method getTraceEx
  192. * @return {array}
  193. */
  194. function getTraceEx()
  195. {
  196. if (isset($this->trace)) {
  197. return $this->trace;
  198. }
  199. return parent::getTrace();
  200. }
  201. /**
  202. * Returns trace as string, can be overridden. Use this in your exception reporting.
  203. * This is the default implementation.
  204. * @method getTraceAsStringEx
  205. * @return {string}
  206. */
  207. function getTraceAsStringEx()
  208. {
  209. if (isset($this->traceAsString)) {
  210. return $this->traceAsString;
  211. }
  212. return parent::getTraceAsString();
  213. }
  214. /**
  215. * Converts an exception or array of exceptions to an array
  216. * @method toArray
  217. * @static
  218. * @param {Exception|array} $exceptions The exception object or array of exceptions to convert
  219. * @return {array}
  220. */
  221. static function toArray($exceptions)
  222. {
  223. if (empty($exceptions)) {
  224. return array();
  225. }
  226. $array_was_passed = true;
  227. if (!is_array($exceptions)) {
  228. $exceptions = array($exceptions);
  229. $array_was_passed = false;
  230. }
  231. $results = array();
  232. $show_fal = Q_Config::get('Q', 'exception', 'showFileAndLine', true);
  233. $show_trace = Q_Config::get('Q', 'exception', 'showTrace', true);
  234. foreach ($exceptions as $e) {
  235. if (!($e instanceof Exception)) {
  236. continue;
  237. }
  238. $message = $e->getMessage();
  239. $code = $e->getCode();
  240. if ($show_fal) {
  241. $line = $e->getLine();
  242. $file = $e->getFile();
  243. }
  244. if ($show_trace) {
  245. if (is_callable(array($e, 'getTraceEx'))) {
  246. $trace = $e->getTraceEx();
  247. } else {
  248. $trace = $e->getTrace();
  249. }
  250. }
  251. $fields = null;
  252. if (is_callable(array($e, 'inputFields'))) {
  253. $fields = $e->inputFields();
  254. }
  255. $classname = get_class($e);
  256. $results[] = compact('message', 'code', 'line', 'file', 'trace', 'fields', 'classname');
  257. }
  258. if ($array_was_passed) {
  259. return $results;
  260. } else {
  261. $ret = reset($results);
  262. return $ret ? $ret : array();
  263. }
  264. }
  265. /**
  266. * Return colored text that you can output in logs or text mode
  267. * @return {string}
  268. */
  269. function colored()
  270. {
  271. return self::coloredString($this);
  272. }
  273. /**
  274. * Return colored text that you can output in logs or text mode
  275. * Pass an exception or
  276. * @param {string|Exception} $exception The exception or an exception message. If the later, you must pass three more arguments.
  277. * @param {string} [$file]
  278. * @param {string} [$line]
  279. * @param {string} [$trace]
  280. * @return {string}
  281. */
  282. static function coloredString($message, $file=null, $line=null, $trace=null)
  283. {
  284. if ($message instanceof Exception) {
  285. $e = $message;
  286. $traceString = is_callable(array($e, 'getTraceAsStringEx'))
  287. ? $e->getTraceAsStringEx()
  288. : $e->getTraceAsString();
  289. return self::coloredString(
  290. $e->getMessage(),
  291. $e->getFile(),
  292. $e->getLine(),
  293. $traceString
  294. );
  295. }
  296. $colors = Q_Config::get('Q', 'exception', 'colors', array());
  297. Q::autoload('Q_Utils');
  298. $fields = array(
  299. 'message' => $message,
  300. 'fileAndLine' => "in $file ($line)",
  301. 'trace' => $trace
  302. );
  303. foreach ($fields as $f => $v) {
  304. $c0 = isset($colors[$f][0]) ? $colors[$f][0] : null;
  305. $c1 = isset($colors[$f][1]) ? $colors[$f][1] : null;
  306. $fields[$f] = Q_Utils::colored($v, $c0, $c1);
  307. }
  308. $reset = Q_Utils::colored("", "", "");
  309. return "$fields[message]\n\n$fields[fileAndLine]\n$fields[trace]\n";
  310. }
  311. /**
  312. * @property $params
  313. * @protected
  314. * @type array
  315. */
  316. public $params = array();
  317. /**
  318. * @property $inputFields
  319. * @protected
  320. * @type array
  321. */
  322. public $inputFields = array();
  323. /**
  324. * @property $codes
  325. * @protected
  326. * @type array
  327. */
  328. protected static $codes = array();
  329. /**
  330. * @property $messages
  331. * @protected
  332. * @type array
  333. */
  334. protected static $messages = array();
  335. /**
  336. * @property $rethrowDestClasses
  337. * @protected
  338. * @type array
  339. */
  340. protected static $rethrowDestClasses = array();
  341. /**
  342. * @property $baseClasses
  343. * @protected
  344. * @type array
  345. */
  346. protected static $baseClasses = array();
  347. /**
  348. * @property $trace
  349. * @protected
  350. * @type array
  351. */
  352. protected $trace = null;
  353. /**
  354. * @property $traceAsString
  355. * @protected
  356. * @type string
  357. */
  358. protected $traceAsString = null;
  359. /**
  360. * @property $code
  361. * @protected
  362. * @type string
  363. */
  364. protected $code = null;
  365. }
  366.