行为的属性和方法注入原理
上面我们了解到了行为的用意在于将自身的属性和方法注入给所依附的类。 那么Yii中是如何将一个行为 yii/base/Behavior 的属性和方法, 注入到一个 yii/base/Component 中的呢? 对于属性而言,是通过 __get() 和 __set() 魔术方法来实现的。 对于方法,是通过 __call() 方法。
属性的注入
以读取为例,如果访问 $Component->property1 ,Yii在幕后干了些什么呢? 这个看看 yii/base/Component::__get()
public function __get($name){ $getter = 'get' . $name; if (method_exists($this, $getter)) { return $this->$getter(); } else { // 注意这个 else 分支的内容,正是与 yii/base/Object::__get() 的 // 不同之处 $this->ensureBehaviors(); foreach ($this->_behaviors as $behavior) { if ($behavior->canGetProperty($name)) { // 属性在行为中须为 public。否则不可能通过下面的形式访问呀。 return $behavior->$name; } } } if (method_exists($this, 'set' . $name)) { throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name); } else { throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name); }}
重点来看 yii/base/Compoent::__get() 与 yii/base/Object::__get() 的不同之处。 就是在于对于未定义getter函数之后的处理, yii/base/Object 是直接抛出异常, 告诉你想要访问的属性不存在之类。 但是 yii/base/Component 则是在不存在getter之后,还要看看是不是注入的行为的属性:
首先,调用了 $this->ensureBehaviors() 。这个方法已经在前面讲过了,主要是确保行为已经绑定。
在确保行为已经绑定后,开始遍历 $this->_behaviors 。 Yii将类所有绑定的行为都保存在 yii/base/Compoent::$_behaviors[] 数组中。
最后,通过行为的 canGetProperty() 判断这个属性, 是否是所绑定行为的可读属性,如果是,就返回这个行为的这个属性 $behavior->name 。 完成属性的读取。 至于 canGetProperty() 已经在 :ref::property 部分已经简单讲过了, 后面还会有针对性地一个介绍。
对于setter,代码类似,这里就不占用篇幅了。
方法的注入
与属性的注入通过 __get() __set() 魔术方法类似, Yii通过 __call() 魔术方法实现对行为中方法的注入:
public function __call($name, $params){ $this->ensureBehaviors(); foreach ($this->_behaviors as $object) { if ($object->hasMethod($name)) { return call_user_func_array([$object, $name], $params); } } throw new UnknownMethodException('Calling unknown method: ' . get_class($this) . "::$name()");}
从上面的代码中可以看出,Yii还是先是调用了 $this->ensureBehaviors() 确保行为已经绑定。
新闻热点
疑难解答