CodeIgniter源码分析之Router.php

1<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3// ------------------------------------------------------------------------ 4 5/** 6* Router Class 7*/ 8class CI_Router { 9 10/** 11* Config class 12*/ 13var $config; 14 15/** 16* List of routes 17 18*/ 19var $routes = array(); 20 21/** 22* List of error routes 23*/ 24var $error_routes = array(); 25 26/** 27* Current class name 28*/ 29var $class = ''; 30 31/** 32* Current method name 33*/ 34var $method = 'index'; 35 36/** 37* Sub-directory that contains the requested controller class 38*/ 39var $directory = ''; 40 41/** 42* Default controller (and method if specific) 43*/ 44var $default_controller; 45 46/** 47* Constructor 48*/ 49function __construct() 50{ 51$this->config =& load_class('Config', 'core'); 52$this->uri =& load_class('URI', 'core'); 53log_message('debug', "Router Class Initialized"); 54} 55 56// -------------------------------------------------------------------- 57 58/** 59* Set the route mapping 60*/ 61function _set_routing() 62{ 63 64//如果项目是允许通过query_strings的形式,并且有通过$_GET的方式请求控制器的话,则以query_string形式,也就是 65//?c=xx的形式确定路由。 66$segments = array(); 67if ($this->config->item('enable_query_strings') === TRUE AND isset($_GET[$this->config->item('controller_trigger')])) 68{ 69//上面这里为什么还要判断有没有通过get的方式指定控制器?其实是因为如果允许query_string的形式请求路由,但是却 70//没有通过query_string(或者说是get)的形式指定路由的话(其实就说明这个通过query_string方式的uri是无效的), 71//此时,我们依然会采用“段”的形式。 72 73//取得目录名,目录名,控制名和方法名传递的变量名都是可以自定义的,在config/config.php里面。 74if (isset($_GET[$this->config->item('directory_trigger')])) 75{ 76$this->set_directory(trim($this->uri->_filter_uri($_GET[$this->config->item('directory_trigger')]))); 77$segments[] = $this->fetch_directory(); 78} 79 80//取得控制器名 81if (isset($_GET[$this->config->item('controller_trigger')])) 82{ 83$this->set_class(trim($this->uri->_filter_uri($_GET[$this->config->item('controller_trigger')]))); 84$segments[] = $this->fetch_class(); 85} 86 87//取得方法名 88if (isset($_GET[$this->config->item('function_trigger')])) 89{ 90$this->set_method(trim($this->uri->_filter_uri($_GET[$this->config->item('function_trigger')]))); 91$segments[] = $this->fetch_method(); 92} 93} 94//。。。。。。。。。。。。。。。位置1 95 96// Load the routes.php file. 97//引入关于路由方面的配置信息。配置文件里面是一个名字为$route的数组。 98if (defined('ENVIRONMENT') AND is_file(APPPATH.'config/'.ENVIRONMENT.'/routes.php')) 99{ 100include(APPPATH.'config/'.ENVIRONMENT.'/routes.php'); 101} 102elseif (is_file(APPPATH.'config/routes.php')) 103{ 104include(APPPATH.'config/routes.php'); 105} 106 107//下面这个莫名出现的$route变量(注意不是$this->routes哦),就是写在配置文件里面。把它copy到$this->routes。 108//这个$routes是用来指定默认控制器和默认方法,404(请求路由不存在)后规定的路由以及一些路由重定向(?这个重写向的 109//实现在Router::_parse_routes()中实现)的信息。 110$this->routes = ( ! isset($route) OR ! is_array($route)) ? array() : $route; 111unset($route);//利用完就干掉。 112 113//根据刚才的配置信息,设定默认控制器,没有的话,就为FLASE。 114$this->default_controller = ( ! isset($this->routes['default_controller']) OR $this->routes['default_controller'] == '') ? FALSE : strtolower($this->routes['default_controller']); 115 116//这个判断的位置放得有点怪,我觉得可以放到上面“位置1”的地方,下面的代码是判断刚才有没有通过query_string的方式拿到 117//路由信息,如果拿到的话,那么就不再尝试“段”的方式确定路由了。直接确定路由,结束本函数。 118if (count($segments) > 0) 119{ 120//_validate_quest($segments);的作用就是确定并设置路由。 121//这个函数执行完之后,Router::$class,Router::$directory(如果有)都会有相应值。 122return $this->_validate_request($segments); 123} 124 125//下面的_fetch_uri_string()详见:URI.php,在这个位置只需知道它的作用是: 126//从uri中检测处理,把我们确定路由需要的信息(例如“index.php/index/welcome/1”后 127//面"index/welcome/1"这串)放到$this->uri->uri_string这个东西中。 128$this->uri->_fetch_uri_string(); 129 130//上面_fetch_uri_string()完了之后,这个uri_string就会有我们要用的信息,如果为空的话,那么就用把路由设置为默认的。 131if ($this->uri->uri_string == '') 132{ 133//移步至Router::_set_default_controller(); 134return $this->_set_default_controller(); 135} 136 137//如果$this->uri->uri_string 不为空,那么,会通过下面的方式确定路由 138 139//这里只是简单地把后缀去掉而已,因为CI允许在uri后面加后缀,但它其实对我们寻找路由是多余,而且会造成影响的,所以先去掉。 140$this->uri->_remove_url_suffix(); 141 142//把最初的uri,变成数组放在segments里面。 143$this->uri->_explode_segments(); 144 145//开始找路由。移步至 Router::_parse_routes(); 146$this->_parse_routes(); 147 148//设置为由1开始。 149$this->uri->_reindex_segments(); 150} 151 152// -------------------------------------------------------------------- 153 154/** 155* Set the default controller 156*/ 157function _set_default_controller() 158{ 159//在Router::_set_routing()函数里面有一个操作,是从配置文件里面读取默认控制器名,如果没有就有FALSE。 160if ($this->default_controller === FALSE) 161{ 162//如果没有默认的话,就报错,结束程序。 163//实质上,这个_set_default_controller()仅仅是在uri没有指定控制器,要求访问默认控制器的时候才 164//被调用,所以如果连默认控制器都没有,那么可以果断报错。 165show_error("Unable to determine what should be displayed. A default route has not been specified in the routing file."); 166} 167 168//如果有,下面我们就来把默认的控制器设置为当前要找的路由。 169 170//这里只是分“有指定默认方法”和“没有指定”两种情况而已。不过要弄点下面那个$this->_set_request($x);CI这几个函数 171//也许写得很妙,但是让人看得纠结。。。 172if (strpos($this->default_controller, '/') !== FALSE) 173{ 174$x = explode('/', $this->default_controller); 175 176$this->set_class($x[0]); 177$this->set_method($x[1]); 178$this->_set_request($x);//移步至Router::_set_request(); 179} 180else 181{ 182$this->set_class($this->default_controller); 183$this->set_method('index'); 184$this->_set_request(array($this->default_controller, 'index')); 185} 186 187// re-index the routed segments array so it starts with 1 rather than 0 188$this->uri->_reindex_segments(); 189 190log_message('debug', "No URI present. Default controller set."); 191} 192 193// -------------------------------------------------------------------- 194 195/** 196* Set the Route 197*/ 198function _set_request($segments = array()) 199{ 200/** 201* 下面来解剖一下这个让人纠结的函数。。第一次看的时候差点被它们这几个函数搞晕。 202*/ 203 204/** 205* 看,这里有调用Router::_validate_request();而Router::_validate_request()的作用是检测寻找出一个 206* 正确存在的路由,并确定它,确定后的值分别放到Rouer::$class这些属性里面。所以使到这个_set_request()也有 207* 这种确定路由的功能。 208* 209* 注: 210* $segments=$this->_validate_request($segments); 等式右边,括号里面的这个$segments,也就是调用 211* _set_request()时传入来的这个参数,它有这样的特点: 212* 1)如果这时_set_request()是在Router::_set_default_controller()中调用的话,那个这个$segments是永远不会为 213* 空数组,嗯,绝对不会。 214* 215* 216* 而左边这个$segments的值,经过下面这行代码后,要么为空数组array(),要么为确定路由后的段数组。 217* 为空数组的原因是,$this->_validate_request();里面没有找到当前目录的默认控制器。此时,右边的 218* $segments要么为空,要么只指定了目录但默认控制器不存在。 219*/ 220$segments = $this->_validate_request($segments); 221 222if (count($segments) == 0) 223{ 224//所以如果上面返回了空数组,就会进到这里。 225//这里居然又调回了_set_default_controller()! 坑爹吧! 226return $this->_set_default_controller(); 227/** 228* 我曾经想过,下面这里会不会死循环: 229* 假如,我在配置文件里面的默认控制器设为welcome,然后controllers/下没有welcome.php,但controllers/下有 230* welcome/有这个目录(里面没东西),然后通过http://localhost/CI/来访问默认控制器,那会怎样呢? 231* 首先,它会进入_set_routing();然后发现$this->uri->uri_string为空,进入_set_default_controller(); 232* 然后发现在_set_default_controller里,发现$this->default_controller不为FALSE,(@@@@),然后再 233* 进入这_set_request()里面,再进入_validate_request()里面,会不会_validate_request里返回空数组?因为 234* 指定了目录,没有指定控制器,访问默认的,又不存在,然后返回空数组,返回空数组后,最终就会走来你正在看的这个位置, 235* 然后这个位置再调用_set_default_controller();然后死循环了。。。 236* 237* 答案是不会的。 238* 原因在于: 239* 我们回到上面解译那个(@@@@)的地方,在这里,发现$this->default_controller不为FALSE后,它会进入这个else 240* 里面 241* else 242* { 243* $this->set_class($this->default_controller); ..............1 244* $this->set_method('index'); ...................2 245* $this->_set_request(array($this->default_controller, 'index')); ..........3 246* } 247* 248* 然后第3行,传入_set_request($segments)中的那个$segments其实是 249* array('welcome','index'),重点在于那个小小的'index'!!!!!!! 250* 这样一来,我们进入_validate_request()的时候,我们实质并没有“指定目录但没有指定控制器,访问默认控制器”, 251* 而是“指定了一个welcome的目录,和一个叫index的控制器!!”,所以才不会死循环。 252* 如果你试着把第3行那个'index'去掉,那么,一定会死循环!!!!!!!!不信试试!CI太牛逼了,居然这样做。汗。。 253* 当然,‘index’还有一个作用,就是设置默认方法啦。 254*/ 255} 256 257$this->set_class($segments[0]); 258 259if (isset($segments[1])) 260{ 261// A standard method request 262$this->set_method($segments[1]); 263} 264else 265{ 266$segments[1] = 'index'; 267} 268 269//这里要说一下,现在是在ROUTER里面为URI赋值,URI里面的这个URI::$rsegments是经过处理,并确定路由后,实质调用的路由的段信息。 270//而URI::$segments (前面少了个r),则是原来没处理前的那个,即直接由网址上面得出来的那个。 271$this->uri->rsegments = $segments; 272} 273 274// -------------------------------------------------------------------- 275 276/** 277* Validates the supplied segments. Attempts to determine the path to 278* the controller. 279*/ 280function _validate_request($segments) 281{ 282if (count($segments) == 0) 283{ 284return $segments; 285} 286 287if (file_exists(APPPATH.'controllers/'.$segments[0].'.php')) 288{ 289//如果直接在controllers这个目录下找到与第一段相应的控制器名,那就说明找到了控制器,确定路由,返回。 290return $segments; 291} 292 293//如果上面没有找到,再看看这个“第一段”是不是一个目录,因为CI是允许控制器放在自定义的目录下的。 294if (is_dir(APPPATH.'controllers/'.$segments[0])) 295{ 296// Set the directory and remove it from the segment array 297//如果的确是目录,那么就可以确定路由的目录部分了。 298$this->set_directory($segments[0]); 299//去掉目录部分。进一步进行路由寻找。 300$segments = array_slice($segments, 1); 301 302//如果uri请求中除了目录还有其它“段”,那说明是有请求某指定控制器的。 303if (count($segments) > 0) 304{ 305//指定请求的控制器找不到的话,那只好报错了。 306if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].'.php')) 307{ 308//报错也有两方式,一种是默认的,一种是自义定的。 309//下面这个404_override就是在config/routes.php定义的一个路由找不到时候的默认处理控制器了,如果有定义 310//我们调用它。 311if ( ! empty($this->routes['404_override'])) 312{ 313$x = explode('/', $this->routes['404_override']); 314 315$this->set_directory('');//把刚才设置好的路由的目录部分去掉,因为现在路由是我们定义的404路由。 316$this->set_class($x[0]);//这里可以看出,我们定义的404路由是不允许放在某个目录下的,只能直接放在controllers/下 317$this->set_method(isset($x[1]) ? $x[1] : 'index');//默认是index方法 318 319return $x; //同样,返回“段”数组 320} 321else 322{ 323//默认找不到路由的方法。在core/Common.php中定义的全局函数(实质调用Exception组件进行处理)。 324show_404($this->fetch_directory().$segments[0]); 325} 326} 327} 328else 329{ 330//来到这里,说明了是uri请求指定了目录,而没有指定控制器的情况下。那么,我们默认当前路由是在当前目录下请求默认的 331//控制器和方法。 332 333//下面这个判断只是判断一下$this->default_controller有没有指定方法而已。 334if (strpos($this->default_controller, '/') !== FALSE) 335{ 336$x = explode('/', $this->default_controller); 337$this->set_class($x[0]); 338$this->set_method($x[1]); 339} 340else 341{ 342$this->set_class($this->default_controller); 343$this->set_method('index');//没有的话就默认为index方法。 344} 345 346// Does the default controller exist in the sub-folder? 347//如果连默认控制器都不存在的话,就无语了,说明uri打算请求这个目录的默认控制器,结果没有这个默认控制器,那暂时 348//返回个空数组。(但是看清楚,上面已经$this->set_class()了,说明即使没有,我们也已经把默认控制器的名字算 349//是确定下来先) 350if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.'.php')) 351{ 352$this->directory = ''; 353return array(); 354} 355 356} 357 358//能够有命来到这一步,是说明的确是某个目录下找到了控制器,或者是找到了定义的默认控制器。 359//但是注意,这个$segments返回的“段”信息都是不包括目录的。它是一个数组形式,第一个元素是控制器名。 360//例如:array('acontroller','amethod','xx','xx')。。 361return $segments; 362} 363 364//来到这里,就说明了,即找不到controllers/下相应的控制器,也找不到这样的目录。那就报错咯。 365if ( ! empty($this->routes['404_override'])) 366{ 367$x = explode('/', $this->routes['404_override']); 368 369$this->set_class($x[0]); 370$this->set_method(isset($x[1]) ? $x[1] : 'index'); 371 372return $x; 373} 374 375// Nothing else to do at this point but show a 404 376show_404($segments[0]); 377} 378 379// -------------------------------------------------------------------- 380 381/** 382* Parse Routes 383*/ 384function _parse_routes() 385{ 386// Turn the segment array into a URI string 387//知道_set_request()是干嘛的之后,下面的条理就比较清晰了。 388$uri = implode('/', $this->uri->segments); 389 390// Is there a literal match? If so we're done 391if (isset($this->routes[$uri])) 392{ 393return $this->_set_request(explode('/', $this->routes[$uri])); 394} 395 396/** 397* CI有路由重定向的功能,重定向的规则和实现就是在这里。 398*/ 399foreach ($this->routes as $key => $val) 400{ 401$key = str_replace(':any', '.+', str_replace(':num', '[0-9]+', $key)); 402 403if (preg_match('#^'.$key.'$#', $uri)) 404{ 405// Do we have a back-reference? 406if (strpos($val, '$') !== FALSE AND strpos($key, '(') !== FALSE) 407{ 408$val = preg_replace('#^'.$key.'$#', $val, $uri); 409} 410 411return $this->_set_request(explode('/', $val)); 412} 413} 414$this->_set_request($this->uri->segments); 415} 416 417// -------------------------------------------------------------------- 418 419/** 420* Set the class name 421*/ 422function set_class($class) 423{ 424$this->class = str_replace(array('/', '.'), '', $class); 425} 426 427// -------------------------------------------------------------------- 428 429/** 430* Fetch the current class 431*/ 432function fetch_class() 433{ 434return $this->class; 435} 436 437// -------------------------------------------------------------------- 438 439/** 440* Set the method name 441*/ 442function set_method($method) 443{ 444$this->method = $method; 445} 446 447// -------------------------------------------------------------------- 448 449/** 450* Fetch the current method 451*/ 452function fetch_method() 453{ 454if ($this->method == $this->fetch_class()) 455{ 456return 'index'; 457} 458 459return $this->method; 460} 461 462// -------------------------------------------------------------------- 463 464/** 465* Set the directory name 466*/ 467function set_directory($dir) 468{ 469$this->directory = str_replace(array('/', '.'), '', $dir).'/'; 470} 471 472// -------------------------------------------------------------------- 473 474/** 475* Fetch the sub-directory (if any) that contains the requested controller class 476*/ 477function fetch_directory() 478{ 479return $this->directory; 480} 481 482// -------------------------------------------------------------------- 483 484/** 485* Set the controller overrides 486*/ 487function _set_overrides($routing) 488{ 489if ( ! is_array($routing)) 490{ 491return; 492} 493 494if (isset($routing['directory'])) 495{ 496$this->set_directory($routing['directory']); 497} 498 499if (isset($routing['controller']) AND $routing['controller'] != '') 500{ 501$this->set_class($routing['controller']); 502} 503 504if (isset($routing['function'])) 505{ 506$routing['function'] = ($routing['function'] == '') ? 'index' : $routing['function']; 507$this->set_method($routing['function']); 508} 509} 510 511}
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )