With the release of PHP 8.2, a significant enhancement was introduced to object-oriented programming (OOP): the readonly class. This feature builds on the success of readonly properties introduced in PHP 8.1, bringing immutability to entire classes rather than just individual properties. A readonly class ensures that all its properties are immutable once assigned, making it a powerful tool for writing safe, predictable, and maintainable code.
In this article, we will explore the concept of a readonly class in PHP, how it works, its benefits, and when to use it. We will also cover practical examples and usage scenarios using PHP’s latest syntax.
What is a Readonly Class in PHP?
A readonly class in PHP is a class where all properties are readonly by default. This means that any property within a readonly class can only be assigned once, typically during object construction. Once initialized, the properties cannot be modified for the lifetime of the object, enforcing a strict immutability constraint across the entire class.
Key Features of a Readonly Class:
- All properties in the class are implicitly readonly, meaning they can only be written to once.
- After initialization, the values of the class properties cannot be changed, promoting immutability.
- Properties cannot be changed directly or indirectly, even through methods.
- It simplifies immutability without requiring the explicit
readonly
keyword for every property.
Defining a Readonly Class
To declare a readonly class, PHP 8.2 introduced the readonly
keyword. This keyword can be applied directly to a class definition. Once a class is declared as readonly, all its non-static properties automatically inherit the readonly behavior, and you no longer need to specify the readonly
modifier for each individual property.
Here's an example of how to define and use a readonly class in PHP 8.2:
<?php
readonly class User {
public string $username;
public int $id;
public function __construct(string $username, int $id) {
$this->username = $username;
$this->id = $id;
}
}
$user = new User('john_doe', 101);
echo $user->username; // Outputs: john_doe
echo $user->id; // Outputs: 101
// Trying to modify any property will cause a runtime error
// $user->username = 'jane_doe'; // Error: Cannot modify readonly property User::$username
Benefits of Using Readonly Classes
Readonly classes offer several advantages, particularly when working with immutable data or domain models. Here are some of the key benefits:
1. Enforced Immutability
Readonly classes make it effortless to implement immutable data structures. By defining an entire class as readonly, you guarantee that none of its properties can be modified after initialization. This is ideal for value objects, DTOs (Data Transfer Objects), or objects that should remain unchanged after creation.
2. Simpler Code
When using readonly properties in previous PHP versions, you had to manually mark each property with the readonly
keyword. Now, with readonly classes, you can apply immutability to all properties without repeating the modifier. This reduces code verbosity and improves readability.
3. Data Integrity
Readonly classes help maintain data integrity by ensuring that critical object state cannot be modified after it is set. This makes your code safer and less prone to bugs caused by unintended side effects or changes to object properties.
4. Improved Thread Safety
Immutability improves concurrency and thread safety in scenarios where multiple parts of the program access the same object. Since readonly classes prevent changes to object properties, you avoid issues like race conditions and shared state mutations.
5. Cleaner Design Patterns
Readonly classes work well with design patterns like Value Objects or Entities in Domain-Driven Design (DDD), where immutability is a core principle. They also align with the Command Query Responsibility Segregation (CQRS) pattern, where objects are only queried for their state without modifying it.
Working with Readonly Classes: Practical Examples
Let’s explore a few practical examples to see how readonly classes can be applied in real-world scenarios.
Data Transfer Objects (DTOs)
DTOs are objects used to transfer data between different parts of an application. They often contain multiple fields, and readonly classes are an excellent way to ensure that the data remains unmodified after construction.
<?php
readonly class UserProfileDTO {
public string $name;
public string $email;
public string $role;
public function __construct(string $name, string $email, string $role) {
$this->name = $name;
$this->email = $email;
$this->role = $role;
}
}
$profile = new UserProfileDTO('John Doe', 'john@example.com', 'admin');
echo $profile->name; // Outputs: John Doe
echo $profile->email; // Outputs: john@example.com
echo $profile->role; // Outputs: admin
// $profile->email = 'jane@example.com'; // Error: Cannot modify readonly property UserProfileDTO::$email
In this scenario, UserProfileDTO
is a simple, immutable object that transfers user profile data between different parts of an application. By using a readonly class, the integrity of the data is preserved, and accidental changes are prevented.
Configuration Classes
Configurations are often static and immutable throughout the lifetime of an application. You can use a readonly class to model configuration data.
<?php
readonly class AppConfig {
public string $appName;
public bool $debugMode;
public function __construct(string $appName, bool $debugMode) {
$this->appName = $appName;
$this->debugMode = $debugMode;
}
}
$config = new AppConfig('MyApp', true);
echo $config->appName; // Outputs: MyApp
echo $config->debugMode ? 'true' : 'false'; // Outputs: true
// $config->debugMode = false; // Error: Cannot modify readonly property AppConfig::$debugMode
By using a readonly class for application configuration, you can be certain that configuration values remain constant throughout the runtime of your application, improving stability.
Considerations When Using Readonly Classes
While readonly classes are powerful, there are a few important considerations to keep in mind:
-
No Default Values for Properties: In PHP, readonly properties must be initialized during construction or directly at the point of declaration. This ensures that properties have valid values and prevents uninitialized readonly properties.
-
Dynamic Properties are Not Allowed: You cannot dynamically add properties to readonly classes. All properties must be declared within the class definition.
-
Immutability: While immutability is often beneficial, it may not be suitable for all use cases. If your class requires frequent updates to its properties, a readonly class is not the best choice.
-
Exceptions for Static Properties: Static properties in a readonly class do not inherit the readonly constraint. This is because static properties belong to the class itself, not to individual instances.
Conclusion
Readonly classes in PHP 8.2 are a powerful tool for developers who want to enforce immutability at the class level. By making all properties in a class readonly by default, this feature simplifies the creation of immutable objects, improves data integrity, and enhances the predictability of your code. Readonly classes are especially useful in scenarios such as value objects, data transfer objects (DTOs), and configuration classes.