Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127969 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 B3BBF1A00BC for ; Wed, 9 Jul 2025 13:18:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1752066982; bh=30Etlc3WPALS8XDv8BsEojQ7FhPKYhWEWusd85cNLuI=; h=From:Subject:Date:In-Reply-To:Cc:To:References:From; b=lQE/+Bxt91B1CLM0ZxKC50Ic+KGTFOtZO8R9wp2FbS7/Ql2L9VItxz9CoNPSQZF8n CuJ/PTl4/SN6IRHtrLPoeHSywfUw8H0iEunmhZCkin+UEp/WCUoHetvnPUVmitgOET fdKhIcsDHRJp3tlfZ+RILp1tdBoaS7/96vrnMp8RP+yUIQNP0qIiPSzC0lWfMwtQn3 Bq+4HcQCqaLiNBZLvVPy/WBHWg0Ikd6LCfvxyVfGEeKAOTwvZaUg+Iu7QZHWdb9Iir 5Tv0cbw6juK4kj0/nf5wSE5hbKM1cs33kCwje+eepHSyDv14CHp3hezAb1+DrBansK 40ldYjU+widvA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 915FF180057 for ; Wed, 9 Jul 2025 13:16:20 +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.7 required=5.0 tests=BAYES_05,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,HTML_MESSAGE, SPF_HELO_NONE,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 avril.gn2.hosting (avril.gn2.hosting [84.19.162.247]) (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 13:16:19 +0000 (UTC) Received: from avril.gn2.hosting (localhost [127.0.0.1]) by avril.gn2.hosting (Postfix) with ESMTP id 672B41C40C76; Wed, 9 Jul 2025 15:18:07 +0200 (CEST) Received: from smtpclient.apple (unknown [111.68.29.103]) by avril.gn2.hosting (Postfix) with ESMTPSA id 0651C1C4081E; Wed, 9 Jul 2025 15:18:01 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nicksdot.dev; s=default; t=1752067083; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=lnd2S4Ya0lN1DPP7HtdfSkOklDnB3r/C1UqtJQswND0=; b=BOthDQDo3nH/l3IOZwxzeeq1d77e9ge1oebZYWviEbvcbrIaoDmzbYl1w8/+3H7YkAwIlw x/RQbb11T2dTGOsP1vzsHka3QiWtcKydXcf2ATpJbaJf/wHOPkhCiC9i4Mft7xRVruimd3 9srSIgwOtFngiHdNp8zoyZNZ1rowQyH/PFUtMMJgWVwy/wfAGXE4+o1Li+oQvra4uIYoTo wOnBYRsY91NQUojtl/Q6W2cwtvMSmZSVO8+dRQePZNusfrgS9fRpEHQ+KVwj71mcLRMt7g mFtN5rTiZ9W7GcT9wjEPpqYNXqZnL0iBK8KrVDfRMyimZw04ksncnyhlCUKlOQ== Message-ID: Content-Type: multipart/alternative; boundary="Apple-Mail=_1282DFED-8A0F-4D0E-9B48-8ECA44B92202" Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3826.600.51.1.1\)) Subject: Re: [PHP-DEV] [RFC] Readonly property hooks Date: Wed, 9 Jul 2025 20:17:47 +0700 In-Reply-To: <46857A6D-5EAF-44AF-A2DE-9B40AF8DE8C8@gmail.com> Cc: Nicolas Grekas , Larry Garfield , php internals To: Claude Pache References: <1e8634d7-ac1a-4025-b4e2-1948aabf5251@app.fastmail.com> <46857A6D-5EAF-44AF-A2DE-9B40AF8DE8C8@gmail.com> X-Mailer: Apple Mail (2.3826.600.51.1.1) From: php@nicksdot.dev (Nick) --Apple-Mail=_1282DFED-8A0F-4D0E-9B48-8ECA44B92202 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 Hey Claude, >> 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 = engine already allows readonly to be bypassed using __get. The added = hook doesn't make anything more lenient. >>=20 >=20 > It is true that readonly could be bypassed by __get(); but this is a = legacy behaviour, and you have to take an explicit step to make it = possible. For those unaware of the awful hack, here is a minimal test = case: >=20 > https://3v4l.org/N78An >=20 > where the `unset(...)` is mandatory to make it =E2=80=9Cwork=E2=80=9D. >=20 > Are we obligated to sanction shortcomings of legacy concepts in newly = introduced concepts that are supposed to replace them? Or can we do = something better? I=E2=80=99ve outlined in a previous email what I think = is a better design for such situation (namely an `init` hook). >=20 > Also, the fact that __get() is not yet deprecated means that we can = still use the aforementioned hack until/unless we=E2=80=99ve implemented = a proper solution. In the worst case, you can still use a non-readonly = hooked property and document the intended invariants in phpdoc. >=20 >=20 >> If a class is final and uses readonly with either hooks or __get, = then 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-readonly way by mistake, so it has to be by intent. >>=20 >=20 > Enforcing as strictly as possible its intended invariants is a good = design for a robust language. Yes, it implies that users cannot (or can = hardly) escape annoying constraints. For example, you can=E2=80=99t = extend a final class, even if you think that you have good reason for = it, like constructing a mock object. >=20 > =E2=80=94Claude I hear you, but I still struggle to fully grasp the issue. It=E2=80=99s = genuinely hard for me to come up with a real-world example that actually = makes sense. Everything I=E2=80=99ve seen so far, including the RFC example and what = I tried myself (I gave it an honest shot), feels either very theoretical = or entirely intentional, and thus perfectly logical in its outcome. In one of your previous mails you brought up an example that requires = calling a class method (read: intentionally changing class state), which = would result in a non-consistent value being returned when calling the = same property more than once. I get it. But what if the user wants = exactly that in their `readonly` class? That said I did address your concern here (actual RFC PR branch against = alternative; PoC): = https://github.com/NickSdot/php-php-src/compare/allow-readonly-hooks...Nic= kSdot:php-php-src:readonly-hooks-strict Larry and I agree that we don=E2=80=99t want this complexity in the = current RFC. Perhaps this is something for a separate `init` hook RFC? Cheers, Nick (Sorry for the duplicate. I forgot to CC the list)= --Apple-Mail=_1282DFED-8A0F-4D0E-9B48-8ECA44B92202 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8 Hey = Claude,

Le 8 = juil. 2025 =C3=A0 17:32, Nicolas Grekas = <nicolas.grekas+php@gmail.com> a =C3=A9crit :

I read Claude's concern, and I agree with Larry's response: the = engine already allows readonly to be bypassed using __get. The added = hook doesn't make anything more lenient.


It = is true that readonly could be bypassed by __get(); but this is a legacy = behaviour, and you have to take an explicit step to make it possible. = For those unaware of the awful hack, here is a minimal test = case:


<= /div>
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 introduced concepts = that are supposed to replace them? Or can we do something better? I=E2=80=99= ve outlined in a previous email what I think is a better design for such = situation (namely an `init` hook).

Also, the = fact that __get() is not yet deprecated means that we can still use the = aforementioned hack until/unless we=E2=80=99ve implemented a proper = solution. In the worst case, you can still use a non-readonly hooked = property and document the intended invariants in = phpdoc.


If a class is final and uses readonly with either = hooks or __get, then 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-readonly way by mistake, so it has to be by = intent.


Enforcing as = strictly as possible its intended invariants is a good design for a = robust language. Yes, it implies that users cannot (or can hardly) = escape annoying constraints. For example, you can=E2=80=99t extend a = final class, even if you think that you have good reason for it, like = constructing a mock = object.

=E2=80=94Claude
<= br>
I hear you, but I still struggle to fully grasp the issue. = It=E2=80=99s genuinely hard for me to come up with a real-world example = that actually makes sense.
Everything I=E2=80=99ve seen so = far, including the RFC example and what I tried myself (I gave it an = honest shot), feels either very theoretical or entirely intentional, and = thus perfectly logical in its outcome.

In one = of your previous mails you brought up an example that requires calling a = class method (read: intentionally changing class state), which =  would result in a non-consistent value being returned when calling = the same property more than once. I get it. But what if the user wants = exactly that in their `readonly` class?

That = said I did address your concern here (actual RFC PR = branch against alternative; PoC):

Larry and I agree that we = don=E2=80=99t want this complexity in the current RFC.
Perhaps = this is something for a separate `init` hook = RFC?

Cheers,
Nick

(Sorry for the duplicate. I forgot to CC the = list)
= --Apple-Mail=_1282DFED-8A0F-4D0E-9B48-8ECA44B92202--