一直以来,在我的理解中,类的成员变量和属性就是同一个东西,直到看了《深入理解Yii2.0》才明白,
类的成员变量和属性其实不是同一个概念,成员变量是就类的结构构成而言的概念,而属性是就类的功能逻辑
而言的概念,而且两者没有必然的对应关系。
类的成员变量有public、protected、private三种类型,其中public类型的成员变量可以视为可读可写的属性,
而另外两种类型的成员变量都不是属性,因为属性应该是可以在类外面进行访问的。
在Yii2.0中,yii\base\Object类通过PHP的魔术方法__get()和__set()来提供对属性的支持,源码如下:
public function __get($name) { $getter = ‘get‘ . $name; if (method_exists($this, $getter)) {//存在对应的getter方法 return $this->$getter(); } elseif (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); } } public function __set($name, $value) { $setter = ‘set‘ . $name; if (method_exists($this, $setter)) {//存在对应的setter方法 $this->$setter($value); } elseif (method_exists($this, ‘get‘ . $name)) {//只读属性 throw new InvalidCallException(‘Setting read-only property: ‘ . get_class($this) . ‘::‘ . $name); } else {//属性不存在 throw new UnknownPropertyException(‘Setting unknown property: ‘ . get_class($this) . ‘::‘ . $name); } }
在yii2.0中,如果一个类需要支持属性,只要继承yii\base\Object类即可,以下举例说明:
class user extends yii\base\Object { public $name; private $addr; private $age; public function getAddress() { return $this->addr; } }
这是一个很简单的类,根据类的成员变量和属性的概念可知,以上这个类中有三个成员变量:$name、$addr、
$age,两个属性:$name、address。其中$name是可读可写属性,而address是只读属性,因为只存在getter方法而没有
对应的setter方法。
假如我们在user类外面创建一个对象$user,通过$user->address来读取user类的address属性的时候,其实是遍历了
一下user类的所有成员变量,没有找到address属性对应的成员变量的时候,再去调用__get()方法看看是否存在对应的
getter方法,有则调用它。可以看到,当类的某个属性没有对应的成员变量的时候,通过$user->address这种方式来访问
属性虽然比较方便但效率是比较低的,而使用$user->getAddress() 直接调用getter方法访问属性则可以省去遍历类成员
变量的过程,比较高效。
除了__get()和__set()之外,yii\base\Object类还提供了与属性相关的其他方法:
__isset():判断属性是否不为null。
__unset():将属性值设置为null。
hasProperty():判断是否存在某个属性。
canGetProperty():判断某个属性是否可读。
canSetProperty():判断某个属性是否可写。
在yii2.0中,创建一个对象的时候,怎么给对象的所有属性赋值呢?Yii2.0提供了一个统一的配置对象的方式,那
就是使用一个键值对形式的配置数组。那么yii2.0又是怎么支持这种配置方式的呢?通过查看源码可以看到,yii\base\Object
类的构造函数__construct()中调用了Yii::configure()方法,该方法源码如下:
public static function configure($object, $properties) { foreach ($properties as $name => $value) { $object->$name = $value; } return $object; }
其实很简单,就是遍历配置数组,配置数组的键名为对象的属性名,对应的值则作为属性值,逐一进行赋值。至于当对象
的属性是对象或数组时,赋值操作该怎么进行就是setter函数的事了。