Свойства

В PHP, переменные-члены класса называются свойства. Эти переменные являются частью объявления класса и используются для хранения состояния объектов этого класса (т.е. именно этим отличается один экземпляр класса от другого). На практике вам часто придётся производить чтение и запись свойств особым образом. Например, вам может понадобиться обрезать строку при её записи в поле label. Для этого вы можете использовать следующий код:

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

Недостатком приведённого выше кода является то, что вам придется вызывать функцию trim() во всех местах, где вы присваиваете значение полю label. Если в будущем понадобится производить еще какие-либо действие, например преобразовать первую букву в верхний регистр, вам придётся изменить каждый участок кода, где производится присваивание значения полю label. Повторение кода приводит к ошибкам и его необходимо избегать всеми силами.

Что бы решить эту проблему, в Yii был добавлен базовый класс yii\base\Object который реализует работу со свойствами через геттеры и сеттеры. Если вашему классу нужна такая возможность, необходимо унаследовать его от yii\base\Object или его потомка.

Info: Почти все внутренние классы Yii наследуются от yii\base\Object или его потомков. Это значит, что всякий раз, когда вы встречаете геттер или сеттер в классах фреймворка, вы можете обращаться к нему как к свойству.

Геттер — это метод, чьё название начинается со слова get. Имя сеттера начинается со слова set. Часть названия после get или set определяет имя свойства. Например, геттер getLabel() и/или сеттер setLabel() определяют свойство label, как показано в коде ниже:

namespace app\components;

use yii\base\Object;

class Foo extends Object
{
    private $_label;

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

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

В коде выше геттер и сеттер реализуют свойство label, значение которого хранится в private свойстве _label.

Свойства, определенные с помощью геттеров и сеттеров, можно использовать как обычные свойства класса. Главное отличие в том, что когда происходит чтение такого свойства, вызывается соответствующий геттер, при присвоении значения такому свойству запускается соответствующий сеттер. Например:

// Идентично вызову $label = $object->getLabel();
$label = $object->label;

// Идентично вызову $object->setLabel('abc');
$object->label = 'abc';

Свойство, для которого объявлен только геттер без сеттера, может использоваться только для чтения. Попытка присвоить ему значение вызовет InvalidCallException. Точно так же, свойство для которого объявлен только сеттер без геттера может использоваться только для записи. Попытка получить его значение так же вызовет исключение. Свойства, предназначенные только для чтения, встречаются не часто.

При определении свойств класса при помощи геттеров и сеттеров нужно помнить о некоторых правилах и ограничениях:

  • Имена таких свойств регистронезависимы. Таким образом, $object->label и $object->Label — одно и то же. Это обусловлено тем, что имена методов в PHP регистронезависимы.
  • Если имя такого свойства уже используется переменной-членом класса, то последнее будет иметь более высокий приоритет. Например, если в классе Foo объявлено свойство label, то при вызове $object->label = 'abc' будет напрямую изменено значение свойства label. А метод setLabel() не будет вызван.
  • Свойства, объявленные таким образом, не поддерживают модификаторы видимости. Это значит, что объявление геттера или сеттера как public, protected или private никак не скажется на области видимости свойства.
  • Свойства могут быть объявлены только с помощью не статичных геттеров и/или сеттеров. Статичные методы не будут обрабатываться подобным образом.
  • Обычный вызов property_exists() не работает для магических свойств. Для них необходимо использовать canGetProperty() или canSetProperty().

Возвращаясь к проблеме необходимости вызова функции trim() во всех местах, где присваивается значение свойству label, описанной в начале этого руководства, функцию trim() теперь необходимо вызывать только один раз — в методе setLabel(). При возникновении нового требования о возведение первой буквы в верхний регистр, можно быстро поправить метод setLabel() не затрагивая остальной код. Эта правка будет распространяться на все присвоения значения свойству label.