clone copies the initialised state of a readonly property, so modifying the copy throws and every wither has to construct a new instance.
// does not work
public function withCurrency(string $c): self
{
$copy = clone $this;
$copy->currency = $c; // Error: already initialised
return $copy;
}
// what works, and gets verbose with six properties
public function withCurrency(string $c): self
{
return new self($this->cents, $c);
}
The verbosity compounds: a value object with six properties lists all six in every wither, and adding a seventh means editing each one. A private helper taking an overrides array and spreading it into named arguments contains the repetition and introduces a coupling between parameter names and the array keys, which is acceptable because the array never leaves the class. The language fixes this properly in 8.3.