From 30d5324617ae136c7a91badb6ed8f7de418fd7f5 Mon Sep 17 00:00:00 2001 From: Andrey Andreev Date: Thu, 16 Jan 2014 14:41:46 +0200 Subject: URI Routing overhaul - Allow multiple levels of controller directories (supersedes PRs #390, #2439) - Add support for per-directory 'defaul_controller' and '404_override' (resolves issue #2611; supersedes PR #939) - Fixed a bug where default_controller was called instead of triggering 404 if the current route is inside a directory - Removed a few calls from CI_Router to CI_URI that made a necessity for otherwise internal CI_URI methods to be public: - Removed CI_URI::_fetch_uri_string() and moved its logic into CI_URI::__construct() - Removed CI_URI::_remove_url_suffix, CI_URI::_explode_segments() and moved their logic into CI_URI::_set_uri_string() - Removed CI_URI::_reindex_segments() altogether ( doesn't need further manipulation, while is public anyway and can be properly (and more effectively) replaced on the spot) --- system/core/CodeIgniter.php | 143 ++++++++++++++----------- system/core/Router.php | 240 +++++++++++++++++++++-------------------- system/core/URI.php | 252 +++++++++++++++----------------------------- 3 files changed, 283 insertions(+), 352 deletions(-) (limited to 'system/core') 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/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 c83b7a74f..80cf0b4e9 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; + } + } + } } // -------------------------------------------------------------------- @@ -239,37 +227,11 @@ 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'.(UTF8_ENABLED ? 'u' : ''), $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); } } -- cgit v1.2.3-24-g4f1b