上一篇用一个小例子让你们看到了当行为遇到事件,注入能力是多么强,这节课我来抛开它的面纱,你会发现?php
我靠,原来这么简单。 数组
固然,这是源于你认真看了以前干货区的另外一片文章 从behaviors()来研究组件绑定行为的原理yii2
那咱就开始吧yii
为了能按部就班的学习,咱们这篇仍是之内置事件为例子,你们都知道,内置事件会被某些方法自动触发,好比你在执行ar的save操做的时候,会触发EVENT_BEFORE_INSERT和EVENT_AFTER_INSERT等事件,若是你以前在这些事件上绑定过实现,那么这些实现逻辑就会被启动。函数
嗯,那就开始吧,这一切要从ensureBehaviors函数讲起,你们都知道,在绑定行为到组件的时候,它起到了保驾护航的做用,看代码学习
public function ensureBehaviors() { if ($this->_behaviors === null) { $this->_behaviors = []; foreach ($this->behaviors() as $name => $behavior) { $this->attachBehaviorInternal($name, $behavior); } } }
而在 $this->attachBehaviorInternal($name, $behavior); 的方法里有一个叫 $behavior->attach($this);的函数还记得么?它将组件绑定到了行为对象自身并赋值了owner属性。this
回忆完了是吧,看看这个重要的函数吧。spa
// vendor/yiisoft/yii2/base/Behavior.php public function attach($owner) { $this->owner = $owner; foreach ($this->events() as $event => $handler) { $owner->on($event, is_string($handler) ? [$this, $handler] : $handler); } }
发现了吧,$owner->on($event, is_string($handler) ? [$this, $handler] : $handler);就是这一句,咱们分析它。code
这个函数在行为内对象
好,各路神仙均已登场,开始顺个中关系。
首先$this有个方法events(),实现以下
public function events(){ return [ ActiveRecord::EVENT_BEFORE_INSERT => 'beforeInsert', ]; }
$handler此刻就是 beforeInsert 字符串,对应的key是 ActiveRecord::EVENT_BEFORE_INSERT
attach函数首先将 $handler 对应的key(一个事件名)绑定到了 $owner 上,若是$handler是一个字符串,则key事件的实现方法是行为类里一个叫作 $handler()的函数,就是你看到的 [$this, $handler]。
若是不是字符串,则这直接使用,它能够是符合事件绑定中的任何一种。(事件绑定方法传送门)
上面的内容有点枯燥,咱们用昨天例子进行说明,当咱们执行了$model->save()以后,行为会在User的username值后面添加一个"+"号,此刻你必定会有一个疑问。
以前咱们能让 ensureBehaviors起做用,是由于咱们经过 __get & __call 实现了行为属性和方法的注入,进而调用了ensureBehaviors函数,可是在昨天的例子中,咱们并无显性的调用HelloBehavior任何属性和方法,那么ensureBehaviors函数是如何被启动的那?
对,它真的无处不在,由于它同时也出如今了组件的事件触发函数中,看代码
// vendor/yiisoft/yii2/base/Component.php 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); // stop further handling if the event is handled if ($event->handled) { return; } } } // invoke class-level attached handlers Event::trigger($this, $name, $event); }
就是在这个时候完成了行为内事件实现的注入行为。
这回看懂了吧,够绕的。
你若是有兴趣,能够整个项目搜索下 ensureBehaviors 函数,看看它出现的各类场景,这将对你学习行为有很大的好处。