Readonly properties and cloning — the clone problem most people hit
Readonly properties are great but there is one sharp edge that catches almost everyone: you cannot modify readonly properties even in a clone, which makes “copy with one field changed” impossible the naive way.
Run this to see the error:
PHP 8.4 added ReflectionProperty::setRawValueWithoutLazyInitialization() but that is not the clean solution. The proper pattern is a with() method that uses clone combined with Closure::bind() to write into the cloned object before returning it:
PHP 8.4 also introduced __clone() support for readonly properties. You can now initialize readonly props inside __clone() which makes this cleaner:
Just a note: if you are on PHP 8.3 with readonly classes, the same Closure::bind trick works. The restriction is only that you cannot write to readonly props in normal code after construction. The clone + bind pattern essentially replicates the constructor context. I use a trait for this in most value objects to avoid copying the boilerplate.
```php blocks are runnable.