Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:128150 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 1A6291A00BC for ; Sun, 20 Jul 2025 20:19:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1753042679; bh=TGr3JQb8ihNt5fQGAPCBKvzSOCWiehULjb1SyzIUThM=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=R+KN25iieeYfVVeTJAgHyT06GixJcRHQzakvgvl7eb5B38CKQZd+v5mYwwEcAw7Kn Xj+vEMar2vNymFuhWb6EEkTdQGSCghVIHqwNCw4oNrk2D5KO0cE2rzzmbxiDXCjkQI SgRGp5KfzXHGMzoU4qUUlhboQEoo1JOaFGd5C2OgGdLZB1Psk8y1F/bi3c0g9pRGgK 5jWo/7pa2xHaI2v0ywPEOyVKYL6/GJ4bIaBO0k2T0jlMhVY8JNpU+/8rXTJYnSivjh 5uhSHDa++sMMNyio/EYFYkn4f1gIsCmLkikSJlYFTYNciuki/C4PVHUyb83eQlMskd n0h1WTWlygVOw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 44116180339 for ; Sun, 20 Jul 2025 20:17:58 +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-b2-smtp.messagingengine.com (fout-b2-smtp.messagingengine.com [202.12.124.145]) (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 ; Sun, 20 Jul 2025 20:17:58 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.phl.internal [10.202.2.45]) by mailfout.stl.internal (Postfix) with ESMTP id 443EA1D0031E; Sun, 20 Jul 2025 16:19:42 -0400 (EDT) Received: from phl-imap-05 ([10.202.2.95]) by phl-compute-05.internal (MEProxy); Sun, 20 Jul 2025 16:19:42 -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=1753042782; x= 1753129182; bh=TGr3JQb8ihNt5fQGAPCBKvzSOCWiehULjb1SyzIUThM=; b=O BWR5AhPMbnKYuIAqKNjLR45t0MKwYfvE8ivRXSHRcc0/LQhD6UDpQdg2UGLtE4WN e3Qke0lELBWyl7PGXZ5q6TROQuEQfLjWwWRYw0DrHOqSg4bLb8G2gop6JqwGKIWu Y5A/kzLF/VwEhwBRffaiQp7QB0RqdGkhdbsIZdWGnlSC/Jk37mMBE9E0tUUN9q0m A2VPZiu6e0Rr5JAXTHgI3O6ZZ2IFk45ubbswWSAsLwFVPGER0OD/5G3nW3zRYUaX JVYarVYExc4TSGd6kOZHtZplbm2Q6+arJjiJ8hJ6OGNMIqcsSHdJIF/mqf4NrCMK s7/0n54ROY2VnYQt2OHQQ== 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= 1753042782; x=1753129182; bh=TGr3JQb8ihNt5fQGAPCBKvzSOCWiehULjb1 SyzIUThM=; b=EKTyi/lpTvyTv6Ha4Kjf66EBU/Np3859C46tohoPqywZ8V6Ps6C l9I324wWjtUxlK4+h1CiN3gi8Tf8i13hgO5ct7Z+MBa8xNoK47hapcKrxQXpTRjz pNmEfyFhXH3ExfYgRjBgCpaJkFtAHg48RYdMXjrFi0AHEyedDjBlWdk1aHDxIZ3o tt4ro9q9qiYxOsdJFSeVSV48NrRfGkZH2yysSZQIUXmogDQa1arLLQUrkn7GkUb0 4nG1MlRgGlVC9fGjxv6hB0jWUIAUT5TSPBMTAjA82TKmlkTr+FH1rGpm0ms/gpn5 tYM+C/mzJFeVh7pASkwdWtQD459CEyazbEg== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgdejtddugecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjug hrpefoggffhffvvefkjghfufgtsegrtderreertdejnecuhfhrohhmpedftfhosgcunfgr nhguvghrshdfuceorhhosgessghothhtlhgvugdrtghouggvsheqnecuggftrfgrthhtvg hrnhepieeuteehvddvfeejhffgieehleehhedthfefkeejffelgfevvdekudetjeejtddt necuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprhhosg essghothhtlhgvugdrtghouggvshdpnhgspghrtghpthhtohepfedpmhhouggvpehsmhht phhouhhtpdhrtghpthhtoheptghlrghuuggvrdhprggthhgvsehgmhgrihhlrdgtohhmpd hrtghpthhtohepvghrihgtthhnohhrrhhishesghhmrghilhdrtghomhdprhgtphhtthho pehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id A0BC11820074; Sun, 20 Jul 2025 16:19:41 -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: Sun, 20 Jul 2025 22:19:20 +0200 To: erictnorris@gmail.com Cc: "Claude Pache" , "php internals" Message-ID: <5a51401a-450b-46f0-8467-88d31efecb9c@app.fastmail.com> In-Reply-To: References: <378cd8b0-c4a8-4e53-b0bd-e9e4f30a454d@app.fastmail.com> <77128196-8D53-40D6-9BB7-77160AD71ED9@gmail.com> <247034b6-04cf-4f3e-a145-a30171af8c12@app.fastmail.com> Subject: Re: [PHP-DEV] [RFC] Readonly property hooks Content-Type: multipart/alternative; boundary=fba92c20c3374f9f9f588cc3319c564e From: rob@bottled.codes ("Rob Landers") --fba92c20c3374f9f9f588cc3319c564e Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Sun, Jul 20, 2025, at 19:18, Eric Norris wrote: > Hi Rob, >=20 > I'm going to respond to a few points from earlier emails here instead > of each one. >=20 > On Sat, Jul 19, 2025 at 6:13=E2=80=AFAM Rob Landers wrote: > > > > > > > > On Sat, Jul 19, 2025, at 12:09, Claude Pache wrote: > > > > > > > > Le 19 juil. 2025 =C3=A0 09:46, Rob Landers a =C3= =A9crit : > > > > > > > > On Sat, Jul 19, 2025, at 03:04, Claude Pache wrote: > > > > > > > > > > Le 19 juil. 2025 =C3=A0 00:41, Rob Landers a =C3= =A9crit : > > > > > > The original author (Nikita) suggested that there's nothing in the o= riginal design that precludes accessors -- and highlights languages wher= e there are both and they are doing just fine more than 5 years later. > > > > > > Hi Rob, > > > > It is indeed entirely reasonable to have both readonly properties an= d hooked properties (aka accessors), and today PHP has indeed both of th= em (and even asymmetric visibility on top of that, as a separate feature= contrarily to C#). But it doesn=E2=80=99t mean that it is reasonable fo= r the *same* property to be *both* readonly and hooked, which is the poi= nt that is currently disputed. =E2=80=94 What do the other languages all= ow? Is it possible to define a readonly property with a user-defined get= ter? (Disclaimer: Even if one of them allows such a thing, I=E2=80=99ll = still think that it is a bad idea.) >=20 > I do not believe I was cherry picking; I share Claude's interpretation > here that the RFC says that readonly doesn't prevent the language from > adopting accessors (hooks) later, which is what the language did, and > notably it did without applying them to readonly properties. >=20 > Could you perhaps walk me through your thinking when the RFC claims > that `assert($prop =3D=3D=3D $prop2)` "always holds"? Hi Eric, I think that is explaining how Nikita arrived at some of the conclusions= (it is in the rationale section, after all) and shouldn't be taken as l= iteral. Here are some snippets that I think should be looked at a little= more closely, that seem to align with the vision of the feature, in the= context of getters/setters: "The closest alternative is to declare the property private, and only ex= pose a public getter" -- indicates to me that a single public getter sho= uld, in fact, be considered readonly. "Support for first-class readonly properties allows you to directly expo= se public readonly properties, without fear that class invariants could = be broken through external modification" -- indicates to me that interio= r mutability (and maybe nondeterminism) should be at the discretion of t= he interior, not the exterior contract. "...readonly properties do not preclude interior mutability. Objects (or= resources) stored in readonly properties may still be modified internal= ly" -- further specifies that interior mutability is allowed; only exter= ior mutability isn't. It doesn't define "interior mutability" stringently, so we can have diff= ering opinions on that; but it seems to be "the value inside the propert= y" which may or may not be an object, resource, or a hook's result. >=20 > > > > =E2=80=94Claude > > > > > > Hey Claude, > > > > From what I've seen in other languages, this combination is fairly c= ommon and not inherently poblematic. > > > > - C# allows get hooks with user-defined logic, even in readonly stru= cts. > > - Kotlin uses val with a get() body, which is readonly from the cons= umer's perspective, even though the value is computed. > > - TypeScript allows a get-only accessor which acts readonly. > > - Swift also allows get-only computed accessors/hooks. >=20 > (disclaimer, I am not a C# expert) >=20 > It seems that C# has both fields and properties, and a readonly field > seems to align with what a few of us are claiming is how PHP readonly > properties should work. C# properties are more open-ended, and don't > actually support the readonly keyword - you can make them "read only" > in the sense of only having a get accessor (and you can mark that > accessor itself as readonly), but this is different from readonly > fields, which enforce a contract about the mutability of the field. >=20 > I think that C# fields, both readonly and not, match PHP's properties > without hooks. C# properties - which I believe cannot be *marked* > readonly, but they can be *made* read only, i.e. only exposing a get > accessor - match PHP's properties with hooks. I would prefer to simply allow specifying a class as readonly so long as= only get hooks are present. However, I'm ok with saying that get hooks = may themselves be readonly which accomplishes the same thing and is easi= er to reason about. >=20 > > > > > > Hi Rob, > > > > The main problem is that we don=E2=80=99t agree on the meaning of =E2= =80=9Creadonly property=E2=80=9D. >=20 > I agree with Claude here. Rob, I'm curious how you interpret the > contract of readonly. Do you think that it is a contract about > writability to consumers of the class? In an earlier email, you said: >=20 > > In the example above, there is nothing mutable about it. It is "read= only" from a public contract point-of-view, we just can't mark it as a = readonly class. >=20 > In this most recent email, you said: >=20 > > The error you get when trying to modify the "get-only accessor" is `= Error: Cannot assign to 'name' because it is a read-only property` > > > > Thus, readonly is implied by the get-only accessor, there's no need = to specify it directly. >=20 > Would you say this is how you are interpreting readonly? That it is a > contract to consumers of the class that they can read but cannot write > to it? That's the definition in the RFC, from my reading of it: the ability to = expose bare properties without worrying that someone else will modify it= . One might argue that there really isn't a reason for readonly anymore,= since we have aviz + hooks. Or rather, that readonly could be just a sh= orthand for that -- plus the ability to write to those properties exactl= y-once. I suspect this is where "write-once" is coming from elsewhere in= the thread, since that is the only difference between "manual readonly"= and "engine-powered readonly". >=20 > (including a snippet from an earlier email) >=20 > > I think an init hook is out of the question for 8.5, so I'm not even= sure it's worth discussing. >=20 > I don't agree that it's not worth discussing alternative solutions to > the problems the RFC authors intend to solve. I think it has been > expressed elsewhere, but I believe we should take the time to make the > best decisions for the language and not rush to add a controversial > feature. Would an init hook actually solve it though? An init hook would basicall= y just be a constructor / default value outside of the constructor that = specifies an instance-level constant. The readonly property RFC goes int= o some details here: "As the default value counts as an initializing assignment, a readonly p= roperty with a default value is essentially the same as a constant, and = thus not particularly useful. The notion could become more useful in the= future, if new expressions are allowed as property default values. At t= he same time, depending on how exactly property initialization would wor= k in that case, having a default value on a readonly property could prec= lude userland serialization libraries from working, as they would not be= able to replace the default-constructed object. Whether or not this is = a concern depends on whether the property is initialized at time of obje= ct creation, or as an implicit part of the constructor (or similar). As = these are open questions, the conservative choice is to forbid default v= alues until these questions are resolved." When would the init hook get called? And in what order? This brings back= some memories of solving some Java language bugs where static variables= wouldn't be initialized in time (they're non-deterministic) causing str= ange crashes, or the potential to "deadlock" yourself and you need to re= ad properties in a specific order in order to ensure the object gets ini= tialized correctly. This isn't something that can be solved in a few weeks. Someone(s) needs= to sit down and think through all the possibilities and then create a d= efinition of an init hook that is better than a constructor. =E2=80=94 Rob --fba92c20c3374f9f9f588cc3319c564e Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable


On Sun, Jul 20, 2025, at 19:18, Eric Norris wrote:
Hi Rob,
=
I'm going to respond to a few points from earlier emails = here instead
of each one.

On Sat, Jul= 19, 2025 at 6:13=E2=80=AFAM Rob Landers <rob@bottled.codes> wrote:
>
>=
>
> On Sat, Jul 19, 2025, at 12:09, Claude Pa= che wrote:
>
>
>
> L= e 19 juil. 2025 =C3=A0 09:46, Rob Landers <rob@bottled.codes> a =C3=A9crit :
>
=
>
>
> On Sat, Jul 19, 2025, at 03:04, = Claude Pache wrote:
>
>
>
>
> Le 19 juil. 2025 =C3=A0 00:41, Rob Landers <rob@bottled.codes> a =C3=A9cri= t :
>
>
> The original author (Ni= kita) suggested that there's nothing in the original design that preclud= es accessors -- and highlights languages where there are both and they a= re doing just fine more than 5 years later.
>
>= ;
> Hi Rob,
>
> It is indeed enti= rely reasonable to have both readonly properties and hooked properties (= aka accessors), and today PHP has indeed both of them (and even asymmetr= ic visibility on top of that, as a separate feature contrarily to C#). B= ut it doesn=E2=80=99t mean that it is reasonable for the *same* property= to be *both* readonly and hooked, which is the point that is currently = disputed. =E2=80=94 What do the other languages allow? Is it possible to= define a readonly property with a user-defined getter? (Disclaimer: Eve= n if one of them allows such a thing, I=E2=80=99ll still think that it i= s a bad idea.)

I do not believe I was cherry pi= cking; I share Claude's interpretation
here that the RFC says = that readonly doesn't prevent the language from
adopting acces= sors (hooks) later, which is what the language did, and
notabl= y it did without applying them to readonly properties.

Could you perhaps walk me through your thinking when the RFC cla= ims
that `assert($prop =3D=3D=3D $prop2)` "always holds"?

Hi Eric,

I th= ink that is explaining how Nikita arrived at some of the conclusions (it= is in the rationale section, after all) and shouldn't be taken as liter= al. Here are some snippets that I think should be looked at a little mor= e closely, that seem to align with the vision of the feature, in the con= text of getters/setters:

"The closest alternati= ve is to declare the property private, and only expose a public getter" = -- indicates to me that a single public getter should, in fact, be consi= dered readonly.

"Support for first-class readon= ly properties allows you to directly expose public readonly properties, = without fear that class invariants could be broken through external modi= fication" -- indicates to me that interior mutability (and maybe nondete= rminism) should be at the discretion of the interior, not the exterior c= ontract.

"...readonly properties do not preclud= e interior mutability. Objects (or resources) stored in readonly propert= ies may still be modified internally" -- further specifies that interior= mutability is allowed; only exterior mutability isn't.

It doesn't define "interior mutability" stringently, so we can = have differing opinions on that; but it seems to be "the value inside th= e property" which may or may not be an object, resource, or a hook's res= ult.

=

>
> =E2=80=94Claude
>
>
> Hey Claude,
>
> F= rom what I've seen in other languages, this combination is fairly common= and not inherently poblematic.
>
> - C# allow= s get hooks with user-defined logic, even in readonly structs.
> - Kotlin uses val with a get() body, which is readonly from the co= nsumer's perspective, even though the value is computed.
> = - TypeScript allows a get-only accessor which acts readonly.
&= gt; - Swift also allows get-only computed accessors/hooks.
(disclaimer, I am not a C# expert)

= It seems that C# has both fields and properties, and a readonly field
seems to align with what a few of us are claiming is how PHP rea= donly
properties should work. C# properties are more open-ende= d, and don't
actually support the readonly keyword - you can m= ake them "read only"
in the sense of only having a get accesso= r (and you can mark that
accessor itself as readonly), but thi= s is different from readonly
fields, which enforce a contract = about the mutability of the field.

I think that= C# fields, both readonly and not, match PHP's properties
with= out hooks. C# properties - which I believe cannot be *marked*
= readonly, but they can be *made* read only, i.e. only exposing a get
accessor - match PHP's properties with hooks.
<= div>
I would prefer to simply allow specifying a class as = readonly so long as only get hooks are present. However, I'm ok with say= ing that get hooks may themselves be readonly which accomplishes the sam= e thing and is easier to reason about.


>
>= ;
> Hi Rob,
>
> The main problem = is that we don=E2=80=99t agree on the meaning of =E2=80=9Creadonly prope= rty=E2=80=9D.

I agree with Claude here. Rob, I'= m curious how you interpret the
contract of readonly. Do you t= hink that it is a contract about
writability to consumers of t= he class? In an earlier email, you said:

> I= n the example above, there is nothing mutable about it. It is "read only= " from a public contract point-of-view, we just can't mark it as a reado= nly class.

In this most recent email, you said:=

> The error you get when trying to modify t= he "get-only accessor" is `Error: Cannot assign to 'name' because it is = a read-only property`
>
> Thus, readonly is im= plied by the get-only accessor, there's no need to specify it directly.<= /div>

Would you say this is how you are interpreting = readonly? That it is a
contract to consumers of the class that= they can read but cannot write
to it?
=
That's the definition in the RFC, from my reading of it: = the ability to expose bare properties without worrying that someone else= will modify it. One might argue that there really isn't a reason for re= adonly anymore, since we have aviz + hooks. Or rather, that readonly cou= ld be just a shorthand for that -- plus the ability to write to those pr= operties exactly-once. I suspect this is where "write-once" is coming fr= om elsewhere in the thread, since that is the only difference between "m= anual readonly" and "engine-powered readonly".


(including= a snippet from an earlier email)

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

I don't agree that it's not wo= rth discussing alternative solutions to
the problems the RFC a= uthors intend to solve. I think it has been
expressed elsewher= e, but I believe we should take the time to make the
best deci= sions for the language and not rush to add a controversial
fea= ture.

Would an init hook actually = solve it though? An init hook would basically just be a constructor / de= fault value outside of the constructor that specifies an instance-level = constant. The readonly property RFC goes into some details here:

"As the default value counts as an initializing assign= ment, a readonly property with a default value is essentially the same a= s a constant, and thus not particularly useful. The notion could become = more useful in the future, if new expressions are allowed as property de= fault values. At the same time, depending on how exactly property initia= lization would work in that case, having a default value on a readonly p= roperty could preclude userland serialization libraries from working, as= they would not be able to replace the default-constructed object. Wheth= er or not this is a concern depends on whether the property is initializ= ed at time of object creation, or as an implicit part of the constructor= (or similar). As these are open questions, the conservative choice is t= o forbid default values until these questions are resolved."
<= br>
When would the init hook get called? And in what order? Th= is brings back some memories of solving some Java language bugs where st= atic variables wouldn't be initialized in time (they're non-deterministi= c) causing strange crashes, or the potential to "deadlock" yourself and = you need to read properties in a specific order in order to ensure the o= bject gets initialized correctly.

This isn't so= mething that can be solved in a few weeks. Someone(s) needs to sit down = and think through all the possibilities and then create a definition of = an init hook that is better than a constructor.

=E2=80=94 Rob
--fba92c20c3374f9f9f588cc3319c564e--