Yii的路由机制分析
在Yii中,route是一個非常重要的步驟。通過route我們可以定制更加個性互的Url。同時很多時候,如果route規(guī)則復(fù)雜也會容易出問題。所以,研究清楚route的機(jī)制是十分重要的。在這里希望你能先參看Yii Framework的process flow分析
首先,讓我們先看一段代碼,此段代碼出現(xiàn)在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 else
32 $route=$this->getUrlManager()->parseUrl($this->getRequest());
33 $this->runController($route);
34 }
這里我特地連注釋也貼過來了,在整個processRequest函數(shù)體中主要做的是根據(jù)route規(guī)則,找到最基本的(原始的)route。這里有點拗口,其實很簡單,比如,我們定義了article/read/1這個是我們設(shè)置的route規(guī)則,article/read?id=1就是我們的原始route。這里可能會有人說,這個不是原始的。這里我只是想說,這里是一種將yii的controller不懂的規(guī)則,轉(zhuǎn)換成了controller能明白的規(guī)則。
接下來我們逐段來講解。首先,先說說catchAllRequest,這個主要是用于維護(hù)模式的,你使用一個controller去處理所有的請求。我們看25到30行,也可以看出來,當(dāng)catchAllRequest設(shè)置了的話,直接就route就是catchAllRequest中的route。并且將在catchAllRequest中設(shè)置的值,附做了GET參數(shù)。
有了上面的分析,我們知道,我們的分析重點是第32行!這行程序有很很好的語義性,我們一看就知道是解析Url的。那么他到底是怎么具體實施的呢?首先讓我們進(jìn)入getUrlManager一探究究。
//CApplication/**
* Returns the URL manager component.
* @return CUrlManager the URL manager component
*/
public function getUrlManager()
{
return $this->getComponent('urlManager');
}
上述代碼出現(xiàn)在CWebApplication的基類CApplication中,我們發(fā)現(xiàn)其就是獲取UrlManager Component。
所以,整個Route的功能其實是由UrlManager實現(xiàn)的。同時Yii強(qiáng)大的擴(kuò)展性又允許我們可以自己擴(kuò)展。所以,好好研究這個UrlManager對我們制作自己的Router非常的重要。接下來我們看看,Yii中默認(rèn)的UrlManager的代碼。CUrlManager.php文件還是蠻長的,這里就不貼整個代碼了。我逐快分析的。我們這里貼下,類視圖
?
我們發(fā)現(xiàn)CUrlManager是繼承自CApplicationComponent的,他最關(guān)鍵的一個特點就是繼承CComponent,實現(xiàn)IApplicationComponent。
CComponent的特點和行為,我們在Yii中的核心CComponent類詳解中已經(jīng)詳細(xì)說明,這里就不多說了。關(guān)于IApplicationComponent,也很簡單,我們看一下他的代碼就知道了
1 interface IApplicationComponent2 {
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所以需要用到該接口,因為在其注冊過程中,需要調(diào)用到這兩個函數(shù)。具體的請參見Yii Framework的process flow分析。
接下來,我們繼續(xù)看CUrlManager,這里我們不深入到具體的代碼中,除了個別重要的會深入分析,其他的我們只是從其作用和角色上進(jìn)行分析。抓住大局,整體把握。對于任何一樣?xùn)|西,我們實現(xiàn)應(yīng)該知道它的形成,所以這里,我們要先研究下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(),當(dāng)CUrlManager被加載時,會使用init進(jìn)行初始化。這個我們要關(guān)注的是processRules方法,
該方法的邏輯比較簡單,分別判斷了有沒有rule,設(shè)置緩存了嗎,以及最重要的,根據(jù)設(shè)置的情況決定是使用內(nèi)聯(lián)的route還是自定義的UrlRule類。這里有些抽象,你一看代碼便知道了,
1 protected function createUrlRule($route,$pattern)2 {
3 if(is_array($route) && isset($route['class']))
4 return $route;
5 else
6 return new $this->urlRuleClass($route,$pattern);
7 }
注意第3行,便是當(dāng)$route中中設(shè)置了其他的類的時候,就使用那個類作為route,那個類也是繼承CUrlRule的。否則,就在這里自己組建一個,根據(jù)傳入的$route和$pattern。Ok,言歸正傳,我們關(guān)鍵要知道,init()方法其實就是給CUrlManager的實例中的$_rules賦值,其是一個數(shù)組,里面的元素都是CUrlRule的實例。這里CUrlRule就不多解釋了,你就認(rèn)為是一個含有Url變換規(guī)則的類,里面含有一系列正向變化和反向變化的方法。$this->urlRuleClass存的內(nèi)容是“CUrlRule”,因此我們也可以改變此值,來使用我們擴(kuò)展的CUrlRule類。我們在這里的關(guān)注點還是在CUrlManager上。我們知道了CUrlManager的初始化的內(nèi)容,讓我們現(xiàn)在回到文章一開始說的地方,來看下這句代碼$route=$this->getUrlManager()->parseUrl($this->getRequest());在getUrlManager部分,我們就已經(jīng)知道他是返回一個CUrlManager的實例,那么我們就繼續(xù)看看這個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 else
23 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 else
30 return '';
31 }
此代碼,判斷了當(dāng)前使用的Url模式,如果是path模式的話,則會遍歷設(shè)置的rule,并且初始化CUrlRule實例(15行),在這里順便提下CUrlRule其最終的基類是CComponent所以,可以使用這個CreateComponent方法。這里主要是返回相應(yīng)的route信息(10-18行), 如果沒有找到相應(yīng)的rule可以匹配出route的,就直接返回pathinfo。接下來的代碼就是針對對普通的情況這里的$this->routeVar的初始值是“r”,是不是很熟悉?r=site/index,想起來了嗎?
總而言之,這個parseUrl返回的是Yii的Controller可以識別的route信息。
現(xiàn)在我們明白了他的大致的流程,我們現(xiàn)在要看看他是具體如何實現(xiàn)path類型的route解析的。注意上段代碼的第16行,這個parseUrl是CUrlRule中的方法,接下來,讓我們看看對應(yīng)的代碼。
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 else
17 $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 URL
23 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 params
51 $manager->parsePathInfo(ltrim(substr($pathInfo,strlen($matches[0])),'/'));
52 if($this->routePattern!==null)
53 return strtr($this->route,$tr);
54 else
55 return $this->route;
56 }
57 else
58 return false;
59 }
第11行表示,如果該CUrlRule自身定義的verb(GET,POST)不為空,并且這個verb又不是request的verb,那么這個時候就返回false。
第14行表示,是否大小寫敏感,并給標(biāo)志位賦值。
第19行表示,去除指定的url后綴。
最關(guān)鍵的是35-56行,這里利用了正則表達(dá)式,進(jìn)行了判斷。并且當(dāng)值缺失的時候,賦予默認(rèn)值。最終返回了route,第53行。
支持我們分析了CUrlManager,最關(guān)鍵的地方還是在CUrlRule的parseUrl方法,也就上述的35-56行。
因此我們要注意,要改變一個route,主要是兩個類一個是CUrlManager,另一個是CUrlRule。前者是大局控制的,主要是控制流程還有例外處理等等的。后者才是真正控制規(guī)則解析的。
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/JosephLiu/archive/2011/12/26/2301771.html
總結(jié)
以上是生活随笔為你收集整理的Yii的路由机制分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android用Intent和Bundl
- 下一篇: 【托管服务qin】WEB网站压力测试教程