PHP 8.1 Readonly Properties: Usage, Rules, and Practical Examples
This article explains PHP 8.1's new readonly property feature, shows how it differs from previous DTO patterns, demonstrates proper syntax, outlines restrictions such as type requirements and inheritance rules, and provides cloning work‑arounds with code examples for backend developers.
PHP 8.1 introduces the readonly keyword, allowing developers to declare properties that can be set only once, typically during object construction, which brings immutability to DTOs and value objects.
Traditional DTOs required explicit private fields and getter methods, as shown in the following example:
class BlogData {
/** @var string */
private $title;
/** @var Status */
private $status;
/** @var \DateTimeImmutable|null */
private $publishedAt;
public function __construct($title, $status, $publishedAt = null) {
$this->title = $title;
$this->status = $status;
$this->publishedAt = $publishedAt;
}
public function getTitle() { return $this->title; }
public function getStatus() { return $this->status; }
public function getPublishedAt() { return $this->publishedAt; }
}With PHP 8.0 promoted properties the same class can be written more concisely:
class BlogData {
public function __construct(
private string $title,
private Status $status,
private ?DateTimeImmutable $publishedAt = null,
) {}
public function getTitle(): string { return $this->title; }
public function getStatus(): Status { return $this->status; }
public function getPublishedAt(): ?DateTimeImmutable { return $this->publishedAt; }
}PHP 8.1 adds readonly to both normal and promoted properties, for example:
class BlogData {
public function __construct(
public readonly string $title,
public readonly Status $status,
public readonly ?DateTimeImmutable $publishedAt = null,
) {}
}Once a readonly property is assigned, any further modification results in a runtime error:
$blog = new BlogData(title: 'PHP 8.1: readonly properties', status: Status::PUBLISHED, publishedAt: now());
$blog->title = 'Another title'; // Fatal error: Cannot modify readonly property BlogData::$titleKey restrictions include:
Readonly properties must be typed; they cannot be declared without a type.
They cannot have a default value unless they are promoted properties.
The readonly flag cannot be added or removed in child classes, and the property cannot be unset.
Reflection now provides ReflectionProperty::isReadOnly() to detect readonly properties.
Because readonly properties cannot be changed or unset, cloning a DTO requires a custom approach. One solution is to create a new instance without invoking the constructor via reflection and manually copy or modify the desired properties, or use a trait that offers a with() method:
class BlogData {
use Cloneable;
public function __construct(public readonly string $title) {}
}
$dataA = new BlogData('Title');
$dataB = $dataA->with(title: 'Another title');In summary, readonly properties give PHP developers a straightforward way to enforce immutability in data‑transfer objects, improving code safety and predictability for backend applications.
php中文网 Courses
php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.