CodeIgniter源码分析之Routerphp.docx
《CodeIgniter源码分析之Routerphp.docx》由会员分享,可在线阅读,更多相关《CodeIgniter源码分析之Routerphp.docx(19页珍藏版)》请在冰豆网上搜索。
CodeIgniter源码分析之Routerphp
phpif(!
defined('BASEPATH'))exit('Nodirectscriptaccessallowed');
//------------------------------------------------------------------------
/**
*RouterClass
*/
classCI_Router{
/**
*Configclass
*/
var$config;
/**
*Listofroutes
*/
var$routes=array();
/**
*Listoferrorroutes
*/
var$error_routes=array();
/**
*Currentclassname
*/
var$class='';
/**
*Currentmethodname
*/
var$method='index';
/**
*Sub-directorythatcontainstherequestedcontrollerclass
*/
var$directory='';
/**
*Defaultcontroller(andmethodifspecific)
*/
var$default_controller;
/**
*Constructor
*/
function__construct()
{
$this->config=&load_class('Config','core');
$this->uri=&load_class('URI','core');
log_message('debug',"RouterClassInitialized");
}
//--------------------------------------------------------------------
/**
*Settheroutemapping
*/
function_set_routing()
{
//如果项目是允许通过query_strings的形式,并且有通过$_GET的方式请求控制器的话,则以query_string形式,也就是
//?
c=xx的形式确定路由。
$segments=array();
if($this->config->item('enable_query_strings')===TRUEANDisset($_GET[$this->config->item('controller_trigger')]))
{
//上面这里为什么还要判断有没有通过get的方式指定控制器?
其实是因为如果允许query_string的形式请求路由,但是却
//没有通过query_string(或者说是get)的形式指定路由的话(其实就说明这个通过query_string方式的uri是无效的),
//此时,我们依然会采用“段”的形式。
//取得目录名,目录名,控制名和方法名传递的变量名都是可以自定义的,在config/config.php里面。
if(isset($_GET[$this->config->item('directory_trigger')]))
{
$this->set_directory(trim($this->uri->_filter_uri($_GET[$this->config->item('directory_trigger')])));
$segments[]=$this->fetch_directory();
}
//取得控制器名
if(isset($_GET[$this->config->item('controller_trigger')]))
{
$this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')])));
$segments[]=$this->fetch_class();
}
//取得方法名
if(isset($_GET[$this->config->item('function_trigger')]))
{
$this->set_method(trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')])));
$segments[]=$this->fetch_method();
}
}
//。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
位置1
//Loadtheroutes.phpfile.
//引入关于路由方面的配置信息。
配置文件里面是一个名字为$route的数组。
if(defined('ENVIRONMENT')ANDis_file(APPPATH.'config/'.ENVIRONMENT.'/routes.php'))
{
include(APPPATH.'config/'.ENVIRONMENT.'/routes.php');
}
elseif(is_file(APPPATH.'config/routes.php'))
{
include(APPPATH.'config/routes.php');
}
//下面这个莫名出现的$route变量(注意不是$this->routes哦),就是写在配置文件里面。
把它copy到$this->routes。
//这个$routes是用来指定默认控制器和默认方法,404(请求路由不存在)后规定的路由以及一些路由重定向(?
这个重写向的
//实现在Router:
:
_parse_routes()中实现)的信息。
$this->routes=(!
isset($route)OR!
is_array($route))?
array():
$route;
unset($route);//利用完就干掉。
//根据刚才的配置信息,设定默认控制器,没有的话,就为FLASE。
$this->default_controller=(!
isset($this->routes['default_controller'])OR$this->routes['default_controller']=='')?
FALSE:
strtolower($this->routes['default_controller']);
//这个判断的位置放得有点怪,我觉得可以放到上面“位置1”的地方,下面的代码是判断刚才有没有通过query_string的方式拿到
//路由信息,如果拿到的话,那么就不再尝试“段”的方式确定路由了。
直接确定路由,结束本函数。
if(count($segments)>0)
{
//_validate_quest($segments);的作用就是确定并设置路由。
//这个函数执行完之后,Router:
:
$class,Router:
:
$directory(如果有)都会有相应值。
return$this->_validate_request($segments);
}
//下面的_fetch_uri_string()详见:
URI.php,在这个位置只需知道它的作用是:
//从uri中检测处理,把我们确定路由需要的信息(例如“index.php/index/welcome/1”后
//面"index/welcome/1"这串)放到$this->uri->uri_string这个东西中。
$this->uri->_fetch_uri_string();
//上面_fetch_uri_string()完了之后,这个uri_string就会有我们要用的信息,如果为空的话,那么就用把路由设置为默认的。
if($this->uri->uri_string=='')
{
//移步至Router:
:
_set_default_controller();
return$this->_set_default_controller();
}
//如果$this->uri->uri_string不为空,那么,会通过下面的方式确定路由
//这里只是简单地把后缀去掉而已,因为CI允许在uri后面加后缀,但它其实对我们寻找路由是多余,而且会造成影响的,所以先去掉。
$this->uri->_remove_url_suffix();
//把最初的uri,变成数组放在segments里面。
$this->uri->_explode_segments();
//开始找路由。
移步至Router:
:
_parse_routes();
$this->_parse_routes();
//设置为由1开始。
$this->uri->_reindex_segments();
}
//--------------------------------------------------------------------
/**
*Setthedefaultcontroller
*/
function_set_default_controller()
{
//在Router:
:
_set_routing()函数里面有一个操作,是从配置文件里面读取默认控制器名,如果没有就有FALSE。
if($this->default_controller===FALSE)
{
//如果没有默认的话,就报错,结束程序。
//实质上,这个_set_default_controller()仅仅是在uri没有指定控制器,要求访问默认控制器的时候才
//被调用,所以如果连默认控制器都没有,那么可以果断报错。
show_error("Unabletodeterminewhatshouldbedisplayed.Adefaultroutehasnotbeenspecifiedintheroutingfile.");
}
//如果有,下面我们就来把默认的控制器设置为当前要找的路由。
//这里只是分“有指定默认方法”和“没有指定”两种情况而已。
不过要弄点下面那个$this->_set_request($x);CI这几个函数
//也许写得很妙,但是让人看得纠结。
。
。
if(strpos($this->default_controller,'/')!
==FALSE)
{
$x=explode('/',$this->default_controller);
$this->set_class($x[0]);
$this->set_method($x[1]);
$this->_set_request($x);//移步至Router:
:
_set_request();
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');
$this->_set_request(array($this->default_controller,'index'));
}
//re-indextheroutedsegmentsarraysoitstartswith1ratherthan0
$this->uri->_reindex_segments();
log_message('debug',"NoURIpresent.Defaultcontrollerset.");
}
//--------------------------------------------------------------------
/**
*SettheRoute
*/
function_set_request($segments=array())
{
/**
*下面来解剖一下这个让人纠结的函数。
。
第一次看的时候差点被它们这几个函数搞晕。
*/
/**
*看,这里有调用Router:
:
_validate_request();而Router:
:
_validate_request()的作用是检测寻找出一个
*正确存在的路由,并确定它,确定后的值分别放到Rouer:
:
$class这些属性里面。
所以使到这个_set_request()也有
*这种确定路由的功能。
*
*注:
*$segments=$this->_validate_request($segments);等式右边,括号里面的这个$segments,也就是调用
*_set_request()时传入来的这个参数,它有这样的特点:
*1)如果这时_set_request()是在Router:
:
_set_default_controller()中调用的话,那个这个$segments是永远不会为
*空数组,嗯,绝对不会。
*
*
*而左边这个$segments的值,经过下面这行代码后,要么为空数组array(),要么为确定路由后的段数组。
*为空数组的原因是,$this->_validate_request();里面没有找到当前目录的默认控制器。
此时,右边的
*$segments要么为空,要么只指定了目录但默认控制器不存在。
*/
$segments=$this->_validate_request($segments);
if(count($segments)==0)
{
//所以如果上面返回了空数组,就会进到这里。
//这里居然又调回了_set_default_controller()!
坑爹吧!
return$this->_set_default_controller();
/**
*我曾经想过,下面这里会不会死循环:
*假如,我在配置文件里面的默认控制器设为welcome,然后controllers/下没有welcome.php,但controllers/下有
*welcome/有这个目录(里面没东西),然后通过http:
//localhost/CI/来访问默认控制器,那会怎样呢?
*首先,它会进入_set_routing();然后发现$this->uri->uri_string为空,进入_set_default_controller();
*然后发现在_set_default_controller里,发现$this->default_controller不为FALSE,(@@@@),然后再
*进入这_set_request()里面,再进入_validate_request()里面,会不会_validate_request里返回空数组?
因为
*指定了目录,没有指定控制器,访问默认的,又不存在,然后返回空数组,返回空数组后,最终就会走来你正在看的这个位置,
*然后这个位置再调用_set_default_controller();然后死循环了。
。
。
*
*答案是不会的。
*原因在于:
*我们回到上面解译那个(@@@@)的地方,在这里,发现$this->default_controller不为FALSE后,它会进入这个else
*里面
*else
*{
*$this->set_class($this->default_controller);..............1
*$this->set_method('index');...................2
*$this->_set_request(array($this->default_controller,'index'));..........3
*}
*
*然后第3行,传入_set_request($segments)中的那个$segments其实是
*array('welcome','index'),重点在于那个小小的'index'!
!
!
!
!
!
!
*这样一来,我们进入_validate_request()的时候,我们实质并没有“指定目录但没有指定控制器,访问默认控制器”,
*而是“指定了一个welcome的目录,和一个叫index的控制器!
!
”,所以才不会死循环。
*如果你试着把第3行那个'index'去掉,那么,一定会死循环!
!
!
!
!
!
!
!
不信试试!
CI太牛逼了,居然这样做。
汗。
。
*当然,‘index’还有一个作用,就是设置默认方法啦。
*/
}
$this->set_class($segments[0]);
if(isset($segments[1]))
{
//Astandardmethodrequest
$this->set_method($segments[1]);
}
else
{
$segments[1]='index';
}
//这里要说一下,现在是在ROUTER里面为URI赋值,URI里面的这个URI:
:
$rsegments是经过处理,并确定路由后,实质调用的路由的段信息。
//而URI:
:
$segments(前面少了个r),则是原来没处理前的那个,即直接由网址上面得出来的那个。
$this->uri->rsegments=$segments;
}
//--------------------------------------------------------------------
/**
*Validatesthesuppliedsegments.Attemptstodeterminethepathto
*thecontroller.
*/
function_validate_request($segments)
{
if(count($segments)==0)
{
return$segments;
}
if(file_exists(APPPATH.'controllers/'.$segments[0].'.php'))
{
//如果直接在controllers这个目录下找到与第一段相应的控制器名,那就说明找到了控制器,确定路由,返回。
return$segments;
}
//如果上面没有找到,再看看这个“第一段”是不是一个目录,因为CI是允许控制器放在自定义的目录下的。
if(is_dir(APPPATH.'controllers/'.$segments[0]))
{
//Setthedirectoryandremoveitfromthesegmentarray
//如果的确是目录,那么就可以确定路由的目录部分了。
$this->set_directory($segments[0]);
//去掉目录部分。
进一步进行路由寻找。
$segments=array_slice($segments,1);
//如果uri请求中除了目录还有其它“段”,那说明是有请求某指定控制器的。
if(count($segments)>0)
{
//指定请求的控制器找不到的话,那只好报错了。
if(!
file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php'))
{
//报错也有两方式,一种是默认的,一种是自义定的。
//下面这个404_override就是在config/routes.php定义的一个路由找不到时候的默认处理控制器了,如果有定义
//我们调用它。
if(!
empty($this->routes['404_override']))
{
$x=explode('/',$this->routes['404_override']);
$this->set_directory('');//把刚才设置好的路由的目录部分去掉,因为现在路由是我们定义的404路由。
$this->set_class($x[0]);//这里可以看出,我们定义的404路由是不允许放在某个目录下的,只能直接放在controllers/下
$this->set_method(isset($x[1])?
$x[1]:
'index');//默认是index方法
return$x;//同样,返回“段”数组
}
else
{
//默认找不到路由的方法。
在core/Common.php中定义的全局函数(实质调用Exception组件进行处理)。
show_404($this->fetch_directory().$segments[0]);
}
}
}
else
{
//来到这里,说明了是uri请求指定了目录,而没有指定控制器的情况下。
那么,我们默认当前路由是在当前目录下请求默认的
//控制器和方法。
//下面这个判断只是判断一下$this->default_controller有没有指定方法而已。
if(strpos($this->default_controller,'/')!
==FALSE)
{
$x=explode('/',$this->default_controller);
$this->set_class($x[0]);
$this->set_method($x[1]);
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');//没有的话就默认为index方法。
}
//Doesthedefaultcontrollerexistinthesub-folder?
//如果连默认控制器都不存在的话,就无语了,说明uri打算请求这个目录的默认控制器,结果没有这个默认控制器,那暂时
//返回个空数组。
(但是看清楚,上面已经$this->set_class()了,说明即使没有,我们也已经把默认控制器的名字算
//是确定下来先)
if(!
file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.'.php'))
{
$this->directory='';
returnarray();
}
}
//能够有命来到这一步,是说明的确是某个目录下找到了控制器,或者是找到了定义的默认控制器。
//但是注意,这个$segments返回的“段”信息都是不包括目录的。
它是一个数组形式,第一个元素是控制器名。
//例如:
array('acontroller'