Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127967 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 595751A00BC for ; Wed, 9 Jul 2025 11:40:01 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1752061090; bh=CL3NUwZAF3ZoalKNUHJ4wbaje+liSvtGSt4jzbkHt+g=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=Q7v5tfUr5JPREd+jrFD6bBucG5SyDMGe8Q9KydMOBgyOezMfGW3KT3mu6tJmLYTTi fVwUTnpfJOFtxG0NTjgVNyfPdK8E4pJPuTnWS00gkq2L74WZv+KNAKk6xB5l4us7Ea GRjjyPgqGhMd64/BAbYQ6jaAatcUq9IO/kqgJye1TNBNP3w6KlH+QpkGlIyWxmwLpo ip8tHul/FIXL4L+Kv9KrBL8kPVpEDtYjj7A/+Z4VxshfDibm5JJiC+o+Q17qe/EnJO WWIt6M8SkTE0rc27Z/JOOmh3tFlxeJz+nIshYimGOJMHyMy3xSNYeqD1iPzZXQA1BE J2m2qyfkwyLig== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 72D8D180086 for ; Wed, 9 Jul 2025 11:38:09 +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.2 required=5.0 tests=BAYES_20,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, FREEMAIL_REPLY,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, 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 mail-lj1-f172.google.com (mail-lj1-f172.google.com [209.85.208.172]) (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 11:37:59 +0000 (UTC) Received: by mail-lj1-f172.google.com with SMTP id 38308e7fff4ca-32b78b5aa39so50546901fa.1 for ; Wed, 09 Jul 2025 04:39:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1752061188; x=1752665988; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=CL3NUwZAF3ZoalKNUHJ4wbaje+liSvtGSt4jzbkHt+g=; b=YyoNwpHZ+P9/Omj4hsvrI/5pEkR0d1i1MT7GCo7tJ41TGg6hzpIMxv6Y93T0Pa2Ihy SgTG2AUzif0BXWJsapfkkS/fjLd6XpG+JLqaQdVktFXGZysBoqKbD2bTw5zJIQVgGN4N Ka6YECnyYeQV/flaAv3TlOc4N3XDJforOZnMVhhKDUd/TcqcH6z+tYKxMq+iSB5MHpPG CYRR3XRxpWFHZdXgnh3ZoBtqgI+fYrS0rlcubOw6cvwaSm4xXogY9OYgWTj4jCcmggSE 8U2l6RdGuk4nHc+tsFTgy2FbQE/tic4OSgycin7Qs/tRWDR6wO0nQj9Rs1JIsJOFDC3Z OuRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752061188; x=1752665988; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=CL3NUwZAF3ZoalKNUHJ4wbaje+liSvtGSt4jzbkHt+g=; b=NtS/r7TS4yuUKpEOKCf0R9jJdUnBnIOuOadyJqw0lVMJ4WPtMlDTLH16uDTEgwM81i l1RdF3ZufdvCpMDpqr/NsYU5pAbwbbQlxm7syakctNr3XHQOMrUNubXO2k+jzSGFprsl k7CxHM9NqJZEWj+kJvIyZW8lyANB1jrAkG8AChtKUXqpsWVjPEFwW4cX1gr0UbaPiJ+F ivmA668IEW935vCJ2Yhvh+yJI0J+ISXlZuKQc2A1TlKa1CsLSRtnMbZNo+xANTcbg4Yv wa7I56LzSLTqOgWUQCGApIie9918nesyxVwrxtET9uSBv9r6tFfe6+IwA4gh+x+OIZ+L zs8w== X-Forwarded-Encrypted: i=1; AJvYcCVu46PAJQ1FBdgzyEyIqmT98QTZPYYosdCSVsH96tISxhq1/+kM0syktD05q+uwLpGHj7QKBVMWpvQ=@lists.php.net X-Gm-Message-State: AOJu0YyI7zuOqzaBqHgRobRDhF1udxCZYlEugE1BRip+fIDyPB1WnxxV YP/w+syXC1DGCOVEFMGZi/S/XIO4hV4MXS3dUs6WKl0lVqMAai77JZvsRHozMgI+UYMrc+qUyW1 RWBjXKMkjl975f6R3hLQL1GvAQn4QDkI= X-Gm-Gg: ASbGncvZhjkGfX3p1X510qaoputWppUHOPgSoxLSLtjN4k1KDyHuTbCShnIswhAz80i Qo2oacOpCjbkToJaWFfsGUUVw9PMZW/6j/HlP6zLMBNmemBjtC4GbrbGpY6FifTfA9T68Tkbovg ppY1Sv7LwtXxjt7IGMR6ajkiFsQFR2nNkeJZpNqiie2g== X-Google-Smtp-Source: AGHT+IHymGbkJL/vtmJGWxAnCjoSJky8XVBFIA6XrvCiBuovTKxE2Oa6ZIns1v+HUb5yovbthln0V0QF+cX3u5k1eF8= X-Received: by 2002:a05:651c:2117:b0:32a:7666:80d0 with SMTP id 38308e7fff4ca-32f485266c9mr7093931fa.23.1752061187500; Wed, 09 Jul 2025 04:39:47 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: <1e8634d7-ac1a-4025-b4e2-1948aabf5251@app.fastmail.com> <46857A6D-5EAF-44AF-A2DE-9B40AF8DE8C8@gmail.com> In-Reply-To: <46857A6D-5EAF-44AF-A2DE-9B40AF8DE8C8@gmail.com> Date: Wed, 9 Jul 2025 13:39:35 +0200 X-Gm-Features: Ac12FXzzyHOg7brs86QfcSJpHwmFBYNjF_P94tvfMiXqpIOTwtrM3kCKAD9XX1Q Message-ID: Subject: Re: [PHP-DEV] [RFC] Readonly property hooks To: Claude Pache Cc: Larry Garfield , php internals Content-Type: multipart/alternative; boundary="000000000000b4b01906397d881b" From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --000000000000b4b01906397d881b Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Thanks for your detailed thoughts, Claude. I'd like to offer my perspective on some of the points you raised. Le mer. 9 juil. 2025 =C3=A0 12:53, Claude Pache a =C3=A9crit : > > > Le 8 juil. 2025 =C3=A0 17:32, Nicolas Grekas 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 cas= e: > > 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 > 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). > > 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 prop= er > solution. In the worst case, you can still use a non-readonly hooked > property and document the intended invariants in phpdoc. > __get is certainly not legacy; removing it would break many use cases without proper alternatives. The behavior after unset() has been promoted to a language feature when readonly properties were introduced *because* it helps solve real world use cases. I've been asked recently by Gina if those use cases were covered by eg native lazy proxies. The answer is *no*, because native lazy proxies cover only part of the lazy-proxying domain: what remains is proxying by interface and proxying internal classes, and those require a way to proxy all property accesses, which is why magic methods are required. With the argument that __get can be used to implement the non-readonly-ness, we could also say that hooks are not needed, because they can be implemented using __get. Yet, language aesthetics are important, and we welcomed hooks for this reason. Being able to easily lazy-init thanks to hooks on readonly would be a welcome improvement to me. That being said, about your init proposal, I think that could work. I'd just do it a bit differently: instead of introducing a new "init" hook, I'd prefer having "set" mean "init" for readonly properties. But I know nothing about the engine on the topic so I can't comment on the feasibility aspect. I'll leave this to others. Just a word about using hooks vs __get for lazy-init: the really hard part when using __get is emulating the public/protected/private visibility rules. Hooks make this a non-issue. Yet hooks - unfortunately - can't be used as a generic lazy-init implementation because of their behavior related to references. That's another topic, but still related, to reinforce that __get is certainly not legacy. > > 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 desig= n > 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 fina= l class, > even if you think that you have good reason for it, like constructing a > mock object. > That's not strictness when the root concept is already filled with conceptual holes... I'm surprised nobody ever proposed the concept of an *immutable* keyword, that'd be like readonly but that'd accept only also-immutable values. Until this happens, using readonly for that is a fallacy I'm sorry... To me that invalidates all related arguments. Nicolas --000000000000b4b01906397d881b Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Thanks for your detailed thoughts, Claude= . I'd like to offer my perspective on some of the points you raised.
Le=C2=A0mer. 9 juil. 2025 =C3=A0=C2=A012:53, Claude Pach= e <claude.pache@gmail.com&= gt; a =C3=A9crit=C2=A0:


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

<= div style=3D"font-family:Helvetica;font-size:14px;font-style:normal;font-va= riant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;te= xt-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-= decoration:none">I read Claude's concern, and I agree with Larry's = response: the engine already allows readonly to be bypassed using __get. Th= e 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 m= ake it possible. For those unaware of the awful hack, here is a minimal tes= t case:


where the `= unset(...)` is mandatory to make it =E2=80=9Cwork=E2=80=9D.

<= /div>
Are we obligated to sanction shortcomings of legacy concepts in n= ewly introduced concepts that are supposed to replace them? Or can we do so= mething better? I=E2=80=99ve 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 ca= n 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 hook= ed property and document the intended invariants in phpdoc.


__get is certainly not legacy; = removing it would break many use cases without proper alternatives.
The behavior after unset() has been promoted to a language feature when = readonly properties were introduced *because* it helps solve real world use= cases.
I've been asked recently by Gina if those use cases w= ere covered by eg native lazy proxies. The answer is *no*, because native l= azy proxies cover only part of the lazy-proxying domain: what remains is pr= oxying by interface and proxying internal classes, and those require a way = to proxy all property accesses, which is why magic methods are required.

With the argument that __get can be used to implemen= t the non-readonly-ness, we could also say that hooks are not needed, becau= se they can be implemented using __get.=C2=A0Yet, language aesthetics are i= mportant, and we welcomed hooks for this reason. Being able to easily lazy-= init thanks to hooks on readonly would be a welcome improvement to me.

That being said, about your init proposal, I think tha= t could work. I'd just do it a bit differently: instead of introducing = a new "init" hook, I'd prefer=C2=A0having "set" mea= n "init" for readonly properties. But I know nothing about the en= gine on the topic so I can't comment on the feasibility aspect. I'l= l leave this to others.

Just a word about using ho= oks vs __get for lazy-init: the really hard part when using __get is emulat= ing the public/protected/private visibility rules. Hooks make this a non-is= sue. Yet hooks - unfortunately - can't be used as a generic=C2=A0lazy-i= nit implementation because of their behavior related to=C2=A0references. Th= at's another topic, but still related, to reinforce that __get is certa= inly not legacy.
=C2=A0



<= /div>
If a class is final and uses= readonly with either hooks or __get, then that's the original author&#= 39;s choice. There's no need for extra engine-assisted strictness in th= is 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) e= scape annoying constraints. For example, you can=E2=80=99t extend a final c= lass, even if you think that you have good reason for it, like constructing= a mock object.


That&#= 39;s not strictness when the root concept is already filled with conceptual= holes...=C2=A0I'm surprised=C2=A0nobody ever proposed the concept of a= n *immutable* keyword, that'd be like readonly but that'd accept on= ly also-immutable values. Until this happens, using readonly for that is a= =C2=A0fallacy I'm sorry... To me that invalidates all related arguments= .

Nicolas
--000000000000b4b01906397d881b--