接下来分析一个我感觉比较重要的类,就是URI类,这个类负责的是URI的解析,其实也很简单,说白了就干了一件事:把各式各样的URI规则都可以解析出来,顺便过滤一下,然后可以有URI的重建功能,这里大量的引用了$_SERVER这个数组,其实想想也很简单,我们或许到的信息,不都是从这些数组里才能看到的嘛.
这里只是负责解析,并没有把它们和控制器的方法的参数对应起来,但是提供了很好的接口,就是说对于一次确定的访问,它可以捕获到特定的段,这就够了.
-
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
-
-
class CI_URI {
-
//缓存的URI查询数组.
-
var $keyval = array();
-
//uri查询字符串.
-
var $uri_string;
-
//uri段
-
var $segments = array();
-
//uri重建之后的段
-
var $rsegments = array();
-
-
-
//构造函数,
-
function __construct()
-
{
-
$this->config =& load_class('Config', 'core');
-
log_message('debug', "URI Class Initialized");
-
}
-
-
-
//得到uri字符串,返回类型为字符串类型.
-
function _fetch_uri_string()
-
{
-
//如果uri是自由模式.
-
if (strtoupper($this->config->item('uri_protocol')) == 'AUTO')
-
{
-
//如果是cli请求,则使用解析cli的方式去解析它.
-
if (defined('STDIN'))
-
{
-
$this->uri_string = $this->_parse_cli_args();
-
return;
-
}
-
-
-
//先调用查询字符串的方式来分析,这在大多数情况下都有效.
-
if ($uri = $this->_detect_uri())
-
{
-
$this->uri_string = $uri;
-
return;
-
}
-
-
//有些服务器使用getenv()会出现问题,因此我们用两种方式检测它.
-
//是否开启了pathinfo模式.
-
$path = (isset($_SERVER['PATH_INFO'])) ? $_SERVER['PATH_INFO'] : @getenv('PATH_INFO');
-
if (trim($path, '/') != '' && $path != "/".SELF)
-
{
-
$this->uri_string = $path;
-
return;
-
}
-
-
-
//没有开启pathinfo模式,那么QUERY_STRING呢?
-
$path = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : @getenv('QUERY_STRING');
-
if (trim($path, '/') != '')
-
{
-
$this->uri_string = $path;
-
return;
-
}
-
-
//最后我们试试GET数组的方式.
-
if (is_array($_GET) && count($_GET) == 1 && trim(key($_GET), '/') != '')
-
{
-
$this->uri_string = key($_GET);
-
return;
-
}
-
-
//我们测试了所有的可能,只能返回一个空字符串.
-
$this->uri_string = '';
-
}
-
else
-
{
-
//首先查看URI使用的方式
-
$uri = strtoupper($this->config->item('uri_protocol'));
-
//如果确定是查询字符串方式的话,交给_detect_uri()函数.
-
if ($uri == 'REQUEST_URI')
-
{
-
$this->uri_string = $this->_detect_uri();
-
return;
-
}
-
//如果是cli模式,交个_parse_cli_args()函数.
-
elseif ($uri == 'CLI')
-
{
-
$this->uri_string = $this->_parse_cli_args();
-
return;
-
}
-
-
$this->uri_string = (isset($_SERVER[$uri])) ? $_SERVER[$uri] : @getenv($uri);
-
}
-
-
// 如果该uri只包含一个斜线,那么直接返回即可.
-
if ($this->uri_string == '/')
-
{
-
$this->uri_string = '';
-
}
-
}
-
-
-
//探测URI,它会自动探测URI并且会修正查询字符串的格式.
-
private function _detect_uri()
-
{
-
//如果访问当前的URI为空,返回空字符串.
-
if ( ! isset($_SERVER['REQUEST_URI']))
-
{
-
return '';
-
}
-
//首先得到该uri
-
$uri = $_SERVER['REQUEST_URI'];
-
//把当前的uri和当前脚本的路径进行对比.
-
//这里要注意一个细节,那就是index.php或者index.html不用写到具体的文件名
-
//因此才会出现下面的dirname的情况.
-
//删除脚本名称,只保留后面的变量部分.
-
if (strpos($uri, $_SERVER['SCRIPT_NAME']) === 0)
-
{
-
$uri = substr($uri, strlen($_SERVER['SCRIPT_NAME']));
-
-
}
-
elseif (strpos($uri, dirname($_SERVER['SCRIPT_NAME'])) === 0)
-
{
-
$uri = substr($uri, strlen(dirname($_SERVER['SCRIPT_NAME'])));
-
}
-
-
//这部分确保即使在Nginx上也会成立.
-
//URI已经找到了,同时修改查询字符串和GET数组.
-
//如果包含"?/",则删掉它.
-
//这是因为我们允许?/welcom/index.php这种格式.
-
if (strncmp($uri, '?/', 2) === 0)
-
{
-
$uri = substr($uri, 2);
-
}
-
-
$parts = preg_split('#\?#i', $uri, 2);
-
$uri = $parts[0];
-
//如果采用的是普通模式.
-
if (isset($parts[1]))
-
{
-
$_SERVER['QUERY_STRING'] = $parts[1];
-
//把查询字符串解析到GET数组中去.
-
parse_str($_SERVER['QUERY_STRING'], $_GET);
-
}
-
else
-
{
-
$_SERVER['QUERY_STRING'] = '';
-
$_GET = array();
-
}
-
-
if ($uri == '/' || empty($uri))
-
{
-
return '/';
-
}
-
//解析url,
-
$uri = parse_url($uri, PHP_URL_PATH);
-
-
// 在URI做一些清理并且返回它.
-
return str_replace(array('//', '../'), '/', trim($uri, '/'));
-
}
-
-
-
-
//解析cli参数,把每一个命令行参数并且假定它是URI的一个段.
-
private function _parse_cli_args()
-
{
-
$args = array_slice($_SERVER['argv'], 1);
-
-
return $args ? '/' . implode('/', $args) : '';
-
}
-
-
-
-
//过滤掉恶意的字符,参数类型为字符串,返回类型为字符串.
-
function _filter_uri($str)
-
{
-
if ($str != '' && $this->config->item('permitted_uri_chars') != '' && $this->config->item('enable_query_strings') == FALSE)
-
{
-
// 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);
-
}
-
}
-
-
//把程序中的字符变成实体.
-
$bad = array('$', '(', ')', '%28', '%29');
-
$good = array('$', '(', ')', '(', ')');
-
-
return str_replace($bad, $good, $str);
-
}
-
-
-
-
-
//如果有必要,除掉URL的伪后缀名.
-
function _remove_url_suffix()
-
{
-
if ($this->config->item('url_suffix') != "")
-
{
-
$this->uri_string = preg_replace("|".preg_quote($this->config->item('url_suffix'))."$|", "", $this->uri_string);
-
}
-
}
-
-
-
//分解URI段,这个独立的段会被存储到该对象的segments数组中去.
-
function _explode_segments()
-
{
-
foreach (explode("/", preg_replace("|/*(.+?)/*$|", "\\1", $this->uri_string)) as $val)
-
{
-
// 为了安全起见,进行过滤.
-
$val = trim($this->_filter_uri($val));
-
-
if ($val != '')
-
{
-
$this->segments[] = $val;
-
}
-
}
-
}
-
-
-
-
//对URI段进行重建索引,数组的索引从1开始而不是从0开始.
-
function _reindex_segments()
-
{
-
array_unshift($this->segments, NULL);
-
array_unshift($this->rsegments, NULL);
-
unset($this->segments[0]);
-
unset($this->rsegments[0]);
-
}
-
-
-
-
-
//获取URI中的一个段.参数为整型,返回类型为字符串.
-
function segment($n, $no_result = FALSE)
-
{
-
return ( ! isset($this->segments[$n])) ? $no_result : $this->segments[$n];
-
}
-
-
-
-
//返回重建路由后的一个段.
-
function rsegment($n, $no_result = FALSE)
-
{
-
return ( ! isset($this->rsegments[$n])) ? $no_result : $this->rsegments[$n];
-
}
-
-
//从URI字符串生成一个键值对.
-
//参数分别为起始段序号,默认的数组名,返回值为数组.
-
function uri_to_assoc($n = 3, $default = array())
-
{
-
return $this->_uri_to_assoc($n, $default, 'segment');
-
}
-
-
//对于ruri实现同样的功能.
-
function ruri_to_assoc($n = 3, $default = array())
-
{
-
return $this->_uri_to_assoc($n, $default, 'rsegment');
-
}
-
-
-
//从URI字符串或者重建URI之后的字符串生成键值对数组.
-
//参数第一个为起始段,第二个为默认值,第三个为使用的数组名.
-
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]))
-
{
-
return $this->keyval[$n];
-
}
-
-
if ($this->$total_segments() < $n)
-
{
-
if (count($default) == 0)
-
{
-
return array();
-
}
-
-
$retval = array();
-
foreach ($default as $val)
-
{
-
$retval[$val] = FALSE;
-
}
-
return $retval;
-
}
-
-
$segments = array_slice($this->$segment_array(), ($n - 1));
-
-
$i = 0;
-
$lastval = '';
-
$retval = array();
-
foreach ($segments as $seg)
-
{
-
if ($i % 2)
-
{
-
$retval[$lastval] = $seg;
-
}
-
else
-
{
-
$retval[$seg] = FALSE;
-
$lastval = $seg;
-
}
-
-
$i++;
-
}
-
-
if (count($default) > 0)
-
{
-
foreach ($default as $val)
-
{
-
if ( ! array_key_exists($val, $retval))
-
{
-
$retval[$val] = FALSE;
-
}
-
}
-
}
-
-
// 为了重用,缓存该数组.
-
$this->keyval[$n] = $retval;
-
return $retval;
-
}
-
-
-
//从一个关联数组生成一个uri字符串.
-
//返回类型为一个数组,参数为一个关联数组.
-
function assoc_to_uri($array)
-
{
-
$temp = array();
-
foreach ((array)$array as $key => $val)
-
{
-
$temp[] = $key;
-
$temp[] = $val;
-
}
-
-
return implode('/', $temp);
-
}
-
-
//取得一个URI段并且加上斜线.
-
function slash_segment($n, $where = 'trailing')
-
{
-
return $this->_slash_segment($n, $where, 'segment');
-
}
-
-
//取得一个重建后的URI段并且加上斜线.
-
function slash_rsegment($n, $where = 'trailing')
-
{
-
return $this->_slash_segment($n, $where, 'rsegment');
-
}
-
-
-
//返回类型为字符串,参数为integer,string,string,string
-
//取得一个uri段并且确定是在头还是尾加上一个斜线.
-
function _slash_segment($n, $where = 'trailing', $which = 'segment')
-
{
-
$leading = '/';
-
$trailing = '/';
-
-
if ($where == 'trailing')
-
{
-
$leading = '';
-
}
-
elseif ($where == 'leading')
-
{
-
$trailing = '';
-
}
-
-
return $leading.$this->$which($n).$trailing;
-
}
-
-
-
//返回类型为数组,路由的段的数组.
-
function segment_array()
-
{
-
return $this->segments;
-
}
-
-
//返回类型为数组,重建路由的段的数组.
-
function rsegment_array()
-
{
-
return $this->rsegments;
-
}
-
-
//返回类型为字符串,返回段的总数.
-
function total_segments()
-
{
-
return count($this->segments);
-
}
-
-
//返回值为整数,即重建路由的段的总数.
-
function total_rsegments()
-
{
-
return count($this->rsegments);
-
}
-
-
-
//取得路由的字符串形式,返回类型为字符串.
-
function uri_string()
-
{
-
return $this->uri_string;
-
}
-
-
-
//返回类型为字符串,取得全部的重建路由的字符串.
-
function ruri_string()
-
{
-
return '/'.implode('/', $this->rsegment_array());
-
}
-
-
}
阅读(1578) | 评论(0) | 转发(0) |