Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:128007 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 E03131A00BC for ; Fri, 11 Jul 2025 09:39:11 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1752226642; bh=PoGO7ZNL86i2J0mW+oM8Ewlz3QgnYaN2DI2rtLysJSo=; h=From:Subject:Date:In-Reply-To:Cc:To:References:From; b=R9LI2Gq0+659hxZYHviRaYPlBlQXRKjv7OOW/J368oBtSiMgntqMOoAvSLp1QXIi+ vofxgtL1ir9WDOIAd3VUJu37hQpuke7sgE/j6auciMO4fciODo1hGkEH1cSjmixV6T JpYoX/zwnzP9BMUhOV2dAt7whi/hNyswF853+SF0L58h8lHnsvACKf81LYLQ4YBeR/ yvxMMv5Tcg03FojyhtM2+24Wn/lEBKjDTNcyzsBZuTb9VW+PYjH2E7NgJRf3CXOZt6 2gJSiS3VVMSe4dMUW8pwCm2ZyZ0SHsDL9meJExb3TTkQ8Yzh3SK2zfzluQ7p6T7/rN s1d4O5KC4veOQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 857B51805D2 for ; Fri, 11 Jul 2025 09:37: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=-2.1 required=5.0 tests=BAYES_00,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 ; Fri, 11 Jul 2025 09:37:19 +0000 (UTC) Received: from avril.gn2.hosting (localhost [127.0.0.1]) by avril.gn2.hosting (Postfix) with ESMTP id BC5C41C4082C; Fri, 11 Jul 2025 11:39:06 +0200 (CEST) Received: from smtpclient.apple (unknown [111.68.29.103]) by avril.gn2.hosting (Postfix) with ESMTPSA id A58381C4029C; Fri, 11 Jul 2025 11:39:05 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=nicksdot.dev; s=default; t=1752226746; 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=fw2sO2yA01sVzz153G7xytm8XdxXSgwWlq2ZGPSUqVQ=; b=s5b8Jekhhnxdi54dgKu6mTkpDCtwu6pjQ+PG/WV7v8gkTzYgPy3I0HGAKrZ3Cab257gPP9 cNs7MdT+ztKUty7qHIClQA2akyJnGX0t8K2f93tjWaXK1ujV2okFF953LPnsTE92sV20rJ /Ri30XIM9nms/ORecFP7nG6PZq6KYF1RkpJHsIvL8LXSCONYLRF/N0SNCsnkB9884sJxGS IYNTQGWFDeMEoFjPbfyXSMurpQnSZ8KL/yy39uz5fFG+jkLBxy1l84SbMEoOev5tWLwmTg H5SzlPQTkPROD4aK0hy6pg6B/7oRiWoVpyar/eHXF7hsnxcCv/v49goAMV/LIw== Message-ID: <203C1BD7-E688-4E26-8EA6-EA5331525470@nicksdot.dev> Content-Type: multipart/alternative; boundary="Apple-Mail=_9B17389C-0168-4F86-891F-7C8AF84B6D1D" 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 16:38:43 +0700 In-Reply-To: <16BD443D-A179-465D-84A0-6E3780F62D8E@gmail.com> Cc: Larry Garfield , php internals To: Claude Pache References: <1e8634d7-ac1a-4025-b4e2-1948aabf5251@app.fastmail.com> <9D5043B2-1589-4FD5-B289-6E98FB1177BE@nicksdot.dev> <16BD443D-A179-465D-84A0-6E3780F62D8E@gmail.com> X-Mailer: Apple Mail (2.3826.600.51.1.1) From: php@nicksdot.dev (Nick) --Apple-Mail=_9B17389C-0168-4F86-891F-7C8AF84B6D1D Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On 11. Jul 2025, at 14:25, Claude Pache = wrote: >=20 >> Le 11 juil. 2025 =C3=A0 06:30, Nick a =C3=A9crit : >>=20 >> To not get this buried in individual answers to others: >>=20 >> I came up with two alternative implementations which cache the = computed `get` hook value. >> One leverages separate cache properties, the other writes directly to = the backing store. >>=20 >> Links to the alternative branches can be found in the description of = the original PR. >> https://github.com/php/php-src/pull/18757 >>=20 >> I believe that these are fair solutions to address the concerns that = came up in the discussion, and I hope people will agree. >>=20 >> Cheers, >> Nick >=20 >=20 > Hi Nick, >=20 > I think that the second alternative described as: >=20 > =E2=80=9CCache computed get hook to it's backing store; never run the = hook again=E2=80=9D >=20 > is near the most reasonable from my point of view (basing my judgment = on the description, as I have not looked the actual implementation), = although there are still some concerns. >=20 > Advantages of that approach: >=20 > 1. relatively to the first alternative solution: there is indeed no = point to have a cache separate from the backing-store, as the = backing-store is supposed to play that role in most cases; >=20 > 2. relatively to the manual =E2=80=9C??=3D=E2=80=9D pattern, it works = correctly with nullable properties. >=20 > Here are my remaining concerns: >=20 > A. It may be confusing to have a getter that is not always called. >=20 > B. The idea of returning the value directly from the backing-store if = initialised is useful also for non-readonly properties. (That can be = emulated with the =E2=80=9C??=3D=E2=80=9D pattern, but only if the = property is not nullable.) >=20 > Both concerns may be resolved with the following amendment: >=20 > * Introduce a `cached` modifier, that enables the caching semantics = (i.e., not executing the getter if the backing-store is initialised). >=20 > The example from the RFC would be written as: >=20 > ```php > readonly class LazyProduct extends Product { >=20 > private DbConnection $dbApi; > =20 > private string $categoryId; > =20 > public Category $category { > cached get =3D> $this->dbApi->loadCategory($this->categoryId); > } > } > ``` >=20 > The `cached` modifier may be applied to any get hook, but it is = mandatory if the property is readonly. >=20 > (In practice, the =E2=80=9Ccached get hook=E2=80=9D corresponds to my = originally proposed =E2=80=9Cinit hook=E2=80=9D, but with the advantage = of not having a separate hook.)=20 >=20 > =E2=80=94Claude Hey Claude, I agree, the second alternative is more neat. > A.=20 Three people seem to think that always running the hook when returning a = cached value would cause confusion. You are now arguing for the exact opposite. I don=E2=80=99t have a very = strong opinion here.=20 However, I also came to the conclusion that caching and not running the = hook is the more obvious and less confusing approach. Also, naturally it=E2=80=99s more performant to not run the hook again.=20= Running the hook again, but then not updating the value feels off.=20 And updating the value would mean no caching, which brings us to the = very beginning and your very concern. So, yeah=E2=80=A6 that=E2=80=99s that. :) > B. I am afraid here I do have a strong opinion. Please remember my very = first mail before discussion [1]. While for Larry the main reason for `readonly` hooks is = lazy-initialisation, for me it is to write less code, to have less noisy = classes.=20 I don=E2=80=99t want to be forced to add readonly to each property. Now you are proposing a mandatory `cached` modifier. Which means *checks = notes*, I would save 2 characters on each property. You will understand that this is not in my interest, and not what I am = proposing here. But aside from that I do not like to write more code. I honestly don=E2=80= =99t see the benefit of having this modifier in the first place. How =E2=80=9Calternative implementation 2=E2=80=9D works now is IMO just = good. And it makes sense because it is limited to the `readonly` = context.=20 There is no technical need for the modifier. As we see, because we have = a working solution at hand. For me: It is clear, it=E2=80=99s solving the main issue you rightfully = brought up, and it is less code to write. I can imagine that `cached` could be helpful on non `readonly` = properties in some cases. However, I feel that brings us almost in similar territory than = immutable classes.=20 It is a whole new topic which I believe really shouldn=E2=80=99t be part = of this RFC that explicitly . Can we please agree on that it is future scope whether or not non = `readonly` hooks should get such a modifier? I=E2=80=99d appreciate if we could settle with =E2=80=9Calternative = implementation 2=E2=80=9D as proposed.=20 Cheers, Nick [1] https://externals.io/message/127529 --Apple-Mail=_9B17389C-0168-4F86-891F-7C8AF84B6D1D Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=utf-8
On 11. Jul = 2025, at 14:25, Claude Pache <claude.pache@gmail.com> = wrote:

Le 11 juil. = 2025 =C3=A0 06:30, Nick <php@nicksdot.dev> a =C3=A9crit = :

To not get this buried = in individual answers to others:

I came up with = two alternative implementations which cache the computed `get` hook = value.
One leverages separate cache properties, the other = writes directly to the backing store.

Links to = the alternative branches can be found in the description of the original = PR.
https://github.com/php/php-src/pull/18757

I believe that these are fair solutions to address the concerns = that came up in the discussion, and I hope people will = agree.

Cheers,
Nick
<= /div>


Hi = Nick,

I think that the second alternative = described as:

=E2=80=9CCache computed get hook = to it's backing store; never run the hook = again=E2=80=9D

is near the most reasonable from = my point of view (basing my judgment on the description, as I have not = looked the actual implementation), although there are still some = concerns.

Advantages of that = approach:

1. relatively to the first = alternative solution: there is indeed no point to have a cache separate = from the backing-store, as the backing-store is supposed to play that = role in most cases;

2. relatively to the manual = =E2=80=9C??=3D=E2=80=9D pattern, it works correctly with nullable = properties.

Here are my remaining = concerns:

A. It may be confusing to have a = getter that is not always called.

B. The idea = of returning the value directly from the backing-store if initialised is = useful also for non-readonly properties. (That can be emulated with the = =E2=80=9C??=3D=E2=80=9D pattern, but only if the property is not = nullable.)

Both concerns may be resolved with = the following amendment:

* Introduce a `cached` = modifier, that enables the caching semantics (i.e., not executing the = getter if the backing-store is = initialised).

The example from the RFC would be = written as:

```php
readonly class = LazyProduct extends Product {

  =  private DbConnection $dbApi;
 
  =  private string $categoryId;
 
  =  public Category $category {
      =  cached get =3D> = $this->dbApi->loadCategory($this->categoryId);
  =  }
}
```

The `cached` = modifier may be applied to any get hook, but it is mandatory if the = property is readonly.

(In practice, the = =E2=80=9Ccached get hook=E2=80=9D corresponds to my originally proposed = =E2=80=9Cinit hook=E2=80=9D, but with the advantage of not having a = separate = hook.) 

=E2=80=94Claude

Hey Claude,

I agree, the = second alternative is more = neat.

A. 

T= hree people seem to think that always running the hook when returning a = cached value would cause confusion.
You are now arguing for = the exact opposite. I don=E2=80=99t have a very strong opinion = here. 
However, I also came to the conclusion that = caching and not running the hook is the more obvious and less confusing = approach.
Also, naturally it=E2=80=99s more performant to not = run the hook again. 

Running the hook = again, but then not updating the value feels off. 
And = updating the value would mean no caching, which brings us to the very = beginning and your very concern.
So, yeah=E2=80=A6 that=E2=80=99= s that. :)

B.

I am = afraid here I do have a strong opinion. Please remember my very first = mail before discussion [1].
While for Larry the main reason = for `readonly` hooks is lazy-initialisation, for me it is to write less = code, to have less noisy classes. 
I don=E2=80=99t want = to be forced to add readonly to each property.
Now you are = proposing a mandatory `cached` modifier. Which means *checks notes*, I = would save 2 characters on each property.
You will understand = that this is not in my interest, and not what I am proposing = here.

But aside from that I do not like to = write more code. I honestly don=E2=80=99t see the benefit of having this = modifier in the first place.
How =E2=80=9Calternative = implementation 2=E2=80=9D works now is IMO just good. And it makes sense = because it is limited to the `readonly` context. 
There = is no technical need for the modifier. As we see, because we have a = working solution at hand.

For me: It is clear, = it=E2=80=99s solving the main issue you rightfully brought up, and it is = less code to write.

I can imagine that `cached` = could be helpful on non `readonly` properties in some = cases.
However, I feel that brings us almost in similar = territory than immutable classes. 
It is a whole new = topic which I believe really shouldn=E2=80=99t be part of this RFC that = explicitly .
Can we please agree on that it is future scope = whether or not non `readonly` hooks should get such a = modifier?

I=E2=80=99d appreciate if we could = settle with =E2=80=9Calternative implementation 2=E2=80=9D as = proposed. 

Cheers,
Nick

[1] = https://externals.io/message/127529












<= div>

= --Apple-Mail=_9B17389C-0168-4F86-891F-7C8AF84B6D1D--