Re: [RFC] Allow Reassignment of Promoted Readonly Properties in Constructor

From: Date: Tue, 03 Feb 2026 09:30:30 +0000
Subject: Re: [RFC] Allow Reassignment of Promoted Readonly Properties in Constructor
References: 1 2 3 4 5 6 7  Groups: php.internals 
Request: Send a blank email to internals+get-129991@lists.php.net to get a copy of this message
On Tue, Feb 3, 2026, at 10:22, Nicolas Grekas wrote: > > > Le mar. 3 févr. 2026 à 10:00, Rob Landers <rob@bottled.codes> a écrit : >> __ >> On Tue, Feb 3, 2026, at 09:56, Nicolas Grekas wrote: >>> Hi Rob, >>> >>> Le mar. 3 févr. 2026 à 09:50, Rob Landers <rob@bottled.codes> a écrit : >>>> __ >>>> On Mon, Feb 2, 2026, at 22:14, Nicolas Grekas wrote: >>>>> Hi Marco, >>>>> >>>>> Le lun. 2 févr. 2026 à 11:54, Marco Pivetta <ocramius@gmail.com> a >>>>> écrit : >>>>>> Hey Nicolas, >>>>>> >>>>>> >>>>>> On Thu, 22 Jan 2026 at 16:34, Nicolas Grekas >>>>>> <nicolas.grekas+php@gmail.com <mailto:nicolas.grekas%2Bphp@gmail.com{4Ý >>>>>> ¹�³>¯õ`Æ8¶>> wrote: >>>>>>> Dear all, >>>>>>> >>>>>>> Here is a new RFC for you to consider: >>>>>>> >>>>>>> https://wiki.php.net/rfc/promoted_readonly_constructor_reassign >>>>>> >>>>>> >>>>>> What happens if one calls $obj->__construct(1, 2, >>>>>> 3) (on an already instantiated $obj) in the context >>>>>> of this patch? >>>>> >>>>> Thanks for asking, I didn't think about this. This made me also think >>>>> about ReflectionClass::newInstanceWithoutConstructor(). >>>>> I clarified this in the RFC, see "Direct __construct() Calls Cannot Bypass >>>>> Readonly" and "Reflection: Objects Created Without Constructor". >>>>> Patch and PR updated also if anyone wants to run some code where this RFC can >>>>> be played with. >>>>> >>>>> Cheers, >>>>> Nicolas >>>> >>>> Hi Nicolas, >>>> >>>> Under "Child Classes Can Reassign Parent Properties": this feels like a >>>> major footgun. Calling parent::__construct() won't allow a reset (per the rules of calling a >>>> constructor directly); which would completely break inheritance... but then in the examples it says >>>> that calling a constructor directly can reset it -- but you can't? >>>> >>>> This feels really inconsistent to me. >>>> >>>> — Rob >>> >>> Yes, the text was ambiguous. The implementation allows parent::__construct() during the >>> initial construction (normal inheritance), and only blocks explicit __construct() calls after >>> construction completed. I’ve clarified this in the RFC. >>> >>> Nicolas >> >> Will this result in a catchable error? I assume so, so a valid pattern during inheritance >> might be to put these in a try/catch so children can set them first? >> >> FWIW, in my Records RFC, properties were fully mutable during construction for exactly this >> reason. >> >> > > The existing behavior is preserved: if a reassignment fails, it throws a catchable Error. The > implicit CPP assignment in a parent constructor happens before the parent body, so a child cannot > "set first" and then call ''parent::__construct()'' to avoid it; a > try/catch in the parent cannot intercept it. But a try/catch in the child can catch of course. > > Does that answer your question? So, this could end up with partial application of state? Or does it rollback? For example: class Box { public function __construct(readonly int $x, readonly int $y, readonly bool $isSquare = false) { $this->isSquare = $x == $y; } } class Square extends Box { public function __construct(readonly int $size) { $this->isSquare = true; try { parent::__construct($size, $size); // what is the state after it throws? } catch(\Throwable) {} } } (I spent over a year thinking about this stuff ... so if you're interested in more edge cases, I can dig up my notes) — Rob

« previous php.internals (#129991) next »