在PHP面向对象的概念中,类的功能分为属性和方法。而Yii2.0则定义了类的三个功能:属性(property)、行为(behavior)、事件(event)。

PHP属性


属性就是我们说的成员变量,首先我们创建一个类User,里面有一个成员变量是name。

  1. <?php
  2. class User
  3. {
  4. public $name;
  5. }
  6. (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 类中

  1. <?php
  2. class BaseObject implements Configurable
  3. {
  4. /**
  5. * Returns the value of an object property.
  6. *
  7. * Do not call this method directly as it is a PHP magic method that
  8. * will be implicitly called when executing `$value = $object->property;`.
  9. * @param string $name the property name
  10. * @return mixed the property value
  11. * @throws UnknownPropertyException if the property is not defined
  12. * @throws InvalidCallException if the property is write-only
  13. * @see __set()
  14. */
  15. public function __get($name)
  16. {
  17. $getter = 'get' . $name;
  18. if (method_exists($this, $getter)) {
  19. return $this->$getter();
  20. } elseif (method_exists($this, 'set' . $name)) {
  21. throw new InvalidCallException('Getting write-only property: ' . get_class($this) . '::' . $name);
  22. }
  23. throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
  24. }
  25. /**
  26. * Sets value of an object property.
  27. *
  28. * Do not call this method directly as it is a PHP magic method that
  29. * will be implicitly called when executing `$object->property = $value;`.
  30. * @param string $name the property name or the event name
  31. * @param mixed $value the property value
  32. * @throws UnknownPropertyException if the property is not defined
  33. * @throws InvalidCallException if the property is read-only
  34. * @see __get()
  35. */
  36. public function __set($name, $value)
  37. {
  38. $setter = 'set' . $name;
  39. if (method_exists($this, $setter)) {
  40. $this->$setter($value);
  41. } elseif (method_exists($this, 'get' . $name)) {
  42. throw new InvalidCallException('Setting read-only property: ' . get_class($this) . '::' . $name);
  43. } else {
  44. throw new UnknownPropertyException('Setting unknown property: ' . get_class($this) . '::' . $name);
  45. }
  46. }
  47. }

封装User类时:

  1. <?php
  2. class User extends BaseObject
  3. {
  4. private $_name;
  5. public function getName() {
  6. return $this->_name;
  7. }
  8. public function setName($value) {
  9. $this->_name = $value;
  10. }
  11. }
  12. (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成员变量不能视作属性。
  • 大多数情况下,属性要由成员变量来实现,但是二者没有必然的联系。