Skip to content

PHP 8.2 Readonly Classes — Complete Guide to Immutable Class Design

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about PHP 8.2 Readonly Classes. We cover key concepts, practical examples, and best practices to help you master this topic.

PHP 8.2 introduces readonly classes that make all properties readonly by default, enabling full immutability with a single class keyword modifier for safer data transfer objects.

What You'll Learn

By the end of this tutorial, you'll create readonly classes, understand inheritance rules, implement immutable DTOs, handle dynamic properties, and design immutable value objects.

Why Readonly Classes Matter

Immutable objects reduce bugs from unintended mutations, simplify debugging, enable Caching, and make concurrent code safer.

Real-World Use

An API response layer uses readonly classes for all DTOs. Once constructed, response objects cannot be modified, preventing accidental data leaks or mutations in middleware.

Readonly Classes Path

flowchart LR
  A[PHP 8 Features] --> B[PHP 8.1 Enums]
  B --> C[Readonly Classes]
  C --> D[JSON Validation]
  D --> E[Property Hooks]
  C --> F{You Are Here}
  style F fill:#f90,color:#fff

Declaring Readonly Classes

Mark an entire class as readonly. All properties become implicitly readonly.

<?php
readonly class UserDTO {
    public function __construct(
        public int $id,
        public string $name,
        public string $email,
        public \DateTimeImmutable $createdAt,
    ) {}
}
$user = new UserDTO(id: 1, name: "Alice", email: "alice@example.com", createdAt: new \DateTimeImmutable());
echo $user->name;
// $user->name = "Bob"; // Error: Cannot modify readonly property

Readonly Inheritance

Readonly classes can extend other readonly classes. Non-readonly parent classes are not allowed.

<?php
readonly class BaseDTO {
    public function __construct(public int $id) {}
}
readonly class ExtendedDTO extends BaseDTO {
    public function __construct(
        int $id,
        public string $name,
    ) {
        parent::__construct($id);
    }
}
$dto = new ExtendedDTO(id: 1, name: "Product");

Dynamic Properties and Readonly

Readonly classes cannot have dynamic properties. All properties must be declared explicitly.

<?php
readonly class ConfigDTO {
    public function __construct(
        public string $host,
        public int $port,
        public string $protocol = "https",
    ) {}
}
$config = new ConfigDTO(host: "api.example.com", port: 443);
// $config->custom = "value"; // Error: Cannot create dynamic property

Immutable Collections

Use readonly classes with immutable collections for safe data structures.

<?php
readonly class OrderCollection {
    public function __construct(
        public array $orders,
    ) {
        $this->orders = array_map(fn($o) => $o instanceof OrderDTO ? $o : new OrderDTO(...$o), $orders);
    }
    public function withOrder(OrderDTO $order): self {
        return new self([...$this->orders, $order]);
    }
}
readonly class OrderDTO {
    public function __construct(
        public int $id,
        public string $product,
        public float $price,
    ) {}
}

Readonly and Cloning

Cloning a readonly class creates a new instance. Properties can be set via new constructor call.

<?php
readonly class Point {
    public function __construct(
        public float $x,
        public float $y,
    ) {}
    public function withX(float $x): self {
        return new self(x: $x, y: $this->y);
    }
    public function withY(float $y): self {
        return new self(x: $this->x, y: $y);
    }
}
$p1 = new Point(x: 1.0, y: 2.0);
$p2 = $p1->withX(5.0);
echo "{$p2->x}, {$p2->y}";

Common Mistakes

1. Trying to Set Properties After Construction

Readonly properties cannot be modified. Use withX() pattern that returns a new instance.

2. Extending Non-Readonly Classes

Readonly classes must extend only other readonly classes or not extend at all.

3. Using Mutable Objects Inside Readonly Properties

Readonly prevents property reassignment, not mutation of the referenced object. Use immutable objects internally.

4. Forgetting Dynamic Properties Are Not Allowed

Readonly classes cannot have dynamic (undeclared) properties. Declare all properties explicitly.

5. Confusing readonly Class with readonly Property

readonly class makes all properties readonly. readonly property makes a single property readonly in a non-readonly class.

Practice Questions

1. What does the readonly modifier on a class do?

Makes all properties of the class implicitly readonly. No dynamic properties are allowed.

2. Can a readonly class extend a non-readonly class?

No. Readonly classes can only extend other readonly classes.

3. How do you modify a property in a readonly class?

Create a new instance with the modified value using the withProperty pattern.

4. Are values inside readonly properties mutable?

Yes. readonly prevents reassignment of the property, not mutation of the object it references.

5. Challenge: Build an immutable configuration system using readonly classes.

<?php
readonly class DatabaseConfig {
    public function __construct(
        public string $host,
        public int $port = 5432,
        public string $database = "app",
        public string $user = "root",
        public string $password = "",
    ) {}
}
readonly class AppConfig {
    public function __construct(
        public DatabaseConfig $database,
        public string $env = "production",
        public bool $debug = false,
    ) {}
}

FAQ

Are readonly classes faster?

Slightly. PHP can optimize property access. The main benefit is safety, not speed.

Can I use readonly classes with JSON serialization?

Yes. json_encode works normally with readonly classes if properties are public.

What is the difference between readonly class and final class?

readonly makes properties immutable. final prevents inheritance. They are orthogonal.

Can readonly classes implement interfaces?

Yes. Readonly classes can implement any interface, supporting polymorphic design.

How do I handle default values in readonly classes?

Use constructor property promotion with defaults as shown in the Config example.

Mini Project: Readonly API Response Builder

Build immutable API response objects using readonly classes.

<?php
readonly class ApiResponse {
    public function __construct(
        public bool $success,
        public mixed $data = null,
        public ?string $error = null,
        public int $statusCode = 200,
        public array $metadata = [],
    ) {}
    public static function success(mixed $data, array $metadata = []): self {
        return new self(success: true, data: $data, metadata: $metadata);
    }
    public static function error(string $message, int $code = 400): self {
        return new self(success: false, error: $message, statusCode: $code);
    }
    public function withMeta(array $metadata): self {
        return new self(
            success: $this->success,
            data: $this->data,
            error: $this->error,
            statusCode: $this->statusCode,
            metadata: array_merge($this->metadata, $metadata),
        );
    }
}

What's Next

PHP 8.3 JSON Validation PHP 8.4 Property Hooks PHP PSR Standards

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro