Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:128122 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 ED3931A00D2 for ; Fri, 18 Jul 2025 17:10:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1752858534; bh=yWu2qgzbnfsjxA4XDqF/ekKtZJNd09BqSdwo1qmQhLw=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=n6FHBTXsvpgMG0sJPo/EWGyI0MUSh7fIP4YIhHlXajZZA2AZA3ibYxO5L38IMQECW TNJtZUcxD8omUfL1dEIZN8HGsLVDbuD8HDrBhVZOAUsvi4WLIt9obaOT2FKJ595U+a RZhtl/g9AQfAnFYPAeFJEcPf2fvGdvEYdxxN9r+Kc7bPNgyZAGIWOkwwYI8/FETWhr N+mXL4a728EXIP4Dv5vc3SFBC4e6UH3rjulB/ko1vJqifWk1hsfAo5VGGXp/FmkAvS eS0Egv/DIvJSqPZhyaw+4VKq3J8Jre0wCNbSOIvdtDd+xSUS2Ws5KhJ3isJkUaOZYM aF+CXPVd4JSOg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 474AE18072F for ; Fri, 18 Jul 2025 17:08:51 +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=-1.2 required=5.0 tests=BAYES_20,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, 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-lf1-f42.google.com (mail-lf1-f42.google.com [209.85.167.42]) (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, 18 Jul 2025 17:08:47 +0000 (UTC) Received: by mail-lf1-f42.google.com with SMTP id 2adb3069b0e04-553d771435fso2507830e87.3 for ; Fri, 18 Jul 2025 10:10:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1752858633; x=1753463433; 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=gMjMgsS5a7G1TyKbR9Qv3Yo2O6hlSJANCvqynWHKknM=; b=jG9dv2mhWnVO4r6dyF0LK9PEMdA0l/5foHG4cBOH4zoE9Bp9cVvuhgA5QvsY+i0g87 7ogLVy/kc7oV+dhUKfp8eM3LGqOEEQ1JHR4aFpHsGPCEgEwxrY/2YXImaFsmEmhDEuQQ RxDWIGVvP2KRY6Qf9ukv7VeoYaCIBqPUj11g1WvmQMFQwoQ9mgaif1hOVD0ssqBtk43m pEJ1A09+hD221pHMy2GZGdiX9txFAHnAxvF0NciLeWhap8rh3CQsV/1MpFOmlKWfN7xs X88RdC4n5NFxU6X9f9ANcjXblxGDlamzhUL0430tz7AG+7M9FBSYASArWQYZSWEK31yZ cYFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752858633; x=1753463433; 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=gMjMgsS5a7G1TyKbR9Qv3Yo2O6hlSJANCvqynWHKknM=; b=X26px7UmMMKVq8KiW5Qen8kK7mz5Vf7+9P4LK0YsDsWqWntpDFme1IoufUKwobY/39 5+ZA3ljaEN3pNT7oGjO7KhmMisboAP2svu1hEBhpWlJ5NEk2IYBVNagKvT9jYcZ1hBkc GeFuKsFDooORGFZVRIOhwzNAyWCm9knB2yci4uKGdRjUia5cfntgZSCNg4NCa/UA8gW2 ItsHY8cj040BVwxeK7lDs5/vorZquibBTU48UP9GMYixIeXpXp1OILgocKP+EX4I9+KM bF3/86EKAJ9qQ4uWxfNCXy8sSUVDSElDXiXEHZ0exdOtMEH4DFcP01xttID0KhG42r7E jtjg== X-Forwarded-Encrypted: i=1; AJvYcCVlWGJMMaWJnX8cjh270acdYHluWSi8FpaGyj1Pv94eOW3TjLd6+UU+MqVQH2BurErptKSt+aLEHsk=@lists.php.net X-Gm-Message-State: AOJu0YyhVLlCY1jMdZ4muM7o2zyewnGCxN2uoa0wDUAw7SO8Y3YTUElh 1JVISTLlQifK0XV2X5lcKFW4H6/zGCZq6tzGLNl6Cn0Aa2WdBiFKvXOXLCXRHXcKSmek5bps9aR E6E9KhjLAof75F2GwqtO8EbkgpexN7OU= X-Gm-Gg: ASbGncvphZjhljySnkBAc4VYkHYbKGKv5CUZHJEq54kzugZVUtT1qSabjk1me/P2nOL 4xKQIpeuADBpg/DV1aUIwCkmOTDvUtk0c8j0XJq3SKgzGXwQarVpVL1b8vbhwUsNfJU+4LoTlUA i12lDw9qmx3GO5I3PDIqvJt2Ba6c9TbEbhZxMpdrSuVzRyhQP4+5Iam475azQQH5QgPbPMoocBo uo7U4M= X-Google-Smtp-Source: AGHT+IFN9POYitLb80WDUOW8gV6oRIQxbVC5uTbgLQZELzYSnHFfuiEMbBinQGQ1MzS4x/2WslitMu0HoHiXgeeAlwg= X-Received: by 2002:a05:6512:1296:b0:553:2154:7bd0 with SMTP id 2adb3069b0e04-55a2331405fmr3847556e87.5.1752858632259; Fri, 18 Jul 2025 10:10:32 -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> <09780759-9ddc-45e2-9376-befb8e378775@app.fastmail.com> <0e8eb146-3b1d-4fc2-ad83-77062f2063c9@bastelstu.be> In-Reply-To: <0e8eb146-3b1d-4fc2-ad83-77062f2063c9@bastelstu.be> Date: Fri, 18 Jul 2025 19:10:19 +0200 X-Gm-Features: Ac12FXzcMgjkqIY2GKQEHRVbVa9zzOPMZks1L1rl0o1LQM3nkbUvd2PNSAu2KhQ Message-ID: Subject: Re: [PHP-DEV] Re: [RFC] Readonly property hooks To: =?UTF-8?Q?Tim_D=C3=BCsterhus?= Cc: Larry Garfield , php internals Content-Type: multipart/alternative; boundary="0000000000001e0d85063a37347e" From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --0000000000001e0d85063a37347e Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Le ven. 18 juil. 2025 =C3=A0 18:32, Tim D=C3=BCsterhus a= =C3=A9crit : > Hi > > On 7/17/25 18:26, Larry Garfield wrote: > > Given the lack of consensus both here and in off-list discussions on ho= w > to handle get hooks, we have done the following: > > > > * Split the RFC into two sections, one for get, one for set. > > * Expanded and refined the examples for both. The implementation is > still the original, however. > > * Split the vote into two: one for allowing readonly get hooks, one for > readonly set hooks. > > > > We will start the vote sometime this weekend, most likely, unless some > major feedback appears before then, and let the chips fall where they may= . > > After working through (most of) the discussion, I've now taken a look at > the updated RFC. I have the following remarks: > > 1. > > > It is really =E2=80=9Cwrite-once=E2=80=9D, which is not the same as imm= utable (as shown > above). But there's no reason that =E2=80=9Cwrite-once=E2=80=9D need be i= ncompatible with > hooks. > > This is a strawman argumentation, as I've outlined in my previous > emails, calling readonly "write-once" is wrong. It is a reasonable user > expectation to always get the identical value when reading a value that > may only be set once. By calling it "write-once" you are trying to shift > the focus to the write operation, which is totally irrelevant for user > expectations when interacting with readonly properties. Especially for > expectations of users that just *use* a class rather than writing one. > To my ears, write-once is more accurate than readonly because it sticks to the facts of how this behaves. That's very relevant. Using readonly to suggest immutable is where the arguments for rejecting this RFC are weak. readonly doesn't mean immutable, no matter how hard some want it to be... > > 2. > > > The concern would only appear if someone is deliberately doing somethin= g > non-stable > > Or if someone accidentally calls a (deep) function chain that is non-pure= . > > 3. > > > Or, for a more real-world and larger example, PHP 8.4 requires this: > > This is false. You are perfectly able to write this in PHP 8.4: > > final readonly class Entry > { > public readonly $terms; > > public function __construct( > public string $word, > public string $slug, > array $terms, > ) { > $this->terms =3D $this->upcastTerms($terms); > } > > private function upcastTerms(array $terms): array > { > $upcast =3D static fn (Term|array $term): Term > =3D> $term instanceof Term ? $term : new Term(...$term); > return array_map($upcast, $value) > } > } > > In no way do you need to use a property hook. > > 4. > > > // New code in 8.5: > > > > $p =3D new PositivePoint(3, 4); > > $p2 =3D clone($p, ['x' =3D> -10]); > > This is not legal code in PHP 8.5. Clone-with respects visibility and > since your asymmetric visibility RFC included the change, you are > probably aware that `readonly` implies `protected(set)`. 5. > > > but are now necessary to ensure that invariants are enforced. > > And therefore with PHP 8.5 hooks are not necessary to enforce > invariants, except in the rare case where a `public(set) readonly` > property is used. > Of course it's rare. It's brand new... Yet this example comes back many times. This is how the community would like to use clone-with. This should be acknowledged. The fact that protected(set) is (currently) the default is not an argument to make public(set) a second class citizen. > 6. > > > So no guarantees are softened by this RFC. > > Yes, they are. Unless `__get()` is implemented on a class (which is > explicitly visible as part of the public API), readonly guarantees the > immutability of identity. > Which is not really relevant when talking about immutability. What everybody is looking for when using that word is immutable objects. 7. > > > While that is an interesting idea that has been floated a few times, it > has enough complexities and edge cases of its own to address that we feel > it is out of scope. > > While it certainly is your right as the RFC authors to consider certain > things out of scope for an RFC, I strongly oppose the notion of shipping > something that is strictly inferior and comes with obvious semantic > issues due to perceived complexity of another solution and then > following up with the proper solution that has already been identified. > As I've outlined in my previous emails, I found defining semantics for > an 'init' hook straight-forward when looking at how PHP works as of today= . > > 8. > > > However, this RFC is in no way incompatible with adding an init hook in > the future should it be proposed. > > This is true, but as I've mentioned before, an 'init' hook would enable > the same use cases without bringing along issues. So it really should be > "one of them, but not both" (with "one of them" being the init hook). > > -------- > > After reading through the discussion, it seems the only argument against > the 'init' hook is perceived complexity. It is not at all clear to me > why this means that we must now rush something with clear issues into > PHP 8.5. > I'd understand the arguments you're pushing for if readonly were appropriate to build immutable objects. Yet that's not the case, so such reasoning is built on sand I'm sorry... To me the RFC enables useful capabilities that authors are going to need. Or find workarounds for. Which means more ugliness to come... Nicolas --0000000000001e0d85063a37347e Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


Le=C2=A0ven. 18 juil= . 2025 =C3=A0=C2=A018:32, Tim D=C3=BCsterhus <tim@bastelstu.be> a =C3=A9crit=C2=A0:
Hi

On 7/17/25 18:26, Larry Garfield wrote:
> Given the lack of consensus both here and in off-list discussions on h= ow to handle get hooks, we have done the following:
>
> * Split the RFC into two sections, one for get, one for set.
> * Expanded and refined the examples for both. The implementation is st= ill the original, however.
> * Split the vote into two: one for allowing readonly get hooks, one fo= r readonly set hooks.
>
> We will start the vote sometime this weekend, most likely, unless some= major feedback appears before then, and let the chips fall where they may.=

After working through (most of) the discussion, I've now taken a look a= t
the updated RFC. I have the following remarks:

1.

> It is really =E2=80=9Cwrite-once=E2=80=9D, which is not the same as im= mutable (as shown above). But there's no reason that =E2=80=9Cwrite-onc= e=E2=80=9D need be incompatible with hooks.

This is a strawman argumentation, as I've outlined in my previous
emails, calling readonly "write-once" is wrong. It is a reasonabl= e user
expectation to always get the identical value when reading a value that may only be set once. By calling it "write-once" you are trying t= o shift
the focus to the write operation, which is totally irrelevant for user
expectations when interacting with readonly properties. Especially for
expectations of users that just *use* a class rather than writing one.
<= /blockquote>

To my ears, write-once is more accurate tha= n readonly=C2=A0because it sticks to the facts of how this behaves. That= 9;s very relevant.
Using readonly to suggest immutable is where t= he arguments for rejecting this RFC are weak.
readonly doesn'= t mean=C2=A0immutable, no=C2=A0matter how hard some want it to be...
<= div>=C2=A0

2.

> The concern would only appear if someone is deliberately doing somethi= ng non-stable

Or if someone accidentally calls a (deep) function chain that is non-pure.<= br>
3.

> Or, for a more real-world and larger example, PHP 8.4 requires this:
This is false. You are perfectly able to write this in PHP 8.4:

=C2=A0 =C2=A0 =C2=A0final readonly class Entry
=C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public readonly $terms;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public function __construct(
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public string $word,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0public string $slug,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0array $terms,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0$this->terms =3D $this-&= gt;upcastTerms($terms);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0private function upcastTerms(array $terms= ): array
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0{
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0$upcast =3D static fn (Term= |array $term): Term
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D> $term= instanceof Term ? $term : new Term(...$term);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return array_map($upcast, $= value)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
=C2=A0 =C2=A0 =C2=A0}

In no way do you need to use a property hook.

4.

> // New code in 8.5:
>=C2=A0
> $p =3D new PositivePoint(3, 4);
> $p2 =3D clone($p, ['x' =3D> -10]);

This is not legal code in PHP 8.5. Clone-with respects visibility and
since your asymmetric visibility RFC included the change, you are
probably aware that `readonly` implies `protected(set)`.=C2=A0
=
5.

> but are now necessary to ensure that invariants are enforced.

And therefore with PHP 8.5 hooks are not necessary to enforce
invariants, except in the rare case where a `public(set) readonly`
property is used.

Of course it'= ;s rare. It's brand new...
Yet this example comes back many t= imes.
This is how the community would like to use clone-with.
This should be acknowledged.

The fact that = protected(set) is (currently) the default is not an argument to make public= (set) a second class citizen.

=C2=A0
6.

> So no guarantees are softened by this RFC.

Yes, they are. Unless `__get()` is implemented on a class (which is
explicitly visible as part of the public API), readonly guarantees the
immutability of identity.

Wh= ich is not really relevant when talking about immutability.
What = everybody is looking=C2=A0for when using that word is immutable objects.
=C2=A0

7.

> While that is an interesting idea that has been floated a few times, i= t has enough complexities and edge cases of its own to address that we feel= it is out of scope.

While it certainly is your right as the RFC authors to consider certain things out of scope for an RFC, I strongly oppose the notion of shipping something that is strictly inferior and comes with obvious semantic
issues due to perceived complexity of another solution and then
following up with the proper solution that has already been identified. As I've outlined in my previous emails, I found defining semantics for =
an 'init' hook straight-forward when looking at how PHP works as of= today.

8.

> However, this RFC is in no way incompatible with adding an init hook i= n the future should it be proposed.

This is true, but as I've mentioned before, an 'init' hook woul= d enable
the same use cases without bringing along issues. So it really should be "one of them, but not both" (with "one of them" being t= he init hook).

--------

After reading through the discussion, it seems the only argument against the 'init' hook is perceived complexity. It is not at all clear to = me
why this means that we must now rush something with clear issues into
PHP 8.5.

I'd understand the argumen= ts you're pushing for if readonly were appropriate to build immutable o= bjects.=C2=A0Yet that's not the case, so such reasoning is built on san= d I'm sorry...

To me the RFC enables useful ca= pabilities that authors are going to need. Or find workarounds for. Which m= eans more ugliness to come...

Nicolas
<= /div>
--0000000000001e0d85063a37347e--