yii2 controller behaviors 函数的 那些事儿

最近抽时间简单研究一下 YII2框架一些源码问题。今天看一下 yii2 controller behaviors 函数。

首先写一个例子,自定义一个过滤器。在siteController中覆盖behaviors函数:

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

        public function actionIndex()
        {
            return '当前action显示';//返回的内容会递交给过滤器,由afterAction进行处理
        }
    }

  关键配置:

       only :   仅对数组中action对应的请求地址有效

       except::排除掉数组中action对应的请求地址有效

TokenFilter 的定义如下:

class TokenFilter extends ActionFilter
{
    /**
     * @param \yii\base\Action $action
     * @return bool
     */
    public function beforeAction($action)
    {
        echo '在调用action前显示';
        return parent::beforeAction($action); // TODO: Change the autogenerated stub
    }

    /**
     * @param \yii\base\Action $action
     * @param mixed $result
     * @return mixed|string
     */
    public function afterAction($action, $result)
    {
        return $result.'在调用action后显示';//可以对action输出的$result进行过滤,retun的内容会直接显示
        //return parent::afterAction($action, $result); // TODO: Change the autogenerated stub
    }

}

最后,测试执行代码,显示结果为:

1   在调用action前显示
2      当前action显示
3   在调用action后显示

关于代码执行底层,为何会自动调用问题:

我们知道controller的拦截器是在behaviors中进行定义的,而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中就将behaviors放在了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,这样就完成了我们的整个操作。需要注意的是如果有多个behaviors的时候,则会根据behaviors数组的顺序,依次进行调用!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值