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了。

这样子,而后联系咱们上一篇讲的,咱们的程序就已经讲解到了咱们本身的控制器了,下一篇将分析执行控制器过程当中的过滤和视图这一块的功能。

相关文章
相关标签/搜索