PHP 8.2: Readonly-классы / классы только для чтения

Источник: «Readonly classes in PHP 8.2»
В PHP 8.2 введён новый способ объявления классов: readonly-классы. Поскольку readonly-классы — просто синтаксический сахар для того, что бы сделать все свойства класса доступными только для чтения, это означает, что те же правила применяются и к readonly-классам.

На практике, добавление в PHP 8.2 readonly-класса, это означает, что все свойства этого класса — readonly-свойства, доступны только для чтения. Это полезно при использовании DTO или VO, когда класс содержит только публичные readonly-свойства.

Другими словами, вместо того, чтобы писать так:

class BlogData
{
public function __construct(
public readonly string $title,
public readonly Status $status,
public readonly ?DateTimeImmutable $publishedAt = null,
) {}
}

Теперь вы можете писать так:

readonly class BlogData
{
public function __construct(
public string $title,
public Status $status,
public ?DateTimeImmutable $publishedAt = null,
) {}
}

Я уже писал о readonly-свойствах, поэтому давайте для начала быстро всё просуммируем:

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

Задаётся один раз

Все свойства readonly-класса могут быть заданы только один раз и не могут быть удалены:

readonly class BlogData { /* … */ }

$blogData = new BlogData(/* … */);

$blogData->title = 'other'; // Not works

unset($blogData->title); // Not works

Только типизированные свойства

Readonly-класс может иметь только типизированные свойства:

readonly class BlogData
{
public string $title;

public $mixed; // Not works
}

Без статических свойств

Поскольку readonly-свойства не могут быть static, readonly-класс не может иметь статических свойств:

readonly class BlogData
{
public static string $title; // Not works
}

Без значений по умолчанию

Свойства readonly-класса не могут иметь значение по умолчанию, если вы не используете свойства определяемые в конструкторе:

readonly class BlogData
{
public string $title = 'default'; // Not works
}
readonly class BlogData
{
public function __construct(
public string $title = 'default', // This works
) {}
}

Никаких изменений при наследовании

Вы не можете изменить readonly флаг класса во время наследования:

readonly class BlogData { /* … */ }

class NewsItemData extends BlogData { /* … */ } // Not works

Без динамических свойств

Readonly-классы не допускают использования динамических свойств. Это не окажет большого влияния, поскольку динамические свойства устарели в PHP 8.2, но это означает, что вы не можете добавить атрибут #[AllowDynamicProperties] для readonly-класса

#[AllowDynamicProperties] // Not works
readonly class BlogData { /* … */ }

Reflection

Наконец, есть новый метод ReflectionClass для определения является ли класс readonly: ReflectionClass::isReadOnly(). Вы также можете использовать ReflectionClass::getModifiers(), который будет включать флаг ReflectionClass::IS_READONLY.

Дополнительные материалы

Предыдущая Статья

PHP 8.1: Клонирование и изменение readonly-свойств

Следующая Статья

PHP 8.2: Что нового. Изменения и новый функционал.