- <?php
-
- /**
- * @module Q
- */
- class Q_Exception extends Exception
- {
- /**
- * Represents a complex exception thrown in Q. It may contain more details.
- * @class Q_Exception
- * @constructor
- * @extends Exception
- * @param {array} [$params=array()] Array of parameters for the exception.
- * Used in the exception message, etc.
- * To access them later, call $e->params()
- * You can also provide a string here, which will
- * then be the exception message.
- * @param {array} [$inputFields=array()] Array of names of input fields to which the exception applies.
- * @param {array} [$code=null] Optionally pass the error code here to override the default
- */
- function __construct(
- $params = array(),
- $inputFields = array(),
- $code = null,
- $file = null,
- $line = null,
- $trace = null,
- $traceAsString = null)
- {
- if (is_string($inputFields)) {
- $inputFields = array($inputFields);
- }
- $this->inputFields = $inputFields;
- if (isset($file)) {
- $this->file = $file;
- }
- if (isset($line)) {
- $this->line = $line;
- }
- if (isset($trace)) {
- $this->trace = $trace;
- }
- if (isset($traceAsString)) {
- $this->traceAsString = $traceAsString;
- }
-
- if (is_string($params)) {
- parent::__construct($params, is_numeric($code) ? $code : -1);
- if (isset($code)) {
- $this->code = $code; // Q_Exception allows non-numeric codes
- }
- return;
- }
- $this->params = is_array($params) ? $params : array();
-
- $className = get_class($this);
- $message = isset(self::$messages[$className])
- ? Q::interpolate(self::$messages[$className], $this->params)
- : $className;
- $code = isset($code) ? $code :
- (isset(self::$codes[$className]) ? self::$codes[$className] : 1);
- parent::__construct($message, $code);
- }
-
- /**
- * Construct a Q_Exception object from an Exception.
- * @method $exception
- * @param $exception
- * @return {Q_Exception}
- */
- static function fromException($exception)
- {
- $result = new Q_Exception();
- $fields = get_object_vars($exception);
- foreach ($fields as $k => $v) {
- $result->$k = $v;
- }
- return $result;
- }
-
- /**
- * @method __get
- * @param {string} $param
- * @return {mixed}
- */
- function __get($param)
- {
- return isset($this->params[$param])
- ? $this->params[$param]
- : null;
- }
-
- /**
- * @method __set
- * @param {string} $param
- * @param {mixed} $value
- */
- function __set($param, $value) {
- $this->params[$param] = $value;
- }
-
- /**
- * Returns the array of parameters the exception was created with.
- * @method params
- * @return {array}
- */
- function params()
- {
- return $this->params;
- }
-
- /**
- * Returns the array of names of input fields the exception applies to.
- * @method inputFields
- * @return {array}
- */
- function inputFields()
- {
- return $this->inputFields;
- }
-
- /**
- * Registers a new exception class that extends Q_Exception
- * @method add
- * @static
- * @param {string} $className The name of the exception class.
- * @param {string} $message The description of the error. Will be eval()-ed before rendering,
- * so it can include references to parameters, such as $my_param.
- * @param {array} [$rethrowDestClasses=array()] The name of the class that should handle this exception,
- * @param {string} [$baseClassName=null] Here you can pass the name of different base class than Q_Exception
- * should it be thrown. Almost all catch() blocks in your code should use
- * `Q_Exception::rethrow($e, __CLASS__)` as the first statement,
- * if the exception might have to be re-thrown further down the stack.
- */
- static function add(
- $className,
- $message,
- $rethrowDestClasses = array(),
- $baseClassName = null)
- {
- if (is_string($rethrowDestClasses)) {
- $baseClassName = $rethrowDestClasses;
- $rethrowDestClasses = array();
- }
- static $exception_code = 10000;
- ++$exception_code; // TODO: improve this somehow
- self::$codes[$className] = $exception_code;
- self::$messages[$className] = $message;
- self::$rethrowDestClasses[$className] = $rethrowDestClasses;
- if (is_array($baseClassName)) {
- $rethrowDestClasses = $baseClassName;
- $baseClassName = null;
- }
- if (isset($baseClassName)) {
- self::$baseClasses[$className] = isset($baseClass) ? $baseClass : 'Q_Exception';
- }
- }
-
- /**
- * Use in your catch() blocks if you think the exception
- * might have to be thrown further down the stack.
- * @method rethrow
- * @static
- * @param {Exception} $exception The exception that was thrown. It is analyzed for
- * whether it should be re-thrown.
- * @param {string} $currentClass If the $rethrowDestClasses was specified in Q_Exception::add
- * when creating this exception's class, and it does not contain
- * $currentClass, this function throws the exception again.
- */
- static function rethrow(
- $exception,
- $currentClass)
- {
- if (!is_callable(array($exception, 'rethrowDestClasses'))) {
- return false;
- }
-
- $rdc = $exception->rethrowDestClasses();
- if ($rdc and !in_array($currentClass, $rdc)) {
- throw $exception;
- }
- }
-
- /**
- * Returns an array of classes to rethrow to, if any.
- * @method rethrowDestClasses
- * @return {array}
- */
- function rethrowDestClasses()
- {
- $className = get_class($this);
- if (isset(self::$rethrowDestClasses[$className])) {
- return self::$rethrowDestClasses[$className];
- }
- return array();
- }
-
- /**
- * Returns the trace array, can be overridden. Use this in your exception reporting.
- * This is the default implementation.
- * @method getTraceEx
- * @return {array}
- */
- function getTraceEx()
- {
- if (isset($this->trace)) {
- return $this->trace;
- }
- return parent::getTrace();
- }
-
- /**
- * Returns trace as string, can be overridden. Use this in your exception reporting.
- * This is the default implementation.
- * @method getTraceAsStringEx
- * @return {string}
- */
- function getTraceAsStringEx()
- {
- if (isset($this->traceAsString)) {
- return $this->traceAsString;
- }
- return parent::getTraceAsString();
- }
-
- /**
- * Converts an exception or array of exceptions to an array
- * @method toArray
- * @static
- * @param {Exception|array} $exceptions The exception object or array of exceptions to convert
- * @return {array}
- */
- static function toArray($exceptions)
- {
- if (empty($exceptions)) {
- return array();
- }
- $array_was_passed = true;
- if (!is_array($exceptions)) {
- $exceptions = array($exceptions);
- $array_was_passed = false;
- }
- $results = array();
- $show_fal = Q_Config::get('Q', 'exception', 'showFileAndLine', true);
- $show_trace = Q_Config::get('Q', 'exception', 'showTrace', true);
- foreach ($exceptions as $e) {
- if (!($e instanceof Exception)) {
- continue;
- }
- $message = $e->getMessage();
- $code = $e->getCode();
- if ($show_fal) {
- $line = $e->getLine();
- $file = $e->getFile();
- }
- if ($show_trace) {
- if (is_callable(array($e, 'getTraceEx'))) {
- $trace = $e->getTraceEx();
- } else {
- $trace = $e->getTrace();
- }
- }
- $fields = null;
- if (is_callable(array($e, 'inputFields'))) {
- $fields = $e->inputFields();
- }
- $classname = get_class($e);
- $results[] = compact('message', 'code', 'line', 'file', 'trace', 'fields', 'classname');
- }
- if ($array_was_passed) {
- return $results;
- } else {
- $ret = reset($results);
- return $ret ? $ret : array();
- }
- }
-
- /**
- * Return colored text that you can output in logs or text mode
- * @return {string}
- */
- function colored()
- {
- return self::coloredString($this);
- }
-
- /**
- * Return colored text that you can output in logs or text mode
- * Pass an exception or
- * @param {string|Exception} $exception The exception or an exception message. If the later, you must pass three more arguments.
- * @param {string} [$file]
- * @param {string} [$line]
- * @param {string} [$trace]
- * @return {string}
- */
- static function coloredString($message, $file=null, $line=null, $trace=null)
- {
- if ($message instanceof Exception) {
- $e = $message;
- $traceString = is_callable(array($e, 'getTraceAsStringEx'))
- ? $e->getTraceAsStringEx()
- : $e->getTraceAsString();
- return self::coloredString(
- $e->getMessage(),
- $e->getFile(),
- $e->getLine(),
- $traceString
- );
- }
- $colors = Q_Config::get('Q', 'exception', 'colors', array());
- Q::autoload('Q_Utils');
- $fields = array(
- 'message' => $message,
- 'fileAndLine' => "in $file ($line)",
- 'trace' => $trace
- );
- foreach ($fields as $f => $v) {
- $c0 = isset($colors[$f][0]) ? $colors[$f][0] : null;
- $c1 = isset($colors[$f][1]) ? $colors[$f][1] : null;
- $fields[$f] = Q_Utils::colored($v, $c0, $c1);
- }
- $reset = Q_Utils::colored("", "", "");
- return "$fields[message]\n\n$fields[fileAndLine]\n$fields[trace]\n";
- }
-
- /**
- * @property $params
- * @protected
- * @type array
- */
- public $params = array();
- /**
- * @property $inputFields
- * @protected
- * @type array
- */
- public $inputFields = array();
-
- /**
- * @property $codes
- * @protected
- * @type array
- */
- protected static $codes = array();
- /**
- * @property $messages
- * @protected
- * @type array
- */
- protected static $messages = array();
- /**
- * @property $rethrowDestClasses
- * @protected
- * @type array
- */
- protected static $rethrowDestClasses = array();
- /**
- * @property $baseClasses
- * @protected
- * @type array
- */
- protected static $baseClasses = array();
- /**
- * @property $trace
- * @protected
- * @type array
- */
- protected $trace = null;
- /**
- * @property $traceAsString
- * @protected
- * @type string
- */
- protected $traceAsString = null;
- /**
- * @property $code
- * @protected
- * @type string
- */
- protected $code = null;
- }
-
-