在PHP面向对象的概念中,类的功能分为属性和方法。而Yii2.0则定义了类的三个功能:属性(property)、行为(behavior)、事件(event)。
PHP属性
属性就是我们说的成员变量,首先我们创建一个类User,里面有一个成员变量是name。
<?php
class User
{
public $name;
}
(new User())->name='xiao li';
实例化User类并为属性name赋值,我们通常会像上面这样做:
这么看确实没有问题,但是我们思考一下,如果User类是一个基础类,那么与之产生关联的类会有很多,这时我们需要对User类的name进行更改,比如需要首字母大写,或者巴拉巴拉一些其他的要求,这时我们就要找到User类的所有实例,然后逐一修改,庞大的工作量,让你耗费精力并且还无法保证正确性,为了解决这种情况,Yii2.0引入了自己的属性概念~
Yii2.0属性
Yii2.0的属性是通过魔术方法getter()和方法setter()来定义的。
getter是get开头的方法,setter是set开头的方法,这两个魔术方法是PHP魔术方法set()和get()的进一步封装。
具体实现在 yii2/base/BaseObject 类中
<?php
class BaseObject implements Configurable
{
/**
* Returns the value of an object property.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `$value = $object->property;`.
* @param string $name the property name
* @return mixed the property value
* @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is write-only
* @see __set()
*/
public function __get($name)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
return $this->$getter();
} elseif (method_exists($this, 'set' . $name)) {
throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name);
}
throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
}
/**
* Sets value of an object property.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `$object->property = $value;`.
* @param string $name the property name or the event name
* @param mixed $value the property value
* @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is read-only
* @see __get()
*/
public function __set($name, $value)
{
$setter = 'set' . $name;
if (method_exists($this, $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);
}
}
}
封装User类时:
<?php
class User extends BaseObject
{
private $_name;
public function getName() {
return $this->_name;
}
public function setName($value) {
$this->_name = $value;
}
}
(new User)->name = 'xiao li';
定义一个私有成员变量$_name,而User类继承BaseObject类,所以当我们给name属性赋值时,会首先调用BaseObject类中的__set()方法,这是$setter变成了setname,通过method_exists()方法判断该实例中是否存在setname方法,PHP方法名不区分大小写,所以setname()和setName()属于同一个方法,进而实现为属性name赋值操作。
这时,我们回头看看最开始的那个问题,如果我需要对name进行变更,那么我只需要在User类的setName()方法中进行修改即可,你品,你细细的品~
总结一下Yii2.0的属性:
需继承BaseObject、需声明一个私有成员变量保存属性、需提供getter、setter方法
Yii2.0的属性存在着一些特殊规则和限制:
- 属性名称和类public级别成员变量相同,则被操作的总是类的成员变量,而不是属性。
- 属性需是私有的(访问限制)。
- 无论是setter还是getter必须时public级别且非静态的。
- Yii2.0的属性不同于PHP的成员变量,所以不能使用property_exists()方法来判断BaseObject及其子类的属性是否存在,而应改用BaseObject中的hasProperty()、canGetProperty()、canSetProperty()等方法~
属性和成员变量的区别
通过上面我们进行一个反思,属性和成员变量之间存在何种差异,
- 成员变量是内在的概念,反映的是类的结构构成。属性是一个外在的概念,反映的是类的逻辑意义。
- 成员变量没有读写控制权限,而属性可以指定为只读、只写或可读可写,这就有了权限的说法。
- 成员变量不对读做任何后处理,不对写做任何预处理,但是属性可以。
- public成员变量可以视作一个可读可写、没有任何后处理和预处理的属性,但是private、protected成员变量不能视作属性。
- 大多数情况下,属性要由成员变量来实现,但是二者没有必然的联系。