Re: [RFC] Allow Reassignment of Promoted Readonly Properties in Constructor
| From: | Nicolas Grekas | Date: | Fri, 20 Feb 2026 11:40:28 +0000 |
| Subject: | Re: [RFC] Allow Reassignment of Promoted Readonly Properties in Constructor | ||
| References: | 1 2 3 4 5 6 7 8 9 10 11 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-130105@lists.php.net to get a copy of this message | ||
Hi everyone,
Le jeu. 19 févr. 2026 à 10:49, Nicolas Grekas <nicolas.grekas+php@gmail.com>
a écrit :
> Hi Tim,
>
> Le mer. 18 févr. 2026 à 22:29, Tim Düsterhus <tim@bastelstu.be> a écrit :
>
>> Hi
>>
>> On 2/16/26 19:20, Nicolas Grekas wrote:
>> > To be sure I understood you well: you are suggesting that mutability
>> should
>> > be scoped to the constructor that declares the property (not any
>> > constructor on the object).
>>
>> Yes, because otherwise you might rely on implementation details of the
>> parent constructor: Depending on whether the parent constructor
>> reassigns internally, your reassignment in the child constructor either
>> succeeds or fails.
>>
>> > This makes sense and I’ve implemented exactly that model:
>> > - Reassignment is allowed only while the declaring class constructor is
>> > active (including methods/closures called from it).
>> > - A child constructor can no longer reassign a parent-declared promoted
>> > readonly property.
>> > - “Child sets first, then parent::__construct()” now throws as expected.
>> > - The thrown Error is catchable from the child (around
>> > parent::__construct()), but not from inside the parent body before
>> implicit
>> > CPP init.
>> > - Calling __construct() on an already-constructed object still cannot
>> > mutate readonly state.
>> >
>> > I also updated the RFC text and examples to state this explicitly, and
>> > added/updated tests for the inheritance/preemption scenarios.
>>
>> Thank you. I've checked the RFC and the explanation and semantics make
>> sense to me. I've also reviewed (parts) of the tests and provided some
>> feedback there. I'll take another look at the tests when you made the
>> adjustments to make sure that everything in the RFC is properly tested
>> to make sure we didn't miss and edge case.
>>
>> > Anything else?
>>
>> Yes, there is one edge case related to inheritance that isn't mentioned
>> in the RFC and from what I see it's not tested either.
>>
>> Child classes can redefine readonly properties and they are then “owned”
>> by the child class. Thus we need to explain what happens in that case.
>> I've prepared example for the three relevant cases I can think of. The
>> follow from the existing semantics in a straight-forward fashion, but
>> it's good to spell them out explicitly (and particularly test them).
>>
>> 1. Parent uses CPP, child redefines and reassigns.
>>
>> class P1 {
>> public function __construct(
>> public readonly string $x = 'P',
>> ) { }
>> }
>>
>> class C1 extends P1 {
>> public readonly string $x;
>>
>> public function __construct() {
>> parent::__construct();
>>
>> $this->x = 'C'; // This should fail.
>> }
>> }
>>
>> 2. Parent uses CPP and reassigns, child redefines.
>>
>> class P2 {
>> public function __construct(
>> public readonly string $x = 'P1',
>> ) {
>> $this->x = 'P2'; // This should be legal.
>> }
>> }
>>
>> class C2 extends P2 {
>> public readonly string $x;
>>
>> public function __construct() {
>> parent::__construct();
>> }
>> }
>>
>> 3. Parent uses CPP, child uses CPP redefinition.
>>
>> class P3 {
>> public function __construct(
>> public readonly string $x = 'P',
>> ) { }
>> }
>>
>> class C3 extends P3 {
>> public function __construct(
>> public readonly string $x = 'C1',
>> ) {
>> parent::__construct(); // This should fail.
>> }
>> }
>>
>>
>
> Thanks, I've added new test cases to cover this.
> I've also improved tests as suggested on the PR.
> And finally I updated the implementation to reuse IS_PROP_REINITABLE
> instead of adding new flags + use an approach that doesn't require walking
> the call stack.
> PR and RFC updated accordingly, all green.
>
One last update following more review comments by Tim: the reassign window
is now scoped to the CPP-owning constructor.
See updated wording at
https://wiki.php.net/rfc/promoted_readonly_constructor_reassign
Cheers,
Nicolas