Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:128008 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 9EC0C1A00BC for ; Fri, 11 Jul 2025 10:03:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1752228079; bh=xP59K+dfHWm6lL6mdFE9bl/IA9JbnErNukR1Jw6WvAc=; h=From:Subject:Date:In-Reply-To:Cc:To:References:From; b=jXpBL2WyASQWFg8joO3Jgg0vy5Qy9eECA4ykB4m7nQsouQr895umyHYKO9kVmYG1d s7ifGBio9TuqHQVEnDsEtjIOlZ2o/GmUqK8TAxFfZ6tqnaTf6+ZMsxJoMmLlKwytQ3 qhEgfuaOYfW8sNtGOF67mgHLC3h+oDoDRXmqUc4rW2f5aViNe1Wcsgsck/7VfdS/5v E2x1zsi11CtcajkPpdgkErmTmfjD6cXH8EYC0LQ1YLbl/g5R+VUQIX/6SX/LDxVL2K GwJiba3GZ+gY6ECppUBmUY7QCtdiwEIaPgHNgRZVCGpIIz/gsJd3PmR8PUAjBbp+Gd v+s+zfGJhWCiA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 751641804B0 for ; Fri, 11 Jul 2025 10:01:18 +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)) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Fri, 11 Jul 2025 10:01:07 +0000 (UTC) Received: from avril.gn2.hosting (localhost [127.0.0.1]) by avril.gn2.hosting (Postfix) with ESMTP id 26DD11C4082C; Fri, 11 Jul 2025 12:02:55 +0200 (CEST) Received: from smtpclient.apple (unknown [111.68.29.103]) by avril.gn2.hosting (Postfix) with ESMTPSA id 40D7E1C405F1; Fri, 11 Jul 2025 12:02:54 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nicksdot.dev; s=default; t=1752228174; 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=/2F9bFcfSIXARSsz3olu2sgGYMdgFmuBYPsXIEZA2Xk=; b=oVpe53yrSyDDQmG5Ej+auv4qD5uvtkANEsZ4dh2vhCjiLygRjM94EyFVF8YE2HX2HHnrAq 6ivGYY0AXk27U8fE58vsrmK9+qYY7aSxj97UMX3iH1qRK6//9N/01lsZ7RHjkvXnnPFQeC 4t0f7i39cFS6BxbEutGnar10f1b7C4DKeHRDb5UH6R1OkpscRG386T4QE7P/g64ouHn9fN VibIuB1NC4gpG6ThmR7aEGV4K4z/AQ4d4NJloru1EfRxzA8SOpRteXW7SV0JfNG6ox6lW3 O6gES3XeYLQOSPkStZtptp45kl7ScobJrbkjXFhDmdry8aYqdLjzO7Dl7tZBDQ== Message-ID: <7C9120ED-36C5-4890-92E5-5F356CEEB16E@nicksdot.dev> Content-Type: multipart/alternative; boundary="Apple-Mail=_58F58D04-F226-4C40-8696-98615F96CB84" 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: Fri, 11 Jul 2025 17:02:37 +0700 In-Reply-To: Cc: internals@lists.php.net To: Rob Landers References: <1e8634d7-ac1a-4025-b4e2-1948aabf5251@app.fastmail.com> <6acab95a554fe5e188364840ea36d2b7@bastelstu.be> X-Mailer: Apple Mail (2.3826.600.51.1.1) From: php@nicksdot.dev (Nick) --Apple-Mail=_58F58D04-F226-4C40-8696-98615F96CB84 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On 11. Jul 2025, at 13:02, Rob Landers wrote: >>=20 >> Please remember that the RFC will allow `readonly` on backed = properties only, not on virtual hooked properties.=20 >> Nothing from your example would work, and it would result in: >> Fatal error: Hooked virtual properties cannot be declared readonly >> My proposed alternative implementation with caching addresses the = concern Claude and Tim had and will make this hold: >>=20 >> ```php >> class Unusual >> { >> public function __construct( >> public readonly int $value { >> get =3D> $this->value * random_int(1, 100); >> } >> ) {} >> } >>=20 >> $unusual =3D new Unusual(1); >> var_dump($unusual->value =3D=3D=3D $unusual->value); // true=20 >> ``` >>=20 >> =E2=80=93 Nick >=20 > Hey Nick, >=20 > I was merely illustrating the different types, you can simply replace = them with non-virtual examples. To your idea of forced memoization, I'm = not sure that's a good idea. In cases where its a single execution = context per request, it may be fine, but in cases of worker mode on = frankenphp, where readonly objects may live for quite awhile, it may not = be. Generally, memoization is an optimization, not a property of a = value. >=20 > In your example above, this breaks the principle of least = astonishment, and potentially violates referential transparency due to = the calculation being non-pure. Referential transparency basically says = we can can replace the use of the variable with it's value (in this = case, $ususual->value with $unusual->backing_value * random_int(1, = 100)). If it is memoized, we cannot. There isn't a way to express that = it is one value the first time (random) and a different value the next = time (the previous computation). This is a bit dangerous because the = programmer has no way to reliably reason about the code as if the = expression can be substituted freely. >=20 > =E2=80=94 Rob Hey Rob, 1) Well, we cannot have it both in the same time. Either we want a = strict guarantee that the value doesn=E2=80=99t change after the first = `get` hook call on a `readonly` property, or we want the opposite. So = the proposed solution now is to be strict by default. If you want it = non-cached you can use non `readonly`. 2) The same point could be made outside of "readonly hooks", for = everything with any kind of randomness or external state (database, = filesystem, =E2=80=A6) source. That=E2=80=99s the trade-off.=20 I find it very acceptable as a solution for the concern brought up by = Claude and Tim. Cheers, Nick Aside: I just learned about your =E2=80=9Crecords=E2=80=9D RFC. Good = stuff!= --Apple-Mail=_58F58D04-F226-4C40-8696-98615F96CB84 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8
On 11. Jul = 2025, at 13:02, Rob Landers <rob@bottled.codes> = wrote:

Please remember = that the RFC will allow `readonly` on backed properties only, not on = virtual hooked properties. 
Nothing from your example = would work, and it would result in:
Fatal error: Hooked virtual properties cannot =
be declared readonly
My proposed alternative = implementation with caching addresses the concern Claude and Tim had and = will make this hold:

```php
class Unusual
{
public function __construct(
public readonly int $value {
get = =3D> $this->value * random_int(1, 100);
= }
) {}
}

$unusual =3D new Unusual(1);
var_dump($unusual->value =3D=3D=3D =
$unusual->value); // true 
```

=E2=80= =93 Nick

Hey = Nick,

I was merely illustrating the different = types, you can simply replace them with non-virtual examples. To your = idea of forced memoization, I'm not sure that's a good idea. In cases = where its a single execution context per request, it may be fine, but in = cases of worker mode on frankenphp, where readonly objects may live for = quite awhile, it may not be. Generally, memoization is an optimization, = not a property of a value.

In your example = above, this breaks the principle of least astonishment, and potentially = violates referential transparency due to the calculation being non-pure. = Referential transparency basically says we can can replace the use of = the variable with it's value (in this case, $ususual->value with = $unusual->backing_value * random_int(1, 100)). If it is memoized, we = cannot. There isn't a way to express that it is one value the first time = (random) and a different value the next time (the previous computation). = This is a bit dangerous because the programmer has no way to reliably = reason about the code as if the expression can be substituted = freely.

=E2=80=94 = Rob

Hey = Rob,


1) Well, we cannot have it = both in the same time. Either we want a strict guarantee that the value = doesn=E2=80=99t change after the first `get` hook call on a `readonly` = property, or we want the opposite. So the proposed solution now is to be = strict by default. If you want it non-cached you can use non = `readonly`.

2) The same point could be made = outside of "readonly hooks", for everything with any kind of randomness = or external state (database, filesystem, =E2=80=A6) source. That=E2=80=99s= the trade-off. 
I find it very acceptable as a solution = for the concern brought up by Claude and = Tim.

Cheers,
Nick

=
Aside: I just learned about your =E2=80=9Crecords=E2=80=9D = RFC. Good stuff!
= --Apple-Mail=_58F58D04-F226-4C40-8696-98615F96CB84--