在此篇中,主要介绍Yii是如何看待Action的。那么在Controller中又是如何如何处理Action的,Action又有哪些需要注意的特性呢?
带着这些问题,我们来看看下面的代码,里面会涉及到Yii Framework的process flow分析文中提到的一些基础,如果没有看过,建议先阅读之。
首先先看段代码,此段代码位于CController中
1 public function createAction($actionID)
2 {
3 if($actionID==='')
4 $actionID=$this->defaultAction;
5 if(method_exists($this,'action'.$actionID) && strcasecmp($actionID,'s')) // we have actions method
6 return new CInlineAction($this,$actionID);
7 else
8 {
9 $action=$this->createActionFromMap($this->actions(),$actionID,$actionID);
10 if($action!==null && !method_exists($action,'run'))
11 throw new CException(Yii::t('yii', 'Action class {class} must implement the "run" method.', array('{class}'=>get_class($action))));
12 return $action;
13 }
14 }
这段代码像我们展示了Yii是如何看待Action的,本质上来说Yii把所有action都看成一个独立的类,这个时候,你可能会奇怪,我们明明可以在Controller中使用以action开头的方法定义Action的方法的,没有见到有类的感觉。那么,请注意此段代码的第6行,这个表示如果在controller中存在对应的以action打头的Action(针对actionID),那么就会使用这个action方法,构造出一个CInlineAction类。这里先说下,9-11行,这里是利用actions这个方法返回的array,生成指定的Action类的实例,同时从第10行可以看出,构造一个Action一定要重写其run方法,尽管有时候用不到,否则会出错。我们这里主要以CInlineAction为例进行说明。
为了更好的解释CInlineAction,我们来看看其和CAtion的代码。
1 class CInlineAction extends CAction
2 {
3 public function run()
4 {
5 $method='action'.$this->getId();
6 $this->getController()->$method();
7 }
8
9 public function runWithParams($params)
10 {
11 $methodName='action'.$this->getId();
12 $controller=$this->getController();
13 $method=new ReflectionMethod($controller, $methodName);
14 if($method->getNumberOfParameters()>0)
15 return $this->runWithParamsInternal($controller, $method, $params);
16 else
17 return $controller->$methodName();
18 }
19
20 }
先看看CInlineAction的代码,这里先重写了下run方法,正如我们说的是为了通过检查,主要是runWithParams。那么你一定奇怪,我们都了后面一个函数,为什么还要前者?这个是yii的历史遗留问题了。此处很简单就是写该如何执行action。接下来,是CAtion的代码
1 abstract class CAction extends CComponent implements IAction
2 {
3 private $_id;
4 private $_controller;
5
6 public function __construct($controller,$id)
7 {
8 $this->_controller=$controller;
9 $this->_id=$id;
10 }
11
12 public function getController()
13 {
14 return $this->_controller;
15 }
16
17 public function getId()
18 {
19 return $this->_id;
20 }
21
22 public function runWithParams($params)
23 {
24 $method=new ReflectionMethod($this, 'run');
25 if($method->getNumberOfParameters()>0)
26 return $this->runWithParamsInternal($this, $method, $params);
27 else
28 return $this->run();
29 }
30
31 protected function runWithParamsInternal($object, $method, $params)
32 {
33 $ps=array();
34 foreach($method->getParameters() as $i=>$param)
35 {
36 $name=$param->getName();
37 if(isset($params[$name]))
38 {
39 if($param->isArray())
40 $ps[]=is_array($params[$name]) ? $params[$name] : array($params[$name]);
41 else if(!is_array($params[$name]))
42 $ps[]=$params[$name];
43 else
44 return false;
45 }
46 else if($param->isDefaultValueAvailable())
47 $ps[]=$param->getDefaultValue();
48 else
49 return false;
50 }
51 $method->invokeArgs($object,$ps);
52 return true;
53 }
54 }
这段代码也没有什么可以多说的。主要是__construct方法和runWithParamsInternal。
首先看看__construct,要注意这里传入了controller的实例和actionID,这两点一定要清楚。前者说明了此action的所在的controller,后者用于拼贴成method,供调用。接下来看看runWithParamsInternal,该方法是$_GET转换为参数,传入带参数的Action中。具体执行部分为第51行。
然后再来看看CAction继承了哪些,分别是CComponent和IAction,IAction没有什么可以多说的。一看代码变知了。
interface IAction
{
/**
* @return string id of the action
*/
public function getId();
/**
* @return CController the controller instance
*/
public function getController();
}
关键是CAction还继承自CComponent,所以CAction会拥有CComponent的所有特性,比较典型的就是behavior了。对于CComponent届时会有专门的一章着重介绍。毕竟他可是很多的基础。
实际上Yii中的action看似神秘,其实原理还是很简单的。