Chris@18: Chris@18: * @author Hans Lellelid Chris@18: * @author Bertrand Mansion Chris@18: * @author Greg Beaver Chris@18: * @copyright 1997-2009 The Authors Chris@18: * @license http://opensource.org/licenses/bsd-license.php New BSD License Chris@18: * @link http://pear.php.net/package/PEAR_Exception Chris@18: * @since File available since Release 1.0.0 Chris@18: */ Chris@18: Chris@18: Chris@18: /** Chris@18: * Base PEAR_Exception Class Chris@18: * Chris@18: * 1) Features: Chris@18: * Chris@18: * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception)) Chris@18: * - Definable triggers, shot when exceptions occur Chris@18: * - Pretty and informative error messages Chris@18: * - Added more context info available (like class, method or cause) Chris@18: * - cause can be a PEAR_Exception or an array of mixed Chris@18: * PEAR_Exceptions/PEAR_ErrorStack warnings Chris@18: * - callbacks for specific exception classes and their children Chris@18: * Chris@18: * 2) Ideas: Chris@18: * Chris@18: * - Maybe a way to define a 'template' for the output Chris@18: * Chris@18: * 3) Inherited properties from PHP Exception Class: Chris@18: * Chris@18: * protected $message Chris@18: * protected $code Chris@18: * protected $line Chris@18: * protected $file Chris@18: * private $trace Chris@18: * Chris@18: * 4) Inherited methods from PHP Exception Class: Chris@18: * Chris@18: * __clone Chris@18: * __construct Chris@18: * getMessage Chris@18: * getCode Chris@18: * getFile Chris@18: * getLine Chris@18: * getTraceSafe Chris@18: * getTraceSafeAsString Chris@18: * __toString Chris@18: * Chris@18: * 5) Usage example Chris@18: * Chris@18: * Chris@18: * require_once 'PEAR/Exception.php'; Chris@18: * Chris@18: * class Test { Chris@18: * function foo() { Chris@18: * throw new PEAR_Exception('Error Message', ERROR_CODE); Chris@18: * } Chris@18: * } Chris@18: * Chris@18: * function myLogger($pear_exception) { Chris@18: * echo $pear_exception->getMessage(); Chris@18: * } Chris@18: * // each time a exception is thrown the 'myLogger' will be called Chris@18: * // (its use is completely optional) Chris@18: * PEAR_Exception::addObserver('myLogger'); Chris@18: * $test = new Test; Chris@18: * try { Chris@18: * $test->foo(); Chris@18: * } catch (PEAR_Exception $e) { Chris@18: * print $e; Chris@18: * } Chris@18: * Chris@18: * Chris@18: * @category PEAR Chris@18: * @package PEAR_Exception Chris@18: * @author Tomas V.V.Cox Chris@18: * @author Hans Lellelid Chris@18: * @author Bertrand Mansion Chris@18: * @author Greg Beaver Chris@18: * @copyright 1997-2009 The Authors Chris@18: * @license http://opensource.org/licenses/bsd-license.php New BSD License Chris@18: * @version Release: @package_version@ Chris@18: * @link http://pear.php.net/package/PEAR_Exception Chris@18: * @since Class available since Release 1.0.0 Chris@18: */ Chris@18: class PEAR_Exception extends Exception Chris@18: { Chris@18: const OBSERVER_PRINT = -2; Chris@18: const OBSERVER_TRIGGER = -4; Chris@18: const OBSERVER_DIE = -8; Chris@18: protected $cause; Chris@18: private static $_observers = array(); Chris@18: private static $_uniqueid = 0; Chris@18: private $_trace; Chris@18: Chris@18: /** Chris@18: * Supported signatures: Chris@18: * - PEAR_Exception(string $message); Chris@18: * - PEAR_Exception(string $message, int $code); Chris@18: * - PEAR_Exception(string $message, Exception $cause); Chris@18: * - PEAR_Exception(string $message, Exception $cause, int $code); Chris@18: * - PEAR_Exception(string $message, PEAR_Error $cause); Chris@18: * - PEAR_Exception(string $message, PEAR_Error $cause, int $code); Chris@18: * - PEAR_Exception(string $message, array $causes); Chris@18: * - PEAR_Exception(string $message, array $causes, int $code); Chris@18: * Chris@18: * @param string $message exception message Chris@18: * @param int|Exception|PEAR_Error|array|null $p2 exception cause Chris@18: * @param int|null $p3 exception code or null Chris@18: */ Chris@18: public function __construct($message, $p2 = null, $p3 = null) Chris@18: { Chris@18: if (is_int($p2)) { Chris@18: $code = $p2; Chris@18: $this->cause = null; Chris@18: } elseif (is_object($p2) || is_array($p2)) { Chris@18: // using is_object allows both Exception and PEAR_Error Chris@18: if (is_object($p2) && !($p2 instanceof Exception)) { Chris@18: if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) { Chris@18: throw new PEAR_Exception( Chris@18: 'exception cause must be Exception, ' . Chris@18: 'array, or PEAR_Error' Chris@18: ); Chris@18: } Chris@18: } Chris@18: $code = $p3; Chris@18: if (is_array($p2) && isset($p2['message'])) { Chris@18: // fix potential problem of passing in a single warning Chris@18: $p2 = array($p2); Chris@18: } Chris@18: $this->cause = $p2; Chris@18: } else { Chris@18: $code = null; Chris@18: $this->cause = null; Chris@18: } Chris@18: parent::__construct($message, $code); Chris@18: $this->signal(); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Add an exception observer Chris@18: * Chris@18: * @param mixed $callback - A valid php callback, see php func is_callable() Chris@18: * - A PEAR_Exception::OBSERVER_* constant Chris@18: * - An array(const PEAR_Exception::OBSERVER_*, Chris@18: * mixed $options) Chris@18: * @param string $label The name of the observer. Use this if you want Chris@18: * to remove it later with removeObserver() Chris@18: * Chris@18: * @return void Chris@18: */ Chris@18: public static function addObserver($callback, $label = 'default') Chris@18: { Chris@18: self::$_observers[$label] = $callback; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Remove an exception observer Chris@18: * Chris@18: * @param string $label Name of the observer Chris@18: * Chris@18: * @return void Chris@18: */ Chris@18: public static function removeObserver($label = 'default') Chris@18: { Chris@18: unset(self::$_observers[$label]); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Generate a unique ID for an observer Chris@18: * Chris@18: * @return int unique identifier for an observer Chris@18: */ Chris@18: public static function getUniqueId() Chris@18: { Chris@18: return self::$_uniqueid++; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Send a signal to all observers Chris@18: * Chris@18: * @return void Chris@18: */ Chris@18: protected function signal() Chris@18: { Chris@18: foreach (self::$_observers as $func) { Chris@18: if (is_callable($func)) { Chris@18: call_user_func($func, $this); Chris@18: continue; Chris@18: } Chris@18: settype($func, 'array'); Chris@18: switch ($func[0]) { Chris@18: case self::OBSERVER_PRINT : Chris@18: $f = (isset($func[1])) ? $func[1] : '%s'; Chris@18: printf($f, $this->getMessage()); Chris@18: break; Chris@18: case self::OBSERVER_TRIGGER : Chris@18: $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE; Chris@18: trigger_error($this->getMessage(), $f); Chris@18: break; Chris@18: case self::OBSERVER_DIE : Chris@18: $f = (isset($func[1])) ? $func[1] : '%s'; Chris@18: die(printf($f, $this->getMessage())); Chris@18: break; Chris@18: default: Chris@18: trigger_error('invalid observer type', E_USER_WARNING); Chris@18: } Chris@18: } Chris@18: } Chris@18: Chris@18: /** Chris@18: * Return specific error information that can be used for more detailed Chris@18: * error messages or translation. Chris@18: * Chris@18: * This method may be overridden in child exception classes in order Chris@18: * to add functionality not present in PEAR_Exception and is a placeholder Chris@18: * to define API Chris@18: * Chris@18: * The returned array must be an associative array of parameter => value like so: Chris@18: *
Chris@18:      * array('name' => $name, 'context' => array(...))
Chris@18:      * 
Chris@18: * Chris@18: * @return array Chris@18: */ Chris@18: public function getErrorData() Chris@18: { Chris@18: return array(); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Returns the exception that caused this exception to be thrown Chris@18: * Chris@18: * @return Exception|array The context of the exception Chris@18: */ Chris@18: public function getCause() Chris@18: { Chris@18: return $this->cause; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Function must be public to call on caused exceptions Chris@18: * Chris@18: * @param array $causes Array that gets filled. Chris@18: * Chris@18: * @return void Chris@18: */ Chris@18: public function getCauseMessage(&$causes) Chris@18: { Chris@18: $trace = $this->getTraceSafe(); Chris@18: $cause = array('class' => get_class($this), Chris@18: 'message' => $this->message, Chris@18: 'file' => 'unknown', Chris@18: 'line' => 'unknown'); Chris@18: if (isset($trace[0])) { Chris@18: if (isset($trace[0]['file'])) { Chris@18: $cause['file'] = $trace[0]['file']; Chris@18: $cause['line'] = $trace[0]['line']; Chris@18: } Chris@18: } Chris@18: $causes[] = $cause; Chris@18: if ($this->cause instanceof PEAR_Exception) { Chris@18: $this->cause->getCauseMessage($causes); Chris@18: } elseif ($this->cause instanceof Exception) { Chris@18: $causes[] = array('class' => get_class($this->cause), Chris@18: 'message' => $this->cause->getMessage(), Chris@18: 'file' => $this->cause->getFile(), Chris@18: 'line' => $this->cause->getLine()); Chris@18: } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) { Chris@18: $causes[] = array('class' => get_class($this->cause), Chris@18: 'message' => $this->cause->getMessage(), Chris@18: 'file' => 'unknown', Chris@18: 'line' => 'unknown'); Chris@18: } elseif (is_array($this->cause)) { Chris@18: foreach ($this->cause as $cause) { Chris@18: if ($cause instanceof PEAR_Exception) { Chris@18: $cause->getCauseMessage($causes); Chris@18: } elseif ($cause instanceof Exception) { Chris@18: $causes[] = array('class' => get_class($cause), Chris@18: 'message' => $cause->getMessage(), Chris@18: 'file' => $cause->getFile(), Chris@18: 'line' => $cause->getLine()); Chris@18: } elseif (class_exists('PEAR_Error') Chris@18: && $cause instanceof PEAR_Error Chris@18: ) { Chris@18: $causes[] = array('class' => get_class($cause), Chris@18: 'message' => $cause->getMessage(), Chris@18: 'file' => 'unknown', Chris@18: 'line' => 'unknown'); Chris@18: } elseif (is_array($cause) && isset($cause['message'])) { Chris@18: // PEAR_ErrorStack warning Chris@18: $causes[] = array( Chris@18: 'class' => $cause['package'], Chris@18: 'message' => $cause['message'], Chris@18: 'file' => isset($cause['context']['file']) ? Chris@18: $cause['context']['file'] : Chris@18: 'unknown', Chris@18: 'line' => isset($cause['context']['line']) ? Chris@18: $cause['context']['line'] : Chris@18: 'unknown', Chris@18: ); Chris@18: } Chris@18: } Chris@18: } Chris@18: } Chris@18: Chris@18: /** Chris@18: * Build a backtrace and return it Chris@18: * Chris@18: * @return array Backtrace Chris@18: */ Chris@18: public function getTraceSafe() Chris@18: { Chris@18: if (!isset($this->_trace)) { Chris@18: $this->_trace = $this->getTrace(); Chris@18: if (empty($this->_trace)) { Chris@18: $backtrace = debug_backtrace(); Chris@18: $this->_trace = array($backtrace[count($backtrace)-1]); Chris@18: } Chris@18: } Chris@18: return $this->_trace; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Gets the first class of the backtrace Chris@18: * Chris@18: * @return string Class name Chris@18: */ Chris@18: public function getErrorClass() Chris@18: { Chris@18: $trace = $this->getTraceSafe(); Chris@18: return $trace[0]['class']; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Gets the first method of the backtrace Chris@18: * Chris@18: * @return string Method/function name Chris@18: */ Chris@18: public function getErrorMethod() Chris@18: { Chris@18: $trace = $this->getTraceSafe(); Chris@18: return $trace[0]['function']; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Converts the exception to a string (HTML or plain text) Chris@18: * Chris@18: * @return string String representation Chris@18: * Chris@18: * @see toHtml() Chris@18: * @see toText() Chris@18: */ Chris@18: public function __toString() Chris@18: { Chris@18: if (isset($_SERVER['REQUEST_URI'])) { Chris@18: return $this->toHtml(); Chris@18: } Chris@18: return $this->toText(); Chris@18: } Chris@18: Chris@18: /** Chris@18: * Generates a HTML representation of the exception Chris@18: * Chris@18: * @return string HTML code Chris@18: */ Chris@18: public function toHtml() Chris@18: { Chris@18: $trace = $this->getTraceSafe(); Chris@18: $causes = array(); Chris@18: $this->getCauseMessage($causes); Chris@18: $html = '' . "\n"; Chris@18: foreach ($causes as $i => $cause) { Chris@18: $html .= '\n"; Chris@18: } Chris@18: $html .= '' . "\n" Chris@18: . '' Chris@18: . '' Chris@18: . '' . "\n"; Chris@18: Chris@18: foreach ($trace as $k => $v) { Chris@18: $html .= '' Chris@18: . '' Chris@18: . '' . "\n"; Chris@18: } Chris@18: $html .= '' Chris@18: . '' Chris@18: . '' . "\n" Chris@18: . '
' Chris@18: . str_repeat('-', $i) . ' ' . $cause['class'] . ': ' Chris@18: . htmlspecialchars($cause['message']) Chris@18: . ' in ' . $cause['file'] . ' ' Chris@18: . 'on line ' . $cause['line'] . '' Chris@18: . "
Exception trace
#FunctionLocation
' . $k . ''; Chris@18: if (!empty($v['class'])) { Chris@18: $html .= $v['class'] . $v['type']; Chris@18: } Chris@18: $html .= $v['function']; Chris@18: $args = array(); Chris@18: if (!empty($v['args'])) { Chris@18: foreach ($v['args'] as $arg) { Chris@18: if (is_null($arg)) { Chris@18: $args[] = 'null'; Chris@18: } else if (is_array($arg)) { Chris@18: $args[] = 'Array'; Chris@18: } else if (is_object($arg)) { Chris@18: $args[] = 'Object('.get_class($arg).')'; Chris@18: } else if (is_bool($arg)) { Chris@18: $args[] = $arg ? 'true' : 'false'; Chris@18: } else if (is_int($arg) || is_double($arg)) { Chris@18: $args[] = $arg; Chris@18: } else { Chris@18: $arg = (string)$arg; Chris@18: $str = htmlspecialchars(substr($arg, 0, 16)); Chris@18: if (strlen($arg) > 16) { Chris@18: $str .= '…'; Chris@18: } Chris@18: $args[] = "'" . $str . "'"; Chris@18: } Chris@18: } Chris@18: } Chris@18: $html .= '(' . implode(', ', $args) . ')' Chris@18: . '' . (isset($v['file']) ? $v['file'] : 'unknown') Chris@18: . ':' . (isset($v['line']) ? $v['line'] : 'unknown') Chris@18: . '
' . ($k+1) . '{main} 
'; Chris@18: return $html; Chris@18: } Chris@18: Chris@18: /** Chris@18: * Generates text representation of the exception and stack trace Chris@18: * Chris@18: * @return string Chris@18: */ Chris@18: public function toText() Chris@18: { Chris@18: $causes = array(); Chris@18: $this->getCauseMessage($causes); Chris@18: $causeMsg = ''; Chris@18: foreach ($causes as $i => $cause) { Chris@18: $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': ' Chris@18: . $cause['message'] . ' in ' . $cause['file'] Chris@18: . ' on line ' . $cause['line'] . "\n"; Chris@18: } Chris@18: return $causeMsg . $this->getTraceAsString(); Chris@18: } Chris@18: } Chris@18: ?>