上一篇用一個小例子讓你們看到了當行爲遇到事件,注入能力是多麼強,這節課我來拋開它的面紗,你會發現?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 函數,看看它出現的各類場景,這將對你學習行爲有很大的好處。