yii2中组件为何能直接使用行为的属性 - 揭秘不一样的get函数。

281 阅读2分钟

上几篇我们讲解了如何为一个组件类配置行为及其运行原理,本篇为大家讲解yii2组件是如何做到像访问自己属性一样访问行为的属性。

首先要说的是这并不复杂,但是它可以解决你之前的很多疑问,比如为何必须是继承组件(Component)的类才能使用行为。我们都知道在php中有一个魔术方法__get,我们需要先了解一下它。

先解释一下__get方法

当访问不存在或者不能访问的成员变量时对象会自动调用__get()方法.

begin

就是通过这个方法,yii2的Component类访问到了关联行为的属性。

看看文件 vendor/yiisoft/yii2/base/Component.php line127 __get方法。

public function __get($name) {
    $getter = 'get' . $name;
    if (method_exists($this, $getter)) {
        // read property, e.g. getName()
        return $this->$getter();
    }

    // behavior property
    $this->ensureBehaviors();
    foreach ($this->_behaviors as $behavior) {
        if ($behavior->canGetProperty($name)) {
            return $behavior->$name;
        }
    }
    ...
}

函数首先判断了 method_exists($this, $getter) 是否存在,如果存在则调用,还记得你如何定义AR的关联方法么,就是这段代码实现的。

接下来我们看重头戏,首先 Component 执行了自身的 $this->ensureBehaviors(); 上一篇我们学习了这个函数保证了所有相关行为对象都各就各位,然后函数遍历了所有行为对象。

如果 $behavior->canGetProperty($name) 为真,则返回行为的相关属性(这个属性必须是public的),实现下面的结果

$model = new User();

$model->name;
↓
$model->__get('name');
↓ //$behavior->canGetProperty($name)
return $behavior->name;

就是这样的逻辑。你明白了么?

canGetProperty

接下来说说 canGetProperty 函数,这是object的一个方法,我们知道Component和Behavior都是它的子类,这个方法主要用于判断一个属性是否存在。

因此就是判断在对应行为对象中属性是否存在,就像上面的 $behavior 的 name属性,如果能访问到自然是好,否则这又是一次轮回,调用__get方法看看getName函数是否存在,如果存在也是可以的,比如下面的行为注入到User类

namespace app\components;

use yii\base\Behavior;

class HelloBehavior extends Behavior {

    public function getName(){
        return "abei20172";
    }
}


// action
$model = new User();
echo $model->name;//abei20172

要注意 canGetProperty 只是判断属性是否存在,并不会检查其范围是否为 public, private, protected。当然最后只有public才能正确访问。

如下代码

namespace app\components;

use yii\base\Behavior;

class HelloBehavior extends Behavior {
    protected $name = "abei2017";
}


// action
$model = new User();
echo $model->name;

我们会得到一个异常。

protected不能直接访问
protected不能直接访问

就这样

我们通过__get方法实现了行为属性对组件类的注入,现在你明白为啥能直接访问了吧?下一篇我们说说行为方法的注入原理。