PHP Readonly Properties and Classes Explained

Modern PHP has introduced powerful features to support immutability and prevent unintended data modification. This article explores readonly properties, introduced in PHP 8.1, and readonly classes, introduced in PHP 8.2. You will learn how these features work, their syntax, key rules, and how they simplify the creation of secure and robust Data Transfer Objects (DTOs) and Value Objects.

Readonly Properties (PHP 8.1)

Readonly properties allow you to declare class properties that can only be initialized once. Once a value is assigned to a readonly property, it cannot be modified or unset from any scope. This ensures that the data inside your object remains consistent throughout its lifecycle.

Key Rules for Readonly Properties

Code Example

class User {
    public readonly string $username;
    public readonly int $id;

    public function __construct(int $id, string $username) {
        $this->id = $id;
        $this->username = $username;
    }
}

$user = new User(1, 'alice');
// $user->username = 'bob'; // Throws a Error: Cannot modify readonly property

Readonly Classes (PHP 8.2)

PHP 8.2 built upon this concept by introducing readonly classes. Instead of marking every single property in a class as readonly, you can mark the class itself. When a class is declared as readonly, all of its non-static properties automatically inherit the readonly behavior.

Key Rules for Readonly Classes

Code Example

readonly class DatabaseConfig {
    public function __construct(
        public string $host,
        public int $port,
        public string $username
    ) {}
}

$config = new DatabaseConfig('127.0.0.1', 3306, 'root');
// $config->port = 5432; // Throws an Error: Cannot modify readonly property

Summary of Use Cases

Use readonly properties when you want a hybrid object where some properties can change over time (such as an updated timestamp or status), but core identifiers (like an ID) must remain constant.

Use readonly classes when you are building strictly immutable objects, such as Data Transfer Objects (DTOs), configuration objects, or Domain Value Objects, where the entire state of the object must remain untouched after creation.