上一篇用一個小例子讓大家看到了當行為遇到事件,注入能力是多麼強,這節課我來拋開它的面紗,你會發現?
我靠,原來這麼簡單。:triumph: :triumph: :triumph:
當然,這是源於你認真看了之前乾貨區的另一片文章 從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 函式,看看它出現的各種場景,這將對你學習行為有很大的好處。