Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127972 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 2D3F21A00BC for ; Wed, 9 Jul 2025 14:06:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1752069856; bh=C1XiXHihHEPqgSEo5lLZ8cdVTYOwxOGt5EfBGIgFctk=; h=Date:From:To:In-Reply-To:References:Subject:From; b=cBkMOGPDXj1My4+OcplBqJSEI4ZDOudfOL9FBGKNwz/oXO84ntNkpXBhUzMnTxHSR cL+Ew6A2cQGVkxz/MStKPtgfBOt2r1ABG2Js3f9HCxmrnoqlniXmh4/07NPbRMX9pT 2WxTMB8ppZjvw0xY9GslZzvL6ZjZ1vUaPBipu0Bd1GdUwKIzgJZ0J3HyJ9BHdvnDBK /iJy/ddRWdiRSLeNk4eJ4lOB3lypfEYnp73/9C3v9AXrgisiTr6NwXC4OI0Ob9vRr0 Vr0TPka4SPqSWqz3+AkatX05XR4akN6HxII2JVAk/GZAvO3SJXIyGKeRZ65DcOzFCF UhOyvtiHow/vg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 03A24180086 for ; Wed, 9 Jul 2025 14:04:13 +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,RCVD_IN_DNSWL_LOW, SPF_HELO_PASS,SPF_NONE 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 fhigh-b1-smtp.messagingengine.com (fhigh-b1-smtp.messagingengine.com [202.12.124.152]) (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 ; Wed, 9 Jul 2025 14:04:12 +0000 (UTC) Received: from phl-compute-10.internal (phl-compute-10.phl.internal [10.202.2.50]) by mailfhigh.stl.internal (Postfix) with ESMTP id 7B5C57A02C3 for ; Wed, 9 Jul 2025 10:06:02 -0400 (EDT) Received: from phl-imap-02 ([10.202.2.81]) by phl-compute-10.internal (MEProxy); Wed, 09 Jul 2025 10:06:02 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= garfieldtech.com; h=cc:content-transfer-encoding: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=fm2; t=1752069962; x=1752156362; bh=Tj1Hk1ISexUUPyb/GW3O5 xLAI6P5M2rmSx/FdNqIqeo=; b=C76JL/YRTsEmNocFfwvrHARwfhldPYvWjCkUF DzQiPCT7TBSrIOO72kbQHiZSQZ77XCW+NyBihZz3sjf+ABqeW7dT0mENdhViH4F+ I2JXXPfexB1F5W9fNFee3m2xPYxtp7GznYtAoR93jwRZqh3++tfDDwcO2zMbFGJS 682CeqU+q6ky1pJWOW3gXTfAguQcVyb2xDYjrGsMBqFc12fDTGZeBNGQc8KmRnQS tRM48HK0L/kXgzUtc+kJF4bHdnUiy/9mHy/6aYHmIG9sEELN+PnXojGTdawE2J4q tBgT7rjvdb6ItWJZhRZIkT8E2QrQM09DCRaakv1uXovhk9dag== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding: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=1752069962; x=1752156362; bh=T j1Hk1ISexUUPyb/GW3O5xLAI6P5M2rmSx/FdNqIqeo=; b=mERJe6i6zZCDRhm7b TMEvTdCi6UHcq6kU4rXEAPeIrzewiDeb7MNP/5UJeEGBb2yk088PviDuNfAs2Sr0 xA1Rj4L8m7TCTf2/fXMnq/U0Hf8iq645XFtKFsxrY16SUQFoDjMIOoPeTFFGPRlU SyTdoXTfju9JxtkeBbDF11DkKV/fT6n9abldik5cKHaey0iKbFE5N6NrBkOXFpKd bJ6cMqXyqVbGg5U1ozfjJTDM7ilHZbIr3kFKdhXNRMVeSQyaOLUcbv3AzABQPERh LHQ966fHZQW8GgxiYP5rRuHcPfJy1g4ZJEH2ZFX/+usVxCsoJPAHLbPprsrUzJNs Uxf/g== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdefgdefjeejhecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpuffrtefokffrpgfnqfghnecuuegr ihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenucfjug hrpefoggffhffvkfgjfhfutgfgsehtqhertdertdejnecuhfhrohhmpedfnfgrrhhrhicu ifgrrhhfihgvlhgufdcuoehlrghrrhihsehgrghrfhhivghlughtvggthhdrtghomheqne cuggftrfgrthhtvghrnhepheffvdetjeetvdfhtefgteehfeejieetheefhfehgfehkeet ffetueejjeejgffgnecuffhomhgrihhnpeefvheglhdrohhrghenucevlhhushhtvghruf hiiigvpedtnecurfgrrhgrmhepmhgrihhlfhhrohhmpehlrghrrhihsehgrghrfhhivghl ughtvggthhdrtghomhdpnhgspghrtghpthhtohepuddpmhhouggvpehsmhhtphhouhhtpd hrtghpthhtohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvth X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id EE0BC700068; Wed, 9 Jul 2025 10:06:01 -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: Taa1a9d92243b6726 Date: Wed, 09 Jul 2025 09:05:41 -0500 To: "php internals" Message-ID: <41241c3d-a601-4fb8-9f32-976bea3b660e@app.fastmail.com> In-Reply-To: <46857A6D-5EAF-44AF-A2DE-9B40AF8DE8C8@gmail.com> References: <1e8634d7-ac1a-4025-b4e2-1948aabf5251@app.fastmail.com> <46857A6D-5EAF-44AF-A2DE-9B40AF8DE8C8@gmail.com> Subject: Re: [PHP-DEV] [RFC] Readonly property hooks Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable From: larry@garfieldtech.com ("Larry Garfield") On Wed, Jul 9, 2025, at 5:52 AM, Claude Pache wrote: >> Le 8 juil. 2025 =C3=A0 17:32, Nicolas Grekas a =C3=A9crit : >>=20 >> I read Claude's concern, and I agree with Larry's response: the engin= e already allows readonly to be bypassed using __get. The added hook doe= sn't make anything more lenient. >>=20 > > It is true that readonly could be bypassed by __get(); but this is a=20 > legacy behaviour, and you have to take an explicit step to make it=20 > possible. For those unaware of the awful hack, here is a minimal test=20 > case: > > https://3v4l.org/N78An > > where the `unset(...)` is mandatory to make it =E2=80=9Cwork=E2=80=9D. > > Are we obligated to sanction shortcomings of legacy concepts in newly=20 > introduced concepts that are supposed to replace them? Or can we do=20 > something better? I=E2=80=99ve outlined in a previous email what I thi= nk is a=20 > better design for such situation (namely an `init` hook). > > Also, the fact that __get() is not yet deprecated means that we can=20 > still use the aforementioned hack until/unless we=E2=80=99ve implement= ed a=20 > proper solution. In the worst case, you can still use a non-readonly=20 > hooked property and document the intended invariants in phpdoc. > > >> If a class is final and uses readonly with either hooks or __get, the= n that's the original author's choice. There's no need for extra engine-= assisted strictness in this case. You cannot write such code in a non-re= adonly way by mistake, so it has to be by intent. >>=20 > > Enforcing as strictly as possible its intended invariants is a good=20 > design for a robust language. Yes, it implies that users cannot (or ca= n=20 > hardly) escape annoying constraints. For example, you can=E2=80=99t ex= tend a=20 > final class, even if you think that you have good reason for it, like=20 > constructing a mock object. > > =E2=80=94Claude Here's the core problem right now: 1. `readonly` bills itself as immutability, but it fundamentally is not.= There are at least two loopholes: __get and a mutable object saved to = a property. So while it offering immutability guarantees is nice in the= ory, it's simply not true in practice. `readonly` has always been misna= med; it should really be `writeonce`, because that's all it is. (Once a= gain, this is likely the most poorly designed feature we've added in man= y years.) 2. In 8.4, if a class is marked `readonly`, you basically forbid it from= having any hooks of any kind, even though you absolutely can honor the = write-once-ness of the properties while still having hooks. And that ap= plies to child classes, too, because `readonly`-ness inherits. So addin= g a single hook means you have to move the readonly to all the other pro= perties individually, which if inheritance is involved you cannot do. The RFC aims to address point 2 in a way that still respects point 1, bu= t only point 1 as it actually is (write-once), not as we wish it to be (= immutability). In practice, there's 2 scenarios that I see as useful (or problematic in= 8.4, that we want to support): * set hooks for validation, which don't impact writeonce-ness. I think = everyone seems on board with that. * Lazy computed properties. I use these a ton, even for internal cachin= g purposes. 99% of the time I cache them because my objects are practic= ally immutable, and $this->foo ??=3D whatever is an easy enough pattern.= (If they're not cached then it would be a virtual property, which we'r= e not dealing with for now.) As long as you're caching it in that fashi= on, the write-once-ness still ends up respected. Honestly, Nick tried to come up with examples yesterday while we were ta= lking that would not fit into one of those two categories, and for every= one of them my answer was "if your code is already that badly designed,= there's nothing we can do for you." :-) Ilija and I had discussed making `readonly` imply cached/lazy/init in th= e original hooks RFC, but decided against it. Mainly, it becomes very c= onfusing if a property is going to store a value, as there's three diffe= rent scenarios to consider: There's a short-set hook, the property is me= ntioned in its own hooks, and then look for readonly. (Would that mean = readonly only works on virtual properties?) It makes a feature that's a= lready, in all honesty, at the edge of reasonable complexity more comple= x. An init hook would be clearer, certainly, though it also has its own edg= e cases. Can you set something that has an init hook? What happens if = there's both a get and init hook? These probably have answers that coul= d be sorted out, but that's a different question from "why the does a readonly class forbid me using even rudimentary hooks???" I'd be open to a follow up RFC for an init hook, though I likely wouldn'= t write it myself. But that's a different topic than what we're address= ing here. --Larry Garfield