On Tue, Jul 15, 2025, at 19:05, Nicolas Grekas wrote:
> Not sure it's really a contribution to the discussion but in case you missed 
> it, you can make public properties "public(set)":
> 
> final readonly class Response {
>     public function __construct(
>         public public(set) int $statusCode,
>         public public(set) string $reasonPhrase,
>         // ...
>     ) {
>         if($this->statusCode >= 600) {
>             throw new LogicException();
>         }
>     }
> }
> 
> Then your example works.
> That's ugly, but that doesn't have to stay that way.
> And I'm now wondering why is that not the default?
> 
> Nicolas

It "works" but it still results in an invalid state (statusCode >= 600 is never 
thrown). Previously, this kind of invalid object would only be possible via 
reflection and skipping the constructor. Now, we have a copy, without a 
copy-constructor to perform any validation or error checking before the copy 
completes; so basically invalid objects are incredibly easy/accidental to be 
made, unless everyone starts changing all validation to be done in 
getters/setters (which is not possible on readonly objects) or by keeping 
things protected(set) and ensuring that only specific functions set them... and 
probably make every readonly class final.

— Rob

Reply via email to