Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:128119 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 9B8751A00BC for ; Fri, 18 Jul 2025 17:04:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1752858137; bh=mHSWiQ8Sgfmj/mrfcsBYYNBIHgQ6662ajioG4vBg0qg=; h=Date:From:To:Cc:In-Reply-To:References:Subject:From; b=MKsUFkKaMZUN9MnA7Q/oIrilby+uuTl+eOUPymUCKB/6KVYNcpYT1EADiSOK+06c5 JQVaBpUXkEQqZv3rcJMuf8chzPUSe2/Tq3MvSa91tM3XVPfWr3snyLx+quCcVpKggE O6KGj534Qlo8xFi6eQr5QDqhXpL30JOIuvYv2Pw4gwf/OxtKi5MCeTsuJrJowI2Sd0 7h2GRwQwMkaSBZLZpaJTbgw4xKFXsP3I5jF9hL8JuEXt74+C4uY/qP6RQEQMPVdECe 2yZXQxjwhpJ87hDbkkTbo7zkGHjqU2ry3VNEFzo5vWTBGrn9UvSCq2WSc2PHvzvtdh 5MSiZEAKHDjPQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 4D1711805C2 for ; Fri, 18 Jul 2025 17:02:14 +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-b3-smtp.messagingengine.com (fout-b3-smtp.messagingengine.com [202.12.124.146]) (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 17:02:12 +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 A343B1D0011E; Fri, 18 Jul 2025 13:03:58 -0400 (EDT) Received: from phl-imap-05 ([10.202.2.95]) by phl-compute-05.internal (MEProxy); Fri, 18 Jul 2025 13:03:58 -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=1752858238; x= 1752944638; bh=sxppVLy6GphMIiWV+f2DXXzlfjNI2NwWsjGh72qyJVo=; b=K Ut9dbUQjE52ttY9Eygew7mbPdHoA6Q2m55bvHhhQG8Z0xjCtuNbfy5g7N8ArZVcG eXgWbuXo7qZDfHK9qipVYtgP7GAad+G0wEYLHF9NUW6R124IXM0cwI8Vc2uYF24Q /Kxs8Vxlbd471Ca4NolVUM2LVm8yCkKQhr9anWtzQmbYE3fj+wdmk0KhN2apyUv5 rhxLEgQ9WH6el0n8wV/4s8+oweM+dJBsjZsOWfBqWSjaRP63DgQlmlgaAQiJlGDE A1xxqZa8c2KYDG8RWWbXjUXEEIe+1scHV9OblXtA6g2NMFpBehFl1E/brj1xraLu RvRi4/uR6S3abEVKxIksg== 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= 1752858238; x=1752944638; bh=sxppVLy6GphMIiWV+f2DXXzlfjNI2NwWsjG h72qyJVo=; b=DpqUL0HIkOtrI3Ezniskmu6RZ0XDSmQAMJoysf+a8TKufYDfD2k yG/bFvFWqynBQxVJ6E/Ay4vWR9D1/Ha/BQ4548p5uaCxZ5f7OtT1ofFZYTxh/Tol yLxXXFtKevds93tDN/SE8TP7f9/EpAI8B95vb/4lrdVw5YX+19QxiCD8x4EO8WCB 1kk76vUjMgm50EFuX01Z5DfjN3fwEG9I3L9ohe9rZM3ofHTw+iQgaVCj3370UUk5 fdhw+v7zUV46Ukp+qhqiRMvdDABrbwBy0+L7FSPUgpqh0wuI+SOoBbs7XC4Q9vLl JC+gt1iZW2AEU2e+3Cs7kee2GJ4x+R+iUfQ== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgdeigedtudcutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjug hrpefoggffhffvvefkjghfufgtsegrtderreertdejnecuhfhrohhmpedftfhosgcunfgr nhguvghrshdfuceorhhosgessghothhtlhgvugdrtghouggvsheqnecuggftrfgrthhtvg hrnhepffegveevgedtfffgffdvffdtvdehueelgeevkeetffeuleeitdegkeejtedtteek necuffhomhgrihhnpehphhhprdhnvghtnecuvehluhhsthgvrhfuihiivgeptdenucfrrg hrrghmpehmrghilhhfrhhomheprhhosgessghothhtlhgvugdrtghouggvshdpnhgspghr tghpthhtohepgedpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepthhimhessggrsh htvghlshhtuhdrsggvpdhrtghpthhtoheplhgrrhhrhiesghgrrhhfihgvlhguthgvtghh rdgtohhmpdhrtghpthhtohepvghrihgtthhnohhrrhhishesghhmrghilhdrtghomhdprh gtphhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 150261820074; Fri, 18 Jul 2025 13:03:58 -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: Fri, 18 Jul 2025 19:03:37 +0200 To: erictnorris@gmail.com Cc: =?UTF-8?Q?Tim_D=C3=BCsterhus?= , "Larry Garfield" , "php internals" Message-ID: <625863a1-f9ec-481b-9227-d42e2300e1ca@app.fastmail.com> In-Reply-To: References: <1e8634d7-ac1a-4025-b4e2-1948aabf5251@app.fastmail.com> <9D5043B2-1589-4FD5-B289-6E98FB1177BE@nicksdot.dev> <0856c89f-2000-448a-bbbf-c145a8699f6a@app.fastmail.com> <2641b8bc-6337-4b75-a5a3-93dee1b03796@bastelstu.be> <0d9a2968-26dd-43dd-9733-d2eb621ab9ec@app.fastmail.com> Subject: Re: [PHP-DEV] [RFC] Readonly property hooks Content-Type: multipart/alternative; boundary=2e4b967d7982479db0c6d86a87a44d7c From: rob@bottled.codes ("Rob Landers") --2e4b967d7982479db0c6d86a87a44d7c Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Fri, Jul 18, 2025, at 18:48, Eric Norris wrote: > On Fri, Jul 18, 2025 at 12:01=E2=80=AFPM Rob Landers wrote: > > > > > > > > On Fri, Jul 18, 2025, at 17:25, Tim D=C3=BCsterhus wrote: > > > > Hi > > > > On 7/14/25 15:38, Larry Garfield wrote: > > > Thanks, Ilija. You expressed my concerns as well. And yes, in pr= actice, readonly classes over-reaching is the main use case; if you're m= arking individual properties readonly, then just don't mark the one that= has a hook on it (use aviz if needed) and there's no issue. > > > > A readonly class is not just a convenience shortcut to mark each > > individual property as readonly. It has important semantics of its o= wn, > > because it forces child classes to also be readonly. And even for fi= nal > > classes it communicates to the user that "I won't be adding non-read= only > > properties to the class". > > > > > > Wasn=E2=80=99t that the entire point of readonly classes? Because it= was painful to write readonly for every property. Then if a property is= readonly, the inherited property is also readonly, so, by extension: a = class extending a readonly class is also readonly. > > > > There=E2=80=99s no =E2=80=9Ccommunication=E2=80=9D here; just logic. > > > > > > Marking a class as readonly must therefore be a deliberate decision, > > since it affects the public API of your class and in turn also user > > expectations. > > > > > > Not really. I can remove the readonly designation and manually mark = every property as readonly. The behavior of the class doesn=E2=80=99t ma= gically change. Or, at least, I hope it doesn=E2=80=99t. > > > > > > > Perhaps we're thinking about this the wrong way, though? So far w= e've talked as though readonly makes the property write-once. But... wh= at if we think of it as applying to the field, aka the backing value? > > > > I think of readonly from the view of the public API surface of an > > object. The property hooks RFC was very explicit in that property ho= oks > > are intended to be =E2=80=9Ctransparent to the user=E2=80=9D and can= be added without > > breaking the public API. In other words: Whether or not a property is > > implemented using a hook should be considered an implementation deta= il > > and as a user of a class I do not care whether there is a backing va= lue > > or not. > > > > > So readonly doesn't limit calling the get hook, or even the set ho= ok, multiple times. Only writing to the actual value in the object tabl= e. That gives the exact same set of guarantees that a getX()/setX() met= hod would give. The methods can be called any number of times, but the = stored value can only be written once. > > > > As a user of a class the "backing table" is mostly inaccessible to me > > when interacting with objects. It's only exposed via var_dump() and > > serialize(), the former of which is a debug functionality and the ou= tput > > of latter not something I must touch. > > > > > It would not guarantee $foo->bar =3D=3D=3D $foo->bar in all cases = (though that would likely hold in the 99% case in practice), but then, $= foo->getBar() =3D=3D=3D $foo->getBar() has never been guaranteed either. > > > > Properties and methods are something different. For methods there a > > reasonable expectation that *behavior* is associated with them, for > > properties there is not. > > > > > > Unless I missed something. Hooks are fancy methods? There is nothing= intrinsic about object properties. There is nothing that says two calls= to the same property=E2=80=99s getters are going to result in the same = values. There is asynchronous php, declare ticks, etc. especially in the= case of globals, there is no guarantee you even have the same object. A= t the end of the day, it is up to the programmer building that system / = program to provide those guarantees=E2=80=94 not the language. >=20 > I do think that, without any additional information, it would be > reasonable to assume that `$foo->bar =3D=3D=3D $foo->bar`, i.e. there = would > not be side-effects until you've called a method or written to the > object in some way. So I share Tim's opinion here, but I do agree that > with hooks available this is not actually a guarantee. You could > certainly have a `$foo->random_value` property and document that it > will be different each time you call it. >=20 > That said, once the programmer has added the readonly designation to a > property, I do think that something says that two calls to the same > property will result in the same values - the readonly designation. I > disagree with the point that it's not up to the language - the > language should provide an affordance for enforcing programmer intent, > and I see no reason to even have a readonly designation if we're going > to make it easily circumventable or otherwise just a "hint". >=20 > It seems that one common counterpoint to the "let's not make it > circumventable" argument is to point out that it's already > circumventable via __get. I agree with Claude that this is not a > justification for making it *easier* to circumvent. I would also like > to note that the original RFC > (https://wiki.php.net/rfc/readonly_properties_v2#unset) seems to allow > this behavior *for the purpose of lazy initialization*. With an `init` > hook, we'd have solved this problem, and could deprecate the `__get` > hack for `readonly` properties / classes. >=20 > Nicolas Grekas said "__get is certainly not legacy; removing it would > break many use cases without proper alternatives.", but note that I'm > only suggesting we could maybe deprecate __get for `readonly` > properties once we had an `init` hook - I'm not proposing deprecating > it generally. Without a counterexample, I don't think there would be > another reason for `__get` to work with `readonly` properties. >=20 I personally feel that making special restrictions and affordances to re= adonly classes is a bad language design. It is a =E2=80=9Cclass=E2=80=9D= and not something special or different like an enum. This is just a cla= ss with its properties made readonly. The word says it all =E2=80=94 rea= d. Only. Personally, I don=E2=80=99t use readonly much any more. The amount of re= strictions and weird behavior just makes it impossible for any real-worl= d use except for narrow cases the original authors of the feature dreame= d up. With hooks and asymmetrical viz, it=E2=80=99s nearly an obsolete f= eature anyway.=20 =E2=80=94 Rob --2e4b967d7982479db0c6d86a87a44d7c Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable


On Fri, Jul 18, 2025, at 18:48, Eric Norris wrote:
On Fri, Jul 18, 20= 25 at 12:01=E2=80=AFPM Rob Landers <rob@bottled.codes> wrote:
>
>
=
>
> On Fri, Jul 18, 2025, at 17:25, Tim D=C3=BCster= hus wrote:
>
> Hi
>
>= ; On 7/14/25 15:38, Larry Garfield wrote:
> > Thanks, Il= ija.  You expressed my concerns as well.  And yes, in practice= , readonly classes over-reaching is the main use case; if you're marking= individual properties readonly, then just don't mark the one that has a= hook on it (use aviz if needed) and there's no issue.
>
> A readonly class is not just a convenience shortcut to mark= each
> individual property as readonly. It has important s= emantics of its own,
> because it forces child classes to a= lso be readonly. And even for final
> classes it communicat= es to the user that "I won't be adding non-readonly
> prope= rties to the class".
>
>
> Wasn=E2= =80=99t that the entire point of readonly classes? Because it was painfu= l to write readonly for every property. Then if a property is readonly, = the inherited property is also readonly, so, by extension: a class exten= ding a readonly class is also readonly.
>
> Th= ere=E2=80=99s no =E2=80=9Ccommunication=E2=80=9D here; just logic.
=
>
>
> Marking a class as readonly must= therefore be a deliberate decision,
> since it affects the= public API of your class and in turn also user
> expectati= ons.
>
>
> Not really. I can remo= ve the readonly designation and manually mark every property as readonly= . The behavior of the class doesn=E2=80=99t magically change. Or, at lea= st, I hope it doesn=E2=80=99t.
>
>
&= gt; > Perhaps we're thinking about this the wrong way, though?  = So far we've talked as though readonly makes the property write-once.&nb= sp; But... what if we think of it as applying to the field, aka the back= ing value?
>
> I think of readonly from the vi= ew of the public API surface of an
> object. The property h= ooks RFC was very explicit in that property hooks
> are int= ended to be =E2=80=9Ctransparent to the user=E2=80=9D and can be added w= ithout
> breaking the public API. In other words: Whether o= r not a property is
> implemented using a hook should be co= nsidered an implementation detail
> and as a user of a clas= s I do not care whether there is a backing value
> or not.<= /div>
>
> > So readonly doesn't limit calling the= get hook, or even the set hook, multiple times.  Only writing to t= he actual value in the object table.  That gives the exact same set= of guarantees that a getX()/setX() method would give.  The methods= can be called any number of times, but the stored value can only be wri= tten once.
>
> As a user of a class the "backi= ng table" is mostly inaccessible to me
> when interacting w= ith objects. It's only exposed via var_dump() and
> seriali= ze(), the former of which is a debug functionality and the output
<= div>> of latter not something I must touch.
>
= > > It would not guarantee $foo->bar =3D=3D=3D $foo->bar in = all cases (though that would likely hold in the 99% case in practice), b= ut then, $foo->getBar() =3D=3D=3D $foo->getBar() has never been gu= aranteed either.
>
> Properties and methods ar= e something different. For methods there a
> reasonable exp= ectation that *behavior* is associated with them, for
> pro= perties there is not.
>
>
> Unles= s I missed something. Hooks are fancy methods? There is nothing intrinsi= c about object properties. There is nothing that says two calls to the s= ame property=E2=80=99s getters are going to result in the same values. T= here is asynchronous php, declare ticks, etc. especially in the case of = globals, there is no guarantee you even have the same object. At the end= of the day, it is up to the programmer building that system / program t= o provide those guarantees=E2=80=94 not the language.

I do think that, without any additional information, it would be<= /div>
reasonable to assume that `$foo->bar =3D=3D=3D $foo->bar= `, i.e. there would
not be side-effects until you've called a = method or written to the
object in some way. So I share Tim's = opinion here, but I do agree that
with hooks available this is= not actually a guarantee. You could
certainly have a `$foo-&g= t;random_value` property and document that it
will be differen= t each time you call it.

That said, once the pr= ogrammer has added the readonly designation to a
property, I d= o think that something says that two calls to the same
propert= y will result in the same values - the readonly designation. I
disagree with the point that it's not up to the language - the
language should provide an affordance for enforcing programmer intent,=
and I see no reason to even have a readonly designation if we= 're going
to make it easily circumventable or otherwise just a= "hint".

It seems that one common counterpoint = to the "let's not make it
circumventable" argument is to point= out that it's already
circumventable via __get. I agree with = Claude that this is not a
justification for making it *easier*= to circumvent. I would also like
to note that the original RF= C
this behavior *for the purpose of lazy initialization= *. With an `init`
hook, we'd have solved this problem, and cou= ld deprecate the `__get`
hack for `readonly` properties / clas= ses.

Nicolas Grekas said "__get is certainly no= t legacy; removing it would
break many use cases without prope= r alternatives.", but note that I'm
only suggesting we could m= aybe deprecate __get for `readonly`
properties once we had an = `init` hook - I'm not proposing deprecating
it generally. With= out a counterexample, I don't think there would be
another rea= son for `__get` to work with `readonly` properties.

=

I personally feel that making special r= estrictions and affordances to readonly classes is a bad language design= . It is a =E2=80=9Cclass=E2=80=9D and not something special or different= like an enum. This is just a class with its properties made readonly. T= he word says it all =E2=80=94 read. Only.

Perso= nally, I don=E2=80=99t use readonly much any more. The amount of restric= tions and weird behavior just makes it impossible for any real-world use= except for narrow cases the original authors of the feature dreamed up.= With hooks and asymmetrical viz, it=E2=80=99s nearly an obsolete featur= e anyway. 

=E2=80=94 R= ob
--2e4b967d7982479db0c6d86a87a44d7c--