yii2 controller behavior函数的beforeAction实现原理

来源:互联网 发布:手机网络加速器破解版 编辑:程序博客网 时间:2024/05/16 04:56

首先写一个例子,在siteController中覆盖behaviors函数

    public function behaviors()    {        return [            'access' => [                'class'=>AuthFilter::className(),                'only'=>['index'],              ],            'access11' => [                'class'=>AuthFilter::className(),                'only'=>['index'],            ],        ];    }

AuthFilter的定义

class AuthFilter extends ActionFilter{    public function beforeAction($action)    {        echo 'this is echo when before Action';        return parent::beforeAction($action); // TODO: Change the autogenerated stub    }    public function afterAction($action, $result)    {        echo 'this is echo when after action';        return parent::afterAction($action, $result); // TODO: Change the autogenerated stub    }}



我们知道controller的拦截器是在behavior中进行定义的,而controller的拦截器的函数必须定义beforeAction  和afterAction,这是为什么呢,因为这是当前Controller的祖父类定义的!!!

我的SiteController的父类是web的Controller,该类的父类是yii\base\controller,每次 一个请求的到来,都会执行祖父类Controller的runAction

 public function runAction($id, $params = [])    {        $action = $this->createAction($id);        if ($action === null) {            throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);        }        Yii::trace('Route to run: ' . $action->getUniqueId(), __METHOD__);        if (Yii::$app->requestedAction === null) {            Yii::$app->requestedAction = $action;        }        $oldAction = $this->action;        $this->action = $action;        $modules = [];        $runAction = true;        // call beforeAction on modules        foreach ($this->getModules() as $module) {            if ($module->beforeAction($action)) {    //这里执行了一次,调用点是yii\web\application                array_unshift($modules, $module);            } else {                $runAction = false;                break;            }        }        $result = null;        if ($runAction && $this->beforeAction($action)) {  //可以看到,执行beforeAction之后,并且返回true,之后参会进行下一步操作            // run the action            $result = $action->runWithParams($params);            $result = $this->afterAction($action, $result);            // call afterAction on modules            foreach ($modules as $module) {                /* @var $module Module */                $result = $module->afterAction($action, $result);            }        }        $this->action = $oldAction;        return $result;    }

祖父类执行了beforeAction,但是大家需要注意的是beforeAction执行了两次,一次是web\application执行beforeAction,这样是不会执行我们controller中的behavior中的beforeAction函数的,而当执行$this->beforeAction的时候其定义如下:

    public function beforeAction($action)    {        $event = new ActionEvent($action);        $this->trigger(self::EVENT_BEFORE_ACTION, $event);        return $event->isValid;    }
这样就触发了trigger函数,其函数定义如下:

  public function trigger($name, Event $event = null)    {        $this->ensureBehaviors();        if (!empty($this->_events[$name])) {            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);//此处进行beforeAction的调用                // stop further handling if the event is handled                if ($event->handled) {                    return;                }            }        }        // invoke class-level attached handlers        Event::trigger($this, $name, $event);    }
因为我们在$this 也就是siteController中定义了behaviors函数,在ensureBahaviors中就将behavior放在了component中的_behaviors数组当中,同时将对应的数据写在了_events当中,其$name 也是beforeAction,这样就会执行call_user_func函数,调用我们的接口

只是此时$name 是beforeAction,而 $handler是一个数组,第一个元素是定义类的对象,第二个是要执行的函数名称"beforeFilter",该函数的定义是在ActionFilter类中定义的

    /**     * @param ActionEvent $event     */    public function beforeFilter($event)    {        if (!$this->isActive($event->action)) {            return;        }        $event->isValid = $this->beforeAction($event->action);        if ($event->isValid) {            // call afterFilter only if beforeFilter succeeds            // beforeFilter and afterFilter should be properly nested            $this->owner->on(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter'], null, false);        } else {            $event->handled = true;        }    }

由该函数转调子类的beforeAction,这样就完成了我们的整个操作,需要注意的是如果有多个behavior的时候,则会根据behaviors数组的顺序,依次进行调用!!!

0 0
原创粉丝点击