2 · курс
0%
Главная/🧠Ключевые концепции

18Компоненты и свойства

BaseObject и Component — два базовых класса Yii; свойства через геттеры и сеттеры, соглашения о конструкторе и init(), жизненный цикл объекта.

⏱ ~4 мин 📚 2 главы руководства

Почти каждый класс Yii наследует yii\base\BaseObject (свойства через геттеры/сеттеры + конфигурация массивом) или yii\base\Component (то же + события и поведения). Понимая эти два класса, вы понимаете, как устроен весь фреймворк.

BaseObject или Component?#

BaseObject
  • свойства через getX()/setX();
  • конфигурация через массив $config;
  • лёгкий — как обычный PHP-объект.

Для структур данных, значений, хелперов-объектов.

Component
  • всё из BaseObject;
  • события: on(), off(), trigger();
  • поведения: behaviors(), attachBehavior();
  • чуть дороже по памяти и времени.

Для всего, что «живёт»: компоненты приложения, модели, контроллеры, виджеты.

Свойства через геттеры и сеттеры#

Метод getLabel() создаёт свойство label для чтения, setLabel() — для записи:

PHP
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()#

PHP
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().

Жизненный цикл объекта#

  1. Конструктор — предварительная инициализация, значения по умолчанию.
  2. Применение $config — свойства из массива перезаписывают умолчания (через сеттеры или публичные поля).
  3. init() — постинициализация: проверить готовность, нормализовать.
  4. Работа — вызовы методов.

Первые три шага происходят внутри конструктора, поэтому любой полученный объект уже полностью сконфигурирован.

Проверь себя4

BaseObject — если нужны только свойства и конфигурация; Component — если объект должен генерировать события или принимать поведения.

InvalidCallException — свойство доступно только для чтения.

Конструктор родителя применяет конфигурацию и вызывает init(); свои умолчания надо задать до этого, а в init() сначала дать отработать родителю, чтобы объект был инициализирован.

$obj->canGetProperty('label') / canSetProperty('label'); property_exists() геттеры не видит.