taras_boh16 May 2026 04:20

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.

Replies (3)
oksdev16 May 2026 05:08

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:

PHP
<?php
class Money
{
public function __construct(
public readonly int $amount,
public readonly string $currency,
) {}
public function withAmount(int $amount): static
{
return clone($this, ['amount' => $amount]);
}
public function withCurrency(string $currency): static
{
return clone($this, ['currency' => $currency]);
}
}
$price = new Money(1000, 'USD');
$discounted = $price->withAmount(800);
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
0
andriy_m16 May 2026 06:05

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.

0
taras_boh16 May 2026 07:10

@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.

0
Write a reply
Markdown. ```php blocks are runnable.