YII的runController執行過程分析

Yii應用的入口腳本最後一句啓動了WebApplication
Yii::createWebApplication($config)->run();
CApplication:php

view sourceprint?web

1.public function run()數組

2.{app

3.$this->onBeginRequest(new CEvent($this));框架

4.$this->processRequest();yii

5.$this->onEndRequest(new CEvent($this));函數

6.}ui

processRequest()開始處理請求,由CWebApplication實現:this

view sourceprint?url

01.public function processRequest()

02.{

03.if(is_array($this->catchAllRequest) && isset($this->catchAllRequest[0]))

04.{

05.$route=$this->catchAllRequest[0];

06.foreach(array_splice($this->catchAllRequest,1) as $name=>$value)

07.$_GET[$name]=$value;

08.}

09.else

10.$route=$this->getUrlManager()->parseUrl($this->getRequest());

11.$this->runController($route);

12.}

urlManager應用組件的parseUrl() 建立了$route (形式爲controllerID/actionID的字符串),runController()建立Controller對象開始處理http請求。
$route 的值可能存在如下幾種狀況:
- 爲空: 用 defaultController 值代替;
- 「moduleID/controllerID/actionID」: module下的
- 「controllerID/actionID」 : 最多見的形式
- 「folder1/folder2/controllerID/actionID」 多級目錄下的控制器
runController首先調用createController()建立控制器對象

view sourceprint?

01.public function runController($route

02.

03.//根據route建立Controller對象數組 

04.if(($ca=$this->createController($route))!==null) 

05.

06.//包含controller對象和actionID 

07.list($controller,$actionID)=$ca

08.//TODO::這裏是幹什麼用的 

09.$oldController=$this->_controller; 

10.$this->_controller=$controller

11.//調用controller對象的初始化方法 

12.$controller->init(); 

13.//使用actionID運行這個Controller 

14.$controller->run($actionID); 

15.$this->_controller=$oldController

16.

17.Else 

18.//若是沒有找到對應的Controller,跳轉到404頁面 

19.throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".'

20.array('{route}'=>$route===''?$this->defaultController:$route))); 

21.}

其實真正的核心處理是在createController,對於createController,咱們着重須要瞭解的是下面的這段註釋:

view sourceprint?

01./**

02.* ……

03.* 這個方法如下面的順序建立一個控制器

04.* 1. 若是第一個字段在controllerMap(初始配置)中,則使用對應的控制器配置來建立控制器

05.* 2.若是第一個字段是一個模塊(module)ID,則使用相應的模塊來建立控制器

06.* 3.若是經過上面兩項均沒法建立控制器,將會搜索controllerPath(根目錄對應的controller文件夾)來建立對應的控制器。 

07.* ……

08.*/ 

09.public function createController($route,$owner=null) 

10.{ 

11.// $owner爲空則設置爲$this,即 $_app對象

12.if($owner===null)

13.$owner=$this;

14.// $route爲空設置爲defaultController,在$config裏配置

15.if(($route=trim($route,'/'))==='')

16.$route=$owner->defaultController;

17.$caseSensitive=$this->getUrlManager()->caseSensitive;

18. 

19.$route.='/';

20.// 逐一取出 $route 按 ‘/’分割後的第一段進行處理

21.while(($pos=strpos($route,'/'))!==false)

22.{

23.// $id 裏存放的是 $route 第一個 ‘/’前的部分

24.$id=substr($route,0,$pos);

25.if(!preg_match('/^\w+$/',$id))

26.return null;

27.if(!$caseSensitive)

28.$id=strtolower($id);

29.// $route 存放’/’後面部分

30.$route=(string)substr($route,$pos+1);

31.if(!isset($basePath)) // 完整$route的第一段

32.{

33.// 若是$id在controllerMap[]裏作了映射

34.// 直接根據$id建立controller對象

35.if(isset($owner->controllerMap[$id]))

36.{

37.return array(

38.Yii::createComponent($owner->controllerMap[$id],$id,$owner===$this?null:$owner),

39.$this->parseActionParams($route),

40.);

41.}

42. 

43.// $id 是系統已定義的 module,根據$id取得module對象做爲$owner參數來createController

44.if(($module=$owner->getModule($id))!==null)

45.return $this->createController($route,$module);

46.// 控制器所在的目錄

47.$basePath=$owner->getControllerPath();

48.$controllerID='';

49.}

50.else

51.$controllerID.='/';

52.$className=ucfirst($id).'Controller';

53.$classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php';

54.// 控制器類文件存在,則require並建立控制器對象&返回

55.if(is_file($classFile))

56.{

57.if(!class_exists($className,false))

58.require($classFile);

59.if(class_exists($className,false) &&is_subclass_of($className,'CController'))

60.{

61.$id[0]=strtolower($id[0]);

62.return array(

63.new $className($controllerID.$id,$owner===$this?null:$owner),

64.$this->parseActionParams($route),

65.);

66.}

67.return null;

68.}

69.// 未找到控制器類文件,多是多級目錄,繼續往子目錄搜索

70.$controllerID.=$id;

71.$basePath.=DIRECTORY_SEPARATOR.$id;

72.}

73.} 

也就是說,對於一個aaaa/bbbb/cccc的路由,yii首先從config/main.php中定義的controllerMap去尋找是否有名爲aaaa的controller,若是有,那麼就已aaaa爲controller進行建立,不然再去尋找是否有名爲aaaa的模塊,若是有,那麼就使用aaaa模塊的名爲bbbb的controller進行建立,不然在protected/controllers下尋找是否有名爲aaaa的controller。
createController() 返回一個建立好的控制器對象和actionID, runController()調用控制器的init()方法和run($actionID)來運行控制器:
$controller->init()裏沒有動做,所以咱們能夠在本身的控制器中重寫這個方法來實現初始化的時候處理數據。
run()方法:

view sourceprint?

01.public function run($actionID)

02.{

03.if(($action=$this->createAction($actionID))!==null)

04.{

05.if(($parent=$this->getModule())===null)

06.$parent=Yii::app();

07.if($parent->beforeControllerAction($this,$action))

08.{

09.$this->runActionWithFilters($action,$this->filters());

10.$parent->afterControllerAction($this,$action);

11.}

12.}

13.else

14.$this->missingAction($actionID);

15.}

$controller->run($actionID)裏首先建立了Action對象:

view sourceprint?

01.public function createAction($actionID)

02.{

03.// 爲空設置爲defaultAction

04.if($actionID==='')

05.$actionID=$this->defaultAction;

06.// 控制器裏存在 'action'.$actionID 的方法,建立CInlineAction對象

07.if(method_exists($this,'action'.$actionID) && strcasecmp($actionID,'s')) // we have actions method

08.return new CInlineAction($this,$actionID);

09.// 不然根據actions映射來建立Action對象(就是咱們本身建立的繼承自CAtion的對象)

10.else

11.return $this->createActionFromMap($this->actions(),$actionID,$actionID);

12.}

這裏能夠看到控制器並非直接調用了action方法,而是須要一個Action對象來運行控制器動做,這樣就統一了控制器方法和actions映射的action對象對action的處理,即兩種形式的action處理都統一爲IAction接口的run()調用。
IAction接口要求實現run(),getId(),getController () 三個方法,Yii提供的CAction類要求構造函數提供Controller和Id並實現了getId()和getController ()的處理,Action類從CAction繼承便可。
這裏其實能夠分爲兩種action,上面的註釋也寫了,第一種就是咱們在控制器中定義了相關的方法的好比:actionIndex這種,這種的話系統是調用CInlineAction對象來處理的。還有一種呢就是咱們沒有定義
實際的方法的而是定在actions方法中的,也就是咱們自定義了一個類的那種,這種的話就是直接調用咱們的這個class來處理了。無論是哪種,都必需要實現一個run方法,由於這個方法是來具體實現
業務的,如系統的run方法就調用了咱們控制器的方法。其實在CInlineAction類中還有個方法就是runWithParams方法,這個方法實際上是重寫了CAtion類中的一個方法,其實框架默認調用的就是這個方法
而不是run方法,這個方法會判斷run方法的參數狀況而後再來處理,可是無論參數狀況如何都會調用這個run方法,所以咱們在咱們自定義的ACTION類中也能夠實現這個runWithParams方法的。
CInlineAction在web/action下,run()是很簡單的處理過程,調用了Controller的action方法:

view sourceprint?

01.class CInlineAction extends CAction

02.{

03.public function run()

04.{

05.$method='action'.$this->getId();

06.$this->getController()->$method();

07.}

08.public function runWithParams($params)

09.{

10.$methodName='action'.$this->getId();

11.$controller=$this->getController();

12.$method=new ReflectionMethod($controller$methodName);

13.//若是run方法有參數的話,那麼就用反射來處理,最後調用run方法

14.if($method->getNumberOfParameters()>0)

15.return $this->runWithParamsInternal($controller$method$params);

16.//直接調用咱們action

17.else

18.return $controller->$methodName();

19.}

20.}

這是CAtion中的runWithParams方法,上面的CInlineAction重寫CAtion中的此方法

view sourceprint?

01.public function runWithParams($params)

02.{

03.$method=new ReflectionMethod($this'run');

04.//若是run方法有參數的話,那麼就用反射來處理,最後調用run方法

05.if($method->getNumberOfParameters()>0)

06.return $this->runWithParamsInternal($this$method$params);

07.//直接調用CInlineAction的run方法

08.else

09.return $this->run();

10.}

回到 $controller->run($actionID)

view sourceprint?

01.public function run($actionID)

02.{

03.if(($action=$this->createAction($actionID))!==null)

04.{

05.if(($parent=$this->getModule())===null)

06.$parent=Yii::app();

07.if($parent->beforeControllerAction($this,$action))

08.{

09.$this->runActionWithFilters($action,$this->filters());

10.$parent->afterControllerAction($this,$action);

11.}

12.}

13.else

14.$this->missingAction($actionID);

Yii::app()->beforeControllerAction() 實際是固定返回true的,因此action對象實際是經過控制器的runActionWithFilters()被run的

view sourceprint?

01.public function runActionWithFilters($action,$filters)

02.{

03.// 控制器裏沒有設置過濾器

04.if(empty($filters))

05.$this->runAction($action);

06.// 控制器裏設置過濾器

07.else

08.{

09.$priorAction=$this->_action;

10.$this->_action=$action;

11.// 建立過濾器鏈對象並運行,其實這一步的執行很簡單,就是按照過濾規則先過濾一下,而後再來執行runAction($action)方法

12.CFilterChain::create($this,$action,$filters)->run();

13.$this->_action=$priorAction;

14.}

15.}

沒有過濾器,runAction()就是最終要調用前面建立的action對象的runWithParams(),而後在這個方法中決定是訪問run仍是$methodName仍是其餘,由於這個方法能夠被用戶重寫。

view sourceprint?

01.public function runAction($action)

02.{

03.$priorAction=$this->_action;

04.$this->_action=$action;

05.if($this->beforeAction($action))

06.{

07.if($action->runWithParams($this->getActionParams())===false)

08.$this->invalidActionParams($action);

09.else

10.$this->afterAction($action);

11.}

12.$this->_action=$priorAction;

13.}

在runWithParams()中調用了咱們真正的方法:

view sourceprint?

01.public function runWithParams($params)

02.{

03.$methodName='action'.$this->getId();

04.$controller=$this->getController();

05.$method=new ReflectionMethod($controller$methodName);

06.if($method->getNumberOfParameters()>0)

07.return $this->runWithParamsInternal($controller$method,$params);

08.else

09.return $controller->$methodName();

10.}

其實在CController類中的run方法並無加上final關鍵字,意味着咱們這控制器中能夠重寫他,而後來實現咱們本身從控制器到方法的這麼一段流程,固然YII已經實現的很perfect了。

這樣子,而後聯繫咱們上一篇講的,咱們的程序就已經講解到了咱們本身的控制器了,下一篇將分析執行控制器過程當中的過濾和視圖這一塊的功能。

相關文章
相關標籤/搜索