yii2-從behaviors()來研究組件綁定行爲的原理

不知不覺已經發布了7篇關於yii2行爲的文章。傳送門,今天再分享一篇到掘金專欄。php


爲什麼使用 yii\base\Component::behaviors() 就能綁定行爲,發生了什麼?html

咱們先來窺視一下類 Component 內部和綁定行爲相關的函數。數組

  • yii\base\Component::behaviors()
  • yii\base\Component::ensureBehaviors()
  • yii\base\Component::attachBehaviorInternal()
  • yii\base\Behavior::attach()

behaviors()

behaviors() 函數上一篇已經講了,主要用來綁定行爲的,裏面接收各類要綁定的行爲,它返回了一個數組,雖然咱們如今知道配置這個函數能起到什麼效果,可是仍是要研究下,咱們先在yii2的目錄下搜索下都哪些函數用了此函數。yii2

只有一句?是的,經過搜索咱們發現只有一個函數調用了它 --- ensureBehaviors()。那就從它開始吧。app

ensureBehaviors()

在研究它以前先看看代碼yii

// 
/** * Makes sure that the behaviors declared in [[behaviors()]] are attached to this component. */
public function ensureBehaviors() {
    if ($this->_behaviors === null) {
        $this->_behaviors = [];
        foreach ($this->behaviors() as $name => $behavior) {
            $this->attachBehaviorInternal($name, $behavior);
        }
    }
}複製代碼

邏輯很簡單,component組件類用一個屬性 _behaviors 來存放它擁有的全部行爲對象,若是判斷爲空,則調用$this->behaviors()函數獲取一下,對每一個行爲執行 attachBehaviorInternal()函數。函數

attachBehaviorInternal()

看函數名 attachBehaviorInternal() 是綁定行爲的意思,那就看一看。this

private function attachBehaviorInternal($name, $behavior) {
    if (!($behavior instanceof Behavior)) {
        $behavior = Yii::createObject($behavior);
    }
    if (is_int($name)) {
        $behavior->attach($this);
        $this->_behaviors[] = $behavior;
    } else {
        if (isset($this->_behaviors[$name])) {
            $this->_behaviors[$name]->detach();
        }
        $behavior->attach($this);
        $this->_behaviors[$name] = $behavior;
    }

    return $behavior;
}複製代碼

在第一個if分支內判斷 $behavior 是否爲 行爲類Behavior的一個對象,若是不是則$behavior確定是一些配置,那根據這些配置獲得相關行爲的對象。spa

總之 $behavior 已是一個行爲對象了,咱們先看函數體最後一行,能夠知道此函數返回了這個對象。code

接下來咱們來看第二個if分支。

if (is_int($name)) {
    $behavior->attach($this);
    $this->_behaviors[] = $behavior;
} else {
    if (isset($this->_behaviors[$name])) {
        $this->_behaviors[$name]->detach();
    }
    $behavior->attach($this);
    $this->_behaviors[$name] = $behavior;
}複製代碼

首先說對於 is_int($name) 的判斷,還記得咱們在綁定行爲的時候麼(傳送門),在 behaviors() 返回的數組中,咱們能夠不爲某個行爲起名字,那叫作匿名指定,那天然這個key會是一個遞增的數字,因此 is_int($name) 在判斷是否爲匿名行爲。

若是是匿名行爲,首先 $behavior->attach($this),而後放到 _behaviors 數組中。

若是不是匿名行爲,先看看 _behaviors 數組中是否存在,若是存在則先 detach()後 $behavior->attach($this),而後放到 _behaviors 數組中。

這樣一圈下來,_behaviors 數組中存放一羣行爲對象,有些是匿名的,有些是有名字的。對吧。

那麼如今咱們已經知道 attachBehaviorInternal函數的第一個功能 --- 填充 _behaviors 數組,反過來回顧 ensureBehaviors的做用,這個ensureBehaviors的一個功能就是確保 _behaviors 數組中有該組件應該有的全部行爲對象。

爲何是第一個那???由於在 attachBehaviorInternal中咱們發現除了填充數組外,還有一個叫作 $behavior->attach($this);的函數,它也將成爲 attachBehaviorInternal / ensureBehaviors 功能之一。

那麼 attach() 函數作了什麼那?

attach()

先看一看它的代碼,它在 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);
    }
}複製代碼

分析一下,在組件處理本身行爲的時候,將$this傳遞給了行爲對象的方法 $behavior->attach($this),而在行爲的 attach 方法中 $this->owner = $owner 一下,這意爲着什麼?

組件的每一個行爲對象都有一個屬性owner存放了使用他們的組件對象,到此刻組件有 _behaviors 數組存放本身的全部行爲對象,而行爲有owner屬性存放使用了本身的組件對象,它們創建了雙向聯繫。

而關於在attach中的foreach循環體主要是處理事件的,咱們會在行爲和事件一篇說明。

此刻,咱們再來概括一下 ensureBehaviors 的功能,也就是綁定方法背後都觸發了哪些動做

  1. 咱們在組件的子類(好比AR、控制器等)中使用behaviors()來綁定一些行爲。
  2. 而後有一個叫作 ensureBehaviors 的函數確保了此組件對象和綁定的行爲對象能夠彼此擁有。

可是

咱們都知道,綁定行爲後,組件對象就能夠像使用自身屬性和方法同樣操做,這彷佛和 ensureBehaviors 沒有啥關係,下篇將爲你解析當咱們直接調用行爲屬性的時候,發生了什麼?以及在這其中 ensureBehaviors 起到了什麼做用?

相關文章
相關標籤/搜索