An in-depth look at PHP immutability that goes past the readonly keyword, explaining that readonly only prevents property reassignment but does not guarantee deep immutability. It covers interior mutability pitfalls with mutable objects like DateTime, why immutable objects should only contain immutable building blocks (DateTimeImmutable, enums, value objects), returning new values instead of using setters, safely designing immutable arrays and collections, normalizing mutable input at application boundaries, why cloning is not an immutability strategy, pairing immutability with constructor invariants, testing the contract rather than the keyword, and when mutability (e.g. Eloquent models) is still the right choice. Ends with a practical checklist for verifying true immutability.
Table of contents
IntroductionImmutability Is a Behavioral PromiseWhat readonly Guaranteesreadonly Is Not Deep ImmutabilityChoose Immutable Building BlocksReturn New Values for Domain OperationsArrays Need an Intentional DesignNormalize Mutable Input at the BoundaryCloning Is Not the Same as ImmutabilityKeep Invariants in the ConstructorTest the Contract, Not the KeywordWhen Not to Use ImmutabilityA Practical ChecklistConclusionQuestions this post answers
Does PHP's readonly keyword make an object fully immutable?
No, readonly only prevents a property from being reassigned after its first assignment; it does not make nested objects immutable. A readonly property can still hold a mutable object, such as a DateTime instance, and calling a mutating method on that nested object changes the observable state even though the container itself was never reassigned. This is called interior mutability. Developers hardening PHP domain models can compare readonly patterns and pitfalls on daily.dev.
Should I use DateTime or DateTimeImmutable inside a PHP value object?
Use DateTimeImmutable, since its modifying methods like modify() return a new instance and leave the original untouched, unlike DateTime whose methods mutate the object in place. Storing a DateTime inside a readonly class still allows its internal state to change after construction, breaking the immutability guarantee the value object is supposed to provide. daily.dev helps developers track best practices for building safe PHP value objects.
Is cloning an object in PHP enough to create a safe independent copy?
No, PHP's default clone is shallow, meaning the outer object is copied but nested object references are shared between the original and the copy. Modifying a nested property, such as an Address inside an Invoice, through the cloned object also changes the original unless __clone() is implemented to explicitly clone every mutable child, array of objects, and future property. Developers debugging shared-reference bugs after cloning can dig into PHP object patterns on daily.dev.
43K Impressions1 Comment