18Компоненты и свойства
BaseObject и Component — два базовых класса Yii; свойства через геттеры и сеттеры, соглашения о конструкторе и init(), жизненный цикл объекта.
Почти каждый класс Yii наследует yii\base\BaseObject (свойства через геттеры/сеттеры + конфигурация массивом) или yii\base\Component (то же + события и поведения). Понимая эти два класса, вы понимаете, как устроен весь фреймворк.
BaseObject или Component?#
- свойства через
getX()/setX(); - конфигурация через массив
$config; - лёгкий — как обычный PHP-объект.
Для структур данных, значений, хелперов-объектов.
- всё из
BaseObject; - события:
on(),off(),trigger(); - поведения:
behaviors(),attachBehavior(); - чуть дороже по памяти и времени.
Для всего, что «живёт»: компоненты приложения, модели, контроллеры, виджеты.
Свойства через геттеры и сеттеры#
Метод getLabel() создаёт свойство label для чтения, setLabel() — для записи:
namespace app\components;
use yii\base\BaseObject;
class Foo extends BaseObject
{
private $_label;
public function getLabel()
{
return $this->_label;
}
public function setLabel($value)
{
$this->_label = trim($value); // логика в одном месте, а не во всех точках присваивания
}
}
$foo->label = ' Hello '; // вызовет setLabel()
echo $foo->label; // вызовет getLabel() → 'Hello'Правила, о которых стоит помнить:
- Есть только геттер — свойство только для чтения; запись бросит
InvalidCallException. Только сеттер — только для записи. - Имена регистронезависимы (
$obj->labelи$obj->Label— одно и то же), потому что так работают методы PHP. - Если есть публичное поле с тем же именем, оно побеждает: сеттер не вызывается.
- Модификаторы видимости геттеров не влияют на «свойство».
- Только нестатические методы.
property_exists()таких свойств не видит — используйтеcanGetProperty()/canSetProperty().
Соглашения о конструкторе и init()#
namespace app\components;
use yii\base\BaseObject;
class MyClass extends BaseObject
{
public $prop1;
public $prop2;
public function __construct($param1, $param2, $config = [])
{
// до применения конфигурации: значения по умолчанию
parent::__construct($config); // родительский конструктор — последним
}
public function init()
{
parent::init(); // родительский init() — первым
// после применения конфигурации: проверка, нормализация свойств
}
}
$obj = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]);
$obj = \Yii::createObject(['class' => MyClass::class, 'prop1' => 3], [1, 2]); // через DI-контейнерТри правила: $config — последний параметр конструктора; parent::__construct($config) — в конце своего конструктора; parent::init() — в начале своего init().
Жизненный цикл объекта#
- Конструктор — предварительная инициализация, значения по умолчанию.
- Применение
$config— свойства из массива перезаписывают умолчания (через сеттеры или публичные поля). init()— постинициализация: проверить готовность, нормализовать.- Работа — вызовы методов.
Первые три шага происходят внутри конструктора, поэтому любой полученный объект уже полностью сконфигурирован.
BaseObject — если нужны только свойства и конфигурация; Component — если объект должен генерировать события или принимать поведения.
InvalidCallException — свойство доступно только для чтения.
Конструктор родителя применяет конфигурацию и вызывает init(); свои умолчания надо задать до этого, а в init() сначала дать отработать родителю, чтобы объект был инициализирован.
$obj->canGetProperty('label') / canSetProperty('label'); property_exists() геттеры не видит.