一對好基友 - yii2行爲和事件那些事 源碼分析篇

上一篇用一個小例子讓你們看到了當行爲遇到事件,注入能力是多麼強,這節課我來拋開它的面紗,你會發現?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

這個函數在行爲內對象

  • $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 函數,看看它出現的各類場景,這將對你學習行爲有很大的好處。


相關文章
相關標籤/搜索