PHP 8.1: оператор new в инициализаторах

PHP 8.1: оператор new в инициализаторах

В PHP 8.1 добавлена интересная ​​функция, которая на первый взгляд может показаться мелочью, но я думаю, что она будет оказывать значительное повседневное влияние на многих людей.

Так в чем же польза от  оператора new в инициализаторах? Давайте посмотрим на пример, раньше мы все писали такой код:


class MyStateMachine
{
public function __construct(
private ?State $state = null,
) {
$this->state ??= new InitialState();
}
}

В этом примере с конечным автоматом мы хотели бы построить наш класс двумя способами: с начальным состоянием и без него. Если мы построим его без начального состояния, мы хотим, чтобы было установлено состояние по умолчанию. PHP,  конечно, поддерживает установку начальных значений непосредственно в списке параметров, но только для примитивных типов. Например, если бы наш конечный автомат использовал строки вместо объектов внутри, мы могли бы написать его конструктор следующим образом:


class MyStateMachine
{
public function __construct(
private string $state = 'initial',
) {
}
}

Таким образом, с PHP 8.1 мы можем использовать тот же синтаксис «значения по умолчанию» и для объектов. Другими словами: вы можете использовать new для аргументов по умолчанию (которые являются одним из примеров «инициализаторов»): 


class MyStateMachine
{
public function __construct(
private State $state = new InitialState(),
) {
}
}

 «Инициализаторы» - это больше, чем значения параметров по умолчанию, вот простое объяснение из RFC:

Этот RFC предлагает разрешить использование новых выражений внутри значений параметров по умолчанию, аргументов атрибутов, инициализаторов статических переменных и инициализаторов глобальных констант.

Да, вы правильно прочитали: атрибуты тоже есть в этом списке! Представьте себе простую библиотеку проверки, которая использует атрибуты для проверки ввода свойств. Возможно, она должна иметь возможность проверять элементы массива, примерно так:


class CreateEmailsRequest extends FormRequestData
{
#[ValidArray(
email: [new Required, new ValidEmail],
name: [new Required, new ValidString],
)]
public array $people;
}

До PHP 8.1 вы не могли писать такой код, потому что вам не разрешалось использовать new в атрибутах из-за способа их оценки, но теперь вы можете! 

Давайте взглянем на некоторые важные детали, о которых стоит упомянуть.

Создание только при необходимости

Такого рода «новые значения» будут создаваться только тогда, когда это действительно необходимо. Это означает, что в нашем первом примере PHP создаст только новый объект, если InitialState не содержит аргументов:


class MyStateMachine
{
public function __construct(
private State $state = new InitialState(),
) {
}
}

new MyStateMachine(new DraftState()); // InitialState не создается
new MyStateMachine(); // Сейчас создается

В случае атрибутов, например, объекты будут созданы только при newInstance вызове атрибута отражения

Не в свойствах класса

Также вы должны знать, что не можете использовать new значение по умолчанию в свойствах класса. Поддержка этой функции привела бы к множеству непредвиденных побочных эффектов, например, при сериализации и десериализации объектов:


class MyStateMachine
{
private State $state = new InitialState();
}

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


class MyStateMachine
{
private State $state;

public function __construct(
State $state = new InitialState(),
) {
$this->state = $state;
}
}

Ограниченный ввод

Возможно, вы уже догадались, но вы можете передать только ограниченный набор входных данных при создании новых объектов в инициализаторах. Например, вы не можете использовать переменные, оператор распаковки, анонимные классы и т.д.


Сергей Мухин

Веб-разработчик со стажем программирования более 9 лет, всегда в процессе учебы и созидания.

Есть вопросы?

Я почти всегда в режиме онлайн

Связаться со мной