-
RFC по объявлению свойств в конструкторе прошел и будет добавлен в PHP 8 . Эта функция сократит количество стандартного кода при создании простых объектов, таких как VO и DTO.
Вкратце: объявлению свойств в конструкторе позволяет вам объединить поля класса, определение конструктора и назначения переменных в один синтаксис в списке параметров конструктора.
Поэтому вместо этого:
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 = 'Sergey',
) {}
}
Приводится к этому:
class MyDTO
{
public string $name;
public function __construct(
string $name = 'Sergey'
) {
$this->name = $name;
}
}
И только потом выполняет. Кстати, обратите внимание, что значение по умолчанию устанавливается не для свойства класса, а для аргументов метода в конструкторе.
Итак, давайте посмотрим, что можно и чего нельзя делать объявляемым свойствам. Здесь стоит упомянуть множество мелких нюансов!
Объявляемые свойства можно использовать только в конструкторах. Это может показаться очевидным, но, думаю, стоит упомянуть об этом, просто для ясности.
Вы не можете объявить свойство класса и объявляемое свойство с тем же именем. Это также довольно логично, поскольку объявляемое свойство просто переносится в свойство класса во время выполнения.
Этот код вызовет ошибку:
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 = 'Sergey',
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 >= 500);
if ($b >= 0) {
throw new InvalidArgumentException('…');
}
}
Вы можете добавлять комментарии к объявляемым свойствам, и они по-прежнему доступны через рефлексии
class MyClass
{
public function __construct(
/** @var string */
public $a,
) {}
}
$property = new ReflectionProperty(MyClass::class, 'a');
$property->getDocComment(); // "/** @var string */"
Как и в случае с док блоками, в объявляемых свойствах разрешены атрибуты. При переносе они будут присутствовать как в параметре конструктора, так и в свойстве класса.
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 как в далеком прошлом для объявления переменных класс, то объявляемые свойства не допускает такой синтаксис. Действительны только ключевые слова public, protected и private.
Этот код будет не валиден:
public function __construct(
var string $a,
) {}
public function __construct(
public string ...$a,
) {}
Все еще ждем дженериков…
У обоих ReflectionProperty и ReflectionParameter есть новый метод isPromoted() для проверки, объявляется ли свойство класса или параметр метода.
Поскольку конструкторам PHP не нужно следовать объявлению их родительского конструктора, то следует что, наследование разрешено. Если вам нужно передать свойства из дочернего конструктора в родительский конструктор, вам нужно будет передать их вручную:
class A
{
public function __construct(
public $a,
) {}
}
class B extends A
{
public function __construct(
$a,
public $b,
) {
parent::__construct($a);
}
}
Веб-разработчик со стажем программирования более 13 лет, постоянно учусь, люблю делать новые проекты.