Yii2 framework backup
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

3.9 KiB

属性(Property)

PHP 中,类的成员变量也被称为 属性(properties)。它们是类定义的一部分,并用来表现一个实例的状态(比如,用于区分某类的各个实例)。在具体实践中,你会常常想用一个稍微特殊些的方法实现属性的读写。例如,如果你想要去掉赋值给 label 属性的字符串前后的空格,就可以用以下代码实现:

$object->label = trim($label);

以上代码的缺点是,只要需要修改 label 属性就必须再次调用 trim() 函数。若将来提出了对 label 属性的新要求,比如首字母大写等,你就被迫必须修改所有给 label 属性赋值的代码。这种代码的重复会导致 bug,且这这种实践显然是你想要尽力避免的。

为解决该问题,Yii 引入了一个名为 yii\base\Object 的基类,它支持基于类内的 gettersetter (读取器和设定器)方法来定义属性。如果某类需要支持这个特性,只需要继承自 yii\base\Object 或其子类即可。

补充:几乎每个 Yii 框架的核心类都继承自 yii\base\Object 或其子类。这意味着无论何时在核心类中见到一个 getter 或 setter 方法,都可以像调用属性一样调用它。

getter 方法是方法名以 get 开头的方法,而 setter 方法名以 set 开头。方法名中 getset 后面的部分就定义了该属性的名字。如下面代码所示,一个 getter 方法 getLabel() 或 setter 方法 setLabel() 就定义了一个名为 label 的属性:

namespace app\components;

use yii\base\Object;

class Foo extend Object
{
    private $_label;

    public function getLabel()
    {
        return $this->_label;
    }

    public function setLabel($value)
    {
        $this->_label = trim($value);
    }
}

(详细解释:getter 和 setter 方法创建了一个名为 label 的属性,在这个例子里,它指向一个私有的内部属性 _label。译者注:习惯上一般把私有属性都定义为 _ 下划线开头的名字。)

getters/setters 定义的属性能像类成员变量那样使用。两者主要的区别是:当这种属性被读取时,对应的 getter 方法将被调用;而当属性被赋值时,对应的 setter 方法就调用。如:

// 等效于 $label = $object->getLabel();
$label = $object->label;

// 等效于 $object->setLabel('abc');
$object->label = 'abc';

只定义了 getter 没有 setter 的属性是只读属性。尝试赋值给这样的属性将导致 yii\base\InvalidCallException (无效调用)异常。类似的,只有 setter 方法而没有 getter 方法定义的属性是只写属性,尝试读取这种属性也会触发异常。使用只写属性的情况几乎没有。

通过 getter 和 setter 定义的属性也有一些特殊规则和限制:

  • 这类属性的名字是 不区分大小写 的。如,$object->label$object->Label 是同一个属性。因为 PHP 方法名是不区分大小写的。
  • 如果此类属性名和类成员变量相同,以后者为准。例如,假设以上 Foo 类有个 label 成员变量,然后给 $object->label = 'abc' 赋值,将赋给成员变量而不是 setter setLabel() 方法。
  • 这类属性不支持可见性(访问限制)。定义属性的 getter 和 setter 方法是 public、protected 还是 private 对属性的可见性没有任何影响。
  • 这类属性的 getter 和 setter 方法只能定义为 非静态的,若定义为静态方法(static)则不会以相同方式处理。

回到开头处提到的问题,与其处处要调用 trim() 函数,现在我们只需在 setter setLabel() 方法内调用一次。如果 label 首字母变成大写的新要求来了,我们只需要修改setLabel() 方法,而无须接触任何其它代码。