Readonly classes in PHP 8.2
PHP 8.2 adds a new way of declaring classes: you can make them readonly. In practice, it means that all properties of that class will be readonly. This is especially useful when you're using data transfer objects or value objects, where a class only has public readonly properties.
In other words, instead of writing this:
class BlogData { public function __construct( public readonly string $title, public readonly Status $status, public readonly ?DateTimeImmutable $publishedAt = null, ) {} }
You can now write this:
readonly class BlogData { public function __construct( public string $title, public Status $status, public ?DateTimeImmutable $publishedAt = null, ) {} }
I've written about readonly properties before, so let's quickly summarise first:
- readonly properties can only be written once — usually in the constructor;
- only typed properties can be made readonly;
- they cannot have a default value (unless you're using promoted properties);
- the
readonly
flag cannot be changed during inheritance; and finally - you cannot unset readonly properties.
Since readonly classes are merely syntactic sugar for making all properties of that class readonly, it means that the same rules apply to readonly classes as well.
# Write once
All properties of a readonly class can only be written once, and can not be unset:
readonly class BlogData { /* … */ } $blogData = new BlogData(/* … */); $blogData->title = 'other'; unset($blogData->title);
# Only typed properties
A readonly class can only have typed properties:
readonly class BlogData { public string $title; public $mixed; }
# No static properties
Since readonly properties cannot be static, readonly classes cannot have any static properties:
readonly class BlogData { public static string $title; }
# No default values
Properties of a readonly class can not have a default value unless you're using promoted properties:
readonly class BlogData { public string $title = 'default'; }
readonly class BlogData { public function __construct( public string $title = 'default', // This works ) {} }
# No changes during inheritance
You cannot change the readonly
class flag during inheritance:
readonly class BlogData { /* … */ } class NewsItemData extends BlogData { /* … */ }
# No dynamic properties
Readonly classes also don't allow dynamic properties. This won't have a big impact since dynamic properties are deprecated in PHP 8.2 anyway, but means that you cannot add the #[AllowDynamicProperties]
attribute to readonly classes:
#[AllowDynamicProperties] readonly class BlogData { /* … */ }
# Reflection
Finally, there's a new reflection method to determine whether a class is readonly: ReflectionClass::isReadOnly()
. You can also use ReflectionClass::getModifiers()
, which will include the ReflectionClass::IS_READONLY
flag.