1.涉及的属性和方法:
yii\base\ActionFilter中
public function attach(){
把beforeFileter绑定到Controller的EVENT_BEFORE_ACTION事件上
}
public function detach(){
1.将beforeFileter从Controller的EVENT_BEFORE_ACTION事件上解绑
2.将afterFilter从Controller的EVENT_AFTER_ACTION事件上解绑
}
public function beforeFilter(){
调用self的beforeAction方法,
返回真则把afterFilter绑定到Controller的EVENT_AFTER_ACTION事件上
返回假。。。。?
}
public function afterFilter(){
调用self的afterAction方法
将afterFilter从Controller的EVENT_AFTER_ACTION事件上解绑
}
public function beforeAction(){
在子类中可以重写并自定义处理,默认返回true
}
public function afterAction(){
在子类中可以重写并自定义处理,默认返回true
}
2.使用方式
我们的研究对象是ActionFilter和Controller,这一点要特别注意ActionFilter在controller的behaviors()方法中添加
例如:
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors[
'access' => [
'class' => AccessComponent::className()//没有use的话需要写类的全名,即包括命名空间的类名,AccessComponent是ActionFilter的子类
]
];
return $behaviors;
}
3.运行机制解析
A.首先我们需要了解一些yii2.0中关于行为的知识,需要深入了解的请移步:http://www.digpage.com/behavior.html
1).yii/base/Component中加入了对行为(behaviors)的支持
Component的ensureBehaviors()方法会获取behaviors()方法返回的数组【注1】,
将数组中配置的行为类通过Yii::createObject创建出对象【注2】,
并调用行为的attach方法(一般是注册一个事件)【注3】,
然后将行为对象放到_behaviors数组中【注4】
下面两段代码是yii/base/Component的源码
public function ensureBehaviors()
{
if ($this->_behaviors === null) {
$this->_behaviors = [];
foreach ($this->behaviors() as $name =>$behavior) { //【注1】
$this->attachBehaviorInternal($name, $behavior);
}
}
}
private function attachBehaviorInternal($name, $behavior)
{
if (!($behavior instanceof Behavior)) {
$behavior = Yii::createObject($behavior);//创建行为对象【注2】
}
if (is_int($name)) {//通过判断是否是索引来判断是否是匿名行为
$behavior->attach($this);//绑定事件【注3】
$this->_behaviors[] = $behavior;【注4】
} else {
if (isset($this->_behaviors[$name])) {//如果该行为已存在,则注销此行为
$this->_behaviors[$name]->detach();
}
$behavior->attach($this);【注3】
$this->_behaviors[$name] = $behavior;【注4】
}
return $behavior;
}
2).
在component的__set,__get,__isset,__unset,__call,on,off,trigger,attachBehavior等方法中,都在调用了ensureBehaviors()方法。也就是说在你调用component对象中不存在的属性和方法,设置不存在的属性和方法,绑定事件,解绑事件,触发事件,绑定行为时,都会首先把behaviors()方法中声明的行为注册(即new 出对象,调用attach方法绑定事件,将行为对象储存到_behaviors属性中)。这么处理是Yii2.0懒加载,即需要用到的时候才会new
3).
还有一个事实是,yii2.0中的组件基本都继承自Component类,其中我们用到的Controller正是继承自Component
B.关于事件的知识
对于Yii2.0的事件,同一个事件可以绑定多个handler(即处理方法),当trigger这个事件的时候,handler会按绑定顺序执行。想要了解更多关于事件的知识,请移步:http://www.digpage.com/event.html
C.关于请求进入Yii2.0的生命周期(重点看如何进入特定Controller的Action)
1).入口脚本index.php
new yii\web\Application($config))->run();
2).run()方法,继承自yii\base\Application
其中有这么一段代码
$response = $this->handleRequest($this->getRequest());
3).handleRequest()方法,在子类yii\web\Application中重写
看这段代码
$result = $this->runAction($route, $params);
4).runAction方法,在yii\base\Module中
这里我们看到,它先new了Controller对象,然后调用controller的runAction方法来执行相应的Action
result = $controller->runAction($actionID, $params);
5).controller的runAction()方法,在yii\base\Controller中
$this->beforeAction($action)
6).beforeAction
$this->trigger(self::EVENT_BEFORE_ACTION, $event);
yii\base\Component中
public function trigger($name, Event $event = null)
{
$this->ensureBehaviors();//注册行为
if (!empty($this->_events[$name])) {//调用事件handler
if ($event === null) {
$event = new Event;
}
if ($event->sender === null) {
$event->sender = $this;
}
$event->handled = false;
$event->name = $name;
foreach ($this->_events[$name] as $handler) {
$event->data = $handler[1];
call_user_func($handler[0], $event);
// stop further handling if the event is handled
if ($event->handled) {
return;
}
}
}
// invoke class-level attached handlers
Event::trigger($this, $name, $event);
}
D.有了以上知识,我们大体可以梳理下ActionFilter的实现机制了
在C的6)之后,$this->trigger会先调用我们在A中提到ensureBehaviors()方法:
1)注册行为(并调用attach()方法,ActionFilter的attach方法是给Controller的before action事件绑定这个类的beforeFilter方法),
2)然后顺序调用对应事件_events数组中的handler(根据A的分析,我们可以判断出:多个ActionFilter的beforeFilter方法是顺序绑定到controller的before action事件中的)
4.总结
一个大体的思路就是
1)request进入index.php
2)new application并执行run
3)new Controller并执行runAction
4)runAction先执行beforeAction
5)beforeAction调用trigger方法,触发before action
6)trigger方法先注册行为,
对于ActionFilter行为来说,在注册过程中会按行为定义的顺序给controller的before action事件添加handler,其中每个handler就是对应的ActionFilter的beforeFilter方法,因为beforeFilter方法会调用它自己的beforeAction方法,可以认为handler就是每一个ActionFilter的beforeAction方法。
7)注册完后,顺序执行before action的handler
自此ActionFilter的功能告一段落,至于afterFilter,读者可以自行梳理,原理差不多是一致的
个人博客链接http://www.tanklh.cc/typecho/index.php/archives/4/