Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129992 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 92B211A00BC for ; Tue, 3 Feb 2026 09:45:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1770111945; bh=+0PqedKAW1OQBVDHnjw7PDeqXKGOPFIPAhzT20g4OG4=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=cvYqXoEc0LfV8vg2tVDHFcTKvt51bGzbgdlva0tG4b6l3YalfWh+3Hbt/NS5qXO/q 6Bf1nwdGEMcPorHHDUoMvbiFIP6Gti8Ed+aS2SheyyyraAvGuIwCq5r4GskDJpDacq IF0GkI6ibwDxzH2ET2RARReReXXKipk5Qokb1F03zXCg7LmyYUji8CGeaMelqMKBAZ zCCzbuveQ4g4v4wPsLByF0GN30QMzHARZYBUdJVwRc+0hEJh1mqhPQ30amZ0iB+OV5 2DVMn2QxuZ+O5HYPWFyc35jt7QVcSoz9aAoceemxInH8uwrMZT++P1TdHIu6V9yM1t fu+OkfGrhK6BQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 433C0180659 for ; Tue, 3 Feb 2026 09:45:44 +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=2.6 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_50, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS, FORGED_GMAIL_RCVD,FREEMAIL_FROM,FREEMAIL_REPLY,HTML_MESSAGE, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-qv1-f54.google.com (mail-qv1-f54.google.com [209.85.219.54]) (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:45:44 +0000 (UTC) Received: by mail-qv1-f54.google.com with SMTP id 6a1803df08f44-89503a3bb83so19414016d6.1 for ; Tue, 03 Feb 2026 01:45:38 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1770111938; cv=none; d=google.com; s=arc-20240605; b=DigAxoqjj4dmq2yn51oB5TOFquimiiGVnydbSXcEfw41ndGW90RxYiFgQJ3yU3N/3R Cy60eZWXKDuWDCAiDqJO43ZB4MNbOhGUwVo4wFHF9nkZUyKC4yAkQw2jJIhINKasPFh/ UX98+YRFKP2O1U4HJspDPICxBT1cwnpsa5lAsR/vg9U3kW/IaXi8l2ZuMh9U4qSG41iC fWvKxmCZaH6OrrgcTre6WPB13GdTO/zlj9gUqzTBMk1ocG2OhRzUg1zy0lIKMJwsCe+2 d8xtReWX+/k5q0qJCB+qysjjsXYujHeCprzync56k9/XW1E73lcTVIzkX2fuas3mst2S 2VoA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=881ptwNnZaSWRaARXDLyarogKLmW6I2p+XMxVIjIIFc=; fh=NauCm4jsxCwrDUWKopy7EgFmshK/3zuubIFdTiA9vLI=; b=V24lFTMOlqpdn9PRognIv0a09N3TF8wxhk+AjpFH6I1x3DswXMqDSzcsfK201m3WQF 66GrRQnjTQ6sfGt78toK8bUxKtKGafFIjC7BZY5ntJdOiYxCzLep/I4OFxNk3zIkN+7A DFbn2JliMQKQL+XzXR4tkL6mdLw/34P8TFAj3Qfv1R3iiV/+IJbpCKmDlPRDIz1Ndcba yk/M97A5bszFZtBfY21mVx31Uhtsq/WD2lErCWitt0Qo5FpEp1sNM0hGFxFg8awDjzRr W8bTG8YDvAlq9IwQ5dqAGKoakVZeAWGlldQBpqVZdZHWAV1k/4jl9LZiR0IF5g4vv5vy cxmw==; darn=lists.php.net ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770111938; x=1770716738; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=881ptwNnZaSWRaARXDLyarogKLmW6I2p+XMxVIjIIFc=; b=mP7J0al75fpxMZQ5UkdClhFj/NFc74bHYhR62v4Y27994RgQE0fC98CzvD1waiyzhX UC1CxbqjkBoGZQb1AcdC0dTUcFxPCGfkPv+yqQgH0Hb/3ChPdjWVtfi918lvK288cJxs hZqri1ShV67FiapkL0foNdUGK/AYtFjwfuCfHVdDCma3kHI16LcCdNn9OWUDECn6no8t 4ojaT25IfUGjPKEdz8umj5ZXxmbsM2N8b8+4E2I4Si8UGB8+zQTkpREJaTSGR48zCWpl 84e4hYyzM0GB7Acv+6i780SSNm6pGXw4sebaAf/8sgv6CnFciRDuZfAUWhHZvtQV/zCQ wR8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770111938; x=1770716738; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=881ptwNnZaSWRaARXDLyarogKLmW6I2p+XMxVIjIIFc=; b=odxQQEpnGt8xl9oTsWck0rc2tzChF9g5T71nAQOFmbgqnm72CNcryhoR/QKSdFaKAK n2Nv75ixjFclWpCESbdAhSJ8W9LJAig79spC8DMC95SdUt6gYDW4+8vkQaNJoNDrL1gV lOwk6cRX8SdWGQTIEbbJzD6AkLPfR9y1lI4mGtxmSl5ioabL0OIiC83A6k/ASilLJqvZ 1B9RePXVZLDd+RiSg5DyFi7nH7h2c92aNbuAk2kR+Ps1xpeog3A9h7qbxnbWIQslLbE+ A+77GmTGhd0OXdrTfZ7Mh+OSdJAJoz3S8tNnYKz5yRafZFzu5n3RNg/EPYczzLQyg6Fa OSiw== X-Gm-Message-State: AOJu0YyUB/yXvXGsWnnkxH4MO41y1F3Mpb9iPphEAjES7a/yQGN1Eewy HekiZp0Ilao6LFT0r3cpiqyHcxdh1TvpbbQtgM2WHUKlA3iW5NQZ0VVu9mtVlNBcatSl/CrXtEw jubgTFwV6tY5iiQInZwxZv3CG8zrtyZc= X-Gm-Gg: AZuq6aI13VQBZrjapVzJpfEWRrjNmqRyLKSZgUXDyd4Hbgjrtrp2PMrZz5GFXK8e1HE RV7IeVP7guCzjIQUUOESZceOv6LTX969Gt39W1vDbyGnps1g1EVm6qZwXxzLwEh8ZKjbeo8l3lg sv/rwsMEuvOk/e2kRe22HygB+q1vQZGMS3pGoqhzAmjzfpyfdXLHF8gXh29Z/h9hQuqzDFt13bI QeJOaUgn4n51DqvGEwQbFML6FmZdv9TKGRVnSodahj258dNN5OFECp2n08WthKpvJcgp6XYZEmE EA2rFRAF6DIWYqZuIWFE9F8R25TP1EYXbjd8uHqjSrtTvEBfisOgPPmXndwhNUxdN5M= X-Received: by 2002:ad4:5cc3:0:b0:880:4fff:7c5 with SMTP id 6a1803df08f44-894ea03a2bemr212294436d6.41.1770111938167; Tue, 03 Feb 2026 01:45:38 -0800 (PST) Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 References: <4b74f9a1-96d2-4104-abdd-fe56c5e7016c@app.fastmail.com> <585bfc82-a522-43ec-be42-b8945952fe8b@app.fastmail.com> <7baec64b-2f2c-49c7-b326-240a2cc14a46@app.fastmail.com> In-Reply-To: <7baec64b-2f2c-49c7-b326-240a2cc14a46@app.fastmail.com> Date: Tue, 3 Feb 2026 10:45:28 +0100 X-Gm-Features: AZwV_QjthrsYNMxUhHb53oPirsWk1L5_0QGK2YY47E29TxFwOodVE70oTv5AU3A Message-ID: Subject: Re: [PHP-DEV] [RFC] Allow Reassignment of Promoted Readonly Properties in Constructor To: Rob Landers Cc: PHP Internals List Content-Type: multipart/alternative; boundary="00000000000049898b0649e84da9" From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --00000000000049898b0649e84da9 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Le mar. 3 f=C3=A9vr. 2026 =C3=A0 10:30, Rob Landers a = =C3=A9crit : > > > On Tue, Feb 3, 2026, at 10:22, Nicolas Grekas wrote: > > > > 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, > > 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, > > Le lun. 2 f=C3=A9vr. 2026 =C3=A0 11:54, Marco Pivetta a =C3=A9crit : > > Hey Nicolas, > > > On Thu, 22 Jan 2026 at 16:34, Nicolas Grekas > 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 Bypas= s > 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 constructo= r > directly can reset it -- but you can't? > > This feels really inconsistent to me. > > =E2=80=94 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=E2=80=99ve 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 s= et > them first? > > FWIW, in my Records RFC, properties were fully mutable during constructio= n > 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 ca= ll > ''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 =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 > throws? > } catch(\Throwable) {} > } > } > > (I spent over a year thinking about this stuff ... so if you're intereste= d > in more edge cases, I can dig up my notes) > You're correct. Although such code explicitly decided to opt-in for ignoring anything from the parent, and that's always a very bad idea. > FWIW, in my Records RFC, properties were fully mutable during construction for exactly this reason. I wouldn't mind making readonly properties mutable during initial construction if we can get a consensus on this. I've not been brave enough to consider this was possible. I might be wrong :D --00000000000049898b0649e84da9 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


Le=C2=A0mar. 3 = f=C3=A9vr. 2026 =C3=A0=C2=A010:30, Rob Landers <rob@bottled.codes> a = =C3=A9crit=C2=A0:


Le=C2=A0mar. 3 f=C3=A9vr. 2026 =C3=A0=C2=A010:00, Rob La= nders <rob@bottled.codes> a =C3=A9crit=C2=A0:

On Tue, Feb 3, 2026, at 09:56, N= icolas Grekas wrote:
Hi Rob,

Le=C2=A0mar. 3 f=C3=A9vr. 2026 =C3= =A0=C2=A009:50, Rob Landers <rob@bottled.codes> a =C3=A9crit=C2=A0:

On Mon, Feb= 2, 2026, at 22:14, Nicolas Grekas wrote:
Hi Marco,

Le=C2=A0lun. 2 f=C3=A9vr. 2026 =C3=A0=C2=A011:54, Marco Pivetta= <ocramius@gmail= .com> a =C3=A9crit=C2=A0:
Hey Nicolas,

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

Here is a=C2=A0ne= w 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. This made me also think about Re= flectionClass::newInstanceWithoutConstructor().
I clarified this = in the RFC, see "Direct __construct() Calls Cannot Bypass Readonly&quo= t; and "Reflection: Objects Created Without Constructor".
Patch and PR updated also if anyone wants to run some code where this RF= C can be played with.

Cheers,
Nicolas

Hi Nicolas,

<= /div>
Under "Child Classes Can Reassign Parent Properties": t= his feels like a major footgun. Calling parent::__construct() won't all= ow a reset (per the rules of calling a constructor directly); which would c= ompletely break inheritance... but then in the examples it says that callin= g a constructor directly can reset it -- but you can't?

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

=E2=80=94 Rob

Yes, t= he text was ambiguous. The implementation allows parent::__construct() duri= ng the initial construction (normal inheritance), and only blocks explicit = __construct() calls after construction completed. I=E2=80=99ve clarified th= is 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 Record= s RFC, properties were fully mutable during construction for exactly this r= eason.=C2=A0



The existing behavior is preserved: if a reassignment fails, it throws a c= atchable 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 t= he parent cannot intercept it. But a try/catch in the child can catch of co= urse.

Does that answer your question?
<= /div>

So, this could end up with partial ap= plication of state? Or does it rollback? For example:

<= div>class Box {
=C2=A0 public function __construct(readonly int $= x, readonly int $y, readonly bool $isSquare =3D false) {
=C2=A0 = =C2=A0 $this->isSquare =3D $x =3D=3D $y;
=C2=A0 }
}<= /div>

class Square extends Box {
=C2=A0 public= function __construct(readonly int $size) {
=C2=A0 =C2=A0 $this-&= gt;isSquare =3D true;
=C2=A0 =C2=A0 try {
=C2=A0 =C2=A0= =C2=A0 parent::__construct($size, $size); // what is the state after it th= rows?
=C2=A0 =C2=A0 } catch(\Throwable) {}
=C2=A0 }
}

(I spent over a year thinking about this = stuff ... so if you're interested in more edge cases, I can dig up my n= otes)

You're correct. Altho= ugh such code explicitly decided to opt-in for ignoring anything from the p= arent, and that's always a very bad idea.

>= FWIW, in my Records RFC, properties were fully mutable during construction= for exactly this reason.=C2=A0

I wouldn't min= d making readonly properties mutable during initial construction if we can = get a consensus on this.
I've not been brave enough to consid= er this was possible. I might be wrong :D


--00000000000049898b0649e84da9--