Если коротко: продвижение свойств конструктора (property promotion) позволяет объединить поля класса, определение конструктора и присвоение переменных в единый синтаксис прямо в списке параметров
class CustomerDTO
{
public string $name;
public string $email;
public DateTimeImmutable $birth_date;
public function __construct(
string $name,
string $email,
DateTimeImmutable $birth_date
) {
$this->name = $name;
$this->email = $email;
$this->birth_date = $birth_date;
}
}.
Так что вместо этого:
class CustomerDTO
{
public string $name;
public string $email;
public DateTimeImmutable $birth_date;
public function __construct(
string $name,
string $email,
DateTimeImmutable $birth_date
) {
$this->name = $name;
$this->email = $email;
$this->birth_date = $birth_date;
}
}Вы напишете следующее:
class CustomerDTO
{
public function __construct(
public string $name,
public string $email,
public DateTimeImmutable $birth_date,
) {}
}Давайте разберем, как это работает!
Как это работает
Основная идея проста: избавиться от всех свойств класса и присвоений переменных, а перед параметрами конструктора добавить префикс public , protected или private . PHP возьмет этот новый синтаксис и под капотом преобразует его в обычный синтаксис перед непосредственным выполнением кода.
То есть код из такого:
class MyDTO
{
public function __construct(
public string $name = 'Brent',
) {}
}Превращается в такой:
class MyDTO
{
public string $name;
public function __construct(
string $name = 'Brent'
) {
$this->name = $name;
}
}И только после этого выполняется.
Кстати, обратите внимание, что значение по умолчанию задается не для свойства класса, а для аргумента метода в конструкторе.
Особенности продвигаемых свойств
Давайте посмотрим, что могут и чего не могут продвигаемые свойства — здесь есть довольно много мелких нюансов, о которых стоит упомянуть!
Только в конструкторах
Продвигаемые свойства можно использовать только в конструкторах. Это может показаться очевидным, но я решил упомянуть об этом для ясности.
Присоединяйтесь к более чем 14 тыс. подписчиков моей рассылки: я пишу о PHP, программировании и держу в курсе того, что происходит в этом блоге. Вы можете подписаться, отправив письмо на адрес brendt@stitcher.io.
Дублирование запрещено
Вы не можете объявить обычное свойство класса и продвигаемое свойство с одинаковым именем. Это вполне логично, так как продвигаемое свойство во время выполнения просто компилируется в свойство класса.
class MyClass
{
public string $a;
public function __construct(
public string $a,
) {}
}Нетипизированные свойства разрешены
Вы можете продвигать нетипизированные свойства, хотя я бы поспорил, что в современном PHP лучше типизировать абсолютно всё.
class MyDTO
{
public function __construct(
public $untyped,
) {}
}Простые значения по умолчанию
Продвигаемые свойства могут иметь значения по умолчанию, но выражения вроде new … не допускаются.
public function __construct(
public string $name = 'Brent',
public DateTimeImmutable $date = new DateTimeImmutable(),
) {}Сочетание продвигаемых и обычных свойств
Не все параметры конструктора обязаны быть продвигаемыми, вы можете комбинировать их по своему усмотрению.
class MyClass
{
public string $b;
public function __construct(
public string $a,
string $b,
) {
$this->b = $b;
}
}Я бы посоветовал: будьте осторожны при смешивании синтаксисов. Если это делает код менее понятным, подумайте об использовании обычного конструктора.
Доступ к продвигаемым свойствам из тела конструктора
Вы можете читать продвигаемые свойства прямо в теле конструктора. Это может быть полезно, если вам нужно выполнить дополнительные проверки и валидацию. Вы можете использовать как локальную переменную, так и переменную экземпляра — оба варианта работают отлично.
public function __construct(
public int $a,
public int $b,
) {
assert($this->a >= 100);
if ($b >= 0) {
throw new InvalidArgumentException('…');
}
}Doc-комментарии для продвигаемых свойств
Вы можете добавлять doc-комментарии к продвигаемым свойствам, и они всё так же будут доступны через рефлексию.
class MyClass
{
public function __construct(
/** @var string */
public $a,
) {}
}$property = new ReflectionProperty(MyClass::class, 'a');
$property->getDocComment(); // "/** @var string */"Атрибуты
Как и doc-блоки, атрибуты разрешены для продвигаемых свойств. При компиляции они будут присутствовать как в параметре конструктора, так и в свойстве класса.
class MyClass
{
public function __construct(
#[MyAttribute]
public $a,
) {}
}Скомпилируется в:
class MyClass
{
#[MyAttribute]
public $a;
public function __construct(
#[MyAttribute]
$a,
) {
$this->a = $a;
}
}Запрещено в абстрактных конструкторах
Я даже не знал, что абстрактные конструкторы вообще существуют, но тем не менее! Продвигаемые свойства в них использовать нельзя.
abstract class A
{
abstract public function __construct(
public string $a,
) {}
}Разрешено в трейтах
С другой стороны, они разрешены в трейтах. Это логично, поскольку скомпилированный синтаксис также валиден для трейтов.
trait MyTrait
{
public function __construct(
public string $a,
) {}
}var не поддерживается
Старые... в смысле, опытные PHP-разработчики, возможно, использовали var в далеком прошлом для объявления переменных класса. С продвижением свойств в конструкторе это запрещено. Валидными ключевыми словами являются только public , protected и private .
public function __construct(
var string $a,
) {}Variadic-параметры нельзя продвигать
Поскольку невозможно привести тип к array of type , продвигать вариативные параметры нельзя.
public function __construct(
public string ...$a,
) {}Всё еще ждем дженерики…
Рефлексия для isPromoted
И ReflectionProperty , и ReflectionParameter получили новый метод isPromoted для проверки того, является ли свойство класса или параметр метода продвигаемым.
Наследование
Поскольку конструкторам в PHP не требуется строго следовать сигнатуре родительского конструктора, тут и сказать особо нечего: наследование разрешено. Однако если вам нужно передать свойства из дочернего конструктора в родительский, вам придется передать их вручную:
class A
{
public function __construct(
public $a,
) {}
}
class B extends A
{
public function __construct(
$a,
public $b,
) {
parent::__construct($a);
}
}Вот и всё, что касается property promotion! Я точно буду этим пользоваться, а вы?
Комментарии (0)
Пока нет комментариев — будьте первым.