A readonly class with a wither, one release from clone-with

8.3 relaxed __clone and did nothing for the case everybody has, which is a wither on a readonly class.

final readonly class Filter
{
    public function __construct(
        public ?string $search = null,
        public ?Status $status = null,
        public int $page = 1,
        public int $perPage = 20,
        public string $sort = 'created_at',
        public Direction $direction = Direction::Desc,
    ) {}

    public function withPage(int $page): self
    {
        return $this->with(['page' => $page]);
    }

    /** @param array<string, mixed> $overrides */
    private function with(array $overrides): self
    {
        return new self(...[...get_object_vars($this), ...$overrides]);
    }
}

The spread of get_object_vars into named arguments contains the repetition and introduces a coupling between property names and array keys that no tool checks — acceptable because the array never leaves the class. It also breaks the moment a promoted parameter is renamed without the override key changing, which is the sort of thing a test with one wither per property catches and nothing else does.