diff options
Diffstat (limited to 'system/core/URI.php')
-rw-r--r-- | system/core/URI.php | 628 |
1 files changed, 306 insertions, 322 deletions
diff --git a/system/core/URI.php b/system/core/URI.php index 3dacd6743..3ccdfa7b0 100644 --- a/system/core/URI.php +++ b/system/core/URI.php @@ -1,24 +1,41 @@ -<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); -/** - * MODIFIED: - * _detect_uri(): ltrim instead of trim at the end to preserve tailing slashes - */ - +<?php /** * CodeIgniter * - * An open source application development framework for PHP 5.1.6 or newer + * An open source application development framework for PHP * - * @package CodeIgniter - * @author ExpressionEngine Dev Team - * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. - * @license http://codeigniter.com/user_guide/license.html - * @link http://codeigniter.com - * @since Version 1.0 + * This content is released under the MIT License (MIT) + * + * Copyright (c) 2014 - 2017, British Columbia Institute of Technology + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @package CodeIgniter + * @author EllisLab Dev Team + * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) + * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/) + * @license http://opensource.org/licenses/MIT MIT License + * @link https://codeigniter.com + * @since Version 1.0.0 * @filesource */ - -// ------------------------------------------------------------------------ +defined('BASEPATH') OR exit('No direct script access allowed'); /** * URI Class @@ -28,382 +45,368 @@ * @package CodeIgniter * @subpackage Libraries * @category URI - * @author ExpressionEngine Dev Team - * @link http://codeigniter.com/user_guide/libraries/uri.html + * @author EllisLab Dev Team + * @link https://codeigniter.com/user_guide/libraries/uri.html */ class CI_URI { /** - * List of cached uri segments + * List of cached URI segments * - * @var array - * @access public + * @var array */ - var $keyval = array(); + public $keyval = array(); + /** - * Current uri string + * Current URI string * - * @var string - * @access public + * @var string */ - var $uri_string; + public $uri_string = ''; + /** - * List of uri segments + * List of URI segments + * + * Starts at 1 instead of 0. * - * @var array - * @access public + * @var array */ - var $segments = array(); + public $segments = array(); + /** - * Re-indexed list of uri segments - * Starts at 1 instead of 0 + * List of routed URI segments + * + * Starts at 1 instead of 0. * - * @var array - * @access public + * @var array */ - var $rsegments = array(); + public $rsegments = array(); /** - * 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 * - * @access public + * @var string */ - function __construct() - { - $this->config =& load_class('Config', 'core'); - log_message('debug', "URI Class Initialized"); - } - - - // -------------------------------------------------------------------- + protected $_permitted_uri_chars; /** - * Get the URI String + * Class constructor * - * @access private - * @return string + * @return void */ - function _fetch_uri_string() + public function __construct() { - if (strtoupper($this->config->item('uri_protocol')) == 'AUTO') - { - // Is the request coming from the command line? - if (php_sapi_name() == 'cli' or defined('STDIN')) - { - $this->_set_uri_string($this->_parse_cli_args()); - return; - } + $this->config =& load_class('Config', 'core'); - // Let's try the REQUEST_URI first, this will work in most situations - if ($uri = $this->_detect_uri()) - { - $this->_set_uri_string($uri); - return; - } + // 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'); - // Is there a PATH_INFO variable? - // Note: some servers seem to have trouble with getenv() so we'll test it two ways - $path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO'); - if (trim($path, '/') != '' && $path != "/".SELF) + // If it's a CLI request, ignore the configuration + if (is_cli()) { - $this->_set_uri_string($path); - return; + $uri = $this->_parse_argv(); } - - // No PATH_INFO?... What about QUERY_STRING? - $path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); - if (trim($path, '/') != '') + else { - $this->_set_uri_string($path); - return; - } + $protocol = $this->config->item('uri_protocol'); + empty($protocol) && $protocol = 'REQUEST_URI'; - // As a last ditch effort lets try using the $_GET array - if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '') - { - $this->_set_uri_string(key($_GET)); - return; + switch ($protocol) + { + case 'AUTO': // For BC purposes only + case 'REQUEST_URI': + $uri = $this->_parse_request_uri(); + break; + case 'QUERY_STRING': + $uri = $this->_parse_query_string(); + break; + case 'PATH_INFO': + default: + $uri = isset($_SERVER[$protocol]) + ? $_SERVER[$protocol] + : $this->_parse_request_uri(); + break; + } } - // We've exhausted all our options... - $this->uri_string = ''; - return; + $this->_set_uri_string($uri); } - $uri = strtoupper($this->config->item('uri_protocol')); - - if ($uri == 'REQUEST_URI') - { - $this->_set_uri_string($this->_detect_uri()); - return; - } - elseif ($uri == 'CLI') - { - $this->_set_uri_string($this->_parse_cli_args()); - return; - } - - $path = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri); - $this->_set_uri_string($path); + log_message('info', 'URI Class Initialized'); } // -------------------------------------------------------------------- /** - * Set the URI String + * Set URI String * - * @access public - * @param string - * @return string + * @param string $str + * @return void */ - function _set_uri_string($str) + protected function _set_uri_string($str) { - // Filter out control characters - $str = remove_invisible_characters($str, FALSE); + // 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); + } + } - // If the URI contains only a slash we'll kill it - $this->uri_string = ($str == '/') ? '' : $str; + $this->segments[0] = NULL; + // Populate the segments array + foreach (explode('/', trim($this->uri_string, '/')) as $val) + { + $val = trim($val); + // Filter segments for security + $this->filter_uri($val); + + if ($val !== '') + { + $this->segments[] = $val; + } + } + + unset($this->segments[0]); + } } // -------------------------------------------------------------------- /** - * Detects the URI + * Parse REQUEST_URI * - * This function will detect the URI automatically and fix the query string - * if necessary. + * Will parse REQUEST_URI and automatically detect the URI from it, + * while fixing the query string if necessary. * - * @access private * @return string */ - private function _detect_uri() + protected function _parse_request_uri() { - if ( ! isset($_SERVER['REQUEST_URI']) OR ! isset($_SERVER['SCRIPT_NAME'])) + if ( ! isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])) { return ''; } - $uri = $_SERVER['REQUEST_URI']; - if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) - { - $uri = substr($uri, strlen($_SERVER['SCRIPT_NAME'])); - } - elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) + // parse_url() returns false if no host is present, but the path or query string + // contains a colon followed by a number + $uri = parse_url('http://dummy'.$_SERVER['REQUEST_URI']); + $query = isset($uri['query']) ? $uri['query'] : ''; + $uri = isset($uri['path']) ? $uri['path'] : ''; + + if (isset($_SERVER['SCRIPT_NAME'][0])) { - $uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); + if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0) + { + $uri = (string) substr($uri, strlen($_SERVER['SCRIPT_NAME'])); + } + elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0) + { + $uri = (string) substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME']))); + } } // This section ensures that even on servers that require the URI to be in the query string (Nginx) a correct // URI is found, and also fixes the QUERY_STRING server var and $_GET array. - if (strncmp($uri, '?/', 2) === 0) - { - $uri = substr($uri, 2); - } - $parts = preg_split('#\?#i', $uri, 2); - $uri = $parts[0]; - if (isset($parts[1])) + if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0) { - $_SERVER['QUERY_STRING'] = $parts[1]; - parse_str($_SERVER['QUERY_STRING'], $_GET); + $query = explode('?', $query, 2); + $uri = $query[0]; + $_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : ''; } else { - $_SERVER['QUERY_STRING'] = ''; - $_GET = array(); + $_SERVER['QUERY_STRING'] = $query; } - if ($uri == '/' || empty($uri)) + parse_str($_SERVER['QUERY_STRING'], $_GET); + + if ($uri === '/' OR $uri === '') { return '/'; } - $uri = parse_url($uri, PHP_URL_PATH); - // Do some final cleaning of the URI and return it - return str_replace(array('//', '../'), '/', ltrim($uri, '/')); + return $this->_remove_relative_directory($uri); } // -------------------------------------------------------------------- /** - * Parse cli arguments + * Parse QUERY_STRING * - * Take each command line argument and assume it is a URI segment. + * Will parse QUERY_STRING and automatically detect the URI from it. * - * @access private * @return string */ - private function _parse_cli_args() + protected function _parse_query_string() { - $args = array_slice($_SERVER['argv'], 1); + $uri = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); - return $args ? '/' . implode('/', $args) : ''; - } - - // -------------------------------------------------------------------- - - /** - * Filter segments for malicious characters - * - * @access private - * @param string - * @return string - */ - function _filter_uri($str) - { - if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE) + if (trim($uri, '/') === '') { - // 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)) - { - show_error('The URI you submitted has disallowed characters.', 400); - } + return ''; + } + elseif (strncmp($uri, '/', 1) === 0) + { + $uri = explode('?', $uri, 2); + $_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : ''; + $uri = $uri[0]; } - // Convert programatic characters to entities - $bad = array('$', '(', ')', '%28', '%29'); - $good = array('$', '(', ')', '(', ')'); + parse_str($_SERVER['QUERY_STRING'], $_GET); - return str_replace($bad, $good, $str); + return $this->_remove_relative_directory($uri); } // -------------------------------------------------------------------- /** - * Remove the suffix from the URL if needed + * Parse CLI arguments * - * @access private - * @return void + * Take each command line argument and assume it is a URI segment. + * + * @return string */ - function _remove_url_suffix() + protected function _parse_argv() { - if ($this->config->item('url_suffix') != "") - { - $this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string); - } + $args = array_slice($_SERVER['argv'], 1); + return $args ? implode('/', $args) : ''; } // -------------------------------------------------------------------- /** - * Explode the URI Segments. The individual segments will - * be stored in the $this->segments array. + * Remove relative directory (../) and multi slashes (///) * - * @access private - * @return void + * Do some final cleaning of the URI and return it, currently only used in self::_parse_request_uri() + * + * @param string $uri + * @return string */ - function _explode_segments() + protected function _remove_relative_directory($uri) { - foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val) + $uris = array(); + $tok = strtok($uri, '/'); + while ($tok !== FALSE) { - // Filter segments for security - $val = trim($this->_filter_uri($val)); - - if ($val != '') + if (( ! empty($tok) OR $tok === '0') && $tok !== '..') { - $this->segments[] = $val; + $uris[] = $tok; } + $tok = strtok('/'); } + + return implode('/', $uris); } // -------------------------------------------------------------------- + /** - * Re-index Segments + * Filter URI * - * This function re-indexes the $this->segment array so that it - * starts at 1 rather than 0. Doing so makes it simpler to - * use functions like $this->uri->segment(n) since there is - * a 1:1 relationship between the segment array and the actual segments. + * Filters segments for malicious characters. * - * @access private + * @param string $str * @return void */ - function _reindex_segments() + public function filter_uri(&$str) { - array_unshift($this->segments, NULL); - array_unshift($this->rsegments, NULL); - unset($this->segments[0]); - unset($this->rsegments[0]); + 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); + } } // -------------------------------------------------------------------- /** - * Fetch a URI Segment + * Fetch URI Segment * - * This function returns the URI segment based on the number provided. - * - * @access public - * @param integer - * @param bool - * @return string + * @see CI_URI::$segments + * @param int $n Index + * @param mixed $no_result What to return if the segment index is not found + * @return mixed */ - function segment($n, $no_result = FALSE) + public function segment($n, $no_result = NULL) { - return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n]; + return isset($this->segments[$n]) ? $this->segments[$n] : $no_result; } // -------------------------------------------------------------------- /** - * Fetch a URI "routed" Segment + * Fetch URI "routed" Segment * - * This function returns the re-routed URI segment (assuming routing rules are used) - * based on the number provided. If there is no routing this function returns the - * same result as $this->segment() + * Returns the re-routed URI segment (assuming routing rules are used) + * based on the index provided. If there is no routing, will return + * the same result as CI_URI::segment(). * - * @access public - * @param integer - * @param bool - * @return string + * @see CI_URI::$rsegments + * @see CI_URI::segment() + * @param int $n Index + * @param mixed $no_result What to return if the segment index is not found + * @return mixed */ - function rsegment($n, $no_result = FALSE) + public function rsegment($n, $no_result = NULL) { - return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n]; + return isset($this->rsegments[$n]) ? $this->rsegments[$n] : $no_result; } // -------------------------------------------------------------------- /** - * Generate a key value pair from the URI string + * URI to assoc * - * This function generates and associative array of URI data starting - * at the supplied segment. For example, if this is your URI: + * Generates an associative array of URI data starting at the supplied + * segment index. For example, if this is your URI: * * example.com/user/search/name/joe/location/UK/gender/male * - * You can use this function to generate an array with this prototype: + * You can use this method to generate an array with this prototype: * - * array ( - * name => joe - * location => UK - * gender => male - * ) + * array ( + * name => joe + * location => UK + * gender => male + * ) * - * @access public - * @param integer the starting segment number - * @param array an array of default values + * @param int $n Index (default: 3) + * @param array $default Default values * @return array */ - function uri_to_assoc($n = 3, $default = array()) + public function uri_to_assoc($n = 3, $default = array()) { return $this->_uri_to_assoc($n, $default, 'segment'); } + + // -------------------------------------------------------------------- + /** - * Identical to above only it uses the re-routed segment array + * Routed URI to assoc * - * @access public - * @param integer the starting segment number - * @param array an array of default values - * @return array + * Identical to CI_URI::uri_to_assoc(), only it uses the re-routed + * segment array. * + * @see CI_URI::uri_to_assoc() + * @param int $n Index (default: 3) + * @param array $default Default values + * @return array */ - function ruri_to_assoc($n = 3, $default = array()) + public function ruri_to_assoc($n = 3, $default = array()) { return $this->_uri_to_assoc($n, $default, 'rsegment'); } @@ -411,57 +414,43 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Generate a key value pair from the URI string or Re-routed URI string + * Internal URI-to-assoc + * + * Generates a key/value pair from the URI string or re-routed URI string. * - * @access private - * @param integer the starting segment number - * @param array an array of default values - * @param string which array we should use + * @used-by CI_URI::uri_to_assoc() + * @used-by CI_URI::ruri_to_assoc() + * @param int $n Index (default: 3) + * @param array $default Default values + * @param string $which Array name ('segment' or 'rsegment') * @return array */ - function _uri_to_assoc($n = 3, $default = array(), $which = 'segment') + protected function _uri_to_assoc($n = 3, $default = array(), $which = 'segment') { - if ($which == 'segment') - { - $total_segments = 'total_segments'; - $segment_array = 'segment_array'; - } - else - { - $total_segments = 'total_rsegments'; - $segment_array = 'rsegment_array'; - } - if ( ! is_numeric($n)) { return $default; } - if (isset($this->keyval[$n])) + if (isset($this->keyval[$which], $this->keyval[$which][$n])) { - return $this->keyval[$n]; + return $this->keyval[$which][$n]; } + $total_segments = "total_{$which}s"; + $segment_array = "{$which}_array"; + if ($this->$total_segments() < $n) { - if (count($default) == 0) - { - return array(); - } - - $retval = array(); - foreach ($default as $val) - { - $retval[$val] = FALSE; - } - return $retval; + return (count($default) === 0) + ? array() + : array_fill_keys($default, NULL); } $segments = array_slice($this->$segment_array(), ($n - 1)); - $i = 0; $lastval = ''; - $retval = array(); + $retval = array(); foreach ($segments as $seg) { if ($i % 2) @@ -470,7 +459,7 @@ class CI_URI { } else { - $retval[$seg] = FALSE; + $retval[$seg] = NULL; $lastval = $seg; } @@ -483,30 +472,31 @@ class CI_URI { { if ( ! array_key_exists($val, $retval)) { - $retval[$val] = FALSE; + $retval[$val] = NULL; } } } // Cache the array for reuse - $this->keyval[$n] = $retval; + isset($this->keyval[$which]) OR $this->keyval[$which] = array(); + $this->keyval[$which][$n] = $retval; return $retval; } // -------------------------------------------------------------------- /** - * Generate a URI string from an associative array + * Assoc to URI * + * Generates a URI string from an associative array. * - * @access public - * @param array an associative array of key/values - * @return array + * @param array $array Input array of key/value pairs + * @return string URI string */ - function assoc_to_uri($array) + public function assoc_to_uri($array) { $temp = array(); - foreach ((array)$array as $key => $val) + foreach ((array) $array as $key => $val) { $temp[] = $key; $temp[] = $val; @@ -518,14 +508,15 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch a URI Segment and add a trailing slash + * Slash segment + * + * Fetches an URI segment with a slash. * - * @access public - * @param integer - * @param string + * @param int $n Index + * @param string $where Where to add the slash ('trailing' or 'leading') * @return string */ - function slash_segment($n, $where = 'trailing') + public function slash_segment($n, $where = 'trailing') { return $this->_slash_segment($n, $where, 'segment'); } @@ -533,14 +524,15 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch a URI Segment and add a trailing slash + * Slash routed segment * - * @access public - * @param integer - * @param string + * Fetches an URI routed segment with a slash. + * + * @param int $n Index + * @param string $where Where to add the slash ('trailing' or 'leading') * @return string */ - function slash_rsegment($n, $where = 'trailing') + public function slash_rsegment($n, $where = 'trailing') { return $this->_slash_segment($n, $where, 'rsegment'); } @@ -548,24 +540,27 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch a URI Segment and add a trailing slash - helper function + * Internal Slash segment + * + * Fetches an URI Segment and adds a slash to it. * - * @access private - * @param integer - * @param string - * @param string + * @used-by CI_URI::slash_segment() + * @used-by CI_URI::slash_rsegment() + * + * @param int $n Index + * @param string $where Where to add the slash ('trailing' or 'leading') + * @param string $which Array name ('segment' or 'rsegment') * @return string */ - function _slash_segment($n, $where = 'trailing', $which = 'segment') + protected function _slash_segment($n, $where = 'trailing', $which = 'segment') { - $leading = '/'; - $trailing = '/'; + $leading = $trailing = '/'; - if ($where == 'trailing') + if ($where === 'trailing') { $leading = ''; } - elseif ($where == 'leading') + elseif ($where === 'leading') { $trailing = ''; } @@ -578,10 +573,9 @@ class CI_URI { /** * Segment Array * - * @access public - * @return array + * @return array CI_URI::$segments */ - function segment_array() + public function segment_array() { return $this->segments; } @@ -591,10 +585,9 @@ class CI_URI { /** * Routed Segment Array * - * @access public - * @return array + * @return array CI_URI::$rsegments */ - function rsegment_array() + public function rsegment_array() { return $this->rsegments; } @@ -604,10 +597,9 @@ class CI_URI { /** * Total number of segments * - * @access public - * @return integer + * @return int */ - function total_segments() + public function total_segments() { return count($this->segments); } @@ -617,10 +609,9 @@ class CI_URI { /** * Total number of routed segments * - * @access public - * @return integer + * @return int */ - function total_rsegments() + public function total_rsegments() { return count($this->rsegments); } @@ -628,32 +619,25 @@ class CI_URI { // -------------------------------------------------------------------- /** - * Fetch the entire URI string + * Fetch URI string * - * @access public - * @return string + * @return string CI_URI::$uri_string */ - function uri_string() + public function uri_string() { return $this->uri_string; } - // -------------------------------------------------------------------- /** - * Fetch the entire Re-routed URI string + * Fetch Re-routed URI string * - * @access public * @return string */ - function ruri_string() + public function ruri_string() { - return '/'.implode('/', $this->rsegment_array()); + return ltrim(load_class('Router', 'core')->directory, '/').implode('/', $this->rsegments); } } -// END URI Class - -/* End of file URI.php */ -/* Location: ./system/core/URI.php */ |