summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml5
-rw-r--r--application/config/autoload.php5
-rw-r--r--system/core/CodeIgniter.php143
-rw-r--r--system/core/Loader.php11
-rw-r--r--system/core/Router.php240
-rw-r--r--system/core/URI.php252
-rw-r--r--system/core/Utf8.php2
-rw-r--r--system/libraries/Cache/drivers/Cache_memcached.php2
-rw-r--r--system/libraries/User_agent.php23
-rw-r--r--tests/codeigniter/core/Benchmark_test.php26
-rw-r--r--tests/codeigniter/core/Input_test.php47
-rw-r--r--tests/codeigniter/core/Model_test.php37
-rw-r--r--tests/codeigniter/core/URI_test.php28
-rw-r--r--tests/codeigniter/core/Utf8_test.php13
-rw-r--r--tests/codeigniter/libraries/Parser_test.php14
-rw-r--r--tests/codeigniter/libraries/Useragent_test.php57
-rw-r--r--tests/mocks/core/utf8.php5
-rw-r--r--user_guide_src/source/changelog.rst28
18 files changed, 519 insertions, 419 deletions
diff --git a/.travis.yml b/.travis.yml
index 27fe3c670..718e6aaa6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,6 +26,11 @@ script: phpunit --coverage-text --configuration tests/travis/$DB.phpunit.xml
matrix:
allow_failures:
- php: hhvm
+ exclude:
+ - php: hhvm
+ env: DB=mysqli
+ env: DB=pgsql
+ env: DB=pdo/pgsql
branches:
only:
diff --git a/application/config/autoload.php b/application/config/autoload.php
index 5a20c943a..43d53155b 100644
--- a/application/config/autoload.php
+++ b/application/config/autoload.php
@@ -77,6 +77,11 @@ $autoload['packages'] = array();
| Prototype:
|
| $autoload['libraries'] = array('database', 'email', 'xmlrpc');
+|
+| You can also supply an alternative library name to be assigned
+| in the controller:
+|
+| $autoload['libraries'] = array('user_agent' => 'ua');
*/
$autoload['libraries'] = array();
diff --git a/system/core/CodeIgniter.php b/system/core/CodeIgniter.php
index cc12f149f..74a9eb0af 100644
--- a/system/core/CodeIgniter.php
+++ b/system/core/CodeIgniter.php
@@ -53,10 +53,10 @@ defined('BASEPATH') OR exit('No direct script access allowed');
*/
if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
{
- require(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
+ require_once(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
}
- require(APPPATH.'config/constants.php');
+ require_once(APPPATH.'config/constants.php');
/*
* ------------------------------------------------------
@@ -209,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.
@@ -225,96 +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.
- $class = ucfirst($RTR->class);
- if ( ! file_exists(APPPATH.'controllers/'.$RTR->directory.$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.$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
+ * 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.
*/
- $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';
- }
- $class = ucfirst($class);
-
- 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';
}
- $class = ucfirst($class);
+ $error_class = ucfirst($error_class);
- if ( ! class_exists($class, FALSE))
+ 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/Loader.php b/system/core/Loader.php
index 78172580d..8c8d5a37c 100644
--- a/system/core/Loader.php
+++ b/system/core/Loader.php
@@ -184,9 +184,16 @@ class CI_Loader {
}
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 $this;
diff --git a/system/core/Router.php b/system/core/Router.php
index 71530ff07..e3c911511 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;
+
// --------------------------------------------------------------------
/**
@@ -106,6 +115,8 @@ class CI_Router {
$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
@@ -146,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.
@@ -188,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.');
}
// --------------------------------------------------------------------
@@ -245,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)
@@ -267,90 +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 = ucfirst($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 = ucfirst($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.ucfirst($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
+ );
+
+ log_message('debug', 'No URI present. Default controller set.');
+ }
+
+ // --------------------------------------------------------------------
- // 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']))
+ /**
+ * 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;
}
// --------------------------------------------------------------------
@@ -377,12 +362,14 @@ class CI_Router {
// Check default routes format
if (is_string($this->routes[$uri]))
{
- return $this->_set_request(explode('/', $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]))
{
- return $this->_set_request(explode('/', $this->routes[$uri][$http_verb]));
+ $this->_set_request(explode('/', $this->routes[$uri][$http_verb]));
+ return;
}
}
@@ -452,7 +439,8 @@ class CI_Router {
$val = preg_replace('#^'.$key.'$#', $val, $uri);
}
- return $this->_set_request(explode('/', $val));
+ $this->_set_request(explode('/', $val));
+ return;
}
}
@@ -519,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, '/')).'/';
+ }
}
// --------------------------------------------------------------------
diff --git a/system/core/URI.php b/system/core/URI.php
index 3d6d202c0..6e0d7f993 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,7 +67,7 @@ class CI_URI {
*
* @var array
*/
- public $rsegments = array();
+ public $rsegments = array();
/**
* Permitted URI chars
@@ -81,91 +81,53 @@ class CI_URI {
/**
* Class constructor
*
- * Simply globalizes the $RTR object. The front
- * loads the Router class early on so it's not available
- * normally as other classes are.
- *
* @return void
*/
public function __construct()
{
$this->config =& load_class('Config', 'core');
- if ($this->config->item('enable_query_strings') !== TRUE OR is_cli())
+ // 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)
{
$this->_permitted_uri_chars = $this->config->item('permitted_uri_chars');
- }
-
- log_message('debug', 'URI Class Initialized');
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Fetch URI String
- *
- * @used-by CI_Router
- * @return void
- */
- public function _fetch_uri_string()
- {
- $protocol = strtoupper($this->config->item('uri_protocol'));
- if ($protocol === 'AUTO')
- {
- // Is the request coming from the command line?
- if (is_cli())
+ // 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');
}
// --------------------------------------------------------------------
@@ -180,6 +142,32 @@ 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);
+ }
+ }
+
+ // 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;
+ }
+ }
+ }
}
// --------------------------------------------------------------------
@@ -240,36 +228,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()
@@ -310,100 +272,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)
- {
- if ( ! empty($str) && ! empty($this->_permitted_uri_chars) && ! preg_match('/^['.$this->_permitted_uri_chars.']+$/i', $str))
- {
- show_error('The URI you submitted has disallowed characters.', 400);
- }
-
- // Convert programatic characters to entities and return
- return str_replace(
- array('$', '(', ')', '%28', '%29'), // Bad
- array('$', '(', ')', '(', ')'), // Good
- $str
- );
- }
-
- // --------------------------------------------------------------------
-
- /**
- * Remove URL suffix
- *
- * Removes the suffix from the URL if needed.
- *
- * @used-by CI_Router
- * @return void
- */
- public function _remove_url_suffix()
+ protected function _remove_relative_directory($uri)
{
- $suffix = (string) $this->config->item('url_suffix');
-
- if ($suffix === '')
+ $uris = array();
+ $tok = strtok($uri, '/');
+ while ($tok !== FALSE)
{
- return;
+ if (( ! empty($tok) OR $tok === '0') && $tok !== '..')
+ {
+ $uris[] = $tok;
+ }
+ $tok = strtok('/');
}
- $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('$', '(', ')', '(', ')'), // Good
+ $str
+ );
}
// --------------------------------------------------------------------
@@ -714,7 +628,7 @@ class CI_URI {
{
global $RTR;
- return ltrim($RTR->directory, '/').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
diff --git a/system/libraries/Cache/drivers/Cache_memcached.php b/system/libraries/Cache/drivers/Cache_memcached.php
index 886357e57..d59847752 100644
--- a/system/libraries/Cache/drivers/Cache_memcached.php
+++ b/system/libraries/Cache/drivers/Cache_memcached.php
@@ -85,7 +85,7 @@ class CI_Cache_memcached extends CI_Driver {
{
if ($raw !== TRUE)
{
- $data = array($data, time, $ttl);
+ $data = array($data, time(), $ttl);
}
if (get_class($this->_memcached) === 'Memcached')
diff --git a/system/libraries/User_agent.php b/system/libraries/User_agent.php
index 3a6b6bc98..1dfa3e72d 100644
--- a/system/libraries/User_agent.php
+++ b/system/libraries/User_agent.php
@@ -145,6 +145,15 @@ class CI_User_agent {
public $robot = '';
/**
+ * HTTP Referer
+ *
+ * @var mixed
+ */
+ public $referer;
+
+ // --------------------------------------------------------------------
+
+ /**
* Constructor
*
* Sets the User Agent and runs the compilation routine
@@ -358,7 +367,7 @@ class CI_User_agent {
{
if ((count($this->languages) === 0) && ! empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
{
- $this->languages = explode(',', preg_replace('/(;q=[0-9\.]+)/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE']))));
+ $this->languages = explode(',', preg_replace('/(;\s?q=[0-9\.]+)|\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_LANGUAGE']))));
}
if (count($this->languages) === 0)
@@ -378,7 +387,7 @@ class CI_User_agent {
{
if ((count($this->charsets) === 0) && ! empty($_SERVER['HTTP_ACCEPT_CHARSET']))
{
- $this->charsets = explode(',', preg_replace('/(;q=.+)/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET']))));
+ $this->charsets = explode(',', preg_replace('/(;\s?q=.+)|\s/i', '', strtolower(trim($_SERVER['HTTP_ACCEPT_CHARSET']))));
}
if (count($this->charsets) === 0)
@@ -471,24 +480,22 @@ class CI_User_agent {
*/
public function is_referral()
{
- static $result;
-
- if ( ! isset($result))
+ if ( ! isset($this->referer))
{
if (empty($_SERVER['HTTP_REFERER']))
{
- $result = FALSE;
+ $this->referer = FALSE;
}
else
{
$referer_host = @parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST);
$own_host = parse_url(config_item('base_url'), PHP_URL_HOST);
- $result = ($referer_host && $referer_host !== $own_host);
+ $this->referer = ($referer_host && $referer_host !== $own_host);
}
}
- return $result;
+ return $this->referer;
}
// --------------------------------------------------------------------
diff --git a/tests/codeigniter/core/Benchmark_test.php b/tests/codeigniter/core/Benchmark_test.php
index aff736a40..7fd0727e2 100644
--- a/tests/codeigniter/core/Benchmark_test.php
+++ b/tests/codeigniter/core/Benchmark_test.php
@@ -27,10 +27,34 @@ class Benchmark_test extends CI_TestCase {
$this->assertEmpty($this->benchmark->elapsed_time('undefined_point'));
$this->benchmark->mark('code_start');
- sleep(1);
$this->benchmark->mark('code_end');
+ // Override values, because time isn't testable, but make sure the markers were set
+ if (isset($this->benchmark->marker['code_start']) && is_float($this->benchmark->marker['code_start']))
+ {
+ $this->benchmark->marker['code_start'] = 1389956144.1944;
+ }
+
+ if (isset($this->benchmark->marker['code_end']) && is_float($this->benchmark->marker['code_end']))
+ {
+ $this->benchmark->marker['code_end'] = 1389956145.1946;
+ }
+
$this->assertEquals('1', $this->benchmark->elapsed_time('code_start', 'code_end', 0));
+ $this->assertEquals('1.0', $this->benchmark->elapsed_time('code_start', 'code_end', 1));
+ $this->assertEquals('1.00', $this->benchmark->elapsed_time('code_start', 'code_end', 2));
+ $this->assertEquals('1.000', $this->benchmark->elapsed_time('code_start', 'code_end', 3));
+ $this->assertEquals('1.0002', $this->benchmark->elapsed_time('code_start', 'code_end', 4));
+ $this->assertEquals('1.0002', $this->benchmark->elapsed_time('code_start', 'code_end'));
+
+ // Test with non-existing 2nd marker, but again - we need to override the value
+ $this->benchmark->elapsed_time('code_start', 'code_end2');
+ if (isset($this->benchmark->marker['code_end2']) && is_float($this->benchmark->marker['code_end2']))
+ {
+ $this->benchmark->marker['code_end2'] = 1389956146.2046;
+ }
+
+ $this->assertEquals('2.0102', $this->benchmark->elapsed_time('code_start', 'code_end2'));
}
// --------------------------------------------------------------------
diff --git a/tests/codeigniter/core/Input_test.php b/tests/codeigniter/core/Input_test.php
index 0a98e556c..95833fc91 100644
--- a/tests/codeigniter/core/Input_test.php
+++ b/tests/codeigniter/core/Input_test.php
@@ -138,13 +138,24 @@ class Input_test extends CI_TestCase {
public function test_valid_ip()
{
- $ip_v4 = '192.18.0.1';
- $this->assertTrue($this->input->valid_ip($ip_v4));
+ $this->assertTrue($this->input->valid_ip('192.18.0.1'));
+ $this->assertTrue($this->input->valid_ip('192.18.0.1', 'ipv4'));
+ $this->assertFalse($this->input->valid_ip('555.0.0.0'));
+ $this->assertFalse($this->input->valid_ip('2001:db8:0:85a3::ac1f:8001', 'ipv4'));
+
+ // v6 tests
+ $this->assertFalse($this->input->valid_ip('192.18.0.1', 'ipv6'));
+
+ $ip_v6 = array(
+ '2001:0db8:0000:85a3:0000:0000:ac1f:8001',
+ '2001:db8:0:85a3:0:0:ac1f:8001',
+ '2001:db8:0:85a3::ac1f:8001'
+ );
- $ip_v6 = array('2001:0db8:0000:85a3:0000:0000:ac1f:8001', '2001:db8:0:85a3:0:0:ac1f:8001', '2001:db8:0:85a3::ac1f:8001');
foreach ($ip_v6 as $ip)
{
$this->assertTrue($this->input->valid_ip($ip));
+ $this->assertTrue($this->input->valid_ip($ip, 'ipv6'));
}
}
@@ -171,4 +182,34 @@ class Input_test extends CI_TestCase {
$this->assertTrue($this->input->is_ajax_request());
}
+ // --------------------------------------------------------------------
+
+ public function test_input_stream()
+ {
+ $this->markTestSkipped('TODO: Find a way to test input://');
+ }
+
+ // --------------------------------------------------------------------
+
+ public function test_set_cookie()
+ {
+ $this->markTestSkipped('TODO: Find a way to test HTTP headers');
+ }
+
+ public function test_ip_address()
+ {
+ // 127.0.0.1 is set in our Bootstrap file
+ $this->assertEquals('127.0.0.1', $this->input->ip_address());
+
+ // Invalid
+ $_SERVER['REMOTE_ADDR'] = 'invalid_ip_address';
+ $this->input->ip_address = FALSE; // reset cached value
+ $this->assertEquals('0.0.0.0', $this->input->ip_address());
+
+ // TODO: Add proxy_ips tests
+
+ // Back to reality
+ $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; // back to reality
+ }
+
} \ No newline at end of file
diff --git a/tests/codeigniter/core/Model_test.php b/tests/codeigniter/core/Model_test.php
new file mode 100644
index 000000000..80dc97b3b
--- /dev/null
+++ b/tests/codeigniter/core/Model_test.php
@@ -0,0 +1,37 @@
+<?php
+
+class Model_test extends CI_TestCase {
+
+ private $ci_obj;
+
+ public function set_up()
+ {
+ $loader = $this->ci_core_class('loader');
+ $this->load = new $loader();
+ $this->ci_obj = $this->ci_instance();
+ $this->ci_set_core_class('model', 'CI_Model');
+
+ $model_code =<<<MODEL
+<?php
+class Test_model extends CI_Model {
+
+ public \$property = 'foo';
+
+}
+MODEL;
+
+ $this->ci_vfs_create('Test_model', $model_code, $this->ci_app_root, 'models');
+ $this->load->model('test_model');
+ }
+
+ // --------------------------------------------------------------------
+
+ public function test__get()
+ {
+ $this->assertEquals('foo', $this->ci_obj->test_model->property);
+
+ $this->ci_obj->controller_property = 'bar';
+ $this->assertEquals('bar', $this->ci_obj->test_model->controller_property);
+ }
+
+} \ No newline at end of file
diff --git a/tests/codeigniter/core/URI_test.php b/tests/codeigniter/core/URI_test.php
index 99d79bbd2..6589c1f5a 100644
--- a/tests/codeigniter/core/URI_test.php
+++ b/tests/codeigniter/core/URI_test.php
@@ -26,6 +26,10 @@ class URI_test extends CI_TestCase {
// --------------------------------------------------------------------
+ /*
+
+ This has been moved to the constructor
+
public function test_fetch_uri_string()
{
define('SELF', 'index.php');
@@ -86,9 +90,14 @@ class URI_test extends CI_TestCase {
// uri_protocol: REQUEST_URI
// uri_protocol: CLI
}
+ */
// --------------------------------------------------------------------
+ /*
+
+ This has been moved into _set_uri_string()
+
public function test_explode_segments()
{
// Let's test the function's ability to clean up this mess
@@ -107,7 +116,7 @@ class URI_test extends CI_TestCase {
$this->assertEquals($a, $this->uri->segments);
}
}
-
+ */
// --------------------------------------------------------------------
public function test_filter_uri()
@@ -145,23 +154,6 @@ class URI_test extends CI_TestCase {
// --------------------------------------------------------------------
- public function test_remove_url_suffix()
- {
- $this->uri->config->set_item('url_suffix', '.html');
-
- $this->uri->uri_string = 'controller/method/index.html';
- $this->uri->_remove_url_suffix();
-
- $this->assertEquals($this->uri->uri_string, 'controller/method/index');
-
- $this->uri->uri_string = 'controller/method/index.htmlify.html';
- $this->uri->_remove_url_suffix();
-
- $this->assertEquals($this->uri->uri_string, 'controller/method/index.htmlify');
- }
-
- // --------------------------------------------------------------------
-
public function test_segment()
{
$this->uri->segments = array(1 => 'controller');
diff --git a/tests/codeigniter/core/Utf8_test.php b/tests/codeigniter/core/Utf8_test.php
index caa7b6986..71299134e 100644
--- a/tests/codeigniter/core/Utf8_test.php
+++ b/tests/codeigniter/core/Utf8_test.php
@@ -11,10 +11,15 @@ class Utf8_test extends CI_TestCase {
public function test_convert_to_utf8()
{
- $this->assertEquals(
- $this->utf8->convert_to_utf8('', 'WINDOWS-1251'),
- 'тест'
- );
+ $this->assertEquals('тест', $this->utf8->convert_to_utf8('', 'WINDOWS-1251'));
+ }
+
+ // --------------------------------------------------------------------
+
+ public function test_is_ascii()
+ {
+ $this->assertTrue($this->utf8->is_ascii_test('foo bar'));
+ $this->assertFalse($this->utf8->is_ascii_test('тест'));
}
} \ No newline at end of file
diff --git a/tests/codeigniter/libraries/Parser_test.php b/tests/codeigniter/libraries/Parser_test.php
index 6e5c192dd..3755cf1a0 100644
--- a/tests/codeigniter/libraries/Parser_test.php
+++ b/tests/codeigniter/libraries/Parser_test.php
@@ -33,7 +33,7 @@ class Parser_test extends CI_TestCase {
// --------------------------------------------------------------------
- public function test_parse_simple_string()
+ public function test_parse_string()
{
$data = array(
'title' => 'Page Title',
@@ -69,11 +69,7 @@ class Parser_test extends CI_TestCase {
{
$data = array(
'title' => 'Super Heroes',
- 'powers' => array(
- array(
- 'invisibility' => 'yes',
- 'flying' => 'no'),
- )
+ 'powers' => array(array('invisibility' => 'yes', 'flying' => 'no'))
);
$template = "{title}\n{powers}{invisibility}\n{flying}{/powers}\nsecond:{powers} {invisibility} {flying}{/powers}";
@@ -87,11 +83,7 @@ class Parser_test extends CI_TestCase {
{
$data = array(
'title' => 'Super Heroes',
- 'powers' => array(
- array(
- 'invisibility' => 'yes',
- 'flying' => 'no'),
- )
+ 'powers' => array(array('invisibility' => 'yes', 'flying' => 'no'))
);
$template = "{title}\n{powers}{invisibility}\n{flying}";
diff --git a/tests/codeigniter/libraries/Useragent_test.php b/tests/codeigniter/libraries/Useragent_test.php
index e3726554e..aed38b8c2 100644
--- a/tests/codeigniter/libraries/Useragent_test.php
+++ b/tests/codeigniter/libraries/Useragent_test.php
@@ -11,9 +11,7 @@ class UserAgent_test extends CI_TestCase {
$_SERVER['HTTP_USER_AGENT'] = $this->_user_agent;
$this->ci_vfs_clone('application/config/user_agents.php');
-
$this->agent = new Mock_Libraries_UserAgent();
-
$this->ci_instance_var('agent', $this->agent);
}
@@ -40,12 +38,27 @@ class UserAgent_test extends CI_TestCase {
// --------------------------------------------------------------------
- public function test_util_is_functions()
+ public function test_is_functions()
{
$this->assertTrue($this->agent->is_browser());
+ $this->assertTrue($this->agent->is_browser('Safari'));
+ $this->assertFalse($this->agent->is_browser('Firefox'));
$this->assertFalse($this->agent->is_robot());
$this->assertFalse($this->agent->is_mobile());
+ }
+
+ // --------------------------------------------------------------------
+
+ public function test_referrer()
+ {
+ $_SERVER['HTTP_REFERER'] = 'http://codeigniter.com/user_guide/';
+ $this->assertTrue($this->agent->is_referral());
+ $this->assertEquals('http://codeigniter.com/user_guide/', $this->agent->referrer());
+
+ $this->agent->referer = NULL;
+ unset($_SERVER['HTTP_REFERER']);
$this->assertFalse($this->agent->is_referral());
+ $this->assertEquals('', $this->agent->referrer());
}
// --------------------------------------------------------------------
@@ -63,7 +76,6 @@ class UserAgent_test extends CI_TestCase {
$this->assertEquals('Safari', $this->agent->browser());
$this->assertEquals('533.20.27', $this->agent->version());
$this->assertEquals('', $this->agent->robot());
- $this->assertEquals('', $this->agent->referrer());
}
// --------------------------------------------------------------------
@@ -71,14 +83,43 @@ class UserAgent_test extends CI_TestCase {
public function test_charsets()
{
$_SERVER['HTTP_ACCEPT_CHARSET'] = 'utf8';
+ $this->agent->charsets = array();
+ $this->agent->charsets();
+ $this->assertTrue($this->agent->accept_charset('utf8'));
+ $this->assertFalse($this->agent->accept_charset('foo'));
+ $this->assertEquals('utf8', $this->agent->charsets[0]);
+
+ $_SERVER['HTTP_ACCEPT_CHARSET'] = '';
+ $this->agent->charsets = array();
+ $this->assertFalse($this->agent->accept_charset());
+ $this->assertEquals('Undefined', $this->agent->charsets[0]);
- $charsets = $this->agent->charsets();
-
- $this->assertEquals('utf8', $charsets[0]);
+ $_SERVER['HTTP_ACCEPT_CHARSET'] = 'iso-8859-5, unicode-1-1; q=0.8';
+ $this->agent->charsets = array();
+ $this->assertTrue($this->agent->accept_charset('iso-8859-5'));
+ $this->assertTrue($this->agent->accept_charset('unicode-1-1'));
+ $this->assertFalse($this->agent->accept_charset('foo'));
+ $this->assertEquals('iso-8859-5', $this->agent->charsets[0]);
+ $this->assertEquals('unicode-1-1', $this->agent->charsets[1]);
unset($_SERVER['HTTP_ACCEPT_CHARSET']);
+ }
- $this->assertFalse($this->agent->accept_charset());
+ public function test_parse()
+ {
+ $new_agent = 'Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0';
+ $this->agent->parse($new_agent);
+
+ $this->assertEquals('Android', $this->agent->platform());
+ $this->assertEquals('Firefox', $this->agent->browser());
+ $this->assertEquals('13.0', $this->agent->version());
+ $this->assertEquals('', $this->agent->robot());
+ $this->assertEquals('Android', $this->agent->mobile());
+ $this->assertEquals($new_agent, $this->agent->agent_string());
+ $this->assertTrue($this->agent->is_browser());
+ $this->assertFalse($this->agent->is_robot());
+ $this->assertTrue($this->agent->is_mobile());
+ $this->assertTrue($this->agent->is_mobile('android'));
}
} \ No newline at end of file
diff --git a/tests/mocks/core/utf8.php b/tests/mocks/core/utf8.php
index 068e74ac1..a43138fbc 100644
--- a/tests/mocks/core/utf8.php
+++ b/tests/mocks/core/utf8.php
@@ -23,4 +23,9 @@ class Mock_Core_Utf8 extends CI_Utf8 {
}
}
+ public function is_ascii_test($str)
+ {
+ return $this->_is_ascii($str);
+ }
+
} \ No newline at end of file
diff --git a/user_guide_src/source/changelog.rst b/user_guide_src/source/changelog.rst
index 5a779cc90..7d6819f7f 100644
--- a/user_guide_src/source/changelog.rst
+++ b/user_guide_src/source/changelog.rst
@@ -389,15 +389,29 @@ Release Date: Not Released
- Core
+ - :doc:`Routing <general/routing>` changes include:
+
+ - Added support for multiple levels of controller directories.
+ - Added support for per-directory *default_controller* and *404_override* classes.
+ - Added possibility to route requests using HTTP verbs.
+ - Added possibility to route requests using callbacks.
+ - Added a new reserved route (*translate_uri_dashes*) to allow usage of dashes in the controller and method URI segments.
+ - Deprecated methods ``fetch_directory()``, ``fetch_class()`` and ``fetch_method()`` in favor of their respective public properties.
+ - Removed method ``_set_overrides()`` and moved its logic to the class constructor.
+
- :doc:`URI Library <libraries/uri>` changes include:
- - Renamed method ``_filter_uri()`` to ``filter_uri()`` and removed the ``preg_quote()`` call from it.
+ - Added conditional PCRE UTF-8 support to the "invalid URI characters" check and removed the ``preg_quote()`` call from it to allow more flexibility.
+ - Renamed method ``_filter_uri()`` to ``filter_uri()``.
- Changed private methods to protected so that MY_URI can override them.
- Renamed internal method ``_parse_cli_args()`` to ``_parse_argv()``.
- Renamed internal method ``_detect_uri()`` to ``_parse_request_uri()``.
- Changed ``_parse_request_uri()`` to accept absolute URIs for compatibility with HTTP/1.1 as per `RFC2616 <http://www.ietf.org/rfc/rfc2616.txt>`.
- Added protected method ``_parse_query_string()`` to URI paths in the the **QUERY_STRING** value, like ``_parse_request_uri()`` does.
- Changed ``_fetch_uri_string()`` to try the **PATH_INFO** variable first when auto-detecting.
+ - Removed methods ``_remove_url_suffix()``, ``_explode_segments()`` and moved their logic into ``_set_uri_string()``.
+ - Removed method ``_fetch_uri_string()`` and moved its logic into the class constructor.
+ - Removed method ``_reindex_segments()``.
- :doc:`Loader Library <libraries/loader>` changes include:
@@ -407,7 +421,7 @@ Release Date: Not Released
- Added autoloading of drivers with ``$autoload['drivers']``.
- ``$config['rewrite_short_tags']`` now has no effect when using PHP 5.4 as ``<?=`` will always be available.
- Changed method ``config()`` to return whatever ``CI_Config::load()`` returns instead of always being void.
- - Added support for model aliasing on autoload.
+ - Added support for library and model aliasing on autoload.
- Changed method ``is_loaded()`` to ask for the (case sensitive) library name instead of its instance name.
- Removed ``$_base_classes`` property and unified all class data in ``$_ci_classes`` instead.
- Added method ``clear_vars()`` to allow clearing the cached variables for views.
@@ -458,14 +472,6 @@ Release Date: Not Released
- Added ``$config['csrf_exclude_uris']``, which allows you list URIs which will not have the CSRF validation methods run.
- Modified method ``sanitize_filename()`` to read a public ``$filename_bad_chars`` property for getting the invalid characters list.
- - :doc:`URI Routing <general/routing>` changes include:
-
- - Added possibility to route requests using HTTP verbs.
- - Added possibility to route requests using callbacks.
- - Added a new reserved route (*translate_uri_dashes*) to allow usage of dashes in the controller and method URI segments.
- - Deprecated methods ``fetch_directory()``, ``fetch_class()`` and ``fetch_method()`` in favor of their respective public properties.
- - Removed method ``_set_overrides()`` and moved its logic to the class constructor.
-
- :doc:`Language Library <libraries/language>` changes include:
- Changed method ``load()`` to filter the language name with ``ctype_alpha()``.
@@ -671,6 +677,8 @@ Bug fixes for 3.0
- Fixed an edge case (#555) - incorrect browser version was reported for Opera 10+ due to a non-standard user-agent string.
- Fixed a bug (#133) - :doc:`Text Helper <helpers/text_helper>` :func:`ascii_to_entities()` stripped the last character if it happens to be in the extended ASCII group.
- Fixed a bug (#2822) - ``fwrite()`` was used incorrectly throughout the whole framework, allowing incomplete writes when writing to a network stream and possibly a few other edge cases.
+- Fixed a bug where :doc:`User Agent Library <libraries/user_agent>` methods ``accept_charset()`` and ``accept_lang()`` didn't properly parse HTTP headers that contain spaces.
+- Fixed a bug where *default_controller* was called instad of triggering a 404 error if the current route is in a controller directory.
Version 2.1.4
=============