/system/core/Kohana_Exception.patch
Patch | 871 lines | 828 code | 43 blank | 0 comment | 0 complexity | ba491e445979ff137482171b9a47afac MD5 | raw file
- Index: Kohana.php
- ===================================================================
- --- Kohana.php (revision 3914)
- +++ Kohana.php (working copy)
- @@ -101,9 +101,28 @@
- Event::add('system.shutdown', array(__CLASS__, 'internal_cache_save'));
- }
- - // Disable notices and "strict" errors
- + // Disable notices and "strict" errors, to prevent some oddities in
- + // PHP 5.2 and when using Kohana under CLI
- $ER = error_reporting(~E_NOTICE & ~E_STRICT);
- + // Send default text/html UTF-8 header
- + header('Content-Type: text/html; charset=UTF-8');
- +
- + // Enable exception handling
- + Kohana_Exception::enable();
- +
- + // Enable error handling
- + Kohana_PHP_Exception::enable();
- +
- + if (self::$configuration['core']['log_threshold'] > 0)
- + {
- + // Set the log directory
- + self::log_directory(self::$configuration['core']['log_directory']);
- +
- + // Enable log writing at shutdown
- + register_shutdown_function(array(__CLASS__, 'log_save'));
- + }
- +
- // Set the user agent
- self::$user_agent = trim($_SERVER['HTTP_USER_AGENT']);
- @@ -128,15 +147,6 @@
- // Set autoloader
- spl_autoload_register(array('Kohana', 'auto_load'));
- - // Set error handler
- - set_error_handler(array('Kohana', 'exception_handler'));
- -
- - // Set exception handler
- - set_exception_handler(array('Kohana', 'exception_handler'));
- -
- - // Send default text/html UTF-8 header
- - header('Content-Type: text/html; charset=UTF-8');
- -
- // Load locales
- $locales = self::config('locale.language');
- @@ -146,15 +156,6 @@
- // Set locale information
- self::$locale = setlocale(LC_ALL, $locales);
- - if (self::$configuration['core']['log_threshold'] > 0)
- - {
- - // Set the log directory
- - self::log_directory(self::$configuration['core']['log_directory']);
- -
- - // Enable log writing at shutdown
- - register_shutdown_function(array(__CLASS__, 'log_save'));
- - }
- -
- // Enable Kohana routing
- Event::add('system.routing', array('Router', 'find_uri'));
- Event::add('system.routing', array('Router', 'setup'));
- @@ -163,7 +164,7 @@
- Event::add('system.execute', array('Kohana', 'instance'));
- // Enable Kohana 404 pages
- - Event::add('system.404', array('Kohana', 'show_404'));
- + Event::add('system.404', array('Kohana_404_Exception', 'trigger'));
- // Enable Kohana output handling
- Event::add('system.shutdown', array('Kohana', 'shutdown'));
- @@ -788,140 +789,6 @@
- }
- /**
- - * Dual-purpose PHP error and exception handler. Uses the kohana_error_page
- - * view to display the message.
- - *
- - * @param integer|object exception object or error code
- - * @param string error message
- - * @param string filename
- - * @param integer line number
- - * @return void
- - */
- - public static function exception_handler($exception, $message = NULL, $file = NULL, $line = NULL)
- - {
- - // PHP errors have 5 args, always
- - $PHP_ERROR = (func_num_args() === 5);
- -
- - // Test to see if errors should be displayed
- - if ($PHP_ERROR AND (error_reporting() & $exception) === 0)
- - return;
- -
- - // This is useful for hooks to determine if a page has an error
- - self::$has_error = TRUE;
- -
- - // Error handling will use exactly 5 args, every time
- - if ($PHP_ERROR)
- - {
- - $code = $exception;
- - $type = 'PHP Error';
- - $template = 'kohana_error_page';
- - }
- - else
- - {
- - $code = $exception->getCode();
- - $type = get_class($exception);
- - $message = $exception->getMessage();
- - $file = $exception->getFile();
- - $line = $exception->getLine();
- - $template = ($exception instanceof Kohana_Exception) ? $exception->getTemplate() : 'kohana_error_page';
- - }
- -
- - if (is_numeric($code))
- - {
- - $codes = self::lang('errors');
- -
- - if ( ! empty($codes[$code]))
- - {
- - list($level, $error, $description) = $codes[$code];
- - }
- - else
- - {
- - $level = 1;
- - $error = $PHP_ERROR ? 'Unknown Error' : get_class($exception);
- - $description = '';
- - }
- - }
- - else
- - {
- - // Custom error message, this will never be logged
- - $level = 5;
- - $error = $code;
- - $description = '';
- - }
- -
- - // Remove the DOCROOT from the path, as a security precaution
- - $file = str_replace('\\', '/', realpath($file));
- - $file = preg_replace('|^'.preg_quote(DOCROOT).'|', '', $file);
- -
- - if ($level <= self::$configuration['core']['log_threshold'])
- - {
- - // Log the error
- - self::log('error', self::lang('core.uncaught_exception', $type, $message, $file, $line));
- - }
- -
- - if ($PHP_ERROR)
- - {
- - $description = self::lang('errors.'.E_RECOVERABLE_ERROR);
- - $description = is_array($description) ? $description[2] : '';
- -
- - if ( ! headers_sent())
- - {
- - // Send the 500 header
- - header('HTTP/1.1 500 Internal Server Error');
- - }
- - }
- - else
- - {
- - if (method_exists($exception, 'sendHeaders') AND ! headers_sent())
- - {
- - // Send the headers if they have not already been sent
- - $exception->sendHeaders();
- - }
- - }
- -
- - while (ob_get_level() > self::$buffer_level)
- - {
- - // Close open buffers
- - ob_end_clean();
- - }
- -
- - // Test if display_errors is on
- - if (self::$configuration['core']['display_errors'] === TRUE)
- - {
- - if ( ! IN_PRODUCTION AND $line != FALSE)
- - {
- - // Remove the first entry of debug_backtrace(), it is the exception_handler call
- - $trace = $PHP_ERROR ? array_slice(debug_backtrace(), 1) : $exception->getTrace();
- -
- - // Beautify backtrace
- - $trace = self::backtrace($trace);
- - }
- -
- - // Load the error
- - require self::find_file('views', empty($template) ? 'kohana_error_page' : $template);
- - }
- - else
- - {
- - // Get the i18n messages
- - $error = self::lang('core.generic_error');
- - $message = self::lang('core.errors_disabled', url::site(), url::site(Router::$current_uri));
- -
- - // Load the errors_disabled view
- - require self::find_file('views', 'kohana_error_disabled');
- - }
- -
- - if ( ! Event::has_run('system.shutdown'))
- - {
- - // Run the shutdown even to ensure a clean exit
- - Event::run('system.shutdown');
- - }
- -
- - // Turn off error reporting
- - error_reporting(0);
- - exit;
- - }
- -
- - /**
- * Provides class auto-loading.
- *
- * @throws Kohana_Exception
- @@ -1485,67 +1352,185 @@
- }
- /**
- - * Displays nice backtrace information.
- - * @see http://php.net/debug_backtrace
- + * Simplifies [back trace][ref-btr] information.
- *
- - * @param array backtrace generated by an exception or debug_backtrace
- - * @return string
- + * [ref-btr]: http://php.net/debug_backtrace
- + *
- + * @return array
- */
- - public static function backtrace($trace)
- + public static function read_trace(array $trace_array)
- {
- - if ( ! is_array($trace))
- - return;
- + $file = NULL;
- - // Final output
- - $output = array();
- -
- - foreach ($trace as $entry)
- + $ouput = array();
- + foreach ($trace_array as $trace)
- {
- - $temp = '<li>';
- -
- - if (isset($entry['file']))
- + if (isset($trace['file']))
- {
- - $temp .= self::lang('core.error_file_line', preg_replace('!^'.preg_quote(DOCROOT).'!', '', $entry['file']), $entry['line']);
- - }
- + $line = '<strong>'.Kohana::debug_path($trace['file']).'</strong>';
- - $temp .= '<pre>';
- + if (isset($trace['line']))
- + {
- + $line .= ', line <strong>'.$trace['line'].'</strong>';
- + }
- - if (isset($entry['class']))
- - {
- - // Add class and call type
- - $temp .= $entry['class'].$entry['type'];
- + $output[] = $line;
- }
- - // Add function
- - $temp .= $entry['function'].'( ';
- -
- - // Add function args
- - if (isset($entry['args']) AND is_array($entry['args']))
- + if (isset($trace['function']))
- {
- - // Separator starts as nothing
- - $sep = '';
- + // Is this an inline function?
- + $inline = in_array($trace['function'], array('require', 'require_once', 'include', 'include_once', 'echo', 'print'));
- - while ($arg = array_shift($entry['args']))
- + $line = array();
- +
- + if (isset($trace['class']))
- {
- - if (is_string($arg) AND is_file($arg))
- + $line[] = $trace['class'];
- +
- + if (isset($trace['type']))
- {
- - // Remove docroot from filename
- - $arg = preg_replace('!^'.preg_quote(DOCROOT).'!', '', $arg);
- + $line[] .= $trace['type'];
- }
- + }
- - $temp .= $sep.html::specialchars(print_r($arg, TRUE));
- + $line[] = $trace['function'].($inline ? ' ' : '(');
- - // Change separator to a comma
- - $sep = ', ';
- + $args = array();
- +
- + if ( ! empty($trace['args']))
- + {
- + foreach ($trace['args'] as $arg)
- + {
- + if (is_string($arg) AND file_exists($arg))
- + {
- + // Sanitize path
- + $arg = Kohana::debug_path($arg);
- + }
- +
- + $args[] = '<code>'.text::limit_chars(html::specialchars(self::debug_var($arg)), 50, '...').'</code>';
- + }
- }
- +
- + $line[] = implode(', ', $args).($inline ? '' : ')');
- +
- + $output[] = "\t".implode('', $line);
- }
- + }
- - $temp .= ' )</pre></li>';
- + return $output;
- + }
- +
- + /**
- + * Removes APPPATH, SYSPATH, MODPATH, and DOCROOT from filenames, replacing
- + * them with the plain text equivalents.
- + *
- + * @param string path to sanitize
- + * @return string
- + */
- + public static function debug_path($file)
- + {
- + if (strpos($file, APPPATH) === 0)
- + {
- + $file = 'APPPATH/'.substr($file, strlen(APPPATH));
- + }
- + elseif (strpos($file, SYSPATH) === 0)
- + {
- + $file = 'SYSPATH/'.substr($file, strlen(SYSPATH));
- + }
- + elseif (strpos($file, MODPATH) === 0)
- + {
- + $file = 'MODPATH/'.substr($file, strlen(MODPATH));
- + }
- + elseif (strpos($file, DOCROOT) === 0)
- + {
- + $file = 'DOCROOT/'.substr($file, strlen(DOCROOT));
- + }
- - $output[] = $temp;
- + return $file;
- + }
- +
- + /**
- + * Similar to print_r or var_dump, generates a string representation of
- + * any variable.
- + *
- + * @param mixed variable to dump
- + * @param boolean internal recursion
- + * @return string
- + */
- + public static function debug_var($var, $recursion = FALSE)
- + {
- + static $objects;
- +
- + if ($recursion === FALSE)
- + {
- + $objects = array();
- }
- - return '<ul class="backtrace">'.implode("\n", $output).'</ul>';
- + switch (gettype($var))
- + {
- + case 'object':
- + // Unique hash of the object
- + $hash = spl_object_hash($var);
- +
- + $object = new ReflectionObject($var);
- + $more = FALSE;
- + $out = 'object '.$object->getName().' { ';
- +
- + if ($recursion === TRUE AND in_array($hash, $objects))
- + {
- + $out .= '*RECURSION*';
- + }
- + else
- + {
- + // Add the hash to the objects, to detect later recursion
- + $objects[] = $hash;
- +
- + foreach ($object->getProperties() as $property)
- + {
- + $out .= ($more === TRUE ? ', ' : '').$property->getName().' => ';
- + if ($property->isPublic())
- + {
- + $out .= self::debug_var($property->getValue($var), TRUE);
- + }
- + elseif ($property->isPrivate())
- + {
- + $out .= '*PRIVATE*';
- + }
- + else
- + {
- + $out .= '*PROTECTED*';
- + }
- + $more = TRUE;
- + }
- + }
- + return $out.' }';
- + case 'array':
- + $more = FALSE;
- + $out = 'array (';
- + foreach ((array) $var as $key => $val)
- + {
- + if ( ! is_int($key))
- + {
- + $key = self::debug_var($key, TRUE).' => ';
- + }
- + else
- + {
- + $key = '';
- + }
- + $out .= ($more ? ', ' : '').$key.self::debug_var($val, TRUE);
- + $more = TRUE;
- + }
- + return $out.')';
- + case 'string':
- + return "'$var'";
- + case 'float':
- + return number_format($var, 6).'…';
- + case 'boolean':
- + return $var === TRUE ? 'TRUE' : 'FALSE';
- + default:
- + return (string) $var;
- + }
- }
- /**
- @@ -1581,25 +1566,97 @@
- } // End Kohana
- -/**
- - * Creates a generic i18n exception.
- - */
- class Kohana_Exception extends Exception {
- - // Template file
- - protected $template = 'kohana_error_page';
- + // Generate HTML errors
- + public static $html_output = TRUE;
- - // Header
- - protected $header = FALSE;
- + // Show stack traces in errors
- + public static $trace_output = TRUE;
- +
- + // Show source code in errors
- + public static $source_output = TRUE;
- + // Error resources have not been loaded
- + protected static $error_resources = FALSE;
- +
- + // To hold unique identifier to distinguish error output
- + protected $instance_identifier;
- +
- + /**
- + * Enable Kohana exception handling.
- + *
- + * @return void
- + */
- + public static function enable()
- + {
- + set_exception_handler(array(__CLASS__, 'handle'));
- + }
- +
- + /**
- + * Disable Kohana exception handling.
- + *
- + * @return void
- + */
- + public static function disable()
- + {
- + restore_exception_handler();
- + }
- +
- + /**
- + * PHP exception handler.
- + *
- + * @param object Exception instance
- + * @return void
- + */
- + public static function handle($exception)
- + {
- + // An error has been triggered
- + Kohana::$has_error = TRUE;
- +
- + if (is_numeric($exception->code))
- + {
- + $codes = Kohana::lang('errors');
- +
- + if ( ! empty($codes[$exception->code]))
- + {
- + list($level, $error) = $codes[$exception->code];
- + }
- + else
- + {
- + $level = 1;
- + $error = get_class($exception);
- + }
- + }
- + else
- + {
- + // Custom error message, this will never be logged
- + $level = 5;
- + $error = $exception->code;
- + }
- +
- + if ($level <= Kohana::config('core.log_threshold'))
- + {
- + // Log the error
- + Kohana::log('error', Kohana::lang('core.uncaught_exception', $error, $exception->message, $exception->file, $exception->line));
- + }
- +
- + echo $exception;
- +
- + // Exceptions must halt execution
- + exit;
- + }
- +
- + // Error template
- + public $template = 'kohana/error';
- +
- // Error code
- protected $code = E_KOHANA;
- /**
- - * Set exception message.
- + * Creates a new i18n Kohana_Exception using the passed error and arguments.
- *
- - * @param string i18n language key for the message
- - * @param array addition line parameters
- + * @return void
- */
- public function __construct($error)
- {
- @@ -1614,18 +1671,144 @@
- $message = 'Unknown Exception: '.$error;
- }
- + $this->instance_identifier = uniqid();
- +
- // Sets $this->message the proper way
- parent::__construct($message);
- }
- /**
- - * Magic method for converting an object to a string.
- + * Outputs an inline error message.
- *
- - * @return string i18n message
- + * @return string
- */
- public function __toString()
- {
- - return (string) $this->message;
- + if (Kohana::config('core.display_errors') === FALSE)
- + {
- + // Load the "errors disabled" message
- + $code = Kohana::lang('core.generic_error');
- + $error = Kohana::lang('core.errors_disabled', url::site(''), url::site(Router::$current_uri));
- +
- + // Do not show the file or line
- + $file = $line = NULL;
- + }
- + else
- + {
- + // Load exception properties locally
- + $code = $this->code;
- + $error = $this->message;
- + $file = $this->file;
- + $line = $this->line;
- + $instance_identifier = $this->instance_identifier;
- +
- + // Load the i18n error name
- + $code = Kohana::lang('errors.'.$code.'.1').' ('.$code.')';
- + }
- +
- + if (Kohana_Exception::$html_output)
- + {
- + if ( ! empty($file))
- + {
- + // Source code
- + $source = '';
- +
- + if (Kohana_Exception::$source_output)
- + {
- + // Lines to read from the source
- + $start_line = $line - 4;
- + $end_line = $line + 3;
- +
- + $file_source = fopen($file, 'r');
- + $file_line = 1;
- +
- + while ($read_line = fgets($file_source))
- + {
- + if ($file_line >= $start_line)
- + {
- + if ($file_line === $line)
- + {
- + // Wrap the text of this line in <span> tags, for highlighting
- + //$read_line = preg_replace('/^(\s+)(.+?)(\s+)$/', '$1<span>$2</span>$3', $read_line);
- + $read_line = '<span>'.html::specialchars($read_line).'</span>';
- + }
- + else
- + {
- + $read_line = html::specialchars($read_line);
- + }
- +
- + $source .= $read_line;
- + }
- +
- + if (++$file_line > $end_line)
- + {
- + // Stop reading lines
- + fclose($file_source);
- +
- + break;
- + }
- + }
- + }
- +
- + if (Kohana_Exception::$trace_output)
- + {
- + $trace = $this->getTrace();
- +
- + // Read trace
- + $trace = Kohana::read_trace($trace);
- + }
- + }
- +
- + if (method_exists($this, 'sendHeaders') AND ! headers_sent())
- + {
- + // Send the headers if they have not already been sent
- + $this->sendHeaders();
- + }
- +
- + // Sanitize filepath for greater security
- + $file = Kohana::debug_path($file);
- + }
- +
- + if ( ! Kohana_Exception::$html_output)
- + {
- + // Show only the error text
- + return $code.': '.$error.' [ '.$file.', '.$line.' ] '."\n";
- + }
- +
- + if (Kohana::config('core.display_errors'))
- + {
- + ob_start();
- +
- + if ( ! self::$error_resources)
- + {
- + // Include error style
- + echo '<style type="text/css">', "\n";
- + include Kohana::find_file('views', 'kohana/error_style', FALSE, 'css');
- + echo "\n", '</style>', "\n";
- +
- + // Include error js
- + echo '<script type="text/javascript">', "\n";
- + include Kohana::find_file('views', 'kohana/error_script', FALSE, 'js');
- + echo "\n", '</script>', "\n";
- +
- + // Error resources have been loaded
- + self::$error_resources = TRUE;
- + }
- +
- + require Kohana::find_file('views', 'kohana/error', FALSE);
- + }
- + else
- + {
- + // Clean and stop all output buffers except the Kohana buffer
- + Kohana::close_buffers(FALSE, FALSE);
- +
- + // Clean the Kohana output buffer
- + ob_clean();
- +
- + require Kohana::find_file('views', 'kohana/error_disabled', FALSE);
- + }
- +
- + return ob_get_clean();
- }
- /**
- @@ -1651,8 +1834,93 @@
- } // End Kohana Exception
- +class Kohana_PHP_Exception extends Kohana_Exception {
- +
- + /**
- + * Enable Kohana PHP error handling.
- + *
- + * @return void
- + */
- + public static function enable()
- + {
- + set_error_handler(array(__CLASS__, 'handle'));
- + }
- +
- + /**
- + * Disable Kohana PHP error handling.
- + *
- + * @return void
- + */
- + public static function disable()
- + {
- + restore_error_handler();
- + }
- +
- + /**
- + * PHP error handler.
- + *
- + * @throws Kohana_PHP_Exception
- + * @return void
- + */
- + public static function handle($code, $error, $file, $line, $context = NULL)
- + {
- + if ((error_reporting() & $code) === 0)
- + {
- + // Respect error_reporting settings
- + return;
- + }
- +
- + // An error has been triggered
- + Kohana::$has_error = TRUE;
- +
- + // Create an exception
- + $exception = new Kohana_PHP_Exception($code, $error, $file, $line, $context);
- +
- + // Get the error level and name
- + list ($level, $error) = Kohana::lang('errors.'.$exception->code);
- +
- + if ($level >= Kohana::config('core.log_threshold'))
- + {
- + // Log the error
- + Kohana::log('error', Kohana::lang('core.uncaught_exception', $error, $exception->message, $exception->file, $exception->line));
- + }
- +
- + echo $exception;
- +
- + if (Kohana::config('core.display_errors') === FALSE)
- + {
- + // Execution must halt
- + exit;
- + }
- + }
- +
- + /**
- + * Create a new PHP error exception.
- + *
- + * @return void
- + */
- + public function __construct($code, $error, $file, $line, $context = NULL)
- + {
- + Exception::__construct($error);
- +
- + // Set the error code, file, line, and context manually
- + $this->code = $code;
- + $this->file = $file;
- + $this->line = $line;
- +
- + $this->instance_identifier = uniqid();
- + }
- +
- + public function sendHeaders()
- + {
- + // Send the 500 header
- + header('HTTP/1.1 500 Internal Server Error');
- + }
- +
- +} // End Kohana PHP Exception
- +
- /**
- - * Creates a custom exception.
- + * Creates a custom exception message.
- */
- class Kohana_User_Exception extends Kohana_Exception {
- @@ -1667,44 +1935,63 @@
- {
- Exception::__construct($message);
- + // Code is the error title
- $this->code = $title;
- if ($template !== FALSE)
- {
- + // Override the default template
- $this->template = $template;
- }
- +
- + $this->instance_identifier = uniqid();
- }
- } // End Kohana PHP Exception
- /**
- - * Creates a Page Not Found exception.
- + * Creates a "Page Not Found" exception.
- */
- class Kohana_404_Exception extends Kohana_Exception {
- + /**
- + * Throws a new 404 exception.
- + *
- + * @throws Kohana_404_Exception
- + * @return void
- + */
- + public static function trigger($page = NULL, $template = NULL)
- + {
- + throw new Kohana_404_Exception($page, $template);
- + }
- +
- protected $code = E_PAGE_NOT_FOUND;
- /**
- * Set internal properties.
- *
- - * @param string URL of page
- + * @param string URI of page
- * @param string custom error template
- */
- - public function __construct($page = FALSE, $template = FALSE)
- + public function __construct($page = NULL, $template = NULL)
- {
- - if ($page === FALSE)
- + if ($page === NULL)
- {
- - // Construct the page URI using Router properties
- - $page = Router::$current_uri.Router::$url_suffix.Router::$query_string;
- + // Use the complete URI
- + $page = Router::$complete_uri;
- }
- - Exception::__construct(Kohana::lang('core.page_not_found', $page));
- + parent::__construct('core.page_not_found', $page);
- - $this->template = $template;
- + if ($template !== NULL)
- + {
- + // Override the default template
- + $this->template = $template;
- + }
- }
- /**
- - * Sends "File Not Found" headers, to emulate server behavior.
- + * Sends 404 headers, to emulate server behavior.
- *
- * @return void
- */
- @@ -1714,4 +2001,5 @@
- header('HTTP/1.1 404 File Not Found');
- }
- -} // End Kohana 404 Exception
- +} // End Kohana 404 Exception
- + No newline at end of file