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 起到了什么做用?

相关文章
相关标签/搜索