PHP 8.5 clone() with syntax for readonly objects: visibility rules are not obvious
PHP 8.5 adds clone($obj, [‘property’ => $value]) syntax for updating properties during cloning. This is useful for immutable value objects where you previously had to call new static(…) with every constructor arg just to change one field.
The visibility rules tripped me up. From outside the class you can only clone-with public properties. For readonly properties that have no asymmetric visibility set, you cannot clone-with from outside the class at all. The property must be declared with public(set) or you need a with() method inside the class.
So if you want external callers to be able to create modified copies of a fully readonly object, you either add a with() helper method or declare your properties as public(set) instead of plain readonly.
The with() method pattern is what we ended up using. Keeps the cloning logic inside the class where it has visibility into the readonly properties:
One thing to be careful about with a generic with(array $props) approach: if you use array_filter to skip null values, you will incorrectly drop intentional null assignments when the property type allows null. Either accept named parameters like @oksdev showed, or explicitly check which keys are present in the array rather than filtering by value.
@andriy_m good catch. We do not have nullable properties in our value objects so it was not a problem for us, but it is a real footgun for the general case. Named per-field methods like withAmount() are safer and also more discoverable via IDE autocomplete.
```php blocks are runnable.