在CI框架中完成地址解析的是URI.php
文件,地址解析是CI框架為了識(shí)別不同風(fēng)格的URL進(jìn)行的配置和預(yù)處理類谨履,針對(duì)配置信息對(duì)URL進(jìn)行預(yù)處理然后進(jìn)行路由。
用戶配置
配置類中影響地址解析的配置項(xiàng)包括
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-‘
绍载,取值用正則表達(dá)式表示URL字符串中允許出現(xiàn)的字符葛峻,不允許出現(xiàn)的字符會(huì)被過(guò)濾;
$config['url_surfix']
表示URL后綴刁赖,定義次配置搁痛,CI框架會(huì)在展示給用戶的URL上添加后綴长搀,當(dāng)然在地址解析時(shí)就需要去掉后綴宇弛;
$config['enable_query_strings’]
,表示是否允許查詢字符串形式的URL源请,其取值為True或False枪芒。
CI默認(rèn)用戶使用搜索引擎友好的URL格式,比如example.com/who/what/where,但是也允許用戶選擇查詢query_string形式的URL谁尸,比如example.com?who=me&what=something&where=here開(kāi)啟條件是將$config['enable_query_strings’]
的值設(shè)置為True舅踪,此時(shí)URI類將不做任何處理,ROUTER類也只會(huì)根據(jù)查詢字符串來(lái)匹配目錄良蛮、控制器抽碌、方法。
下面分析$config['enable_query_strings’]
取值為False的情況决瞳,此時(shí)$config['uri_protocol’]
配置型也會(huì)影響對(duì)URL的解析方法货徙。
$config['uri_protocol’]
,其取值范為REQUEST_URI皮胡,QUERT_STRING痴颊,PATH_INFO
這項(xiàng)配置是為了應(yīng)對(duì)用戶的不同的URL風(fēng)格,對(duì)服務(wù)器的影響是選擇取回URL字符串的方式不同屡贺,具體信息如下所示:
- REQUEST_URI 使用的是$_SERVER['REQUEST_URI’] 蠢棱,返回的是用戶訪問(wèn)地址,即被訪問(wèn)的文件之后的所有URI段 [包括path_info和query_string]甩栈。
- QUERY_STRING 使用的是$_SERVER['QUERY_STRING’]泻仙,返回查詢字符串即符號(hào)?之后的URL部分。
- PATH_INFO 使用的是$_SERVER['PATH_INFO’]量没,返回真實(shí)腳本文件之后和查詢字符串之前的URL部分玉转。
有關(guān)$_SERVER的知識(shí)不清楚建議查閱PHP手冊(cè)或相關(guān)博文
至此,我們已經(jīng)清楚了鉤子的開(kāi)啟和配置方法允蜈,下面我們開(kāi)始分析CI_URI類的代碼冤吨,由于方法比較多且具部分方法比較簡(jiǎn)單并且也只在該類的其他方法中使用,因此本文只就個(gè)人理解的重點(diǎn)方法進(jìn)行介紹饶套。
屬性概覽
屬性名稱 | 注釋 |
---|---|
public $keyval = array() | 用緩存保存的URL字符列表 |
public $uri_string = '' | 當(dāng)前的URL字符 |
public $segments = array() | 保存URL字符列表漩蟆,數(shù)組下標(biāo)從1開(kāi)始 |
public $rsegments = array() | 保存路由的URL字符列表 |
protected $_permitted_uri_chars | 保存允許的URL字符集合 |
方法概覽
方法名稱 | 注釋 |
---|---|
__construct() | 構(gòu)造函數(shù) |
_set_uri_string($str) | 保存解析后的uri并設(shè)置segements數(shù)組 |
_parse_request_uri() | request_uri配置下的解析方法 |
_parse_query_string() | query_string配置下的解析方法 |
_parse_argv() | cli模式下參數(shù)處理方法 |
_remove_relative_directory($uri) | 去除相對(duì)路徑和多個(gè)斜線 |
filter_uri(&$str) | 安全處理過(guò)濾不允許的字符 |
segment($n, $no_result = NULL) | |
rsegment($n, $no_result = NULL) | |
uri_to_assoc($n = 3, $default = array()) | |
ruri_to_assoc($n = 3, $default = array()) | |
_uri_to_assoc($n = 3, $default = array(), $which = 'segment') | |
assoc_to_uri($array) | |
slash_segment($n, $where = 'trailing') | |
slash_rsegment($n, $where = 'trailing') | |
_slash_segment($n, $where = 'trailing', $which = 'segment') | |
segment_array() | |
rsegment_array() | |
total_segments() | |
total_rsegments() | |
uri_string() | |
ruri_string() |
構(gòu)造函數(shù)__construct
public function __construct()
{
$this->config =& load_class('Config', 'core');
//如果配置項(xiàng)中config['enable_query_string']取值為True,我們不需要處理當(dāng)前的URL
//但是這項(xiàng)配置在CLI模式下不生效妓蛮,因此要處理CLI模式和不允許query_string的情況
if (is_cli() OR $this->config->item('enable_query_strings') !== TRUE)
{
//從配置項(xiàng)中獲取允許字符集合的正則
$this->_permitted_uri_chars = $this->config->item('permitted_uri_chars');
//如果是CLI模式忽略配置怠李,將參數(shù)拼接城argv1/argv2的形式
if (is_cli())
{
$uri = $this->_parse_argv();
}
else
{
//不允許query_string時(shí),根據(jù)配置的uri_protocol選擇取回的字符串,默認(rèn)為REQUEST_URI
$protocol = $this->config->item('uri_protocol');
empty($protocol) && $protocol = 'REQUEST_URI';
//根據(jù)配置項(xiàng)的不同捺癞,選擇不同的函數(shù)處理URL
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;
}
}
//保存解析后的uri夷蚊,并設(shè)置segements數(shù)組
$this->_set_uri_string($uri);
}
log_message('info', 'URI Class Initialized');
}
REQUEST_URI配置的解析_prase_request_uri
protected function _parse_request_uri()
{
if ( ! isset($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']))
{
return '';
}
//如果不提供host,函數(shù)parse_url()會(huì)返回false
//從憑借的uri中獲取get請(qǐng)求參數(shù)$query和請(qǐng)求的路徑$url
$uri = parse_url('[http://dummy](http://dummy/)'.$_SERVER['REQUEST_URI']);
$query = isset($uri['query']) ? $uri['query'] : '';
$uri = isset($uri['path']) ? $uri['path'] : '';
//去掉$uri中包含的$_SERVER['SCRIPT_NAME']
//Q:當(dāng)前的uri已經(jīng)是取path得到的不應(yīng)該再包含腳本名稱髓介,應(yīng)該是為了防止惡意的URL引起的解析錯(cuò)誤
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'])));
}
}
//對(duì)于服務(wù)器請(qǐng)求的uri的處理惕鼓,保證再服務(wù)器下也能正確解析uri
// 并且修復(fù)了QUERY_STRING和$_GET的值.
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;
}
//將字符串解析為多個(gè)變量存入$_GET中
parse_str($_SERVER['QUERY_STRING'], $_GET);
if ($uri === '/' OR $uri === '')
{
return '/';
}
//去除相對(duì)路徑"../"和多余的斜線"http:///"并返回
return $this->_remove_relative_directory($uri);
}
QUERY_STRING配置的解析
protected function _parse_query_string()
{
$uri = isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
//如果去掉斜線之后$uri為空返回空串
if (trim($uri, '/') === '')
{
return '';
}
elseif (strncmp($uri, '/', 1) === 0)
{
//根據(jù)?分割$uri并修復(fù)$_SERVER['QUERY_STRING']的值
$uri = explode('?', $uri, 2);
$_SERVER['QUERY_STRING'] = isset($uri[1]) ? $uri[1] : '';
$uri = $uri[0];
}
parse_str($_SERVER['QUERY_STRING'], $_GET);
//去除相對(duì)路徑"../"和多個(gè)斜線"http:///",然后返回
return $this->_remove_relative_directory($uri);
}
設(shè)置uri_string的值_set_uri_string($str)
protected function _set_uri_string($str)
{
//將處理過(guò)的$str經(jīng)過(guò)去除不可見(jiàn)字符和trim之后保存在屬性$uri_string中
$this->uri_string = trim(remove_invisible_characters($str, FALSE), '/');
if ($this->uri_string !== '')
{
//如果定義了url后綴且當(dāng)前存在就移除后綴
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;
//以"/"分割url_string用來(lái)填充segments數(shù)組
foreach (explode('/', trim($this->uri_string, '/')) as $val)
{
$val = trim($val);
//安全處理唐础,過(guò)濾$val
$this->filter_uri($val);
if ($val !== '')
{
$this->segments[] = $val;
}
}
//由于這一步箱歧,實(shí)際有用的數(shù)據(jù)從segments的下標(biāo)1開(kāi)始
unset($this->segments[0]);
}
}