在Yii中,route是一个非常重要的步骤。通过route我们可以定制更加个性互的Url。同时很多时候,如果route规则复杂也会容易出问题。所以,研究清楚route的机制是十分重要的。在这里希望你能先参看Yii Framework的process flow分析

首先,让我们先看一段代码,此段代码出现在CWebApplication中,

 1 /** 2      * @var array the configuration specifying a controller which should handle 3      * all user requests. This is mainly used when the application is in maintenance mode 4      * and we should use a controller to handle all incoming requests. 5      * The configuration specifies the controller route (the first element) 6      * and GET parameters (the rest name-value pairs). For example, 7      * <pre> 8      * array( 9      *     'offline/notice',10      *     'param1'=>'value1',11      *     'param2'=>'value2',12      * )13      * </pre>14      * Defaults to null, meaning catch-all is not effective.15 */16     public $catchAllRequest;17 18 /**19      * Processes the current request.20      * It first resolves the request into controller and action,21      * and then creates the controller to perform the action.22 */23     public function processRequest()24     {25         if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0]))26         {27             $route=$this->catchAllRequest[0];28             foreach(array_splice($this->catchAllRequest,1) as $name=>$value)29                 $_GET[$name]=$value;30         }31         else32             $route=$this->getUrlManager()->parseUrl($this->getRequest());33         $this->runController($route);34     }

这里我特地连注释也贴过来了,在整个processRequest函数体中主要做的是根据route规则,找到最基本的(原始的)route。这里有点拗口,其实很简单,比如,我们定义了article/read/1这个是我们设置的route规则,article/read?id=1就是我们的原始route。这里可能会有人说,这个不是原始的。这里我只是想说,这里是一种将yii的controller不懂的规则,转换成了controller能明白的规则。

接下来我们逐段来讲解。首先,先说说catchAllRequest,这个主要是用于维护模式的,你使用一个controller去处理所有的请求。我们看25到30行,也可以看出来,当catchAllRequest设置了的话,直接就route就是catchAllRequest中的route。并且将在catchAllRequest中设置的值,附做了GET参数。

有了上面的分析,我们知道,我们的分析重点是第32行!这行程序有很很好的语义性,我们一看就知道是解析Url的。那么他到底是怎么具体实施的呢?首先让我们进入getUrlManager一探究究。

    //CApplication    /**     * Returns the URL manager component.     * @return CUrlManager the URL manager component*/public function getUrlManager()    {return $this->getComponent('urlManager');    }

上述代码出现在CWebApplication的基类CApplication中,我们发现其就是获取UrlManager Component。
所以,整个Route的功能其实是由UrlManager实现的。同时Yii强大的扩展性又允许我们可以自己扩展。所以,好好研究这个UrlManager对我们制作自己的Router非常的重要。接下来我们看看,Yii中默认的UrlManager的代码。CUrlManager.php文件还是蛮长的,这里就不贴整个代码了。我逐快分析的。我们这里贴下,类视图

我们发现CUrlManager是继承自CApplicationComponent的,他最关键的一个特点就是继承CComponent,实现IApplicationComponent。

CComponent的特点和行为,我们在Yii中的核心CComponent类详解中已经详细说明,这里就不多说了。关于IApplicationComponent,也很简单,我们看一下他的代码就知道了

 1 interface IApplicationComponent 2 { 3     /** 4      * Initializes the application component. 5      * This method is invoked after the application completes configuration. 6 */ 7     public function init(); 8     /** 9      * @return boolean whether the {@link init()} method has been invoked.10 */11     public function getIsInitialized();12 }

就只有两个方法,一个是初始化,另一个是用来判断有没有初始化的。因为CUrlManager是一个CoreComponent所以需要用到该接口,因为在其注册过程中,需要调用到这两个函数。具体的请参见Yii Framework的process flow分析。

接下来,我们继续看CUrlManager,这里我们不深入到具体的代码中,除了个别重要的会深入分析,其他的我们只是从其作用和角色上进行分析。抓住大局,整体把握。对于任何一样东西,我们实现应该知道它的形成,所以这里,我们要先研究下CUrlManager的初始化,上代码

 1     /** 2      * Initializes the application component. 3 */ 4     public function init() 5     { 6         parent::init(); 7         $this->processRules(); 8     } 9 10     /**11      * Processes the URL rules.12 */13     protected function processRules()14     {15         if(empty($this->rules) || $this->getUrlFormat()===self::GET_FORMAT)16             return;17         if($this->cacheID!==false && ($cache=Yii::app()->getComponent($this->cacheID))!==null)18         {19             $hash=md5(serialize($this->rules));20             if(($data=$cache->get(self::CACHE_KEY))!==false && isset($data[1]) && $data[1]===$hash)21             {22                 $this->_rules=$data[0];23                 return;24             }25         }26         foreach($this->rules as $pattern=>$route)27             $this->_rules[]=$this->createUrlRule($route,$pattern);28         if(isset($cache))29             $cache->set(self::CACHE_KEY,array($this->_rules,$hash));30     }

这里,我们看init(),当CUrlManager被加载时,会使用init进行初始化。这个我们要关注的是processRules方法,

该方法的逻辑比较简单,分别判断了有没有rule,设置缓存了吗,以及最重要的,根据设置的情况决定是使用内联的route还是自定义的UrlRule类。这里有些抽象,你一看代码便知道了,

1     protected function createUrlRule($route,$pattern)2     {3         if(is_array($route) && isset($route['class']))4             return $route;5         else6             return new $this->urlRuleClass($route,$pattern);7     }

注意第3行,便是当$route中中设置了其他的类的时候,就使用那个类作为route,那个类也是继承CUrlRule的。否则,就在这里自己组建一个,根据传入的$route和$pattern。Ok,言归正传,我们关键要知道,init()方法其实就是给CUrlManager的实例中的$_rules赋值,其是一个数组,里面的元素都是CUrlRule的实例。这里CUrlRule就不多解释了,你就认为是一个含有Url变换规则的类,里面含有一系列正向变化和反向变化的方法。$this->urlRuleClass存的内容是“CUrlRule”,因此我们也可以改变此值,来使用我们扩展的CUrlRule类。我们在这里的关注点还是在CUrlManager上。我们知道了CUrlManager的初始化的内容,让我们现在回到文章一开始说的地方,来看下这句代码$route=$this->getUrlManager()->parseUrl($this->getRequest());在getUrlManager部分,我们就已经知道他是返回一个CUrlManager的实例,那么我们就继续看看这个parseUrl是做了什么,这个方法还是在CUrlManager中的。

 1     /** 2      * Parses the user request. 3      * @param CHttpRequest $request the request application component 4      * @return string the route (controllerID/actionID) and perhaps GET parameters in path format. 5 */ 6     public function parseUrl($request) 7     { 8         if($this->getUrlFormat()===self::PATH_FORMAT) 9         {10             $rawPathInfo=$request->getPathInfo();11             $pathInfo=$this->removeUrlSuffix($rawPathInfo,$this->urlSuffix);12             foreach($this->_rules as $i=>$rule)13             {14                 if(is_array($rule))15                     $this->_rules[$i]=$rule=Yii::createComponent($rule);16                 if(($r=$rule->parseUrl($this,$request,$pathInfo,$rawPathInfo))!==false)17                     return isset($_GET[$this->routeVar]) ? $_GET[$this->routeVar] : $r;18             }19             if($this->useStrictParsing)20                 throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".',21                     array('{route}'=>$pathInfo)));22             else23                 return $pathInfo;24         }25         else if(isset($_GET[$this->routeVar]))26             return $_GET[$this->routeVar];27         else if(isset($_POST[$this->routeVar]))28             return $_POST[$this->routeVar];29         else30             return '';31     }

此代码,判断了当前使用的Url模式,如果是path模式的话,则会遍历设置的rule,并且初始化CUrlRule实例(15行),在这里顺便提下CUrlRule其最终的基类是CComponent所以,可以使用这个CreateComponent方法。这里主要是返回相应的route信息(10-18行), 如果没有找到相应的rule可以匹配出route的,就直接返回pathinfo。接下来的代码就是针对对普通的情况这里的$this->routeVar的初始值是“r”,是不是很熟悉?r=site/index,想起来了吗?

总而言之,这个parseUrl返回的是Yii的Controller可以识别的route信息。

现在我们明白了他的大致的流程,我们现在要看看他是具体如何实现path类型的route解析的。注意上段代码的第16行,这个parseUrl是CUrlRule中的方法,接下来,让我们看看对应的代码。

 1 /** 2      * Parses a URL based on this rule. 3      * @param CUrlManager $manager the URL manager 4      * @param CHttpRequest $request the request object 5      * @param string $pathInfo path info part of the URL 6      * @param string $rawPathInfo path info that contains the potential URL suffix 7      * @return mixed the route that consists of the controller ID and action ID or false on error 8 */ 9     public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)10     {11         if($this->verb!==null && !in_array($request->getRequestType(), $this->verb, true))12             return false;13 14         if($manager->caseSensitive && $this->caseSensitive===null || $this->caseSensitive)15             $case='';16         else17             $case='i';18 19         if($this->urlSuffix!==null)20             $pathInfo=$manager->removeUrlSuffix($rawPathInfo,$this->urlSuffix);21 22         // URL suffix required, but not found in the requested URL23         if($manager->useStrictParsing && $pathInfo===$rawPathInfo)24         {25             $urlSuffix=$this->urlSuffix===null ? $manager->urlSuffix : $this->urlSuffix;26             if($urlSuffix!='' && $urlSuffix!=='/')27                 return false;28         }29 30         if($this->hasHostInfo)31             $pathInfo=strtolower($request->getHostInfo()).rtrim('/'.$pathInfo,'/');32 33         $pathInfo.='/';34 35         if(preg_match($this->pattern.$case,$pathInfo,$matches))36         {37             foreach($this->defaultParams as $name=>$value)38             {39                 if(!isset($_GET[$name]))40                     $_REQUEST[$name]=$_GET[$name]=$value;41             }42             $tr=array();43             foreach($matches as $key=>$value)44             {45                 if(isset($this->references[$key]))46                     $tr[$this->references[$key]]=$value;47                 else if(isset($this->params[$key]))48                     $_REQUEST[$key]=$_GET[$key]=$value;49             }50             if($pathInfo!==$matches[0]) // there're additional GET params51                 $manager->parsePathInfo(ltrim(substr($pathInfo,strlen($matches[0])),'/'));52             if($this->routePattern!==null)53                 return strtr($this->route,$tr);54             else55                 return $this->route;56         }57         else58             return false;59     }

第11行表示,如果该CUrlRule自身定义的verb(GET,POST)不为空,并且这个verb又不是request的verb,那么这个时候就返回false。

第14行表示,是否大小写敏感,并给标志位赋值。

第19行表示,去除指定的url后缀。

最关键的是35-56行,这里利用了正则表达式,进行了判断。并且当值缺失的时候,赋予默认值。最终返回了route,第53行。

支持我们分析了CUrlManager,最关键的地方还是在CUrlRule的parseUrl方法,也就上述的35-56行。

因此我们要注意,要改变一个route,主要是两个类一个是CUrlManager,另一个是CUrlRule。前者是大局控制的,主要是控制流程还有例外处理等等的。后者才是真正控制规则解析的。

转载于:https://www.cnblogs.com/JosephLiu/archive/2011/12/26/2301771.html

Yii的路由机制分析相关推荐

  1. Linux x86_64 APIC中断路由机制分析

    不同CPU体系间的中断控制器工作原理有较大差异,本文是<Linux mips64r2 PCI中断路由机制分析>的姊妹篇,主要分析Broadwell-DE X86_64 APIC中断路由原理 ...

  2. libpcap捕包机制分析(三)

    目前,在linux操作系统下的网络数据包捕获系统普遍是建立在libpcap捕包平台上的,libpcap的英文意思是Library of Packet Capture,即数据包捕获函数库.该库提供的C函 ...

  3. C#WebApi路由机制详解

    随着前后端分离的大热,WebApi在项目中的作用也是越来越重要,可单独部署.与前端和App交互都很方便,既然有良好的发展趋势,我们当然应该顺势而为--搞懂WebApi!Restful相当于给Http请 ...

  4. VBS 请求WebAPI接口_C#进阶系列——WebApi 路由机制剖析:你准备好了吗?

    正文 前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分. 它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home/Index,那么你只需要配置一个默认路由就能简单搞 ...

  5. WebApi 路由机制剖析

    阅读目录 一.MVC和WebApi路由机制比较 1.MVC里面的路由 2.WebApi里面的路由 二.WebApi路由基础 1.默认路由 2.自定义路由 3.路由原理 三.WebApi路由过程 1.根 ...

  6. WebApi路由机制详解

    随着前后端分离的大热,WebApi在项目中的作用也是越来越重要,由于公司的原因我之前一直没有机会参与前后端分离的项目,但WebApi还是要学的呀,因为这东西确实很有用,可单独部署.与前端和App交互都 ...

  7. php+yii框架,yii框架源码分析(一)

    yii框架源码分析(一) 本文将对yii中的mvc,路由器,filter,组件机制等最主要的部分进行自己的一点浅析,力求说明自己做一个php mvc不是那么的遥不可及,其实是很简单的. 源码基于yii ...

  8. WebApi路由机制详解——看完不会用你打我

    随着前后端分离的大热,WebApi在项目中的作用也是越来越重要,由于公司的原因我之前一直没有机会参与前后端分离的项目,但WebApi还是要学的呀,因为这东西确实很有用,可单独部署.与前端和App交互都 ...

  9. Google Test(GTest)使用方法和源码解析——结果统计机制分析

    在分析源码之前,我们先看一个例子.以<Google Test(GTest)使用方法和源码解析--概况 >一文中最后一个实例代码为基准,修改最后一个"局部测试"结果为错误 ...

最新文章

  1. 使用原型相对于直接在构造函数中定义方法的优势? [重复]
  2. 服务器系统安装iis7.0,Windows2003服务器架IIS7.0怎么安装
  3. Dev C++ 运行后显示Failed to execute 文件夹位置: Error 0: 操作成功完成。(只需一步)
  4. TypeScript入门-接口
  5. table td的宽度详解
  6. C#实现网页加载后将页面截取成长图片 | Playwright版
  7. 【李宏毅机器学习】Recurrent Neural Network Part1 循环神经网络(p20) 学习笔记
  8. 一则故事表达:并发,并行,同步,异步,线程,多线程
  9. kalilinux装到u盘上的弊端_你有一个 U 盘制作多系统安装盘的需求吗,YUMI 帮你秒实现!...
  10. java hssfworkbook 乱码_java各种乱码汇总
  11. 用JavaScript制作一个贷款计算器(注:附带详细注释)
  12. [转]WinRAR破解方法
  13. 数据结构:按成绩输出名次排序
  14. Javascript 将 jpeg、png转换为webp
  15. SQL 语句中 where 条件后 写上 1=1 是什么意思!
  16. Silicon EFR32BG22低功耗蓝牙开发入门篇
  17. 基于Docker部署SRS流媒体服务
  18. python基础知识7——元组
  19. MySQL三表查询(学生表、课程表、成绩表)查询出语文成绩比数学成绩高的学生信息
  20. ionic 指定蒙文字体

热门文章

  1. Linux入门笔记——less
  2. LeetCode 404. 左叶子之和思考分析
  3. 什么是alpha测试_什么是ALPHA?
  4. linux测试固态硬盘读写速度,在 Linux 上检测 IDE/SATA SSD 硬盘的传输速度
  5. librtmp分析(接收数据包处理)
  6. 分析FLV文件分析和解析器的开源代码
  7. 对一个简单汇编程序分析
  8. UVA 10895——Matrix Transpose
  9. Effective C++学习第八天
  10. Linux网络编程——tcp并发服务器(epoll实现)