Объявление свойств в конструкторе [перевод]

Larry Garfield пишет отличную серию статей о том, что из себя представляет PHP 8: о скорости, атрибутах, свойствах в конструкторе и многом другом. Сегодня посмотрим на его статью о объявляемых в конструкторе свойствах.

https://platform.sh/blog/2020/php-80-feature-focus-constructor-property-promotion/

Возможно, наиболее существенное улучшение PHP 8, в плане удобства разработки, это объявление свойств в конструкторе. Синтаксис в основном заимствован из Hack — PHP-форка от Facebook, но похожий синтаксис используется и в других языках, например TypeScript или Kotlin.

Сразу приведу простой пример. Рассмотрим типичный класс в современном приложении:

class FormRenderer
{
    private ThemeSystem $theme;
    private UserRepository $users;
    private ModuleSystem $modules;

    public function __construct(ThemeSystem $theme, UserRepository $users, ModuleSystem $modules) {
        $this->theme = $theme;
        $this->users = $users;
        $this->modules = $modules;
    }

    public function renderForm(Form $form): string
    {
        // ...
    }
}

Класс имеет 3 зависимости. Название переменной для каждой зависимости повторяется 4 раза: при описании аргумента конструктора, при описании свойства класса, при описании ссылки на свойство внутри конструктора и при инициализации этой ссылки. Получается очень громоздко, и хотя большинство IDE решают такие задачи авто-подстановкой, это всё ещё боль. Я знаю, что многие люди избегают инъекцию зависимостей из-за громоздкого описания.

В PHP 8 вышеописанную запись сократили до:

<?php

class FormRenderer
{
    public function __construct(
        private ThemeSystem $theme, 
        private UserRepository $users, 
        private ModuleSystem $modules,
    ) { }

    public function renderForm(Form $form): string
    {
        // ...
    }
}

Перемещение области видимости свойства в конструктор говорит PHP «Этот параметр — свойство, ты должен назначить ему значение». Эффект во время выполнения точно такой же, как в первом примере, но для такой записи нам потребовалось в 4 раза меньше текста.

Тем не менее, мы всё ещё можем описывать параметры по-старинке, и конструктор, конечно, всё ещё может иметь тело конструктора. В этом примере тело конструктора — пустое, но если нам требуется сделать ещё что-то, помимо инъекции зависимостей, мы можем сделать это в теле конструктора, и это будет выполнено после инициализации свойств:

<?php

class FormRenderer
{
    private User $currentUser;

    public function __construct(
        private ThemeSystem $theme, 
        private UserRepository $users, 
        private ModuleSystem $modules,
    ) { 
        $this->currentUser = $this->users->getActiveUser();
    }

    public function renderForm(Form $form): string
    {
        // ...
    }
}

Но на практике, большинство классов, которые я писал за последние несколько лет — имели конструктор, где не было ничего кроме инициализации.

Объявление свойств в конструкторе удобно использовать для простых объектов. Простые объекты — это объекты с некоторым количеством свойств и геттеров/сеттеров для этих свойств. Для таких объектов весь класс можно свести к конструктору:

<?php

Class MailMessage
{
    public function __construct(
        public string $to,
        public string $subject,
        public string $from,
        public string $body,
        public array $cc,
        public array $bcc,
        public string $attachmentPath,
    ) {}
}

Для внутреннего (не являющегося частью API) объекта public-свойства вполне могут использоваться. Если же объект является частью API или взаимодействует с какой-либо внешней системой, то свойства нужно инкапсулировать, и добавить методы для работы с ними. В любом случае код станет компактнее и удобнее.

При объявлении свойств в конструкторе нужно учитывать несколько моментов:

  • Определение области видимости применимо только к конструкторам, использование их в любой другой функции приведёт к ошибке;
  • Свойство не может быть объявлено как в конструкторе, так и за его пределами.

Спасибо Никите Попову за этот RFC, хотя поводом послужила моя запись в начале года в этом блоге, так что я требую себе 0.1% 🙂

Мы уже в плотную подошли к релизу PHP 8 и вот-вот сможем увидеть на практике что из себя представляет объявление свойств в конструкторе.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *