readonly properties and clone don't work well together. If you write "with" methods that return a copy with one value changed, a readonly property gets in the way. This RFC from Volker Dusch and Tim Düsterhus lets clone take a second argument: an array of properties to change on the copy.
How it works
Before, a withStatus() method on a readonly class had to rebuild the object from scratch:
public function withStatus($code, $reasonPhrase = ''): Response { $values = get_object_vars($this); $values['statusCode'] = $code; $values['reasonPhrase'] = $reasonPhrase; return new self(...$values); }
Now it can do this:
public function withStatus($code, $reasonPhrase = ''): Response { return clone($this, [ "statusCode" => $code, "reasonPhrase" => $reasonPhrase, ]); }
clone now behaves like a function call, so clone $x, clone($x) and clone($x, [...]) all work. You can also pass it as a callable, as in array_map(clone(...), $objects).
The rules:
__clone()runs first, and then the new values are applied.- Values are set in the order they appear in the array, and the first error stops the clone.
- Each value is set like a normal assignment. The only difference is that
readonlyproperties can be written one more time. - Visibility still applies, so you can't change a private property from outside the class.
- Property hooks and
__set()run, and dynamic properties follow the usual rules.
A public readonly property is protected(set) by default. To change it from outside the class, it needs public(set), which is how PHP already works.
What it means for existing code
Nothing breaks. The old clone $x syntax works as before, and __clone() doesn't change. You can't define your own clone function in a namespace or turn it off with disable_functions. Extensions with custom clone handling need an update to support the new form.
The vote
Accepted. Voting closed on June 18, 2025 with 16 in favor and 4 against, meeting the two-thirds majority it needed. It shipped in PHP 8.5.