Yii2.0的ActionFilter类详细解读

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/

你可能感兴趣的:(PHP,Yii)