Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:128129 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 3CA701A00BC for ; Fri, 18 Jul 2025 22:36:30 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1752878083; bh=kIXs5UIHq/PMLgV28+0/mp+l3LY6vMJ0T1X2V5YFr4M=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=bSL3uYEMM6VXO2xeqXLxjv4vG+8uscXK9bZBwQdNDMN431xueORZLctBCHUVsdGv9 mJQvlD0thsIIRC3u9gRYerSD4xJW4d/ShrdJqyJmY1RvukYOLFVK9oVDCo2V7x4s7n nWbZVh6nFM6EYvOdIc4SZrqhCgR5VZS9vBzwvYoQRk5hCP12RxRtwJG0jWACH5GVfw NT+1I+pT03AvOnvejzf+HfYasNGy8SQjvc5XIB29tlQjnp6+3XKg+MZJqGVQGjI1rk 2VGFzsa/G/0zn+L7GcyYEDohO6SE0o4L8MXLOpc74I5FS8n7FOJAoKMWoNuEVQlyNb xXbUvEJQmyStQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 2DFD0180054 for ; Fri, 18 Jul 2025 22:34:42 +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.8 required=5.0 tests=BAYES_00,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: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from fout-a4-smtp.messagingengine.com (fout-a4-smtp.messagingengine.com [103.168.172.147]) (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 ; Fri, 18 Jul 2025 22:34:41 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.phl.internal [10.202.2.45]) by mailfout.phl.internal (Postfix) with ESMTP id B793EEC0119; Fri, 18 Jul 2025 18:36:27 -0400 (EDT) Received: from phl-imap-05 ([10.202.2.95]) by phl-compute-05.internal (MEProxy); Fri, 18 Jul 2025 18:36:27 -0400 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=fm3; t=1752878187; x= 1752964587; bh=fy2BS/mVNtbHGHZssBt8C32CCcGHLGGJFTrST+0x9wg=; b=k zQR2xORb54H8WeJ9Gjj6qD33AqYyBV5tREItlPbdQIvEFM7nSRWt+U94PXKr2cXV HyjpaN2rn/eM/emgWe9hbi5VgOgJoNpJAS1tjCFiHSkb6UlFsVWXyvV9jeQgWtpB K5coEd2209X3VepP7cofrPzs/f3cbqhaGpVk7GsZwJUd5IvS33m/BRH0qzhzfMcq iBdhT48hNoiavyLyXdOQGOxuOC/IxqzdrLIFfFKqxvss8gy2wcBASZJVi9DOjlHO ebvUfhOtTztSSvGhGCKJtvNPswzNNgi7XCFIE+0xh2AczZzQU+KWlfJ3rX239EMG 5z43U6/728ASjt8DRKJCQ== 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=fm2; t= 1752878187; x=1752964587; bh=fy2BS/mVNtbHGHZssBt8C32CCcGHLGGJFTr ST+0x9wg=; b=e8AAAeXERHSScJX9j4/0z2myzIr6OQEorvzOu5RU9zvSmwfNP74 hbxnA8WpR/RelEdkgl43xkVupl0jhnjYBsIFM2KCDk26dXeiQEEAcdk2+al4DLMS tqIxw53O2D2+XUU/jhtQschlcGvywQBzB/hG6I4vHjpAGu4hcmjZpCmdHL+iAFyn k8kyvOiq2+x7jvzfSN5itsE+m21UfjXxd1cgWyCS8PjmFX7H+lj8rkcFZfemWkUf jAOpSFG6SXYa1pGvelnnco4UWTW32lFtvMSNs2EDyXbmYSTfTg0JRHsVrf7c414G WGAdEsjlq+9pN/PkHnwJSch9/r7/6BhbP6A== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgdeigeeikecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjug hrpefoggffhffvvefkjghfufgtsegrtderreertdejnecuhfhrohhmpedftfhosgcunfgr nhguvghrshdfuceorhhosgessghothhtlhgvugdrtghouggvsheqnecuggftrfgrthhtvg hrnhepffegveevgedtfffgffdvffdtvdehueelgeevkeetffeuleeitdegkeejtedtteek necuffhomhgrihhnpehphhhprdhnvghtnecuvehluhhsthgvrhfuihiivgeptdenucfrrg hrrghmpehmrghilhhfrhhomheprhhosgessghothhtlhgvugdrtghouggvshdpnhgspghr tghpthhtohephedpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepthhimhessggrsh htvghlshhtuhdrsggvpdhrtghpthhtoheplhgrrhhrhiesghgrrhhfihgvlhguthgvtghh rdgtohhmpdhrtghpthhtohepvghrihgtthhnohhrrhhishesghhmrghilhdrtghomhdprh gtphhtthhopehnihgtohhlrghsrdhgrhgvkhgrshdophhhphesghhmrghilhdrtghomhdp rhgtphhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id D00FB182007A; Fri, 18 Jul 2025 18:36:26 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: T5f4527d1a0d4de24 Date: Sat, 19 Jul 2025 00:36:06 +0200 To: erictnorris@gmail.com, "Nicolas Grekas" , "Larry Garfield" Cc: =?UTF-8?Q?Tim_D=C3=BCsterhus?= , "php internals" Message-ID: <378cd8b0-c4a8-4e53-b0bd-e9e4f30a454d@app.fastmail.com> In-Reply-To: References: <1e8634d7-ac1a-4025-b4e2-1948aabf5251@app.fastmail.com> <09780759-9ddc-45e2-9376-befb8e378775@app.fastmail.com> <0e8eb146-3b1d-4fc2-ad83-77062f2063c9@bastelstu.be> Subject: Re: [PHP-DEV] Re: [RFC] Readonly property hooks Content-Type: multipart/alternative; boundary=16c3cc64a348406ea3d78ce7c610b9e6 From: rob@bottled.codes ("Rob Landers") --16c3cc64a348406ea3d78ce7c610b9e6 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Fri, Jul 18, 2025, at 21:43, Eric Norris wrote: > Nick, Larry, >=20 > On Fri, Jul 18, 2025 at 2:01=E2=80=AFPM Nicolas Grekas > >= wrote: > > > > > > > > Le ven. 18 juil. 2025 =C3=A0 18:32, Tim D=C3=BCsterhus a =C3=A9crit : > >> > >> Hi > >> > >> On 7/17/25 18:26, Larry Garfield wrote: > >> > Given the lack of consensus both here and in off-list discussions= on how to handle get hooks, we have done the following: > >> > > >> > * Split the RFC into two sections, one for get, one for set. > >> > * Expanded and refined the examples for both. The implementation = is still the original, however. > >> > * Split the vote into two: one for allowing readonly get hooks, o= ne for readonly set hooks. > >> > > >> > We will start the vote sometime this weekend, most likely, unless= some major feedback appears before then, and let the chips fall where t= hey may. > >> > >> After working through (most of) the discussion, I've now taken a lo= ok at > >> the updated RFC. I have the following remarks: > >> > >> 1. > >> > >> > It is really =E2=80=9Cwrite-once=E2=80=9D, which is not the same = as immutable (as shown above). But there's no reason that =E2=80=9Cwrite= -once=E2=80=9D need be incompatible with hooks. > >> > >> This is a strawman argumentation, as I've outlined in my previous > >> emails, calling readonly "write-once" is wrong. It is a reasonable = user > >> expectation to always get the identical value when reading a value = that > >> may only be set once. By calling it "write-once" you are trying to = shift > >> the focus to the write operation, which is totally irrelevant for u= ser > >> expectations when interacting with readonly properties. Especially = for > >> expectations of users that just *use* a class rather than writing o= ne. > > > > > > To my ears, write-once is more accurate than readonly because it sti= cks to the facts of how this behaves. That's very relevant. > > Using readonly to suggest immutable is where the arguments for rejec= ting this RFC are weak. > > readonly doesn't mean immutable, no matter how hard some want it to = be... >=20 > (including a snippet from a separate email from Larry below) >=20 > > Does readonly refer to the value returned? If so, that's already be= en broken since the beginning because the property can be a mutable obje= ct, so trusting the data returned to be "the same" is already not safe. >=20 > It seems to me that the original intent of `readonly` was to mean > immutable, and points to a property always equaling itself in the > rationale section > (https://wiki.php.net/rfc/readonly_properties_v2#rationale): >=20 > ``` > $prop =3D $this->prop; > $fn(); // Any code may run here. > $prop2 =3D $this->prop; > assert($prop =3D=3D=3D $prop2); // Always holds. > ``` >=20 > It even calls out that this *does not* restrict *interior* mutability, > which I believe you are using to argue that it doesn't actually mean > immutable: >=20 > "However, readonly properties do not preclude interior mutability. > Objects (or resources) stored in readonly properties may still be > modified internally." >=20 > This is exactly how I think about `readonly`. The identity of property > won't change, but the object itself might. Now if the object is also a > `readonly` class (and recursive for any of those class's properties > that are objects), then you *would* truly have an immutability > guarantee of both the identity of the object and the object's > properties. This is ignoring __get, which I have pointed out elsewhere > is worth ignoring, since we can conceivably remove that from readonly > classes if we pass `init` hooks. I don't want to accuse you of cherry-picking ... but this is clearly che= rry picking. From that same text: --- It is worth noting that having a readonly property feature does not prec= lude introduction of accessors. C# supports both readonly properties and= accessors. C# also provides properties with implicit backing storage th= rough accessor syntax, but this is not the only way to do it. For exampl= e, Swift has special syntax for asymmetric visibility, rather than speci= fying visibility on implicitly implemented accessors. Even if we have property accessors, I believe it may be worthwhile to li= mit them to computed properties only, and solve use-cases that involve e= ngine-managed storage through other mechanisms, such as readonly propert= ies and property-level asymmetric visibility. This avoids confusion rela= ting to the two kinds of accessors (implicit and explicit), and also all= ows us to make their behavior independent of accessor constraints. For e= xample, a first-class asymmetric visibility feature would shield the use= r from considering distinctions such as `get;` vs `&get;` accessors. The= se are externalities of the general accessor feature and not needed for = asymmetric visibility. A separate implementation can also be more efficient. After initializati= on, a readonly property will have the same performance characteristics a= s a normal property. Accessor-based properties, even with implicit stora= ge, still carry a performance penalty. --- The original author (Nikita) suggested that there's nothing in the origi= nal design that precludes accessors -- and highlights languages where th= ere are both and they are doing just fine more than 5 years later. [snip] > I am failing to understand what capabilities are not going to be > addressed by an `init` hook, which some of us (if I'm allowed to speak > for us) seem to think is the correct approach here. >=20 > I have noticed in some discussions with my coworkers that it seems > that some people think that readonly implies a *contract about > writability to consumers of the class*, that is, it implies that > consumers of the class can only *read* the value, not write it. I > think I could understand why people that think this way would have no > problem with `get` hooks, since it still upholds what they think the > contract is. I feel, however, that that desired contract is actually > achieved by asymmetric visibility, e.g. public(get) protected(set), > which I view as orthogonal to readonly. >=20 > When I point out that asymmetric visibility exists, everyone I've > talked to so far agrees that readonly makes more sense as a *contract > that the value won't change*. And, as I've pointed out above, I > believe that is the intent of the original RFC text for the feature. >=20 > With this understanding of the contract of readonly, I struggle to > understand why an author *who wants to use readonly* would need `get` > hook capabilities and not in fact `init` hook capabilities. If they > needed generic `get` hook capabilities for a property (and not just > lazy loading), then my position is that *they don't actually want > readonly*. >=20 I think an init hook is out of the question for 8.5, so I'm not even sur= e it's worth discussing. =E2=80=94 Rob --16c3cc64a348406ea3d78ce7c610b9e6 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable


On Fri, Jul 18, 2025, at 21:43, Eric Norris wrote:
Nick, Larry,
=

On Fri, Jul 18, 2025 at 2:01=E2=80=AFPM Nicolas Grek= as
>
>
>
> Le ven. 18 juil. 2025 =C3=A0 18:32, Tim D=C3=BC= sterhus <tim@bastelstu.be>= a =C3=A9crit :
>>
>> Hi
>&= gt;
>> On 7/17/25 18:26, Larry Garfield wrote:
>> > Given the lack of consensus both here and in off-list dis= cussions on how to handle get hooks, we have done the following:
>> >
>> > * Split the RFC into two sectio= ns, one for get, one for set.
>> > * Expanded and ref= ined the examples for both. The implementation is still the original, ho= wever.
>> > * Split the vote into two: one for allowi= ng readonly get hooks, one for readonly set hooks.
>> &g= t;
>> > We will start the vote sometime this weekend,= most likely, unless some major feedback appears before then, and let th= e chips fall where they may.
>>
>> After= working through (most of) the discussion, I've now taken a look at
>> the updated RFC. I have the following remarks:
= >>
>> 1.
>>
>> >= ; It is really =E2=80=9Cwrite-once=E2=80=9D, which is not the same as im= mutable (as shown above). But there's no reason that =E2=80=9Cwrite-once= =E2=80=9D need be incompatible with hooks.
>>
= >> This is a strawman argumentation, as I've outlined in my previo= us
>> emails, calling readonly "write-once" is wrong. It= is a reasonable user
>> expectation to always get the i= dentical value when reading a value that
>> may only be = set once. By calling it "write-once" you are trying to shift
&= gt;> the focus to the write operation, which is totally irrelevant fo= r user
>> expectations when interacting with readonly pr= operties. Especially for
>> expectations of users that j= ust *use* a class rather than writing one.
>
>=
> To my ears, write-once is more accurate than readonly be= cause it sticks to the facts of how this behaves. That's very relevant.<= /div>
> Using readonly to suggest immutable is where the argument= s for rejecting this RFC are weak.
> readonly doesn't mean = immutable, no matter how hard some want it to be...

=
(including a snippet from a separate email from Larry below)
<= div>
> Does readonly refer to the value returned? = If so, that's already been broken since the beginning because the prope= rty can be a mutable object, so trusting the data returned to be "the sa= me" is already not safe.

It seems to me that th= e original intent of `readonly` was to mean
immutable, and poi= nts to a property always equaling itself in the
rationale sect= ion



=
won't change, but the object itself might. Now if the object is als= o a
`readonly` class (and recursive for any of those class's p= roperties
that are objects), then you *would* truly have an im= mutability
guarantee of both the identity of the object and th= e object's
properties. This is ignoring __get, which I have po= inted out elsewhere
is worth ignoring, since we can conceivabl= y remove that from readonly
classes if we pass `init` hooks.

I don't want to accuse you of cherr= y-picking ... but this is clearly cherry picking. From that same text:

---

It is worth noting that having a readonly= property feature does not preclude introduction of accessors. C# suppor= ts both readonly properties and accessors. C# also provides properties w= ith implicit backing storage through accessor syntax, but this is not th= e only way to do it. For example, Swift has special syntax for asymmetri= c visibility, rather than specifying visibility on implicitly implemente= d accessors.
Even if we have property accessors, I believe it may be worthwhile to l= imit them to computed properties only, and solve use-cases that involve = engine-managed storage through other mechanisms, such as readonly proper= ties and property-level asymmetric visibility. This avoids confusion rel= ating to the two kinds of accessors (implicit and explicit), and also al= lows us to make their behavior independent of accessor constraints. For = example, a first-class asymmetric visibility feature would shield the us= er from considering distinctions such as get; vs &get; accessors. These ar= e externalities of the general accessor feature and not needed for asymm= etric visibility.
A separate implementation can also be more efficient. After initia= lization, a readonly property will have the same performance characteris= tics as a normal property. Accessor-based properties, even with implicit= storage, still carry a performance penalty.

---

The original author (Nikita) suggested that there's nothing i= n the original design that precludes accessors -- and highlights languag= es where there are both and they are doing just fine more than 5 years l= ater.

[snip]
<= div class=3D"align-start" style=3D"text-align:start;">
I am failing to understand w= hat capabilities are not going to be
addressed by an `init` ho= ok, which some of us (if I'm allowed to speak
for us) seem to = think is the correct approach here.

I have noti= ced in some discussions with my coworkers that it seems
that s= ome people think that readonly implies a *contract about
writa= bility to consumers of the class*, that is, it implies that
co= nsumers of the class can only *read* the value, not write it. I
think I could understand why people that think this way would have no<= /div>
problem with `get` hooks, since it still upholds what they thi= nk the
contract is. I feel, however, that that desired contrac= t is actually
achieved by asymmetric visibility, e.g. public(g= et) protected(set),
which I view as orthogonal to readonly.

When I point out that asymmetric visibility exist= s, everyone I've
talked to so far agrees that readonly makes m= ore sense as a *contract
that the value won't change*. And, as= I've pointed out above, I
believe that is the intent of the o= riginal RFC text for the feature.

With this und= erstanding of the contract of readonly, I struggle to
understa= nd why an author *who wants to use readonly* would need `get`
= hook capabilities and not in fact `init` hook capabilities. If they
needed generic `get` hook capabilities for a property (and not jus= t
lazy loading), then my position is that *they don't actually= want
readonly*.


I think an init hook is out of the question for 8.5, so I'm not = even sure it's worth discussing.

=E2=80=94 Rob
--16c3cc64a348406ea3d78ce7c610b9e6--