不知不覺已經發布了7篇關於yii2行爲的文章。傳送門,今天再分享一篇到掘金專欄。php
爲什麼使用 yii\base\Component::behaviors() 就能綁定行爲,發生了什麼?html
咱們先來窺視一下類 Component 內部和綁定行爲相關的函數。數組
behaviors() 函數上一篇已經講了,主要用來綁定行爲的,裏面接收各類要綁定的行爲,它返回了一個數組,雖然咱們如今知道配置這個函數能起到什麼效果,可是仍是要研究下,咱們先在yii2的目錄下搜索下都哪些函數用了此函數。yii2
只有一句?是的,經過搜索咱們發現只有一個函數調用了它 --- ensureBehaviors()。那就從它開始吧。app
在研究它以前先看看代碼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() 是綁定行爲的意思,那就看一看。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() 函數作了什麼那?
先看一看它的代碼,它在 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 的功能,也就是綁定方法背後都觸發了哪些動做
咱們都知道,綁定行爲後,組件對象就能夠像使用自身屬性和方法同樣操做,這彷佛和 ensureBehaviors 沒有啥關係,下篇將爲你解析當咱們直接調用行爲屬性的時候,發生了什麼?以及在這其中 ensureBehaviors 起到了什麼做用?