Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:130095 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 9F7FE1A00BC for ; Thu, 19 Feb 2026 09:49:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1771494584; bh=P8qaZWvJmWDgm8Qn/vmC9PehVCWyXkAWinXaJBhjAMQ=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=gCmRxTuq0D0t7K8anTnHsFmgfS5RU8zhRIVYervFsHK+HCNx3NJTKnkUQp4vKn5a0 Zs/hEvcOf2b/e6nYHVkWz6SGFf2XmPvrYn3pW2dLaH7SALi+4wwsyG+jMlwlTEtzQN iNlzG4cBS22sroQmZ4X6+w98dYgFDtzUlV62bKl0LyRf7I7MIBWujoQwbg7MeyWg98 mDEjvW7ZvwevUDxpQ7b0Nc7p06KP4Ej0bAHLskyDANYPDkwgDcVFLPHpRSUfkeAX4d lqbk31kQxSkPznAd4kW3IDJSBJYDdIGlzNgP2gX37KUWlDYVyrWFRS8A7t3AixoaRl Ab4wGU48R+rLg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 2FFC2180087 for ; Thu, 19 Feb 2026 09:49:43 +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.6 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_50, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS, FREEMAIL_FROM,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-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) (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 ; Thu, 19 Feb 2026 09:49:42 +0000 (UTC) Received: by mail-qv1-f50.google.com with SMTP id 6a1803df08f44-896fb37d1f0so12738996d6.2 for ; Thu, 19 Feb 2026 01:49:37 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1771494577; cv=none; d=google.com; s=arc-20240605; b=B5NWIb+N2cOey/vyyYfgjR6mZ5a23UHpzQv1T9L0lmq8QgNq81RmY/FaRs9WsvJNBI /NpDcVYbeO30VSfApjQz9CaNwq0Qu0FZ1U1mijGVapLORFlyqsx0dnN9k0dfKmXunU5+ xR8Oy7oLJn56zPm9GU98JOZWulooWl4P5kvVxs9n3CdMpuk29uoYPdMkCokZffReIe1Y 7eJgPLOw05gtLICGqd7d1CD2FMTrJbg4QJDOCse18qrCqh19N8wm1IWe+ODpVKoxIATc TuXDhEAsj8qS1yptB5Qqx+zA+4C4nRVNvVrvtRJPMzdP3T6NgXdpSPgKnTcIobSdDrOO e7Cg== 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=JRB7wDJMOryAMyK1u8XZq52xGSPhkVythYwM4qhuk04=; fh=Xr/ogOWGTRFA1d+q7p9CE+6uwwIHZm+lXGw7EES2NUs=; b=TBaPIKvsdIV8uIl/o93we6ylVccZusxmMz5R84c5GRqioeDmmWPgGbRdKHSKBmS3GJ OBmduNxLpHSMXT/RO2mu9EFXi5+Xwwk/mufI0sl7kxS5Eemtqy3fDFDDrSqUsDQPdc3w uPRN0UhQZA5jd+VooQVKN0lFR4Y2A+d4zIamhKAUpYRJ0NUGDug2/6rdxUsvWYEcua5A Kr9mU9TOoNm1ka5vrzAOUo81FYHmApFS+ED7HNvQXN+1EAMC2nF6U4sUo0KmViNUeJSQ rv6XTMHUAXifE9UnDTmSeVPTbQck2KfeGBw+M+u9rtACgCgBW+OH5jp792NsJJuTXLT/ PWsw==; 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=1771494577; x=1772099377; 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=JRB7wDJMOryAMyK1u8XZq52xGSPhkVythYwM4qhuk04=; b=L9WLFEfehCugwpUSVkEOxEoPbis3oXiK1ck2MTsy/fVNfwEC0BDS1vR6nn3yRNE7kC +mF85OyM8qNh3Iq+hD7pRxDVcPd0TEhO1iWURNIrT9a3ZtGD+BgzX4rSKunm2flJJSnJ VCoOIgcBMvRsrWDzKnrhQCDy+XckCYV/ZWMLn0h0xfhZcgUATnMuXfvW+1u96VIsn67o c14XTrupzLWxF0DUc8bBSadzyisHgrNNOVNnGtDpypyeU2nRK/+BxblTyk+xbhJZJkNp fBAr3D4PbKQ6BkBTnl8kud2+rDaeGlsfz4sULvVIeMvxMAGCQoF5ZwBN3GgURq/ykA4v drDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771494577; x=1772099377; 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=JRB7wDJMOryAMyK1u8XZq52xGSPhkVythYwM4qhuk04=; b=rPqjyF2ug6E/nJsbO7THoIzI4YUcwoMJAN9BcVFWhQ5sF0aN7Tc2o8fVL6K7sgEzO9 Ewt2teukt7UXuPmLk+nwAAjDougo0Iy/pwIR5PJGxUqawpC1Chw+M+kDI+Gj7i86gwdq BIqJW9GTTXVVaElOrDCcEbRQHsfTE88QS3bT7MBr9K69qdl4RKS/wsZRe8dNODhAHz1c J0TEsZij4f9HW7W/9R5fVyv1UOEe1li6ldu+D0KgItJo2zxeKePSXzDmXxnO/N2Pp3dE 8u2VpnvClXFeCjkUv9cmq4fVDk0fOWJZ1oh6CGk+f2Tku5/dz+Q2YA3r+DKBMoon5q8e N3oQ== X-Gm-Message-State: AOJu0Yw1Uay8y9eimjKWlJGmqpC2BlLFMzVV2LHUzAJQMQQyCxRd+Yg2 czfg5bLTxpwJWBaDPQ3rwdwF9t+FtSyGoRutUEGCAdIA4Ap2MW6nePXkK9KL6DIoHS9GCser4jy 0R8AAVveWXlpxWvWZkXCYrWu77wKAlhrY1UZN X-Gm-Gg: AZuq6aJIrM7gQhB7dIsU64CQutWnsXvtXYt2DuAbIfKlcqfi3RrZ2WQFbZ+2stHLOSY qc69eg1tXr08vOKcreIKZdCzK6ZXbVjcJXFYhS4lkOpqPvtmUjDptVSJ3EciH39zhol3onq2mED Q73c6MhMdGvu3SDH4SyP7yLAgNPGof/QFdePeCrp8gtFSKpX/F06wvBIgEXpubE34M65VSYdAM7 zFQ2SwU8/pgQvth6XUZ3BTPdPNdpN8msyxGlJLufwwxaovrs9VsDgzCq8RR2IaQ3Z7JkBDxOcxg Wgr0hHt5/Igwo38SI2Ndje9QWu15K4MuRr9sXjnpeZIJaaoHooeWCx3ao7EKm8+S9TvE X-Received: by 2002:a05:6214:2a87:b0:888:4930:82ab with SMTP id 6a1803df08f44-897404fa0f3mr245913316d6.71.1771494577168; Thu, 19 Feb 2026 01:49:37 -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> <3cd0d869-5a5b-467c-8eb4-3f8647ba7aa7@bastelstu.be> In-Reply-To: <3cd0d869-5a5b-467c-8eb4-3f8647ba7aa7@bastelstu.be> Date: Thu, 19 Feb 2026 10:49:27 +0100 X-Gm-Features: AaiRm53jPfPAmhRuZ4ceOd8PLz4e4Q-o1fKvhUjmc1oW40_Am5pMFo579cXNYzs Message-ID: Subject: Re: [PHP-DEV] [RFC] Allow Reassignment of Promoted Readonly Properties in Constructor To: =?UTF-8?Q?Tim_D=C3=BCsterhus?= Cc: PHP Internals List Content-Type: multipart/alternative; boundary="000000000000fe652c064b2a382b" From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --000000000000fe652c064b2a382b Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Tim, Le mer. 18 f=C3=A9vr. 2026 =C3=A0 22:29, Tim D=C3=BCsterhus a =C3=A9crit : > 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=E2=80=99ve 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. > > - =E2=80=9CChild sets first, then parent::__construct()=E2=80=9D now th= rows 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 =E2=80= =9Cowned=E2=80=9D > 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 =3D 'P', > ) { } > } > > class C1 extends P1 { > public readonly string $x; > > public function __construct() { > parent::__construct(); > > $this->x =3D 'C'; // This should fail. > } > } > > 2. Parent uses CPP and reassigns, child redefines. > > class P2 { > public function __construct( > public readonly string $x =3D 'P1', > ) { > $this->x =3D '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 =3D 'P', > ) { } > } > > class C3 extends P3 { > public function __construct( > public readonly string $x =3D '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. Nicolas --000000000000fe652c064b2a382b Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi Tim,

Le=C2=A0mer.= 18 f=C3=A9vr. 2026 =C3=A0=C2=A022:29, Tim D=C3=BCsterhus <tim@bastelstu.be> a =C3=A9crit=C2=A0:
Hi

On 2/16/26 19:20, Nicolas Grekas wrote:
> To be sure I understood you well: you are suggesting that mutability s= hould
> 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=E2=80=99ve implemented exactly that model:
> - Reassignment is allowed only while the declaring class constructor i= s
> active (including methods/closures called from it).
> - A child constructor can no longer reassign a parent-declared promote= d
> readonly property.
> - =E2=80=9CChild sets first, then parent::__construct()=E2=80=9D now t= hrows as expected.
> - The thrown Error is catchable from the child (around
> parent::__construct()), but not from inside the parent body before imp= licit
> CPP init.
> - Calling __construct() on an already-constructed object still cannot<= br> > 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 <= br> 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 =E2=80=9Co= wned=E2=80=9D
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.

=C2=A0 =C2=A0 =C2=A0class P1 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public function __construct(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public readonly string $x = =3D 'P',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0) { }
=C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0class C1 extends P1 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public readonly string $x;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public function __construct() {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0parent::__construct();

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0$this->x =3D 'C'= ; // This should fail.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0}

2. Parent uses CPP and reassigns, child redefines.

=C2=A0 =C2=A0 =C2=A0class P2 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public function __construct(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public readonly string $x = =3D 'P1',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0$this->x =3D 'P2'= ;; // This should be legal.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0class C2 extends P2 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public readonly string $x;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public function __construct() {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0parent::__construct();
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0}

3. Parent uses CPP, child uses CPP redefinition.

=C2=A0 =C2=A0 =C2=A0class P3 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public function __construct(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public readonly string $x = =3D 'P',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0) { }
=C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0class C3 extends P3 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public function __construct(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public readonly string $x = =3D 'C1',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0parent::__construct(); // T= his should fail.
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0}



Thanks, I've added = new test cases to cover this.
I've also improved tests as sug= gested on the PR.
And finally I updated the implementation to reu= se IS_PROP_REINITABLE instead of adding new flags=C2=A0+ use an approach th= at doesn't require walking the call stack.
PR and RFC updated= accordingly, all green.

Nicolas
--000000000000fe652c064b2a382b--