8.3 relaxed one readonly restriction and not the one everybody wants, so a wither is still a constructor call.
final readonly class Money
{
public function __construct(public int $cents, public string $currency) {}
public function withCents(int $cents): self
{
$copy = clone $this;
$copy->cents = $cents; // Error: cannot modify readonly
return new self($cents, $this->currency); // the only way
}
}
// what 8.3 DID allow: reinitialising a readonly property
// from inside __clone(), which helps deep-cloning and not this.
The __clone relaxation is narrower than it first reads: it permits a readonly property to be written during the clone magic method, which makes a deep clone of a nested object possible and does nothing for the wither pattern, where the caller wants to change a value. The constructor call stays, and on a class with seven properties it stays verbose — a private helper taking an overrides array is the usual containment.