上一篇用一个小例子让大家看到了当行为遇到事件,注入能力是多么强,这节课我来抛开它的面纱,你会发现?
我靠,原来这么简单。
当然,这是源于你认真看了之前干货区的另一片文章 从behaviors()来研究组件绑定行为的原理
那咱就开始吧
思想准备阶段
为了能循序渐进的学习,我们这篇还是以内置事件为例子,大家都知道,内置事件会被某些方法自动触发,比如你在执行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属性。
回忆完了是吧,看看这个重要的函数吧。
// 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);就是这一句,我们分析它。
这个函数在行为内
- $owner 表示当前绑定了此行为的对象,也就是说这句话核心是组件类绑定了自己的事件。
- $this 表示当前的行为对象
- $handler 是行为events()方法返回数组每一项的value值
好,各路神仙均已登场,开始顺个中关系。
首先$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函数是如何被启动的那?
无处不在的 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 函数,看看它出现的各种场景,这将对你学习行为有很大的好处。