Backend Development 7 min read

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中文网 Courses
php中文网 Courses
php中文网 Courses
PHP 8.1 Readonly Properties: Usage, Rules, and Practical Examples

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::$title

Key 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.

DTOobject-orientedReadonly PropertiesPHP 8.1
php中文网 Courses
Written by

php中文网 Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.