summaryrefslogtreecommitdiffstats
path: root/system/core
diff options
context:
space:
mode:
authorAndrey Andreev <narf@devilix.net>2014-01-20 14:03:43 +0100
committerAndrey Andreev <narf@devilix.net>2014-01-20 14:03:43 +0100
commitea801ab4ab80042638ffddc6056483a1ec43fa80 (patch)
treef75f30c0df6a8f861ca7df22af09fa3240b0bbd6 /system/core
parent1c08d557a21ecb0f79cd1a1de4e06817a26e0537 (diff)
parent4d0571666d03511ac5b4a1f2a6882ccb1509a209 (diff)
Merge branch 'develop' into feature/user-guide-cleanup
Diffstat (limited to 'system/core')
-rw-r--r--system/core/CodeIgniter.php174
-rw-r--r--system/core/Common.php174
-rw-r--r--system/core/Config.php38
-rw-r--r--system/core/Exceptions.php53
-rw-r--r--system/core/Hooks.php39
-rw-r--r--system/core/Input.php113
-rw-r--r--system/core/Lang.php2
-rw-r--r--system/core/Loader.php225
-rw-r--r--system/core/Log.php17
-rw-r--r--system/core/Model.php3
-rw-r--r--system/core/Output.php288
-rw-r--r--system/core/Router.php328
-rw-r--r--system/core/Security.php68
-rw-r--r--system/core/URI.php280
-rw-r--r--system/core/Utf8.php2
15 files changed, 988 insertions, 816 deletions
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
index c68266408..74a9eb0af 100644
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -48,24 +48,22 @@ defined('BASEPATH') OR exit('No direct script access allowed');
/*
* ------------------------------------------------------
- * Load the global functions
+ * Load the framework constants
* ------------------------------------------------------
*/
- require_once(BASEPATH.'core/Common.php');
+ if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
+ {
+ require_once(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
+ }
+
+ require_once(APPPATH.'config/constants.php');
/*
* ------------------------------------------------------
- * Load the framework constants
+ * Load the global functions
* ------------------------------------------------------
*/
- if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
- {
- require(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
- }
- else
- {
- require(APPPATH.'config/constants.php');
- }
+ require_once(BASEPATH.'core/Common.php');
/*
* ------------------------------------------------------
@@ -73,11 +71,10 @@ defined('BASEPATH') OR exit('No direct script access allowed');
* ------------------------------------------------------
*/
set_error_handler('_exception_handler');
+ register_shutdown_function('_shutdown_handler');
- if ( ! is_php('5.4'))
- {
- @ini_set('magic_quotes_runtime', 0); // Kill magic quotes
- }
+ // Kill magic quotes
+ is_php('5.4') OR @ini_set('magic_quotes_runtime', 0);
/*
* ------------------------------------------------------
@@ -88,7 +85,7 @@ defined('BASEPATH') OR exit('No direct script access allowed');
* The subclass prefix allows CI to know if a core class is
* being extended via a library in the local application
* "libraries" folder. Since CI allows config items to be
- * overriden via data set in the main index. php file,
+ * overriden via data set in the main index.php file,
* before proceeding we need to know if a subclass_prefix
* override exists. If so, we will set this value now,
* before any classes are loaded
@@ -166,12 +163,6 @@ defined('BASEPATH') OR exit('No direct script access allowed');
*/
$RTR =& load_class('Router', 'core');
- // Set any routing overrides that may exist in the main index file
- if (isset($routing))
- {
- $RTR->_set_overrides($routing);
- }
-
/*
* ------------------------------------------------------
* Instantiate the output class
@@ -218,7 +209,7 @@ defined('BASEPATH') OR exit('No direct script access allowed');
*
*/
// Load the base controller class
- require BASEPATH.'core/Controller.php';
+ require_once BASEPATH.'core/Controller.php';
/**
* Reference to the CI_Controller method.
@@ -234,92 +225,117 @@ defined('BASEPATH') OR exit('No direct script access allowed');
if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
{
- require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
+ require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
}
- // Load the local application controller
- // Note: The Router class automatically validates the controller path using the router->_validate_request().
- // If this include fails it means that the default controller in the Routes.php file is not resolving to something valid.
- if ( ! file_exists(APPPATH.'controllers/'.$RTR->directory.$RTR->class.'.php'))
- {
- show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');
- }
-
- include(APPPATH.'controllers/'.$RTR->directory.$RTR->class.'.php');
-
// Set a mark point for benchmarking
$BM->mark('loading_time:_base_classes_end');
/*
* ------------------------------------------------------
- * Security check
+ * Sanity checks
* ------------------------------------------------------
*
- * None of the methods in the app controller or the
- * loader class can be called via the URI, nor can
- * controller functions that begin with an underscore.
+ * The Router class has already validated the request,
+ * leaving us with 3 options here:
+ *
+ * 1) an empty class name, if we reached the default
+ * controller, but it didn't exist;
+ * 2) a query string which doesn't go through a
+ * file_exists() check
+ * 3) a regular request for a non-existing page
+ *
+ * We handle all of these as a 404 error.
+ *
+ * Furthermore, none of the methods in the app controller
+ * or the loader class can be called via the URI, nor can
+ * controller methods that begin with an underscore.
*/
- $class = $RTR->class;
- $method = $RTR->method;
-
- if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
- {
- if ( ! empty($RTR->routes['404_override']))
- {
- if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $class, $method) !== 2)
- {
- $method = 'index';
- }
-
- if ( ! class_exists($class, FALSE))
- {
- if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
- {
- show_404($class.'/'.$method);
- }
- include_once(APPPATH.'controllers/'.$class.'.php');
- }
- }
- else
- {
- show_404($class.'/'.$method);
- }
- }
+ $e404 = FALSE;
+ $class = ucfirst($RTR->class);
+ $method = $RTR->method;
- if (method_exists($class, '_remap'))
+ if (empty($class) OR ! file_exists(APPPATH.'controllers/'.$RTR->directory.$class.'.php'))
{
- $params = array($method, array_slice($URI->rsegments, 2));
- $method = '_remap';
+ $e404 = TRUE;
}
else
{
+ require_once(APPPATH.'controllers/'.$RTR->directory.$class.'.php');
+
+ if ( ! class_exists($class, FALSE) OR $method[0] === '_' OR method_exists('CI_Controller', $method))
+ {
+ $e404 = TRUE;
+ }
+ elseif (method_exists($class, '_remap'))
+ {
+ $params = array($method, array_slice($URI->rsegments, 2));
+ $method = '_remap';
+ }
// WARNING: It appears that there are issues with is_callable() even in PHP 5.2!
// Furthermore, there are bug reports and feature/change requests related to it
// that make it unreliable to use in this context. Please, DO NOT change this
// work-around until a better alternative is available.
- if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE))
+ elseif ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($class)), TRUE))
{
- if (empty($RTR->routes['404_override']))
- {
- show_404($class.'/'.$method);
- }
- elseif (sscanf($RTR->routes['404_override'], '%[^/]/%s', $class, $method) !== 2)
+ $e404 = TRUE;
+ }
+ }
+
+ if ($e404)
+ {
+ if ( ! empty($RTR->routes['404_override']))
+ {
+ if (sscanf($RTR->routes['404_override'], '%[^/]/%s', $error_class, $error_method) !== 2)
{
- $method = 'index';
+ $error_method = 'index';
}
- if ( ! class_exists($class, FALSE))
+ $error_class = ucfirst($error_class);
+
+ if ( ! class_exists($error_class, FALSE))
{
- if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
+ if (file_exists(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php'))
{
- show_404($class.'/'.$method);
+ require_once(APPPATH.'controllers/'.$RTR->directory.$error_class.'.php');
+ $e404 = ! class_exists($error_class, FALSE);
}
-
- include_once(APPPATH.'controllers/'.$class.'.php');
+ // Were we in a directory? If so, check for a global override
+ elseif ( ! empty($RTR->directory) && file_exists(APPPATH.'controllers/'.$error_class.'.php'))
+ {
+ require_once(APPPATH.'controllers/'.$error_class.'.php');
+ if (($e404 = ! class_exists($error_class, FALSE)) === FALSE)
+ {
+ $RTR->directory = '';
+ }
+ }
+ }
+ else
+ {
+ $e404 = FALSE;
}
}
+ // Did we reset the $e404 flag? If so, set the rsegments, starting from index 1
+ if ( ! $e404)
+ {
+ $class = $error_class;
+ $method = $error_method;
+
+ $URI->rsegments = array(
+ 1 => $class,
+ 2 => $method
+ );
+ }
+ else
+ {
+ show_404($RTR->directory.$class.'/'.$method);
+ }
+ }
+
+ if ($method !== '_remap')
+ {
$params = array_slice($URI->rsegments, 2);
}
diff --git a/system/core/Common.php b/system/core/Common.php
index b95a05db9..00e303098 100644
--- a/system/core/Common.php
+++ b/system/core/Common.php
@@ -82,7 +82,7 @@ if ( ! function_exists('is_really_writable'))
function is_really_writable($file)
{
// If we're on a Unix server with safe_mode off we call is_writable
- if (DIRECTORY_SEPARATOR === '/' && (bool) @ini_get('safe_mode') === FALSE)
+ if (DIRECTORY_SEPARATOR === '/' && (is_php('5.4') OR (bool) @ini_get('safe_mode') === FALSE))
{
return is_writable($file);
}
@@ -224,56 +224,51 @@ if ( ! function_exists('get_config'))
* @param array
* @return array
*/
- function &get_config($replace = array())
+ function &get_config(Array $replace = array())
{
static $_config;
- if (isset($_config))
+ if (empty($_config))
{
- return $_config[0];
- }
+ $file_path = APPPATH.'config/config.php';
+ $found = FALSE;
+ if (file_exists($file_path))
+ {
+ $found = TRUE;
+ require($file_path);
+ }
- $file_path = APPPATH.'config/config.php';
- $found = FALSE;
- if (file_exists($file_path))
- {
- $found = TRUE;
- require($file_path);
- }
+ // Is the config file in the environment folder?
+ if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
+ {
+ require($file_path);
+ }
+ elseif ( ! $found)
+ {
+ set_status_header(503);
+ echo 'The configuration file does not exist.';
+ exit(EXIT_CONFIG);
+ }
- // Is the config file in the environment folder?
- if (file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php'))
- {
- require($file_path);
- }
- elseif ( ! $found)
- {
- set_status_header(503);
- echo 'The configuration file does not exist.';
- exit(EXIT_CONFIG);
- }
+ // Does the $config array exist in the file?
+ if ( ! isset($config) OR ! is_array($config))
+ {
+ set_status_header(503);
+ echo 'Your config file does not appear to be formatted correctly.';
+ exit(EXIT_CONFIG);
+ }
- // Does the $config array exist in the file?
- if ( ! isset($config) OR ! is_array($config))
- {
- set_status_header(503);
- echo 'Your config file does not appear to be formatted correctly.';
- exit(EXIT_CONFIG);
+ // references cannot be directly assigned to static variables, so we use an array
+ $_config[0] =& $config;
}
- // Are any values being dynamically replaced?
- if (count($replace) > 0)
+ // Are any values being dynamically added or replaced?
+ foreach ($replace as $key => $val)
{
- foreach ($replace as $key => $val)
- {
- if (isset($config[$key]))
- {
- $config[$key] = $val;
- }
- }
+ $_config[0][$key] = $val;
}
- return $_config[0] =& $config;
+ return $_config[0];
}
}
@@ -360,6 +355,24 @@ if ( ! function_exists('is_https'))
// ------------------------------------------------------------------------
+if ( ! function_exists('is_cli'))
+{
+
+ /**
+ * Is CLI?
+ *
+ * Test to see if a request was made from the command line.
+ *
+ * @return bool
+ */
+ function is_cli()
+ {
+ return (PHP_SAPI === 'cli' OR defined('STDIN'));
+ }
+}
+
+// ------------------------------------------------------------------------
+
if ( ! function_exists('show_error'))
{
/**
@@ -434,29 +447,19 @@ if ( ! function_exists('log_message'))
*
* @param string the error level: 'error', 'debug' or 'info'
* @param string the error message
- * @param bool whether the error is a native PHP error
* @return void
*/
- function log_message($level, $message, $php_error = FALSE)
+ function log_message($level, $message)
{
- static $_log, $_log_threshold;
-
- if ($_log_threshold === NULL)
- {
- $_log_threshold = config_item('log_threshold');
- }
-
- if ($_log_threshold === 0)
- {
- return;
- }
+ static $_log;
if ($_log === NULL)
{
- $_log =& load_class('Log', 'core');
+ // references cannot be directly assigned to static variables, so we use an array
+ $_log[0] =& load_class('Log', 'core');
}
- $_log->write_log($level, $message, $php_error);
+ $_log[0]->write_log($level, $message);
}
}
@@ -538,7 +541,7 @@ if ( ! function_exists('set_status_header'))
$server_protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : FALSE;
- if (strpos(php_sapi_name(), 'cgi') === 0)
+ if (strpos(PHP_SAPI, 'cgi') === 0)
{
header('Status: '.$code.' '.$text, TRUE);
}
@@ -556,22 +559,35 @@ if ( ! function_exists('_exception_handler'))
/**
* Exception Handler
*
- * This is the custom exception handler that is declaired at the top
- * of Codeigniter.php. The main reason we use this is to permit
+ * This is the custom exception handler that is declared at the top
+ * of CodeIgniter.php. The main reason we use this is to permit
* PHP errors to be logged in our own log files since the user may
* not have access to server logs. Since this function
* effectively intercepts PHP errors, however, we also need
* to display errors based on the current error_reporting level.
* We do that with the use of a PHP error template.
*
- * @param int
- * @param string
- * @param string
- * @param int
+ * @param int $severity
+ * @param string $message
+ * @param string $filepath
+ * @param int $line
* @return void
*/
function _exception_handler($severity, $message, $filepath, $line)
{
+ $is_error = (((E_ERROR | E_COMPILE_ERROR | E_CORE_ERROR | E_USER_ERROR) & $severity) === $severity);
+
+ // When an error occurred, set the status header to '500 Internal Server Error'
+ // to indicate to the client something went wrong.
+ // This can't be done within the $_error->show_php_error method because
+ // it is only called when the display_errors flag is set (which isn't usually
+ // the case in a production environment) or when errors are ignored because
+ // they are above the error_reporting threshold.
+ if ($is_error)
+ {
+ set_status_header(500);
+ }
+
$_error =& load_class('Exceptions', 'core');
// Should we ignore the error? We'll get the current error_reporting
@@ -588,6 +604,42 @@ if ( ! function_exists('_exception_handler'))
}
$_error->log_exception($severity, $message, $filepath, $line);
+
+ // If the error is fatal, the execution of the script should be stopped because
+ // errors can't be recovered from. Halting the script conforms with PHP's
+ // default error handling. See http://www.php.net/manual/en/errorfunc.constants.php
+ if ($is_error)
+ {
+ exit(EXIT_ERROR);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------
+
+if ( ! function_exists('_shutdown_handler'))
+{
+ /**
+ * Shutdown Handler
+ *
+ * This is the shutdown handler that is declared at the top
+ * of CodeIgniter.php. The main reason we use this is to simulate
+ * a complete custom exception handler.
+ *
+ * E_STRICT is purposivly neglected because such events may have
+ * been caught. Duplication or none? None is preferred for now.
+ *
+ * @link http://insomanic.me.uk/post/229851073/php-trick-catching-fatal-errors-e-error-with-a
+ * @return void
+ */
+ function _shutdown_handler()
+ {
+ $last_error = error_get_last();
+ if (isset($last_error) &&
+ ($last_error['type'] & (E_ERROR | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING)))
+ {
+ _exception_handler($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']);
+ }
}
}
diff --git a/system/core/Config.php b/system/core/Config.php
index 7e64444bc..a0e830abe 100644
--- a/system/core/Config.php
+++ b/system/core/Config.php
@@ -184,16 +184,16 @@ class CI_Config {
*
* @param string $item Config item name
* @param string $index Index name
- * @return string|bool The configuration item or FALSE on failure
+ * @return string|null The configuration item or NULL if the item doesn't exist
*/
public function item($item, $index = '')
{
if ($index == '')
{
- return isset($this->config[$item]) ? $this->config[$item] : FALSE;
+ return isset($this->config[$item]) ? $this->config[$item] : NULL;
}
- return isset($this->config[$index], $this->config[$index][$item]) ? $this->config[$index][$item] : FALSE;
+ return isset($this->config[$index], $this->config[$index][$item]) ? $this->config[$index][$item] : NULL;
}
// --------------------------------------------------------------------
@@ -202,13 +202,13 @@ class CI_Config {
* Fetch a config file item with slash appended (if not empty)
*
* @param string $item Config item name
- * @return string|bool The configuration item or FALSE on failure
+ * @return string|null The configuration item or NULL if the item doesn't exist
*/
public function slash_item($item)
{
if ( ! isset($this->config[$item]))
{
- return FALSE;
+ return NULL;
}
elseif (trim($this->config[$item]) === '')
{
@@ -228,13 +228,21 @@ class CI_Config {
* @uses CI_Config::_uri_string()
*
* @param string|string[] $uri URI string or an array of segments
+ * @param string $protocol
* @return string
*/
- public function site_url($uri = '')
+ public function site_url($uri = '', $protocol = NULL)
{
+ $base_url = $this->slash_item('base_url');
+
+ if (isset($protocol))
+ {
+ $base_url = $protocol.substr($base_url, strpos($base_url, '://'));
+ }
+
if (empty($uri))
{
- return $this->slash_item('base_url').$this->item('index_page');
+ return $base_url.$this->item('index_page');
}
$uri = $this->_uri_string($uri);
@@ -255,14 +263,14 @@ class CI_Config {
}
}
- return $this->slash_item('base_url').$this->slash_item('index_page').$uri;
+ return $base_url.$this->slash_item('index_page').$uri;
}
elseif (strpos($uri, '?') === FALSE)
{
$uri = '?'.$uri;
}
- return $this->slash_item('base_url').$this->item('index_page').$uri;
+ return $base_url.$this->item('index_page').$uri;
}
// -------------------------------------------------------------
@@ -275,11 +283,19 @@ class CI_Config {
* @uses CI_Config::_uri_string()
*
* @param string|string[] $uri URI string or an array of segments
+ * @param string $protocol
* @return string
*/
- public function base_url($uri = '')
+ public function base_url($uri = '', $protocol = NULL)
{
- return $this->slash_item('base_url').ltrim($this->_uri_string($uri), '/');
+ $base_url = $this->slash_item('base_url');
+
+ if (isset($protocol))
+ {
+ $base_url = $protocol.substr($base_url, strpos($base_url, '://'));
+ }
+
+ return $base_url.ltrim($this->_uri_string($uri), '/');
}
// -------------------------------------------------------------
diff --git a/system/core/Exceptions.php b/system/core/Exceptions.php
index 9c68d06a5..809dc027a 100644
--- a/system/core/Exceptions.php
+++ b/system/core/Exceptions.php
@@ -91,7 +91,7 @@ class CI_Exceptions {
public function log_exception($severity, $message, $filepath, $line)
{
$severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;
- log_message('error', 'Severity: '.$severity.' --> '.$message. ' '.$filepath.' '.$line, TRUE);
+ log_message('error', 'Severity: '.$severity.' --> '.$message.' '.$filepath.' '.$line);
}
// --------------------------------------------------------------------
@@ -107,13 +107,21 @@ class CI_Exceptions {
*/
public function show_404($page = '', $log_error = TRUE)
{
- $heading = '404 Page Not Found';
- $message = 'The page you requested was not found.';
+ if (is_cli())
+ {
+ $heading = 'Not Found';
+ $message = 'The controller/method pair you requested was not found.';
+ }
+ else
+ {
+ $heading = '404 Page Not Found';
+ $message = 'The page you requested was not found.';
+ }
// By default we log this, but allow a dev to skip it
if ($log_error)
{
- log_message('error', '404 Page Not Found --> '.$page);
+ log_message('error', $heading.': '.$page);
}
echo $this->show_error($heading, $message, 'error_404', 404);
@@ -137,16 +145,24 @@ class CI_Exceptions {
*/
public function show_error($heading, $message, $template = 'error_general', $status_code = 500)
{
- set_status_header($status_code);
-
- $message = '<p>'.implode('</p><p>', is_array($message) ? $message : array($message)).'</p>';
+ if (is_cli())
+ {
+ $message = "\t".(is_array($message) ? implode("\n\t", $message) : $message);
+ $template = 'cli'.DIRECTORY_SEPARATOR.$template;
+ }
+ else
+ {
+ set_status_header($status_code);
+ $message = '<p>'.(is_array($message) ? implode('</p><p>', $message) : $message).'</p>';
+ $template = 'html'.DIRECTORY_SEPARATOR.$template;
+ }
if (ob_get_level() > $this->ob_level + 1)
{
ob_end_flush();
}
ob_start();
- include(VIEWPATH.'errors/'.$template.'.php');
+ include(VIEWPATH.'errors'.DIRECTORY_SEPARATOR.$template.'.php');
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
@@ -166,13 +182,22 @@ class CI_Exceptions {
public function show_php_error($severity, $message, $filepath, $line)
{
$severity = isset($this->levels[$severity]) ? $this->levels[$severity] : $severity;
- $filepath = str_replace('\\', '/', $filepath);
- // For safety reasons we do not show the full file path
- if (FALSE !== strpos($filepath, '/'))
+ // For safety reasons we don't show the full file path in non-CLI requests
+ if ( ! is_cli())
+ {
+ $filepath = str_replace('\\', '/', $filepath);
+ if (FALSE !== strpos($filepath, '/'))
+ {
+ $x = explode('/', $filepath);
+ $filepath = $x[count($x)-2].'/'.end($x);
+ }
+
+ $template = 'html'.DIRECTORY_SEPARATOR.'error_php';
+ }
+ else
{
- $x = explode('/', $filepath);
- $filepath = $x[count($x)-2].'/'.end($x);
+ $template = 'cli'.DIRECTORY_SEPARATOR.'error_php';
}
if (ob_get_level() > $this->ob_level + 1)
@@ -180,7 +205,7 @@ class CI_Exceptions {
ob_end_flush();
}
ob_start();
- include(VIEWPATH.'errors/error_php.php');
+ include(VIEWPATH.'errors'.DIRECTORY_SEPARATOR.$template.'.php');
$buffer = ob_get_contents();
ob_end_clean();
echo $buffer;
diff --git a/system/core/Hooks.php b/system/core/Hooks.php
index b3b111991..9bcc23a65 100644
--- a/system/core/Hooks.php
+++ b/system/core/Hooks.php
@@ -54,6 +54,13 @@ class CI_Hooks {
public $hooks = array();
/**
+ * Array with class objects to use hooks methods
+ *
+ * @var array
+ */
+ protected $_objects = array();
+
+ /**
* In progress flag
*
* Determines whether hook is in progress, used to prevent infinte loops
@@ -184,7 +191,7 @@ class CI_Hooks {
$function = empty($data['function']) ? FALSE : $data['function'];
$params = isset($data['params']) ? $data['params'] : '';
- if ($class === FALSE && $function === FALSE)
+ if (empty($function))
{
return FALSE;
}
@@ -195,19 +202,39 @@ class CI_Hooks {
// Call the requested class and/or function
if ($class !== FALSE)
{
- if ( ! class_exists($class, FALSE))
+ // The object is stored?
+ if (isset($this->_objects[$class]))
{
- require($filepath);
+ if (method_exists($this->_objects[$class], $function))
+ {
+ $this->_objects[$class]->$function($params);
+ }
+ else
+ {
+ return $this->_in_progress = FALSE;
+ }
}
+ else
+ {
+ class_exists($class, FALSE) OR require_once($filepath);
+
+ if ( ! class_exists($class, FALSE) OR ! method_exists($class, $function))
+ {
+ return $this->_in_progress = FALSE;
+ }
- $HOOK = new $class();
- $HOOK->$function($params);
+ // Store the object and execute the method
+ $this->_objects[$class] = new $class();
+ $this->_objects[$class]->$function($params);
+ }
}
else
{
+ function_exists($function) OR require_once($filepath);
+
if ( ! function_exists($function))
{
- require($filepath);
+ return $this->_in_progress = FALSE;
}
$function($params);
diff --git a/system/core/Input.php b/system/core/Input.php
index 0ef81128e..ccb70daec 100644
--- a/system/core/Input.php
+++ b/system/core/Input.php
@@ -47,7 +47,7 @@ class CI_Input {
public $ip_address = FALSE;
/**
- * User agent strin
+ * User agent string
*
* @var string
*/
@@ -63,7 +63,7 @@ class CI_Input {
protected $_allow_get_array = TRUE;
/**
- * Standartize new lines flag
+ * Standardize new lines flag
*
* If set to TRUE, then newlines are standardized.
*
@@ -121,9 +121,10 @@ class CI_Input {
{
log_message('debug', 'Input Class Initialized');
- $this->_allow_get_array = (config_item('allow_get_array') === TRUE);
- $this->_enable_xss = (config_item('global_xss_filtering') === TRUE);
- $this->_enable_csrf = (config_item('csrf_protection') === TRUE);
+ $this->_allow_get_array = (config_item('allow_get_array') === TRUE);
+ $this->_enable_xss = (config_item('global_xss_filtering') === TRUE);
+ $this->_enable_csrf = (config_item('csrf_protection') === TRUE);
+ $this->_sandardize_newlines = (bool) config_item('standardize_newlines');
global $SEC;
$this->security =& $SEC;
@@ -151,8 +152,10 @@ class CI_Input {
* @param bool $xss_clean Whether to apply XSS filtering
* @return mixed
*/
- protected function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE)
+ protected function _fetch_from_array(&$array, $index = '', $xss_clean = NULL)
{
+ is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
if (isset($array[$index]))
{
$value = $array[$index];
@@ -197,8 +200,10 @@ class CI_Input {
* @param bool $xss_clean Whether to apply XSS filtering
* @return mixed
*/
- public function get($index = NULL, $xss_clean = FALSE)
+ public function get($index = NULL, $xss_clean = NULL)
{
+ is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
// Check if a field has been provided
if ($index === NULL)
{
@@ -229,8 +234,10 @@ class CI_Input {
* @param bool $xss_clean Whether to apply XSS filtering
* @return mixed
*/
- public function post($index = NULL, $xss_clean = FALSE)
+ public function post($index = NULL, $xss_clean = NULL)
{
+ is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
// Check if a field has been provided
if ($index === NULL)
{
@@ -261,8 +268,10 @@ class CI_Input {
* @param bool $xss_clean Whether to apply XSS filtering
* @return mixed
*/
- public function get_post($index = '', $xss_clean = FALSE)
+ public function post_get($index = '', $xss_clean = NULL)
{
+ is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
return isset($_POST[$index])
? $this->post($index, $xss_clean)
: $this->get($index, $xss_clean);
@@ -271,14 +280,34 @@ class CI_Input {
// --------------------------------------------------------------------
/**
+ * Fetch an item from GET data with fallback to POST
+ *
+ * @param string $index Index for item to be fetched from $_GET or $_POST
+ * @param bool $xss_clean Whether to apply XSS filtering
+ * @return mixed
+ */
+ public function get_post($index = '', $xss_clean = NULL)
+ {
+ is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
+ return isset($_GET[$index])
+ ? $this->get($index, $xss_clean)
+ : $this->post($index, $xss_clean);
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
* Fetch an item from the COOKIE array
*
* @param string $index Index for item to be fetched from $_COOKIE
* @param bool $xss_clean Whether to apply XSS filtering
* @return mixed
*/
- public function cookie($index = '', $xss_clean = FALSE)
+ public function cookie($index = '', $xss_clean = NULL)
{
+ is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
return $this->_fetch_from_array($_COOKIE, $index, $xss_clean);
}
@@ -291,8 +320,10 @@ class CI_Input {
* @param bool $xss_clean Whether to apply XSS filtering
* @return mixed
*/
- public function server($index = '', $xss_clean = FALSE)
+ public function server($index = '', $xss_clean = NULL)
{
+ is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
return $this->_fetch_from_array($_SERVER, $index, $xss_clean);
}
@@ -307,8 +338,10 @@ class CI_Input {
* @param bool $xss_clean Whether to apply XSS filtering
* @return mixed
*/
- public function input_stream($index = '', $xss_clean = FALSE)
+ public function input_stream($index = '', $xss_clean = NULL)
{
+ is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
+
// The input stream can only be read once, so we'll need to check
// if we have already done that first.
if (is_array($this->_input_stream))
@@ -345,7 +378,7 @@ class CI_Input {
* @param bool $httponly Whether to only makes the cookie accessible via HTTP (no javascript)
* @return void
*/
- public function set_cookie($name = '', $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE)
+ public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = FALSE, $httponly = FALSE)
{
if (is_array($name))
{
@@ -671,13 +704,22 @@ class CI_Input {
// but that when present will trip our 'Disallowed Key Characters' alarm
// http://www.ietf.org/rfc/rfc2109.txt
// note that the key names below are single quoted strings, and are not PHP variables
- unset($_COOKIE['$Version']);
- unset($_COOKIE['$Path']);
- unset($_COOKIE['$Domain']);
+ unset(
+ $_COOKIE['$Version'],
+ $_COOKIE['$Path'],
+ $_COOKIE['$Domain']
+ );
foreach ($_COOKIE as $key => $val)
{
- $_COOKIE[$this->_clean_input_keys($key)] = $this->_clean_input_data($val);
+ if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE)
+ {
+ $_COOKIE[$cookie_key] = $this->_clean_input_data($val);
+ }
+ else
+ {
+ unset($_COOKIE[$key]);
+ }
}
}
@@ -685,12 +727,12 @@ class CI_Input {
$_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']);
// CSRF Protection check
- if ($this->_enable_csrf === TRUE && ! $this->is_cli_request())
+ if ($this->_enable_csrf === TRUE && ! is_cli())
{
$this->security->csrf_verify();
}
- log_message('debug', 'Global POST and COOKIE data sanitized');
+ log_message('debug', 'Global POST, GET and COOKIE data sanitized');
}
// --------------------------------------------------------------------
@@ -733,13 +775,7 @@ class CI_Input {
}
// Remove control characters
- $str = remove_invisible_characters($str);
-
- // Should we filter the input data?
- if ($this->_enable_xss === TRUE)
- {
- $str = $this->security->xss_clean($str);
- }
+ $str = remove_invisible_characters($str, FALSE);
// Standardize newlines if needed
if ($this->_standardize_newlines === TRUE)
@@ -760,15 +796,25 @@ class CI_Input {
* only named with alpha-numeric text and a few other items.
*
* @param string $str Input string
- * @return string
+ * @param string $fatal Whether to terminate script exection
+ * or to return FALSE if an invalid
+ * key is encountered
+ * @return string|bool
*/
- protected function _clean_input_keys($str)
+ protected function _clean_input_keys($str, $fatal = TRUE)
{
if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str))
{
- set_status_header(503);
- echo 'Disallowed Key Characters.';
- exit(EXIT_USER_INPUT);
+ if ($fatal === TRUE)
+ {
+ return FALSE;
+ }
+ else
+ {
+ set_status_header(503);
+ echo 'Disallowed Key Characters.';
+ exit(EXIT_USER_INPUT);
+ }
}
// Clean UTF-8 if supported
@@ -868,11 +914,12 @@ class CI_Input {
*
* Test to see if a request was made from the command line.
*
- * @return bool
+ * @deprecated 3.0.0 Use is_cli() instead
+ * @return bool
*/
public function is_cli_request()
{
- return (php_sapi_name() === 'cli' OR defined('STDIN'));
+ return is_cli();
}
// --------------------------------------------------------------------
diff --git a/system/core/Lang.php b/system/core/Lang.php
index 3236709f2..290b38bea 100644
--- a/system/core/Lang.php
+++ b/system/core/Lang.php
@@ -166,7 +166,7 @@ class CI_Lang {
* @param bool $log_errors Whether to log an error message if the line is not found
* @return string Translation
*/
- public function line($line = '', $log_errors = TRUE)
+ public function line($line, $log_errors = TRUE)
{
$value = ($line === '' OR ! isset($this->language[$line])) ? FALSE : $this->language[$line];
diff --git a/system/core/Loader.php b/system/core/Loader.php
index 70a6b6fa6..8c8d5a37c 100644
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -76,13 +76,6 @@ class CI_Loader {
protected $_ci_helper_paths = array(APPPATH, BASEPATH);
/**
- * List of loaded base classes
- *
- * @var array
- */
- protected $_base_classes = array(); // Set by the controller class
-
- /**
* List of cached variables
*
* @var array
@@ -120,6 +113,8 @@ class CI_Loader {
'user_agent' => 'agent'
);
+ // --------------------------------------------------------------------
+
/**
* Class constructor
*
@@ -129,7 +124,8 @@ class CI_Loader {
*/
public function __construct()
{
- $this->_ci_ob_level = ob_get_level();
+ $this->_ci_ob_level = ob_get_level();
+ $this->_ci_classes =& is_loaded();
log_message('debug', 'Loader Class Initialized');
}
@@ -147,7 +143,6 @@ class CI_Loader {
*/
public function initialize()
{
- $this->_base_classes =& is_loaded();
$this->_ci_autoloader();
}
@@ -165,7 +160,7 @@ class CI_Loader {
*/
public function is_loaded($class)
{
- return isset($this->_ci_classes[$class]) ? $this->_ci_classes[$class] : FALSE;
+ return array_search(ucfirst($class), $this->_ci_classes, TRUE);
}
// --------------------------------------------------------------------
@@ -179,23 +174,29 @@ class CI_Loader {
* @param string $library Library name
* @param array $params Optional parameters to pass to the library class constructor
* @param string $object_name An optional object name to assign to
- * @return void
+ * @return object
*/
- public function library($library = '', $params = NULL, $object_name = NULL)
+ public function library($library, $params = NULL, $object_name = NULL)
{
- if (is_array($library))
+ if (empty($library))
+ {
+ return $this;
+ }
+ elseif (is_array($library))
{
- foreach ($library as $class)
+ foreach ($library as $key => $value)
{
- $this->library($class, $params);
+ if (is_int($key))
+ {
+ $this->library($value, $params);
+ }
+ else
+ {
+ $this->library($key, $params, $value);
+ }
}
- return;
- }
-
- if ($library === '' OR isset($this->_base_classes[$library]))
- {
- return;
+ return $this;
}
if ($params !== NULL && ! is_array($params))
@@ -204,6 +205,7 @@ class CI_Loader {
}
$this->_ci_load_class($library, $params, $object_name);
+ return $this;
}
// --------------------------------------------------------------------
@@ -216,21 +218,22 @@ class CI_Loader {
* @param string $model Model name
* @param string $name An optional object name to assign to
* @param bool $db_conn An optional database connection configuration to initialize
- * @return void
+ * @return object
*/
public function model($model, $name = '', $db_conn = FALSE)
{
if (empty($model))
{
- return;
+ return $this;
}
elseif (is_array($model))
{
foreach ($model as $key => $value)
{
- $this->model(is_int($key) ? $value : $key, $value);
+ is_int($key) ? $this->model($value, '', $db_conn) : $this->model($key, $value, $db_conn);
}
- return;
+
+ return $this;
}
$path = '';
@@ -252,7 +255,7 @@ class CI_Loader {
if (in_array($name, $this->_ci_models, TRUE))
{
- return;
+ return $this;
}
$CI =& get_instance();
@@ -261,36 +264,35 @@ class CI_Loader {
show_error('The model name you are loading is the name of a resource that is already being used: '.$name);
}
- $model = strtolower($model);
-
- foreach ($this->_ci_model_paths as $mod_path)
+ if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
{
- if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
+ if ($db_conn === TRUE)
{
- continue;
+ $db_conn = '';
}
- if ($db_conn !== FALSE && ! class_exists('CI_DB', FALSE))
- {
- if ($db_conn === TRUE)
- {
- $db_conn = '';
- }
+ $CI->load->database($db_conn, FALSE, TRUE);
+ }
- $CI->load->database($db_conn, FALSE, TRUE);
- }
+ if ( ! class_exists('CI_Model', FALSE))
+ {
+ load_class('Model', 'core');
+ }
- if ( ! class_exists('CI_Model', FALSE))
+ $model = ucfirst(strtolower($model));
+
+ foreach ($this->_ci_model_paths as $mod_path)
+ {
+ if ( ! file_exists($mod_path.'models/'.$path.$model.'.php'))
{
- load_class('Model', 'core');
+ continue;
}
require_once($mod_path.'models/'.$path.$model.'.php');
- $model = ucfirst($model);
$CI->$name = new $model();
$this->_ci_models[] = $name;
- return;
+ return $this;
}
// couldn't find the model
@@ -307,8 +309,8 @@ class CI_Loader {
* @param bool $query_builder Whether to enable Query Builder
* (overrides the configuration setting)
*
- * @return void|object|bool Database object if $return is set to TRUE,
- * FALSE on failure, void in any other case
+ * @return object|bool Database object if $return is set to TRUE,
+ * FALSE on failure, CI_Loader instance in any other case
*/
public function database($params = '', $return = FALSE, $query_builder = NULL)
{
@@ -334,6 +336,7 @@ class CI_Loader {
// Load the DB class
$CI->db =& DB($params, $query_builder);
+ return $this;
}
// --------------------------------------------------------------------
@@ -342,8 +345,8 @@ class CI_Loader {
* Load the Database Utilities Class
*
* @param object $db Database object
- * @param bool $return Whether to return the DB Forge class object or not
- * @return void|object
+ * @param bool $return Whether to return the DB Utilities class object or not
+ * @return object
*/
public function dbutil($db = NULL, $return = FALSE)
{
@@ -365,6 +368,7 @@ class CI_Loader {
}
$CI->dbutil = new $class($db);
+ return $this;
}
// --------------------------------------------------------------------
@@ -374,7 +378,7 @@ class CI_Loader {
*
* @param object $db Database object
* @param bool $return Whether to return the DB Forge class object or not
- * @return void|object
+ * @return object
*/
public function dbforge($db = NULL, $return = FALSE)
{
@@ -408,6 +412,7 @@ class CI_Loader {
}
$CI->dbforge = new $class($db);
+ return $this;
}
// --------------------------------------------------------------------
@@ -422,7 +427,7 @@ class CI_Loader {
* to be extracted for use in the view
* @param bool $return Whether to return the view output
* or leave it to the Output class
- * @return void
+ * @return object|string
*/
public function view($view, $vars = array(), $return = FALSE)
{
@@ -436,7 +441,7 @@ class CI_Loader {
*
* @param string $path File path
* @param bool $return Whether to return the file output
- * @return void|string
+ * @return object|string
*/
public function file($path, $return = FALSE)
{
@@ -455,9 +460,9 @@ class CI_Loader {
* An associative array or object containing values
* to be set, or a value's name if string
* @param string $val Value to set, only used if $vars is a string
- * @return void
+ * @return object
*/
- public function vars($vars = array(), $val = '')
+ public function vars($vars, $val = '')
{
if (is_string($vars))
{
@@ -473,6 +478,23 @@ class CI_Loader {
$this->_ci_cached_vars[$key] = $val;
}
}
+
+ return $this;
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Clear Cached Variables
+ *
+ * Clears the cached variables.
+ *
+ * @return object
+ */
+ public function clear_vars()
+ {
+ $this->_ci_cached_vars = array();
+ return $this;
}
// --------------------------------------------------------------------
@@ -510,7 +532,7 @@ class CI_Loader {
* Helper Loader
*
* @param string|string[] $helpers Helper name(s)
- * @return void
+ * @return object
*/
public function helper($helpers = array())
{
@@ -567,6 +589,8 @@ class CI_Loader {
show_error('Unable to load the requested file: helpers/'.$helper.'.php');
}
}
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -579,11 +603,11 @@ class CI_Loader {
*
* @uses CI_Loader::helper()
* @param string|string[] $helpers Helper name(s)
- * @return void
+ * @return object
*/
public function helpers($helpers = array())
{
- $this->helper($helpers);
+ return $this->helper($helpers);
}
// --------------------------------------------------------------------
@@ -595,18 +619,19 @@ class CI_Loader {
*
* @param string|string[] $files List of language file names to load
* @param string Language name
- * @return void
+ * @return object
*/
- public function language($files = array(), $lang = '')
+ public function language($files, $lang = '')
{
$CI =& get_instance();
-
is_array($files) OR $files = array($files);
foreach ($files as $langfile)
{
$CI->lang->load($langfile, $lang);
}
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -622,10 +647,9 @@ class CI_Loader {
* @param bool $fail_gracefully Whether to just return FALSE or display an error message
* @return bool TRUE if the file was loaded correctly or FALSE on failure
*/
- public function config($file = '', $use_sections = FALSE, $fail_gracefully = FALSE)
+ public function config($file, $use_sections = FALSE, $fail_gracefully = FALSE)
{
- $CI =& get_instance();
- return $CI->config->load($file, $use_sections, $fail_gracefully);
+ return get_instance()->config->load($file, $use_sections, $fail_gracefully);
}
// --------------------------------------------------------------------
@@ -639,10 +663,10 @@ class CI_Loader {
* @param array $params Optional parameters to pass to the driver
* @param string $object_name An optional object name to assign to
*
- * @return void|object|bool Object or FALSE on failure if $library is a string
- * and $object_name is set. void otherwise.
+ * @return object|bool Object or FALSE on failure if $library is a string
+ * and $object_name is set. CI_Loader instance otherwise.
*/
- public function driver($library = '', $params = NULL, $object_name = NULL)
+ public function driver($library, $params = NULL, $object_name = NULL)
{
if (is_array($library))
{
@@ -650,10 +674,10 @@ class CI_Loader {
{
$this->driver($driver);
}
- return;
- }
- if ($library === '')
+ return $this;
+ }
+ elseif (empty($library))
{
return FALSE;
}
@@ -689,7 +713,7 @@ class CI_Loader {
*
* @param string $path Path to add
* @param bool $view_cascade (default: TRUE)
- * @return void
+ * @return object
*/
public function add_package_path($path, $view_cascade = TRUE)
{
@@ -704,6 +728,8 @@ class CI_Loader {
// Add config file path
$config =& $this->_ci_get_component('config');
$config->_config_paths[] = $path;
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -731,7 +757,7 @@ class CI_Loader {
* added path will be removed removed.
*
* @param string $path Path to remove
- * @return void
+ * @return object
*/
public function remove_package_path($path = '')
{
@@ -773,6 +799,8 @@ class CI_Loader {
$this->_ci_model_paths = array_unique(array_merge($this->_ci_model_paths, array(APPPATH)));
$this->_ci_view_paths = array_merge($this->_ci_view_paths, array(APPPATH.'views/' => TRUE));
$config->_config_paths = array_unique(array_merge($config->_config_paths, array(APPPATH)));
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -788,7 +816,7 @@ class CI_Loader {
* @used-by CI_Loader::view()
* @used-by CI_Loader::file()
* @param array $_ci_data Data to load
- * @return void
+ * @return object
*/
protected function _ci_load($_ci_data)
{
@@ -912,6 +940,8 @@ class CI_Loader {
$_ci_CI->output->append_output(ob_get_contents());
@ob_end_clean();
}
+
+ return $this;
}
// --------------------------------------------------------------------
@@ -1118,30 +1148,35 @@ class CI_Loader {
// Set the variable name we will assign the class to
// Was a custom class name supplied? If so we'll use it
- $class = strtolower($class);
-
- if ($object_name === NULL)
+ if (empty($object_name))
{
- $classvar = isset($this->_ci_varmap[$class]) ? $this->_ci_varmap[$class] : $class;
+ $object_name = strtolower($class);
+ if (isset($this->_ci_varmap[$object_name]))
+ {
+ $object_name = $this->_ci_varmap[$object_name];
+ }
}
- else
+
+ // Don't overwrite existing properties
+ $CI =& get_instance();
+ if (isset($CI->$object_name))
{
- $classvar = $object_name;
+ if ($CI->$object_name instanceof $name)
+ {
+ log_message('debug', $class." has already been instantiated as '".$object_name."'. Second attempt aborted.");
+ return;
+ }
+
+ show_error("Resource '".$object_name."' already exists and is not a ".$class." instance.");
}
// Save the class name and object name
- $this->_ci_classes[$class] = $classvar;
+ $this->_ci_classes[$object_name] = $class;
// Instantiate the class
- $CI =& get_instance();
- if ($config !== NULL)
- {
- $CI->$classvar = new $name($config);
- }
- else
- {
- $CI->$classvar = new $name();
- }
+ $CI->$object_name = isset($config)
+ ? new $name($config)
+ : new $name();
}
// --------------------------------------------------------------------
@@ -1198,6 +1233,15 @@ class CI_Loader {
}
}
+ // Autoload drivers
+ if (isset($autoload['drivers']))
+ {
+ foreach ($autoload['drivers'] as $item)
+ {
+ $this->driver($item);
+ }
+ }
+
// Load libraries
if (isset($autoload['libraries']) && count($autoload['libraries']) > 0)
{
@@ -1215,15 +1259,6 @@ class CI_Loader {
}
}
- // Autoload drivers
- if (isset($autoload['drivers']))
- {
- foreach ($autoload['drivers'] as $item)
- {
- $this->driver($item);
- }
- }
-
// Autoload models
if (isset($autoload['model']))
{
diff --git a/system/core/Log.php b/system/core/Log.php
index e4d72b544..63fef2088 100644
--- a/system/core/Log.php
+++ b/system/core/Log.php
@@ -140,10 +140,9 @@ class CI_Log {
*
* @param string the error level: 'error', 'debug' or 'info'
* @param string the error message
- * @param bool whether the error is a native PHP error
* @return bool
*/
- public function write_log($level, $msg, $php_error = FALSE)
+ public function write_log($level, $msg)
{
if ($this->_enabled === FALSE)
{
@@ -176,10 +175,18 @@ class CI_Log {
return FALSE;
}
- $message .= $level.' '.($level === 'INFO' ? ' -' : '-').' '.date($this->_date_fmt).' --> '.$msg."\n";
+ $message .= $level.' - '.date($this->_date_fmt).' --> '.$msg."\n";
flock($fp, LOCK_EX);
- fwrite($fp, $message);
+
+ for ($written = 0, $length = strlen($message); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, substr($message, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
flock($fp, LOCK_UN);
fclose($fp);
@@ -188,7 +195,7 @@ class CI_Log {
@chmod($filepath, FILE_WRITE_MODE);
}
- return TRUE;
+ return is_int($result);
}
}
diff --git a/system/core/Model.php b/system/core/Model.php
index 1eb6f909b..11e60759b 100644
--- a/system/core/Model.php
+++ b/system/core/Model.php
@@ -59,8 +59,7 @@ class CI_Model {
*/
public function __get($key)
{
- $CI =& get_instance();
- return $CI->$key;
+ return get_instance()->$key;
}
}
diff --git a/system/core/Output.php b/system/core/Output.php
index 06d7a866b..2ad8e90fa 100644
--- a/system/core/Output.php
+++ b/system/core/Output.php
@@ -58,21 +58,21 @@ class CI_Output {
*
* @var array
*/
- public $headers = array();
+ public $headers = array();
/**
* List of mime types
*
* @var array
*/
- public $mimes = array();
+ public $mimes = array();
/**
* Mime-type for the current page
*
* @var string
*/
- protected $mime_type = 'text/html';
+ protected $mime_type = 'text/html';
/**
* Enable Profiler flag
@@ -82,11 +82,18 @@ class CI_Output {
public $enable_profiler = FALSE;
/**
- * zLib output compression flag
+ * php.ini zlib.output_compression flag
*
* @var bool
*/
- protected $_zlib_oc = FALSE;
+ protected $_zlib_oc = FALSE;
+
+ /**
+ * CI output compression flag
+ *
+ * @var bool
+ */
+ protected $_compress_output = FALSE;
/**
* List of profiler sections
@@ -102,7 +109,7 @@ class CI_Output {
*
* @var bool
*/
- public $parse_exec_vars = TRUE;
+ public $parse_exec_vars = TRUE;
/**
* Class constructor
@@ -114,6 +121,11 @@ class CI_Output {
public function __construct()
{
$this->_zlib_oc = (bool) @ini_get('zlib.output_compression');
+ $this->_compress_output = (
+ $this->_zlib_oc === FALSE
+ && config_item('compress_output') === TRUE
+ && extension_loaded('zlib')
+ );
// Get mime types for later
$this->mimes =& get_mimes();
@@ -436,15 +448,14 @@ class CI_Output {
if ($this->parse_exec_vars === TRUE)
{
$memory = round(memory_get_usage() / 1024 / 1024, 2).'MB';
-
$output = str_replace(array('{elapsed_time}', '{memory_usage}'), array($elapsed, $memory), $output);
}
// --------------------------------------------------------------------
// Is compression requested?
- if ($CFG->item('compress_output') === TRUE && $this->_zlib_oc === FALSE
- && extension_loaded('zlib')
+ if (isset($CI) // This means that we're not serving a cache file, if we were, it would already be compressed
+ && $this->_compress_output === TRUE
&& isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
{
ob_start('ob_gzhandler');
@@ -468,6 +479,21 @@ class CI_Output {
// simply echo out the data and exit.
if ( ! isset($CI))
{
+ if ($this->_compress_output === TRUE)
+ {
+ if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
+ {
+ header('Content-Encoding: gzip');
+ header('Content-Length: '.strlen($output));
+ }
+ else
+ {
+ // User agent doesn't support gzip compression,
+ // so we'll have to decompress our cache
+ $output = gzinflate(substr($output, 10, -8));
+ }
+ }
+
echo $output;
log_message('debug', 'Final output sent to browser');
log_message('debug', 'Total execution time: '.$elapsed);
@@ -530,9 +556,9 @@ class CI_Output {
return;
}
- $uri = $CI->config->item('base_url').
- $CI->config->item('index_page').
- $CI->uri->uri_string();
+ $uri = $CI->config->item('base_url')
+ .$CI->config->item('index_page')
+ .$CI->uri->uri_string();
$cache_path .= md5($uri);
@@ -542,17 +568,39 @@ class CI_Output {
return;
}
- $expire = time() + ($this->cache_expiration * 60);
-
- // Put together our serialized info.
- $cache_info = serialize(array(
- 'expire' => $expire,
- 'headers' => $this->headers
- ));
-
if (flock($fp, LOCK_EX))
{
- fwrite($fp, $cache_info.'ENDCI--->'.$output);
+ // If output compression is enabled, compress the cache
+ // itself, so that we don't have to do that each time
+ // we're serving it
+ if ($this->_compress_output === TRUE)
+ {
+ $output = gzencode($output);
+
+ if ($this->get_header('content-type') === NULL)
+ {
+ $this->set_content_type($this->mime_type);
+ }
+ }
+
+ $expire = time() + ($this->cache_expiration * 60);
+
+ // Put together our serialized info.
+ $cache_info = serialize(array(
+ 'expire' => $expire,
+ 'headers' => $this->headers
+ ));
+
+ $output = $cache_info.'ENDCI--->'.$output;
+
+ for ($written = 0, $length = strlen($output); $written < $length; $written += $result)
+ {
+ if (($result = fwrite($fp, substr($output, $written))) === FALSE)
+ {
+ break;
+ }
+ }
+
flock($fp, LOCK_UN);
}
else
@@ -560,13 +608,22 @@ class CI_Output {
log_message('error', 'Unable to secure a file lock for file at: '.$cache_path);
return;
}
+
fclose($fp);
- @chmod($cache_path, FILE_WRITE_MODE);
- log_message('debug', 'Cache file written: '.$cache_path);
+ if (is_int($result))
+ {
+ @chmod($cache_path, FILE_WRITE_MODE);
+ log_message('debug', 'Cache file written: '.$cache_path);
- // Send HTTP cache-control headers to browser to match file cache settings.
- $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
+ // Send HTTP cache-control headers to browser to match file cache settings.
+ $this->set_cache_header($_SERVER['REQUEST_TIME'], $expire);
+ }
+ else
+ {
+ @unlink($cache_path);
+ log_message('error', 'Unable to write the complete cache content at: '.$cache_path);
+ }
}
// --------------------------------------------------------------------
@@ -701,7 +758,7 @@ class CI_Output {
else
{
header('Pragma: public');
- header('Cache-Control: max-age=' . $max_age . ', public');
+ header('Cache-Control: max-age='.$max_age.', public');
header('Expires: '.gmdate('D, d M Y H:i:s', $expiration).' GMT');
header('Last-modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
}
@@ -740,13 +797,13 @@ class CI_Output {
preg_match_all('{<style.+</style>}msU', $output, $style_clean);
foreach ($style_clean[0] as $s)
{
- $output = str_replace($s, $this->_minify_script_style($s, TRUE), $output);
+ $output = str_replace($s, $this->_minify_js_css($s, 'css', TRUE), $output);
}
// Minify the javascript in <script> tags.
foreach ($javascript_clean[0] as $s)
{
- $javascript_mini[] = $this->_minify_script_style($s, TRUE);
+ $javascript_mini[] = $this->_minify_js_css($s, 'js', TRUE);
}
// Replace multiple spaces with a single space.
@@ -792,13 +849,14 @@ class CI_Output {
break;
case 'text/css':
+
+ return $this->_minify_js_css($output, 'css');
+
case 'text/javascript':
case 'application/javascript':
case 'application/x-javascript':
- $output = $this->_minify_script_style($output);
-
- break;
+ return $this->_minify_js_css($output, 'js');
default: break;
}
@@ -809,134 +867,100 @@ class CI_Output {
// --------------------------------------------------------------------
/**
- * Minify Style and Script
- *
- * Reduce excessive size of CSS/JavaScript content. To remove spaces this
- * script walks the string as an array and determines if the pointer is inside
- * a string created by single quotes or double quotes. spaces inside those
- * strings are not stripped. Opening and closing tags are severed from
- * the string initially and saved without stripping whitespace to preserve
- * the tags and any associated properties if tags are present
+ * Minify JavaScript and CSS code
*
- * Minification logic/workflow is similar to methods used by Douglas Crockford
- * in JSMIN. http://www.crockford.com/javascript/jsmin.html
+ * Strips comments and excessive whitespace characters
*
- * KNOWN ISSUE: ending a line with a closing parenthesis ')' and no semicolon
- * where there should be one will break the Javascript. New lines after a
- * closing parenthesis are not recognized by the script. For best results
- * be sure to terminate lines with a semicolon when appropriate.
- *
- * @param string $output Output to minify
- * @param bool $has_tags Specify if the output has style or script tags
- * @return string Minified output
+ * @param string $output
+ * @param string $type 'js' or 'css'
+ * @param bool $tags Whether $output contains the 'script' or 'style' tag
+ * @return string
*/
- protected function _minify_script_style($output, $has_tags = FALSE)
+ protected function _minify_js_css($output, $type, $tags = FALSE)
{
- // We only need this if there are tags in the file
- if ($has_tags === TRUE)
+ if ($tags === TRUE)
{
- // Remove opening tag and save for later
- $pos = strpos($output, '>') + 1;
- $open_tag = substr($output, 0, $pos);
- $output = substr_replace($output, '', 0, $pos);
+ $tags = array('close' => strrchr($output, '<'));
- // Remove closing tag and save it for later
- $pos = strpos($output, '</');
- $closing_tag = substr($output, $pos, strlen($output));
- $output = substr_replace($output, '', $pos);
- }
+ $open_length = strpos($output, '>') + 1;
+ $tags['open'] = substr($output, 0, $open_length);
- // Remove CSS comments
- $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!i', '', $output);
+ $output = substr($output, $open_length, -strlen($tags['close']));
- // Remove spaces around curly brackets, colons,
- // semi-colons, parenthesis, commas
- $chunks = preg_split('/([\'|"]).+(?![^\\\]\\1)\\1/iU', $output, -1, PREG_SPLIT_OFFSET_CAPTURE);
- for ($i = count($chunks) - 1; $i >= 0; $i--)
- {
- $output = substr_replace(
- $output,
- preg_replace('/\s*(:|;|,|}|{|\(|\))\s*/i', '$1', $chunks[$i][0]),
- $chunks[$i][1],
- strlen($chunks[$i][0])
- );
+ // Strip spaces from the tags
+ $tags = preg_replace('#\s{2,}#', ' ', $tags);
}
- // Replace tabs with spaces
- // Replace carriage returns & multiple new lines with single new line
- // and trim any leading or trailing whitespace
- $output = trim(preg_replace(array('/\t+/', '/\r/', '/\n+/'), array(' ', "\n", "\n"), $output));
+ $output = trim($output);
- // Remove spaces when safe to do so.
- $in_string = $in_dstring = $prev = FALSE;
- $array_output = str_split($output);
- foreach ($array_output as $key => $value)
+ if ($type === 'js')
{
- if ($in_string === FALSE && $in_dstring === FALSE)
+ // Catch all string literals and comment blocks
+ if (preg_match_all('#((?:((?<!\\\)\'|")|(/\*)|(//)).*(?(2)(?<!\\\)\2|(?(3)\*/|\n)))#msuUS', $output, $match, PREG_OFFSET_CAPTURE))
{
- if ($value === ' ')
+ $js_literals = $js_code = array();
+ for ($match = $match[0], $c = count($match), $i = $pos = $offset = 0; $i < $c; $i++)
{
- // Get the next element in the array for comparisons
- $next = $array_output[$key + 1];
-
- // Strip spaces preceded/followed by a non-ASCII character
- // or not preceded/followed by an alphanumeric
- // or not preceded/followed \ $ and _
- if ((preg_match('/^[\x20-\x7f]*$/D', $next) OR preg_match('/^[\x20-\x7f]*$/D', $prev))
- && ( ! ctype_alnum($next) OR ! ctype_alnum($prev))
- && ! in_array($next, array('\\', '_', '$'), TRUE)
- && ! in_array($prev, array('\\', '_', '$'), TRUE)
- )
+ $js_code[$pos++] = trim(substr($output, $offset, $match[$i][1] - $offset));
+ $offset = $match[$i][1] + strlen($match[$i][0]);
+
+ // Save only if we haven't matched a comment block
+ if ($match[$i][0][0] !== '/')
{
- unset($array_output[$key]);
+ $js_literals[$pos++] = array_shift($match[$i]);
}
}
- else
- {
- // Save this value as previous for the next iteration
- // if it is not a blank space
- $prev = $value;
- }
- }
+ $js_code[$pos] = substr($output, $offset);
- if ($value === "'")
- {
- $in_string = ! $in_string;
+ // $match might be quite large, so free it up together with other vars that we no longer need
+ unset($match, $offset, $pos);
}
- elseif ($value === '"')
+ else
{
- $in_dstring = ! $in_dstring;
+ $js_code = array($output);
+ $js_literals = array();
}
+
+ $varname = 'js_code';
+ }
+ else
+ {
+ $varname = 'output';
}
- // Put the string back together after spaces have been stripped
- $output = implode($array_output);
+ // Standartize new lines
+ $$varname = str_replace(array("\r\n", "\r"), "\n", $$varname);
- // Remove new line characters unless previous or next character is
- // printable or Non-ASCII
- preg_match_all('/[\n]/', $output, $lf, PREG_OFFSET_CAPTURE);
- $removed_lf = 0;
- foreach ($lf as $feed_position)
+ if ($type === 'js')
{
- foreach ($feed_position as $position)
- {
- $position = $position[1] - $removed_lf;
- $next = $output[$position + 1];
- $prev = $output[$position - 1];
- if ( ! ctype_print($next) && ! ctype_print($prev)
- && ! preg_match('/^[\x20-\x7f]*$/D', $next)
- && ! preg_match('/^[\x20-\x7f]*$/D', $prev)
- )
- {
- $output = substr_replace($output, '', $position, 1);
- $removed_lf++;
- }
- }
+ $patterns = array(
+ '#\s*([!\#%&()*+,\-./:;<=>?@\[\]^`{|}~])\s*#' => '$1', // Remove spaces following and preceeding JS-wise non-special & non-word characters
+ '#\s{2,}#' => ' ' // Reduce the remaining multiple whitespace characters to a single space
+ );
+ }
+ else
+ {
+ $patterns = array(
+ '#/\*.*(?=\*/)\*/#s' => '', // Remove /* block comments */
+ '#\n?//[^\n]*#' => '', // Remove // line comments
+ '#\s*([^\w.\#%])\s*#U' => '$1', // Remove spaces following and preceeding non-word characters, excluding dots, hashes and the percent sign
+ '#\s{2,}#' => ' ' // Reduce the remaining multiple space characters to a single space
+ );
+ }
+
+ $$varname = preg_replace(array_keys($patterns), array_values($patterns), $$varname);
+
+ // Glue back JS quoted strings
+ if ($type === 'js')
+ {
+ $js_code += $js_literals;
+ ksort($js_code);
+ $output = implode($js_code);
+ unset($js_code, $js_literals, $varname, $patterns);
}
- // Put the opening and closing tags back if applicable
- return isset($open_tag)
- ? $open_tag.$output.$closing_tag
+ return is_array($tags)
+ ? $tags['open'].$output.$tags['close']
: $output;
}
diff --git a/system/core/Router.php b/system/core/Router.php
index cc3916f86..633524023 100644
--- a/system/core/Router.php
+++ b/system/core/Router.php
@@ -91,6 +91,15 @@ class CI_Router {
*/
public $translate_uri_dashes = FALSE;
+ /**
+ * Enable query strings flag
+ *
+ * Determines wether to use GET parameters or segment URIs
+ *
+ * @var bool
+ */
+ public $enable_query_strings = FALSE;
+
// --------------------------------------------------------------------
/**
@@ -102,9 +111,34 @@ class CI_Router {
*/
public function __construct()
{
+ global $routing;
+
$this->config =& load_class('Config', 'core');
$this->uri =& load_class('URI', 'core');
+
+ $this->enable_query_strings = ( ! is_cli() && $this->config->item('enable_query_strings') === TRUE);
$this->_set_routing();
+
+ // Set any routing overrides that may exist in the main index file
+ if (isset($routing) && is_array($routing))
+ {
+ if (isset($routing['directory']))
+ {
+ $this->set_directory($routing['directory']);
+ }
+
+ if ( ! empty($routing['controller']))
+ {
+ $this->set_class($routing['controller']);
+ }
+
+ if (isset($routing['function']))
+ {
+ $routing['function'] = empty($routing['function']) ? 'index' : $routing['function'];
+ $this->set_method($routing['function']);
+ }
+ }
+
log_message('debug', 'Router Class Initialized');
}
@@ -123,26 +157,39 @@ class CI_Router {
// Are query strings enabled in the config file? Normally CI doesn't utilize query strings
// since URI segments are more search-engine friendly, but they can optionally be used.
// If this feature is enabled, we will gather the directory/class/method a little differently
- $segments = array();
- if ($this->config->item('enable_query_strings') === TRUE
- && ! empty($_GET[$this->config->item('controller_trigger')])
- && is_string($_GET[$this->config->item('controller_trigger')])
- )
+ if ($this->enable_query_strings)
{
- if (isset($_GET[$this->config->item('directory_trigger')]) && is_string($_GET[$this->config->item('directory_trigger')]))
+ $_d = $this->config->item('directory_trigger');
+ $_d = isset($_GET[$_d]) ? trim($_GET[$_d], " \t\n\r\0\x0B/") : '';
+ if ($_d !== '')
{
- $this->set_directory(trim($this->uri->_filter_uri($_GET[$this->config->item('directory_trigger')])));
- $segments[] = $this->directory;
+ $this->set_directory($this->uri->filter_uri($_d));
}
- $this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')])));
- $segments[] = $this->class;
+ $_c = $this->config->item('controller_trigger');
+ if ( ! empty($_GET[$_c]))
+ {
+ $this->set_class(trim($this->uri->filter_uri(trim($_GET[$_c]))));
+
+ $_f = $this->config->item('function_trigger');
+ if ( ! empty($_GET[$_f]))
+ {
+ $this->set_method(trim($this->uri->filter_uri($_GET[$_f])));
+ }
- if ( ! empty($_GET[$this->config->item('function_trigger')]) && is_string($_GET[$this->config->item('function_trigger')]))
+ $this->uri->rsegments = array(
+ 1 => $this->class,
+ 2 => $this->method
+ );
+ }
+ else
{
- $this->set_method(trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')])));
- $segments[] = $this->method;
+ $this->_set_default_controller();
}
+
+ // Routing rules don't apply to query strings and we don't need to detect
+ // directories, so we're done here
+ return;
}
// Load the routes.php file.
@@ -165,53 +212,15 @@ class CI_Router {
$this->routes = $route;
}
- // Were there any query string segments? If so, we'll validate them and bail out since we're done.
- if (count($segments) > 0)
+ // Is there anything to parse?
+ if ($this->uri->uri_string !== '')
{
- return $this->_validate_request($segments);
+ $this->_parse_routes();
}
-
- // Fetch the complete URI string
- $this->uri->_fetch_uri_string();
-
- // Is there a URI string? If not, the default controller specified in the "routes" file will be shown.
- if ($this->uri->uri_string == '')
+ else
{
- return $this->_set_default_controller();
+ $this->_set_default_controller();
}
-
- $this->uri->_remove_url_suffix(); // Remove the URL suffix
- $this->uri->_explode_segments(); // Compile the segments into an array
- $this->_parse_routes(); // Parse any custom routing that may exist
- $this->uri->_reindex_segments(); // Re-index the segment array so that it starts with 1 rather than 0
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Set default controller
- *
- * @return void
- */
- protected function _set_default_controller()
- {
- if (empty($this->default_controller))
- {
- show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.');
- }
-
- // Is the method being specified?
- if (sscanf($this->default_controller, '%[^/]/%s', $class, $method) !== 2)
- {
- $method = 'index';
- }
-
- $this->_set_request(array($class, $method));
-
- // re-index the routed segments array so it starts with 1 rather than 0
- $this->uri->_reindex_segments();
-
- log_message('debug', 'No URI present. Default controller set.');
}
// --------------------------------------------------------------------
@@ -222,16 +231,19 @@ class CI_Router {
* Takes an array of URI segments as input and sets the class/method
* to be called.
*
+ * @used-by CI_Router::_parse_routes()
* @param array $segments URI segments
* @return void
*/
protected function _set_request($segments = array())
{
$segments = $this->_validate_request($segments);
-
- if (count($segments) === 0)
+ // If we don't have any segments left - try the default controller;
+ // WARNING: Directories get shifted out of the segments array!
+ if (empty($segments))
{
- return $this->_set_default_controller();
+ $this->_set_default_controller();
+ return;
}
if ($this->translate_uri_dashes === TRUE)
@@ -244,92 +256,86 @@ class CI_Router {
}
$this->set_class($segments[0]);
- isset($segments[1]) OR $segments[1] = 'index';
- $this->set_method($segments[1]);
+ if (isset($segments[1]))
+ {
+ $this->set_method($segments[1]);
+ }
- // Update our "routed" segment array to contain the segments.
- // Note: If there is no custom routing, this array will be
- // identical to $this->uri->segments
+ array_unshift($segments, NULL);
+ unset($segments[0]);
$this->uri->rsegments = $segments;
}
// --------------------------------------------------------------------
/**
- * Validate request
- *
- * Attempts validate the URI request and determine the controller path.
+ * Set default controller
*
- * @param array $segments URI segments
- * @return array URI segments
+ * @return void
*/
- protected function _validate_request($segments)
+ protected function _set_default_controller()
{
- if (count($segments) === 0)
+ if (empty($this->default_controller))
{
- return $segments;
+ show_error('Unable to determine what should be displayed. A default route has not been specified in the routing file.');
}
- $test = ($this->translate_uri_dashes === TRUE)
- ? str_replace('-', '_', $segments[0]) : $segments[0];
-
- // Does the requested controller exist in the root folder?
- if (file_exists(APPPATH.'controllers/'.$test.'.php'))
+ // Is the method being specified?
+ if (sscanf($this->default_controller, '%[^/]/%s', $class, $method) !== 2)
{
- return $segments;
+ $method = 'index';
}
- // Is the controller in a sub-folder?
- if (is_dir(APPPATH.'controllers/'.$segments[0]))
+ if ( ! file_exists(APPPATH.'controllers/'.$this->directory.ucfirst($class).'.php'))
{
- // Set the directory and remove it from the segment array
- $this->set_directory(array_shift($segments));
- if (count($segments) > 0)
- {
- $test = ($this->translate_uri_dashes === TRUE)
- ? str_replace('-', '_', $segments[0]) : $segments[0];
+ // This will trigger 404 later
+ return;
+ }
- // Does the requested controller exist in the sub-directory?
- if ( ! file_exists(APPPATH.'controllers/'.$this->directory.$test.'.php'))
- {
- if ( ! empty($this->routes['404_override']))
- {
- $this->directory = '';
- return explode('/', $this->routes['404_override'], 2);
- }
- else
- {
- show_404($this->directory.$segments[0]);
- }
- }
- }
- else
- {
- // Is the method being specified in the route?
- $segments = explode('/', $this->default_controller);
- if ( ! file_exists(APPPATH.'controllers/'.$this->directory.$segments[0].'.php'))
- {
- $this->directory = '';
- }
- }
+ $this->set_class($class);
+ $this->set_method($method);
- return $segments;
- }
+ // Assign routed segments, index starting from 1
+ $this->uri->rsegments = array(
+ 1 => $class,
+ 2 => $method
+ );
- // If we've gotten this far it means that the URI does not correlate to a valid
- // controller class. We will now see if there is an override
- if ( ! empty($this->routes['404_override']))
+ log_message('debug', 'No URI present. Default controller set.');
+ }
+
+ // --------------------------------------------------------------------
+
+ /**
+ * Validate request
+ *
+ * Attempts validate the URI request and determine the controller path.
+ *
+ * @used-by CI_Router::_set_request()
+ * @param array $segments URI segments
+ * @return mixed URI segments
+ */
+ protected function _validate_request($segments)
+ {
+ $c = count($segments);
+ // Loop through our segments and return as soon as a controller
+ // is found or when such a directory doesn't exist
+ while ($c-- > 0)
{
- if (sscanf($this->routes['404_override'], '%[^/]/%s', $class, $method) !== 2)
+ $test = $this->directory
+ .ucfirst($this->translate_uri_dashes === TRUE ? str_replace('-', '_', $segments[0]) : $segments[0]);
+
+ if ( ! file_exists(APPPATH.'controllers/'.$test.'.php') && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]))
{
- $method = 'index';
+ $this->set_directory(array_shift($segments), TRUE);
+ continue;
}
- return array($class, $method);
+ return $segments;
}
- // Nothing else to do at this point but show a 404
- show_404($segments[0]);
+ // This means that all segments were actually directories
+ return $segments;
}
// --------------------------------------------------------------------
@@ -347,16 +353,43 @@ class CI_Router {
// Turn the segment array into a URI string
$uri = implode('/', $this->uri->segments);
+ // Get HTTP verb
+ $http_verb = isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : 'cli';
+
// Is there a literal match? If so we're done
- if (isset($this->routes[$uri]) && is_string($this->routes[$uri]))
+ if (isset($this->routes[$uri]))
{
- return $this->_set_request(explode('/', $this->routes[$uri]));
+ // Check default routes format
+ if (is_string($this->routes[$uri]))
+ {
+ $this->_set_request(explode('/', $this->routes[$uri]));
+ return;
+ }
+ // Is there a matching http verb?
+ elseif (is_array($this->routes[$uri]) && isset($this->routes[$uri][$http_verb]))
+ {
+ $this->_set_request(explode('/', $this->routes[$uri][$http_verb]));
+ return;
+ }
}
- // Loop through the route array looking for wild-cards
+ // Loop through the route array looking for wildcards
foreach ($this->routes as $key => $val)
{
- // Convert wild-cards to RegEx
+ // Check if route format is using http verb
+ if (is_array($val))
+ {
+ if (isset($val[$http_verb]))
+ {
+ $val = $val[$http_verb];
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ // Convert wildcards to RegEx
$key = str_replace(array(':any', ':num'), array('[^/]+', '[0-9]+'), $key);
// Does the RegEx match?
@@ -406,13 +439,14 @@ class CI_Router {
$val = preg_replace('#^'.$key.'$#', $val, $uri);
}
- return $this->_set_request(explode('/', $val));
+ $this->_set_request(explode('/', $val));
+ return;
}
}
// If we got this far it means we didn't encounter a
// matching route so we'll set the site default route
- $this->_set_request($this->uri->segments);
+ $this->_set_request(array_values($this->uri->segments));
}
// --------------------------------------------------------------------
@@ -473,11 +507,19 @@ class CI_Router {
* Set directory name
*
* @param string $dir Directory name
+ * @param bool $appent Whether we're appending rather then setting the full value
* @return void
*/
- public function set_directory($dir)
+ public function set_directory($dir, $append = FALSE)
{
- $this->directory = str_replace(array('/', '.'), '', $dir).'/';
+ if ($append !== TRUE OR empty($this->directory))
+ {
+ $this->directory = str_replace('.', '', trim($dir, '/')).'/';
+ }
+ else
+ {
+ $this->directory .= str_replace('.', '', trim($dir, '/')).'/';
+ }
}
// --------------------------------------------------------------------
@@ -496,38 +538,6 @@ class CI_Router {
return $this->directory;
}
- // --------------------------------------------------------------------
-
- /**
- * Set controller overrides
- *
- * @param array $routing Route overrides
- * @return void
- */
- public function _set_overrides($routing)
- {
- if ( ! is_array($routing))
- {
- return;
- }
-
- if (isset($routing['directory']))
- {
- $this->set_directory($routing['directory']);
- }
-
- if ( ! empty($routing['controller']))
- {
- $this->set_class($routing['controller']);
- }
-
- if (isset($routing['function']))
- {
- $routing['function'] = empty($routing['function']) ? 'index' : $routing['function'];
- $this->set_method($routing['function']);
- }
- }
-
}
/* End of file Router.php */
diff --git a/system/core/Security.php b/system/core/Security.php
index 196d61144..95957a3d8 100644
--- a/system/core/Security.php
+++ b/system/core/Security.php
@@ -38,6 +38,30 @@ defined('BASEPATH') OR exit('No direct script access allowed');
class CI_Security {
/**
+ * List of sanitize filename strings
+ *
+ * @var array
+ */
+ public $filename_bad_chars = array(
+ '../', '<!--', '-->', '<', '>',
+ "'", '"', '&', '$', '#',
+ '{', '}', '[', ']', '=',
+ ';', '?', '%20', '%22',
+ '%3c', // <
+ '%253c', // <
+ '%3e', // >
+ '%0e', // >
+ '%28', // (
+ '%29', // )
+ '%2528', // (
+ '%26', // &
+ '%24', // $
+ '%3f', // ?
+ '%3b', // ;
+ '%3d' // =
+ );
+
+ /**
* XSS Hash
*
* Random Hash for protecting URLs.
@@ -93,7 +117,6 @@ class CI_Security {
'document.write' => '[removed]',
'.parentNode' => '[removed]',
'.innerHTML' => '[removed]',
- 'window.location' => '[removed]',
'-moz-binding' => '[removed]',
'<!--' => '&lt;!--',
'-->' => '--&gt;',
@@ -108,6 +131,7 @@ class CI_Security {
*/
protected $_never_allowed_regex = array(
'javascript\s*:',
+ '(document|(document\.)?window)\.(location|on\w*)',
'expression\s*(\(|&\#40;)', // CSS and IE
'vbscript\s*:', // IE, surprise!
'Redirect\s+302',
@@ -527,13 +551,13 @@ class CI_Security {
do
{
- $matches = $matches1 = 0;
+ $m1 = $m2 = 0;
+ $str = preg_replace('/(&#x0*[0-9a-f]{2,5})(?![0-9a-f;])/iS', '$1;', $str, -1, $m1);
+ $str = preg_replace('/(&#\d{2,4})(?![0-9;])/S', '$1;', $str, -1, $m2);
$str = html_entity_decode($str, ENT_COMPAT, $charset);
- $str = preg_replace('~&#x(0*[0-9a-f]{2,5})~ei', 'chr(hexdec("\\1"))', $str, -1, $matches);
- $str = preg_replace('~&#([0-9]{2,4})~e', 'chr(\\1)', $str, -1, $matches1);
}
- while ($matches OR $matches1);
+ while ($m1 OR $m2);
return $str;
}
@@ -549,24 +573,7 @@ class CI_Security {
*/
public function sanitize_filename($str, $relative_path = FALSE)
{
- $bad = array(
- '../', '<!--', '-->', '<', '>',
- "'", '"', '&', '$', '#',
- '{', '}', '[', ']', '=',
- ';', '?', '%20', '%22',
- '%3c', // <
- '%253c', // <
- '%3e', // >
- '%0e', // >
- '%28', // (
- '%29', // )
- '%2528', // (
- '%26', // &
- '%24', // $
- '%3f', // ?
- '%3b', // ;
- '%3d' // =
- );
+ $bad = $this->filename_bad_chars;
if ( ! $relative_path)
{
@@ -596,7 +603,7 @@ class CI_Security {
*/
public function strip_image_tags($str)
{
- return preg_replace(array('#<img\s+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img\s+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str);
+ return preg_replace(array('#<img[\s/]+.*?src\s*=\s*["\'](.+?)["\'].*?\>#', '#<img[\s/]+.*?src\s*=\s*(.+?).*?\>#'), '\\1', $str);
}
// ----------------------------------------------------------------
@@ -641,8 +648,8 @@ class CI_Security {
*/
protected function _remove_evil_attributes($str, $is_image)
{
- // All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns
- $evil_attributes = array('on\w*', 'style', 'xmlns', 'formaction');
+ // Formaction, style, and xmlns
+ $evil_attributes = array('style', 'xmlns', 'formaction');
if ($is_image === TRUE)
{
@@ -830,14 +837,15 @@ class CI_Security {
* Add a semicolon if missing. We do this to enable
* the conversion of entities to ASCII later.
*/
- $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', '\\1;\\2', $str);
+ $str = preg_replace('/(&#\d{2,4})(?![0-9;])/', '$1;', $str);
+ $str = preg_replace('/(&[a-z]{2,})(?![a-z;])/i', '$1;', $str);
/*
* Validate UTF16 two byte encoding (x00)
*
* Just as above, adds a semicolon if missing.
*/
- $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i', '\\1\\2;', $str);
+ $str = preg_replace('/(&#x0*[0-9a-f]{2,5})(?![0-9a-f;])/i', '$1;', $str);
/*
* Un-Protect GET variables in URLs
@@ -877,7 +885,7 @@ class CI_Security {
{
if ($this->_csrf_hash === '')
{
- // If the cookie exists we will use it's value.
+ // If the cookie exists we will use its value.
// We don't necessarily want to regenerate it with
// each page load since a page could contain embedded
// sub-pages causing this feature to fail
@@ -887,7 +895,7 @@ class CI_Security {
return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];
}
- $this->_csrf_hash = md5(uniqid(rand(), TRUE));
+ $this->_csrf_hash = md5(uniqid(mt_rand(), TRUE));
$this->csrf_set_cookie();
}
diff --git a/system/core/URI.php b/system/core/URI.php
index bc086d223..13682cbee 100644
--- a/system/core/URI.php
+++ b/system/core/URI.php
@@ -44,21 +44,21 @@ class CI_URI {
*
* @var array
*/
- public $keyval = array();
+ public $keyval = array();
/**
* Current URI string
*
* @var string
*/
- public $uri_string;
+ public $uri_string = '';
/**
* List of URI segments
*
* @var array
*/
- public $segments = array();
+ public $segments = array();
/**
* Re-indexed list of URI segments
@@ -67,90 +67,67 @@ class CI_URI {
*
* @var array
*/
- public $rsegments = array();
+ public $rsegments = array();
/**
- * Class constructor
+ * Permitted URI chars
*
- * Simply globalizes the $RTR object. The front
- * loads the Router class early on so it's not available
- * normally as other classes are.
+ * PCRE character group allowed in URI segments
*
- * @return void
+ * @var string
*/
- public function __construct()
- {
- $this->config =& load_class('Config', 'core');
- log_message('debug', 'URI Class Initialized');
- }
-
- // --------------------------------------------------------------------
+ protected $_permitted_uri_chars;
/**
- * Fetch URI String
+ * Class constructor
*
- * @used-by CI_Router
* @return void
*/
- public function _fetch_uri_string()
+ public function __construct()
{
- $protocol = strtoupper($this->config->item('uri_protocol'));
+ $this->config =& load_class('Config', 'core');
- if ($protocol === 'AUTO')
+ // If query strings are enabled, we don't need to parse any segments.
+ // However, they don't make sense under CLI.
+ if (is_cli() OR $this->config->item('enable_query_strings') !== TRUE)
{
- // Is the request coming from the command line?
- if ($this->_is_cli_request())
+ $this->_permitted_uri_chars = $this->config->item('permitted_uri_chars');
+
+ // If it's a CLI request, ignore the configuration
+ if (is_cli() OR ($protocol = strtoupper($this->config->item('uri_protocol'))) === 'CLI')
{
$this->_set_uri_string($this->_parse_argv());
- return;
}
-
- // Is there a PATH_INFO variable? This should be the easiest solution.
- if (isset($_SERVER['PATH_INFO']))
+ elseif ($protocol === 'AUTO')
{
- $this->_set_uri_string($_SERVER['PATH_INFO']);
- return;
+ // Is there a PATH_INFO variable? This should be the easiest solution.
+ if (isset($_SERVER['PATH_INFO']))
+ {
+ $this->_set_uri_string($_SERVER['PATH_INFO']);
+ }
+ // No PATH_INFO? Let's try REQUST_URI or QUERY_STRING then
+ elseif (($uri = $this->_parse_request_uri()) !== '' OR ($uri = $this->_parse_query_string()) !== '')
+ {
+ $this->_set_uri_string($uri);
+ }
+ // As a last ditch effor, let's try using the $_GET array
+ elseif (is_array($_GET) && count($_GET) === 1 && trim(key($_GET), '/') !== '')
+ {
+ $this->_set_uri_string(key($_GET));
+ }
}
-
- // Let's try REQUEST_URI then, this will work in most situations
- if (($uri = $this->_parse_request_uri()) !== '')
+ elseif (method_exists($this, ($method = '_parse_'.strtolower($protocol))))
{
- $this->_set_uri_string($uri);
- return;
+ $this->_set_uri_string($this->$method());
}
-
- // No REQUEST_URI either?... What about QUERY_STRING?
- if (($uri = $this->_parse_query_string()) !== '')
+ else
{
+ $uri = isset($_SERVER[$protocol]) ? $_SERVER[$protocol] : @getenv($protocol);
$this->_set_uri_string($uri);
- return;
- }
-
- // As a last ditch effort let's try using the $_GET array
- if (is_array($_GET) && count($_GET) === 1 && trim(key($_GET), '/') !== '')
- {
- $this->_set_uri_string(key($_GET));
- return;
}
-
- // We've exhausted all our options...
- $this->uri_string = '';
- return;
}
- if ($protocol === 'CLI')
- {
- $this->_set_uri_string($this->_parse_argv());
- return;
- }
- elseif (method_exists($this, ($method = '_parse_'.strtolower($protocol))))
- {
- $this->_set_uri_string($this->$method());
- return;
- }
-
- $uri = isset($_SERVER[$protocol]) ? $_SERVER[$protocol] : @getenv($protocol);
- $this->_set_uri_string($uri);
+ log_message('debug', 'URI Class Initialized');
}
// --------------------------------------------------------------------
@@ -165,6 +142,35 @@ class CI_URI {
{
// Filter out control characters and trim slashes
$this->uri_string = trim(remove_invisible_characters($str, FALSE), '/');
+
+ if ($this->uri_string !== '')
+ {
+ // Remove the URL suffix, if present
+ if (($suffix = (string) $this->config->item('url_suffix')) !== '')
+ {
+ $slen = strlen($suffix);
+
+ if (substr($this->uri_string, -$slen) === $suffix)
+ {
+ $this->uri_string = substr($this->uri_string, 0, -$slen);
+ }
+ }
+
+ $this->segments[0] = NULL;
+ // Populate the segments array
+ foreach (explode('/', preg_replace('|/*(.+?)/*$|', '\\1', $this->uri_string)) as $val)
+ {
+ // Filter segments for security
+ $val = trim($this->filter_uri($val));
+
+ if ($val !== '')
+ {
+ $this->segments[] = $val;
+ }
+ }
+
+ unset($this->segments[0]);
+ }
}
// --------------------------------------------------------------------
@@ -225,36 +231,10 @@ class CI_URI {
// --------------------------------------------------------------------
/**
- * Remove relative directory (../) and multi slashes (///)
- *
- * Do some final cleaning of the URI and return it, currently only used in self::_parse_request_uri()
- *
- * @param string $url
- * @return string
- */
- protected function _remove_relative_directory($uri)
- {
- $uris = array();
- $tok = strtok($uri, '/');
- while ($tok !== FALSE)
- {
- if (( ! empty($tok) OR $tok === '0') && $tok !== '..')
- {
- $uris[] = $tok;
- }
- $tok = strtok('/');
- }
- return implode('/', $uris);
- }
-
- // --------------------------------------------------------------------
-
- /**
* Parse QUERY_STRING
*
* Will parse QUERY_STRING and automatically detect the URI from it.
*
- * @used-by CI_URI::_fetch_uri_string()
* @return string
*/
protected function _parse_query_string()
@@ -280,23 +260,6 @@ class CI_URI {
// --------------------------------------------------------------------
/**
- * Is CLI Request?
- *
- * Duplicate of method from the Input class to test to see if
- * a request was made from the command line.
- *
- * @see CI_Input::is_cli_request()
- * @used-by CI_URI::_fetch_uri_string()
- * @return bool
- */
- protected function _is_cli_request()
- {
- return (PHP_SAPI === 'cli') OR defined('STDIN');
- }
-
- // --------------------------------------------------------------------
-
- /**
* Parse CLI arguments
*
* Take each command line argument and assume it is a URI segment.
@@ -312,104 +275,52 @@ class CI_URI {
// --------------------------------------------------------------------
/**
- * Filter URI
+ * Remove relative directory (../) and multi slashes (///)
*
- * Filters segments for malicious characters.
+ * Do some final cleaning of the URI and return it, currently only used in self::_parse_request_uri()
*
- * @used-by CI_Router
- * @param string $str
+ * @param string $url
* @return string
*/
- public function _filter_uri($str)
+ protected function _remove_relative_directory($uri)
{
- if ($str !== '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') === FALSE)
+ $uris = array();
+ $tok = strtok($uri, '/');
+ while ($tok !== FALSE)
{
- // preg_quote() in PHP 5.3 escapes -, so the str_replace() and addition of - to preg_quote() is to maintain backwards
- // compatibility as many are unaware of how characters in the permitted_uri_chars will be parsed as a regex pattern
- if ( ! preg_match('|^['.str_replace(array('\\-', '\-'), '-', preg_quote($this->config->item('permitted_uri_chars'), '-')).']+$|i', $str))
+ if (( ! empty($tok) OR $tok === '0') && $tok !== '..')
{
- show_error('The URI you submitted has disallowed characters.', 400);
+ $uris[] = $tok;
}
+ $tok = strtok('/');
}
- // Convert programatic characters to entities and return
- return str_replace(
- array('$', '(', ')', '%28', '%29'), // Bad
- array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;'), // Good
- $str);
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Remove URL suffix
- *
- * Removes the suffix from the URL if needed.
- *
- * @used-by CI_Router
- * @return void
- */
- public function _remove_url_suffix()
- {
- $suffix = (string) $this->config->item('url_suffix');
-
- if ($suffix === '')
- {
- return;
- }
-
- $slen = strlen($suffix);
-
- if (substr($this->uri_string, -$slen) === $suffix)
- {
- $this->uri_string = substr($this->uri_string, 0, -$slen);
- }
+ return implode('/', $uris);
}
// --------------------------------------------------------------------
/**
- * Explode URI segments
+ * Filter URI
*
- * The individual segments will be stored in the $this->segments array.
+ * Filters segments for malicious characters.
*
- * @see CI_URI::$segments
- * @used-by CI_Router
- * @return void
+ * @param string $str
+ * @return string
*/
- public function _explode_segments()
+ public function filter_uri($str)
{
- foreach (explode('/', preg_replace('|/*(.+?)/*$|', '\\1', $this->uri_string)) as $val)
+ if ( ! empty($str) && ! empty($this->_permitted_uri_chars) && ! preg_match('/^['.$this->_permitted_uri_chars.']+$/i'.(UTF8_ENABLED ? 'u' : ''), $str))
{
- // Filter segments for security
- $val = trim($this->_filter_uri($val));
-
- if ($val !== '')
- {
- $this->segments[] = $val;
- }
+ show_error('The URI you submitted has disallowed characters.', 400);
}
- }
- // --------------------------------------------------------------------
-
- /**
- * Re-index Segments
- *
- * Re-indexes the CI_URI::$segment array so that it starts at 1 rather
- * than 0. Doing so makes it simpler to use methods like
- * CI_URI::segment(n) since there is a 1:1 relationship between the
- * segment array and the actual segments.
- *
- * @used-by CI_Router
- * @return void
- */
- public function _reindex_segments()
- {
- array_unshift($this->segments, NULL);
- array_unshift($this->rsegments, NULL);
- unset($this->segments[0]);
- unset($this->rsegments[0]);
+ // Convert programatic characters to entities and return
+ return str_replace(
+ array('$', '(', ')', '%28', '%29'), // Bad
+ array('&#36;', '&#40;', '&#41;', '&#40;', '&#41;'), // Good
+ $str
+ );
}
// --------------------------------------------------------------------
@@ -720,12 +631,7 @@ class CI_URI {
{
global $RTR;
- if (($dir = $RTR->directory) === '/')
- {
- $dir = '';
- }
-
- return $dir.implode('/', $this->rsegment_array());
+ return ltrim($RTR->directory, '/').implode('/', $this->rsegments);
}
}
diff --git a/system/core/Utf8.php b/system/core/Utf8.php
index a78616d40..828a8aeba 100644
--- a/system/core/Utf8.php
+++ b/system/core/Utf8.php
@@ -66,7 +66,7 @@ class CI_Utf8 {
}
if (
- @preg_match('/./u', 'é') === 1 // PCRE must support UTF-8
+ defined('PREG_BAD_UTF8_ERROR') // PCRE must support UTF-8
&& function_exists('iconv') // iconv must be installed
&& MB_ENABLED === TRUE // mbstring must be enabled
&& $charset === 'UTF-8' // Application charset must be UTF-8