yii2 controller behavior函数的beforeAction实现原理
首先写一个例子,在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,该类的父类是yiiasecontroller,每次 一个请求的到来,都会执行祖父类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)) { //这里执行了一次,调用点是yiiwebapplication 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执行了两次,一次是webapplication执行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数组的顺序,依次进行调用!!!
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。
- 上一篇: Yii2 理解Controller
- 下一篇: Yii2.0 RESTful Web服务(3)