PHP 8.2 Readonly Classes — Complete Guide to Immutable Class Design
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
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