URI類主要處理地址字符串,將uri分解成對應的片段,存到segments數組中。querystring分解後存到$_GET數組
ROUTER路由類在之後的解析路由動作中,也主要依靠URI類的segments屬性數組來獲取當前上下文的請求URI信息。
在CI中如果允許傳統的querystirng模式,即設置$config['enable_query_strings'] = TRUE,URI類將不做任何處理,ROUTER類也只會匹配目錄、控制器、方法。
CI體系中的方法參數都是從URI片段中取的,並按順序傳遞給方法參數。不支持將querstring中的變量通過方法參數名傳給方法,只能用$_GET獲取。
在開始之前,先看配置文件
/* |-------------------------------------------------------------------------- | URI PROTOCOL |-------------------------------------------------------------------------- | | This item determines which server global should be used to retrieve the | URI string. The default setting of 'REQUEST_URI' works for most servers. | If your links do not seem to work, try one of the other delicious flavors: | | 'REQUEST_URI' Uses $_SERVER['REQUEST_URI'] | 'QUERY_STRING' Uses $_SERVER['QUERY_STRING'] | 'PATH_INFO' Uses $_SERVER['PATH_INFO'] | | WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded! */ $config['uri_protocol'] = 'REQUEST_URI';
從應用層面上來說:
設置為QUERY_STRING解析的地址的用例如下:
http://www.nc.com/index.php?/news/list/fruit?page=2
設置為AUTO,REQUEST_URI,PATH_INFO解析的地址的用例如下:
http://www.nc.com/index.php?/news/list/fruit?page=2(和上面一樣,兼容這種用例)
http://www.nc.com/index.php/news/list/fruit?page=2
http://www.nc.com/news/list/fruit?page=2
$config['uri_protocol']配置不但決定以哪個函數處理URI,同時決定了從哪個全局變量裏獲取當前上下文的uri地址。對應關系是:
'REQUEST_URI' 使用 $_SERVER['REQUEST_URI']
'QUERY_STRING' 使用 $_SERVER['QUERY_STRING']'PATH_INFO' 使用 $_SERVER['PATH_INFO']
那麽這三個變量有什麽區別呢?
$_SERVER['REQUEST_URI']獲取的是url地址中主機頭後面所有的字符
$_SERVER['QUERY_STRING']獲取的url地址中"?"後面的部分
$_SERVER['PATH_INFO']獲取的是url地址中腳本文件($_SERVER['SCRIPT_NAME'])之後"?"之前的字符內容
例:index.php中代碼:
echo((isset($_SERVER['PATH_INFO'])? "\$_SERVER['PATH_INFO']:{$_SERVER['PATH_INFO']}" :"PATH_INFO is NULL")."</br>");
echo((isset($_SERVER['REQUEST_URI'])? "\$_SERVER['REQUEST_URI']:{$_SERVER['REQUEST_URI']}" :"REQUEST_URI is NULL")."</br>");
echo((isset($_SERVER['QUERY_STRING'])? "\$_SERVER['QUERY_STRING']:{$_SERVER['QUERY_STRING']}" :"QUERY_STRING is NULL")."</br>");
1、訪問http://www.example.twm/index.php?c=mall&a=lists&page=2
結果如下:
PATH_INFO is NULL
$_SERVER['REQUEST_URI']:/index.php?c=mall&a=lists&page=2
$_SERVER['QUERY_STRING']:c=mall&a=lists&page=2
2、訪問http://www.example.twm/index.php/mall/lists/2?searchkey=Apple
結果如下:
$_SERVER['PATH_INFO']:/mall/lists/2
$_SERVER['REQUEST_URI']:/index.php/mall/lists/2?searchkey=apple
$_SERVER['QUERY_STRING']:searchkey=apple
3、在本地測試時,設置.htaccess,重寫隱藏index.php
<IfModule mod_rewrite.c> <IfModule mod_negotiation.c> Options -MultiViews </IfModule> RewriteEngine On # Redirect Trailing Slashes... RewriteRule ^(.*)/$ /$1 [L,R=301] # Handle Front Controller... RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] #RewriteRule ^(.*)$ /index.php?/$1 [L] #註意以上兩種RewriteRule情況下的$_SERVER['PATH_INFO']的差別 </IfModule>訪問:http://www.example.twm/mall/lists/2?searchkey=apple
顯示如下(註意,這種情況下$_SERVER['PATH_INFO']為空):
PATH_INFO is NULL
$_SERVER['REQUEST_URI']:/mall/lists/2?searchkey=apple
$_SERVER['QUERY_STRING']:searchkey=apple
下面分析URI類中的幾個重要函數
一、構造函數_construct():
功能及流程:解析命令行或url地址,獲取querystring存入_GET全局數組中,並返回刪除了入口文件的url地址;再調用$this->_set_uri_string($uri)生成segments數組。URI類對於存入GET中的參數,並不做安全處理(安全處理在INPUT類中實現)1、處理命令行參數$uri = $this->_parse_argv();
使用場景:我們用命令執行PHP腳本:[root@twm ~]#php index.php mall lists
即:$uri="mall/lists"
protected function _parse_argv() { $args = array_slice($_SERVER['argv'], 1); return $args ? implode('/', $args) : ''; }2、獲取querystring存入_GET全局數組中,並返回刪除入口文件的url地址
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;可以看出:
_parse_request_uri方法處理 AUTO,REQUEST_URI,PATH_INFO
_parse_query_string方法只處理QUERY_STRING
3、調用$this->_set_uri_string($uri)生成segments數組。
二、_parse_request_uri方法註釋
protected function _parse_request_uri() { if ( ! isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME'])) { return ''; } // parse_url() returns false if no host is present, but the path or query string // contains a colon followed by a number //從$_SERVER['REQUEST_URI']取值,解析成$uri和$query兩個字符串,分別存儲請求的路徑和get請求參數 $uri = parse_url('http://dummy'.$_SERVER['REQUEST_URI']); $query = isset($uri['query']) ? $uri['query'] : ''; $uri = isset($uri['path']) ? $uri['path'] : ''; //去掉uri包含的$_SERVER['SCRIPT_NAME'],比如uri是http://www.example.twm/index.php/news/view/crm,經過處理後就變成/news/view/crm了 if (isset($_SERVER['SCRIPT_NAME'][0])) { 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. //對於請求服務器的具體URI包含在查詢字符串這種情況。 //例如$uri以?/開頭的 ,實際上if條件換種寫法就是if(strncmp($uri, '?/', 2) === 0)),類似: //http://www.example.twm/index.php?/welcome/index if (trim($uri, '/') === '' && strncmp($query, '/', 1) === 0) { $query = explode('?', $query, 2); $uri = $query[0]; $_SERVER['QUERY_STRING'] = isset($query[1]) ? $query[1] : ''; } else { //其它情況直接$_SERVER['QUERY_STRING'] = $query; 如下面這種請求uri: //http://www.example.twm/mall/lists?page=7 $_SERVER['QUERY_STRING'] = $query; } //將查詢字符串按鍵名存入_GET數組 parse_str($_SERVER['QUERY_STRING'], $_GET); if ($uri === '/' OR $uri === '') { return '/'; } // Do some final cleaning of the URI and return it //調用 _remove_relative_directory($uri)函數作安全處理,移除$uri中的../相對路徑字符和反斜杠//// return $this->_remove_relative_directory($uri); }
三、_parse_query_string()方法註釋
protected function _parse_query_string() { //從$_SERVER['QUERY_STRING']取值 $uri = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING'); //對於沒有實際內容的,直接返回空。 if (trim($uri, '/') === '') { return ''; } elseif (strncmp($uri, '/', 1) === 0) { //對應生成$_SERVER['QUERY_STRING']和$uri //最後將$_SERVER['QUERY_STRING']解析於_GET數組parse_str($_SERVER['QUERY_STRING'], $_GET); $uri = explode('?', $uri, 2); $_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : ''; $uri = $uri[0]; } parse_str($_SERVER['QUERY_STRING'], $_GET); ////調用 _remove_relative_directory($uri)函數作安全處理,移除$uri中的../相對路徑字符和反斜杠//// return $this->_remove_relative_directory($uri); }
四、 _set_uri_string($str)方法,解析$url填充到$this->segments數組中去
protected function _set_uri_string($str) { // Filter out control characters and trim slashes //移除$str不可見字符: $this->uri_string=trim(remove_invisible_characters($str, FALSE), '/') //這樣做的意義在於防止在字符中間夾入空字符造成漏洞,比如Java\0script $this->uri_string = trim(remove_invisible_characters($str, FALSE), '/'); if ($this->uri_string !== '') { // 移除url後綴,如果配置文件中設置過。 if (($suffix = (string) $this->config->item('url_suffix')) !== '') { $slen = strlen($suffix); if (substr($this->uri_string, -$slen) === $suffix) { $this->uri_string = substr($this->uri_string, 0, -$slen); } } $this->segments[0] = NULL; // Populate the segments array //解析$url,用"/"分段,填充到$this->segments數組中去 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]); } }
五、to_assoc函數簇:uri_to_assoc($n = 3, $default = array()) , ruri_to_assoc($n = 3, $default = array())
該方法用於將 URI 的段轉換為一個包含鍵值對的關聯數組。如下 URI:http://example.twm/user/search/name/joe/location/UK/gender/male
使用這個方法可以將 URI 轉為如下的數組原型:
[array]
(
'name' => 'joe'
'location' => 'UK'
'gender' => 'male'
)
可以通過第一個參數設置一個位移,默認值為 3 ,這是因為URI 的前兩段通常都是控制器和方法。 例如:
$array = $this->uri->uri_to_assoc(3);
echo $array['name'];
第二個參數用於設置默認的鍵名,這樣即使 URI 中缺少某個鍵名,也能保證返回的數組中包含該索引。 例如:
$default = array('name', 'gender', 'location', 'type', 'sort');
$array = $this->uri->uri_to_assoc(3, $default);
按照CI體系,segments前兩個對應的分別是控制器名和方法名,所以默認從第三個取
六、segment函數簇
1、segment($n, $no_result = NULL) rsegment($n, $no_result = NULL)用於從 URI 中獲取指定段。參數 n 為你希望獲取的段序號,URI 的段從左到右進行編號。 例如,如果你的完整 URL 是這樣的:
http://example.twm/index.php/news/local/metro/crime_is_up
那麽每個分段如下:
news
local
metro
crime_is_up
$product_id = $this->uri->segment(3, 0);返回metro,不存在時返回0
2、segment_array() , rsegment_array()
返回 URI 所有的段組成的數組。例如:
$segs = $this->uri->segment_array();
foreach ($segs as $segment)
{
echo $segment;
echo '<br />';
}
3、total_segments() , total_rsegments()
返回 URI 的總段數
4、slash_segment($n, $where = 'trailing') , slash_rsegment($n, $where = 'trailing')
該方法和 segment() 類似,只是它會根據第二個參數在返回結果的前面或/和後面添加斜線。 如果第二個參數未設置,斜線會添加到後面
根據源代碼看,如果第二個參數不是trailing,也不是leading,將會在頭尾都加斜杠。
protected function _slash_segment($n, $where = 'trailing', $which = 'segment') { $leading = $trailing = '/'; if ($where === 'trailing') { $leading = ''; } elseif ($where === 'leading') { $trailing = ''; } return $leading.$this->$which($n).$trailing; }
七、uri_string() , ruri_string()
返回一個相對的 URI 字符串,例如,如果你的完整 URL 為:http://example.twm/index.php/news/local/345
該方法返回:
news/local/345
Tags: delicious default 配置文件 setting server
文章來源: