Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129991 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id 083DE1A00BC for ; Tue, 3 Feb 2026 09:30:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1770111058; bh=SQPWQIl3AXM3uqk6J7RZ+PutATXGJm8g3zQCpfCB27E=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=mGUAMhwucqlUGwtwQW1K0A1npE4GOVZV2knwgR/owJVLiv1ElJis2jvssjAZ4J4B9 JueP446XJqTmINnhmjJSXn4S4EFM4PWfu2fKLfq2IUNcR+RVvsmf/mvYvq+hnpmGxW nw/sKUZRJTVXY0VcpcPmJNAcvMu9fIYminb/Yg/Q9cPV9anCevQe/ODsLpQGYrEWAR aFwf9ePa4qBVXbtOo9CuZFPZJ1sD2XSJq2qaVRP3fNaYwq/Nq4QRFXc5Yd+EhqfFZS tI9yF8XjJQWIKDvO1+VQHn50xnr9qJZwZzIP4e8T8+azlUL2F8eXcJqMQHTYUl4GIz AxH9eGnYmiiRQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id A73591804C0 for ; Tue, 3 Feb 2026 09:30:57 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from fhigh-b2-smtp.messagingengine.com (fhigh-b2-smtp.messagingengine.com [202.12.124.153]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 3 Feb 2026 09:30:57 +0000 (UTC) Received: from phl-compute-12.internal (phl-compute-12.internal [10.202.2.52]) by mailfhigh.stl.internal (Postfix) with ESMTP id 3545B7A0052; Tue, 3 Feb 2026 04:30:51 -0500 (EST) Received: from phl-imap-05 ([10.202.2.95]) by phl-compute-12.internal (MEProxy); Tue, 03 Feb 2026 04:30:51 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:cc:content-type:content-type:date:date:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=fm1; t=1770111050; x= 1770197450; bh=lFWQYBMbUPTTwWeRrJWmh8jRJb7O6HJYrYYGGUFXg0c=; b=m jMV8H8lvzGcvYuYvP3W35hcGrtNqfW1h9ATw+KuzfRtrz/85rZkdlvKIjBXMAQj6 HNKxkRK4jErm+dhghRIIrvCjF1dahLE4OvvvSuj/973fPto/9UKdeXIfge28W50L C0+lQrXRNnzSdw2mqv9GHhxDYG3nyKyfabEFB59+LtGAxJKOQvqCu5ck/CgdmgKQ zmp6qIcN6rtFPtxIBNOa9Mlc4gakv2B8QxikSCysVUxwIykX9y3+rAIlbGpXbwYC PiMirf1vaKsixGbg1X7AzffDP8HKAy6omGE4pkrEv2wXUSIMr7odA6XK77oGLkB3 002CVvpuKZ/v+vXnQg8bw== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t= 1770111050; x=1770197450; bh=lFWQYBMbUPTTwWeRrJWmh8jRJb7O6HJYrYY GGUFXg0c=; b=ZNnaLyj7ek7874pyDcFXvQmZwtWwBTeW/PiiuR+M7Rj2TwMwxJC gNSz9o1HYcmtyd3v4Snf8jOheOxGpNkZbCL1k49rwWFjyxldfqpWZw/FMbLZ/N6m YBZVkbdGQMCXPAPFsTmPzsHZsdO9ICCCmUjipgdAa0IdNDwGJjTbozdBky68K9dz jihY0agAiqW/d0wKjIiM0xZbt0ixEVoE+88h54G7LSZZr/w61YNjOOYXlj6oeruE DwhImQLq/WYYem4BkuKURXM1Jj7cbOrBC2o+NNFsO5wrvtC6a1hYw6Khd6uBgqEe cXZiiD9bW8G5GAH2Jf4G/6YDTre7dE4VGSA== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefgedrtddtgddujeelleeiucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepofggfffhvfevkfgjfhfutgesrgdtreerredtjeenucfhrhhomhepfdftohgsucfn rghnuggvrhhsfdcuoehrohgssegsohhtthhlvggurdgtohguvghsqeenucggtffrrghtth gvrhhnpeffgeevveegtdffgfffvdfftddvheeuleegveekteffueeliedtgeekjeettdet keenucffohhmrghinhepphhhphdrnhgvthenucevlhhushhtvghrufhiiigvpedtnecurf grrhgrmhepmhgrihhlfhhrohhmpehrohgssegsohhtthhlvggurdgtohguvghspdhnsggp rhgtphhtthhopedvpdhmohguvgepshhmthhpohhuthdprhgtphhtthhopehnihgtohhlrg hsrdhgrhgvkhgrshdophhhphesghhmrghilhdrtghomhdprhgtphhtthhopehinhhtvghr nhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 9CC90182007A; Tue, 3 Feb 2026 04:30:50 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: AZ7fesbM8yI- Date: Tue, 03 Feb 2026 10:30:30 +0100 To: "Nicolas Grekas" Cc: "PHP Internals List" Message-ID: <7baec64b-2f2c-49c7-b326-240a2cc14a46@app.fastmail.com> In-Reply-To: References: <4b74f9a1-96d2-4104-abdd-fe56c5e7016c@app.fastmail.com> <585bfc82-a522-43ec-be42-b8945952fe8b@app.fastmail.com> Subject: Re: [PHP-DEV] [RFC] Allow Reassignment of Promoted Readonly Properties in Constructor Content-Type: multipart/alternative; boundary=f090c4d76a9b43c19f9340e3543eceb9 From: rob@bottled.codes ("Rob Landers") --f090c4d76a9b43c19f9340e3543eceb9 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Tue, Feb 3, 2026, at 10:22, Nicolas Grekas wrote: >=20 >=20 > Le mar. 3 f=C3=A9vr. 2026 =C3=A0 10:00, Rob Landers a =C3=A9crit : >> __ >> On Tue, Feb 3, 2026, at 09:56, Nicolas Grekas wrote: >>> Hi Rob, >>>=20 >>> Le mar. 3 f=C3=A9vr. 2026 =C3=A0 09:50, Rob Landers a =C3=A9crit : >>>> __ >>>> On Mon, Feb 2, 2026, at 22:14, Nicolas Grekas wrote: >>>>> Hi Marco, >>>>>=20 >>>>> Le lun. 2 f=C3=A9vr. 2026 =C3=A0 11:54, Marco Pivetta a =C3=A9crit : >>>>>> Hey Nicolas, >>>>>>=20 >>>>>>=20 >>>>>> On Thu, 22 Jan 2026 at 16:34, Nicolas Grekas > wrote: >>>>>>> Dear all, >>>>>>>=20 >>>>>>> Here is a new RFC for you to consider: >>>>>>> https://wiki.php.net/rfc/promoted_readonly_constructor_reassign >>>>>>=20 >>>>>>=20 >>>>>> What happens if one calls `$obj->__construct(1, 2, 3)` (on an alr= eady instantiated `$obj`) in the context of this patch? >>>>>=20 >>>>> Thanks for asking, I didn't think about this. This made me also th= ink about ReflectionClass::newInstanceWithoutConstructor(). >>>>> I clarified this in the RFC, see "Direct __construct() Calls Canno= t Bypass Readonly" and "Reflection: Objects Created Without Constructor". >>>>> Patch and PR updated also if anyone wants to run some code where t= his RFC can be played with. >>>>>=20 >>>>> Cheers, >>>>> Nicolas >>>>=20 >>>> Hi Nicolas, >>>>=20 >>>> Under "Child Classes Can Reassign Parent Properties": this feels li= ke a major footgun. Calling parent::__construct() won't allow a reset (p= er the rules of calling a constructor directly); which would completely = break inheritance... but then in the examples it says that calling a con= structor directly can reset it -- but you can't? >>>>=20 >>>> This feels really inconsistent to me. >>>>=20 >>>> =E2=80=94 Rob >>>=20 >>> Yes, the text was ambiguous. The implementation allows parent::__con= struct() during the initial construction (normal inheritance), and only = blocks explicit __construct() calls after construction completed. I=E2=80= =99ve clarified this in the RFC. >>>=20 >>> Nicolas >>=20 >> Will this result in a catchable error? I assume so, so a valid patter= n during inheritance might be to put these in a try/catch so children ca= n set them first? >>=20 >> FWIW, in my Records RFC, properties were fully mutable during constru= ction for exactly this reason.=20 >>=20 >>=20 >=20 > 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 c= all ''parent::__construct()'' to avoid it; a try/catch in the parent can= not intercept it. But a try/catch in the child can catch of course. >=20 > Does that answer your question? So, this could end up with partial application of state? Or does it roll= back? For example: class Box { public function __construct(readonly int $x, readonly int $y, readonly= bool $isSquare =3D false) { $this->isSquare =3D $x =3D=3D $y; } } class Square extends Box { public function __construct(readonly int $size) { $this->isSquare =3D true; try { parent::__construct($size, $size); // what is the state after it t= hrows? } catch(\Throwable) {} } } (I spent over a year thinking about this stuff ... so if you're interest= ed in more edge cases, I can dig up my notes) =E2=80=94 Rob --f090c4d76a9b43c19f9340e3543eceb9 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable


On Tue, Feb 3, 2026, at 10:22, Nicolas Grekas wrote:


Le ma= r. 3 f=C3=A9vr. 2026 =C3=A0 10:00, Rob Landers <rob@bottled.code= s> a =C3=A9crit :

On Tue, F= eb 3, 2026, at 09:56, Nicolas Grekas wrote:
H= i Rob,

Le mar. 3 f=C3=A9v= r. 2026 =C3=A0 09:50, Rob Landers <rob@bottled.codes> a =C3=A9= crit :
=
On Mon, Feb 2, 2026, at 22:14, Nicolas Grekas wr= ote:
Hi Marco,
<= div>
Le lun. 2 f=C3=A9vr. 2026 =C3=A0=  11:54, Marco Pivetta <ocramius@gmail.com> a =C3=A9crit :
Hey Nicolas,


On Thu, 22 Jan 2026 at 16:34, Nicolas Grekas <nicolas.grekas= +php@gmail.com> wrote:
Dear all,

Here = is a new RFC for you to consider:
=


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. Thi= s made me also think about ReflectionClass::newInstanceWithoutConstructo= r().
I clarified this in the RFC, see "Direct __construct() Ca= lls Cannot Bypass Readonly" and "Reflection: Objects Created Without Con= structor".
Patch and PR updated also if anyone wants to run so= me code where this RFC can be played with.

Chee= rs,
Nicolas

= Hi Nicolas,

Under "Child Classes Can Reassign P= arent Properties": this feels like a major footgun. Calling parent::__co= nstruct() won't allow a reset (per the rules of calling a constructor di= rectly); which would completely break inheritance... but then in the exa= mples it says that calling a constructor directly can reset it -- but yo= u can't?

This feels really inconsistent to me.<= /div>

=E2=80=94 Rob

Yes, the text was ambiguous. The implementation allows parent::__= construct() during the initial construction (normal inheritance), and on= ly blocks explicit __construct() calls after construction completed. I=E2= =80=99ve clarified this in the RFC.

Nicolas

Will this result in a cat= chable error? I assume so, so a valid pattern during inheritance might b= e to put these in a try/catch so children can set them first?
=
FWIW, in my Records RFC, properties were fully mutable du= ring construction for exactly this reason. 



The existing behavior is preserved: if a reassign= ment 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/ca= tch 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 u= p with partial application of state? Or does it rollback? For example:

class Box {
  public function __c= onstruct(readonly int $x, readonly int $y, readonly bool $isSquare =3D f= alse) {
    $this->isSquare =3D $x =3D=3D $y;
  }
}

class Square extend= s Box {
  public function __construct(readonly int $size)= {
    $this->isSquare =3D 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 int= erested in more edge cases, I can dig up my notes)

<= div id=3D"sig121229152">=E2=80=94 Rob
--f090c4d76a9b43c19f9340e3543eceb9--