From 5c3c2e2d9c7d7e41533579e516e9c74d99b6db62 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 16 Aug 2011 12:26:52 -0500 Subject: [PATCH] refactored error handling for better architecture. --- system/error.php | 119 ----------------------------------- system/exception/handler.php | 117 ++++++++++++++++++++++++++++++++++ system/exception/wrapper.php | 110 ++++++++++++++++++++++++++++++++ system/file.php | 23 +++++++ system/laravel.php | 16 ++--- 5 files changed, 259 insertions(+), 126 deletions(-) delete mode 100644 system/error.php create mode 100644 system/exception/handler.php create mode 100644 system/exception/wrapper.php diff --git a/system/error.php b/system/error.php deleted file mode 100644 index 2a020d20..00000000 --- a/system/error.php +++ /dev/null @@ -1,119 +0,0 @@ - 'Error', - E_ERROR => 'Error', - E_WARNING => 'Warning', - E_PARSE => 'Parsing Error', - E_NOTICE => 'Notice', - E_CORE_ERROR => 'Core Error', - E_CORE_WARNING => 'Core Warning', - E_COMPILE_ERROR => 'Compile Error', - E_COMPILE_WARNING => 'Compile Warning', - E_USER_ERROR => 'User Error', - E_USER_WARNING => 'User Warning', - E_USER_NOTICE => 'User Notice', - E_STRICT => 'Runtime Notice' - ); - - /** - * Handle an exception. - * - * @param Exception $e - * @return void - */ - public static function handle($e) - { - // Clear the output buffer so nothing is sent to the browser except the error - // message. This prevents any views that have already been rendered from being - // in an incomplete or erroneous state. - if (ob_get_level() > 0) ob_clean(); - - $severity = (array_key_exists($e->getCode(), static::$levels)) ? static::$levels[$e->getCode()] : $e->getCode(); - - $message = static::format($e); - - if (Config::get('error.log')) - { - call_user_func(Config::get('error.logger'), $severity, $message, $e->getTraceAsString()); - } - - static::show($e, $severity, $message); - - exit(1); - } - - /** - * Format the error message for a given exception. - * - * @param Exception $e - * @return string - */ - private static function format($e) - { - $file = str_replace(array(APP_PATH, SYS_PATH), array('APP_PATH/', 'SYS_PATH/'), $e->getFile()); - - return rtrim($e->getMessage(), '.').' in '.$file.' on line '.$e->getLine().'.'; - } - - /** - * Show the error view. - * - * @param Exception $e - * @param string $severity - * @param string $message - * @return void - */ - private static function show($e, $severity, $message) - { - if (Config::get('error.detail')) - { - $view = View::make('error/exception') - ->bind('severity', $severity) - ->bind('message', $message) - ->bind('line', $e->getLine()) - ->bind('trace', $e->getTraceAsString()) - ->bind('contexts', static::context($e->getFile(), $e->getLine())); - - Response::make($view, 500)->send(); - } - else - { - Response::error('500')->send(); - } - } - - /** - * Get the code surrounding a given line in a file. - * - * @param string $path - * @param int $line - * @param int $padding - * @return string - */ - private static function context($path, $line, $padding = 5) - { - if (file_exists($path)) - { - $file = file($path, FILE_IGNORE_NEW_LINES); - - array_unshift($file, ''); - - if (($start = $line - $padding) < 0) $start = 0; - - if (($length = ($line - $start) + $padding + 1) < 0) $length = 0; - - return array_slice($file, $start, $length, true); - } - - return array(); - } - -} \ No newline at end of file diff --git a/system/exception/handler.php b/system/exception/handler.php new file mode 100644 index 00000000..08166d72 --- /dev/null +++ b/system/exception/handler.php @@ -0,0 +1,117 @@ +exception = new Wrapper($e); + } + + /** + * Create a new exception handler instance. + * + * @param Exception $e + * @return Handler + */ + public static function make($e) + { + return new static($e); + } + + /** + * Handle the exception and display the error report. + * + * The exception will be logged if error logging is enabled. + * + * The output buffer will be cleaned so nothing is sent to the browser except the + * error message. This prevents any views that have already been rendered from + * being shown in an incomplete or erroneous state. + * + * After the exception is displayed, the request will be halted. + * + * @return void + */ + public function handle() + { + if (ob_get_level() > 0) ob_clean(); + + if (Config::get('error.log')) $this->log(); + + $this->get_response(Config::get('error.detail'))->send(); + + exit(1); + } + + /** + * Log the exception using the logger closure specified in the error configuration. + * + * @return void + */ + private function log() + { + $parameters = array( + $this->exception->severity(), + $this->exception->message(), + $this->exception->getTraceAsString(), + ); + + call_user_func_array(Config::get('error.logger'), $parameters); + } + + /** + * Get the error report response for the exception. + * + * @param bool $detailed + * @return Resposne + */ + private function get_response($detailed) + { + return ($detailed) ? $this->detailed_response() : $this->generic_response(); + } + + /** + * Get the detailed error report for the exception. + * + * @return Response + */ + private function detailed_response() + { + $data = array( + 'severity' => $this->exception->severity(), + 'message' => $this->exception->message(), + 'line' => $this->exception->getLine(), + 'trace' => $this->exception->getTraceAsString(), + 'contexts' => $this->exception->context(), + ); + + return Response::make(View::make('error.exception', $data), 500); + } + + /** + * Get the generic error report for the exception. + * + * @return Response + */ + private function generic_response() + { + return Response::error('500'); + } + +} \ No newline at end of file diff --git a/system/exception/wrapper.php b/system/exception/wrapper.php new file mode 100644 index 00000000..39030e5a --- /dev/null +++ b/system/exception/wrapper.php @@ -0,0 +1,110 @@ + 'Error', + E_ERROR => 'Error', + E_WARNING => 'Warning', + E_PARSE => 'Parsing Error', + E_NOTICE => 'Notice', + E_CORE_ERROR => 'Core Error', + E_CORE_WARNING => 'Core Warning', + E_COMPILE_ERROR => 'Compile Error', + E_COMPILE_WARNING => 'Compile Warning', + E_USER_ERROR => 'User Error', + E_USER_WARNING => 'User Warning', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Runtime Notice' + ); + + /** + * Create a new exception wrapper instance. + * + * @param Exception $e + * @return void + */ + public function __construct($e) + { + $this->exception = $e; + } + + /** + * Get a human-readable version of the exception error code. + * + * @return string + */ + public function severity() + { + if (array_key_exists($this->exception->getCode(), $this->levels)) + { + return $this->levels[$this->exception->getCode()]; + } + + return $this->exception->getCode(); + } + + /** + * Get the exception error message formatted for use by Laravel. + * + * The exception file paths will be shortened, and the file name and line number + * will be added to the exception message. + * + * @return string + */ + public function message() + { + $file = str_replace(array(APP_PATH, SYS_PATH), array('APP_PATH/', 'SYS_PATH/'), $this->exception->getFile()); + + return rtrim($this->exception->getMessage(), '.').' in '.$file.' on line '.$this->exception->getLine().'.'; + } + + /** + * Get the code surrounding the line where the exception occurred. + * + * @return array + */ + public function context() + { + return File::snapshot($this->exception->getFile(), $this->exception->getLine()); + } + + /** + * Magic Method to handle getting properties from the exception. + */ + public function __get($key) + { + return $this->exception->$key; + } + + /** + * Magic Method to handle setting properties on the exception. + */ + public function __set($key, $value) + { + $this->exception->$key = $value; + } + + /** + * Magic Method to pass function calls to the exception. + */ + public function __call($method, $parameters) + { + return call_user_func_array(array($this->exception, $method), $parameters); + } + +} \ No newline at end of file diff --git a/system/file.php b/system/file.php index 8575b0c3..cc776069 100644 --- a/system/file.php +++ b/system/file.php @@ -48,6 +48,29 @@ public static function extension($path) return pathinfo($path, PATHINFO_EXTENSION); } + /** + * Get the lines surrounding a given line in a file. + * + * @param string $path + * @param int $line + * @param int $padding + * @return array + */ + public static function snapshot($path, $line, $padding = 5) + { + if ( ! file_exists($path)) return array(); + + $file = file($path, FILE_IGNORE_NEW_LINES); + + array_unshift($file, ''); + + if (($start = $line - $padding) < 0) $start = 0; + + if (($length = ($line - $start) + $padding + 1) < 0) $length = 0; + + return array_slice($file, $start, $length, true); + } + /** * Get a file MIME type by extension. * diff --git a/system/laravel.php b/system/laravel.php index f5f1d084..1788a84e 100644 --- a/system/laravel.php +++ b/system/laravel.php @@ -59,25 +59,27 @@ // -------------------------------------------------------------- set_exception_handler(function($e) { - require_once SYS_PATH.'error'.EXT; + require_once SYS_PATH.'exception/handler'.EXT; - Error::handle($e); + Exception\Handler::make($e)->handle(); }); set_error_handler(function($number, $error, $file, $line) { - require_once SYS_PATH.'error'.EXT; + require_once SYS_PATH.'exception/handler'.EXT; - Error::handle(new \ErrorException($error, $number, 0, $file, $line)); + Exception\Handler::make(new \ErrorException($error, $number, 0, $file, $line))->handle(); }); register_shutdown_function(function() { if ( ! is_null($error = error_get_last())) { - require_once SYS_PATH.'error'.EXT; - - Error::handle(new \ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); + require_once SYS_PATH.'exception/handler'.EXT; + + extract($error); + + Exception\Handler::make(new \ErrorException($message, $type, 0, $file, $line))->handle(); } });