yii2中组件为何能直接使用行为的属性 - 揭秘不一样的get函数。
上几篇我们讲解了如何为一个组件类配置行为及其运行原理,本篇为大家讲解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类
<?php 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;
我们会得到一个异常。
就这样
我们通过__get方法实现了行为属性对组件类的注入,现在你明白为啥能直接访问了吧?下一篇我们说说行为方法的注入原理。