As the code base of a project grows larger and larger, maintainability, predictability and “clean code” is getting more and more a key strategy for successful projects. While it is totally ok to have “quick and dirty” smelling code for prototypes and MVP’s, things change dramatically for scaling projects in production.
In this blog post, I want to address the big topic of immutability and highlight one major feature: readonly classes. First, I want to give a theoretic background before I place it in the context of PHP and give my final opinion about that. Stay tuned 🙂
Readonly Classes: What and Why?
In the ever-evolving realm of software design, there’s been a rising interest in immutability and its merits. While many languages offer constructs that promote immutability, like “readonly” properties, the theoretical concept extends to broader structures, including the idea of “readonly classes”.
At its core, immutability refers to the inability of an object to change after its creation. In practical terms, once an object is initialized with a set of values or properties, those values cannot be altered. In particular, immutability through readonliness provides the following advantages:
- Predictability: Immutable objects are inherently more predictable. Since they don’t change, you don’t have to worry about unforeseen side effects altering their state.
- Safety in Concurrency/Robustness: Immutable objects are thread-safe by design. Multiple threads can access an immutable object without the risk of one thread changing the object’s state in a way that might surprise another. Readonly classes can serve as sturdy building blocks in a system, guaranteeing that they won’t unexpectedly change state.
- Easier Debugging: When things go wrong, there’s less state to track, making it easier to diagnose issues.
- No Deep Cloning: With truly immutable objects, there’s no need to deep clone objects for safety since the originals can’t be altered.
On the other hand, this approach brings its downsides as well. For instance, continuously creating new instances instead of modifying existing ones can have memory and performance implications. Another point is, that immutability locks data structures, requirs redesign in worst case or new constructs when requirements evolve.
Immutability and Readonly Classes in PHP
PHP introduced the readonly keyword with version 8.1. Back then, it was only possible to set readonly properties, either directly in the class declaration or constructor property promotion. With version 8.2, the feature get extended by declaring the entire class readonly. Readonly classes are therefore a useful extension and in this context only syntactic sugar.
So instead of doing this:
public function __construct(
public readonly Engine $engine,
public readonly Color $color,
public readonly Brand $brand,
public readonly FuelType $fuelType,
we can simply do this:
readonly class Car
public function __construct(
public Engine $engine,
public Color $color,
public Brand $brand,
public FuelType $fuelType,
In essence, setting a class to “readonly” not only ensures all its properties are immutable but also prevents the addition of dynamic properties to the class. Readonly classes can only extend readonly classes.
The theory behind “readonly classes” or any immutable structure is rooted in the desire for more predictable, maintainable, and robust software design. While the implementation details might differ from one language to another, the underlying principles remain universally valuable. As software complexity grows, so does the importance of embracing techniques that reduce unpredictability — and immutability is a shining star in that arena.
In summary, the advantages of immutability are numerous and have already been explained in detail in this paper. The inherent support provided by the programming language not only simplifies the process, but also helps in the early detection of errors, rather than simply relying on conventions such as readonlines. Marking classes as read-only and possibly final serves as a checkpoint for developers, especially those new to the field. It encourages reflection on software design principles and gets them thinking about why certain elements resist modification, inheritance, or extension. This not only leads to good programming habits, but also deepens the understanding of basic design principles.