CodeIgniter框架源碼筆記(5)——識別多種URI風格:地址解析類URI.php

分類:編程 時間:2016-11-07
[摘要:URI類首要處置懲罰地點字符串,將uri分化成對應的片斷,存到segments數組中。querystring分化後存到$_GET數組 ROUTER路由類正在以後的剖析路由舉措中,也首要依附URI類的segments屬性數]

URI類主要處理地址字符串,將uri分解成對應的片段,存到segments數組中。querystring分解後存到$_GET數組
ROUTER路由類在之後的解析路由動作中,也主要依靠URI類的segments屬性數組來獲取當前上下文的請求URI信息。

在CI中如果允許傳統的querystirng模式,即設置$config['enable_query_strings'] = TRUE,URI類將不做任何處理,ROUTER類也只會匹配目錄、控制器、方法。

CI體系中的方法參數都是從URI片段中取的,並按順序傳遞給方法參數。不支持將querstring中的變量通過方法參數名傳給方法,只能用$_GET獲取。


以下分析默認config['enable_query_strings'] = FALSE。
在開始之前,先看配置文件
/*
|--------------------------------------------------------------------------
| 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

文章來源:


ads
ads

相關文章
ads

相關文章

ad