On Fri, 18 Jul 2025, 15:16 Tim Düsterhus, <t...@bastelstu.be> wrote:
> Hi > > On 7/9/25 16:05, Larry Garfield wrote: > > 1. `readonly` bills itself as immutability, but it fundamentally is > not. There are at least two loopholes: __get and a mutable object saved to > a property. So while it offering immutability guarantees is nice in > theory, it's simply not true in practice. `readonly` has always been > misnamed; it should really be `writeonce`, because that's all it is. (Once > again, this is likely the most poorly designed feature we've added in many > years.) > > No, readonly is readonly, not writeonce. Stop trying to redefine > readonly as writeonce to justify bad design decisions. > > Readonly guarantees that once I successfully read from a property that > I'll get the same thing out on subsequent reads and I consider this to > be valuable and strongly disagree on the "most poorly designed feature" > bit. > > Yes, I understand that __get() currently is an exception to that > guarantee, but that does not mean that further exceptions should be > added to water down readonly into something that is completely useless. > > > 2. In 8.4, if a class is marked `readonly`, you basically forbid it from > having any hooks of any kind, even though you absolutely can honor the > write-once-ness of the properties while still having hooks. And that > applies to child classes, too, because `readonly`-ness inherits. So adding > a single hook means you have to move the readonly to all the other > properties individually, which if inheritance is involved you cannot do. > > > > The RFC aims to address point 2 in a way that still respects point 1, > but only point 1 as it actually is (write-once), not as we wish it to be > (immutability). > > Readonly is immutability of values (or in other words immutability of > identity). For objects this means immutability of the object handle, for > other types this means actual immutability. > > I also feel compelled to mention at this point that the commonly > repeated statement of "Objects are passed by reference" is incorrect. > It's that "the object handle is passed by value". And then it's fully > consistent with how readonly works as of now. > > > * set hooks for validation, which don't impact writeonce-ness. I think > everyone seems on board with that. > > Yes, allowing set hooks for readonly properties seems sound to me. > > > * Lazy computed properties. I use these a ton, even for internal > caching purposes. 99% of the time I cache them because my objects are > practically immutable, and $this->foo ??= whatever is an easy enough > pattern. (If they're not cached then it would be a virtual property, which > we're not dealing with for now.) As long as you're caching it in that > fashion, the write-once-ness still ends up respected. > > > > Honestly, Nick tried to come up with examples yesterday while we were > talking that would not fit into one of those two categories, and for every > one of them my answer was "if your code is already that badly designed, > there's nothing we can do for you." :-) > > It's nice to hear that there are no other usecases for hooks on readonly > properties, since this means that we can just allow the 'set' hook and > add an 'init' hook for the lazy computation use-case without needing to > violate the semantics of `readonly` by allowing a `get` hook. > > > An init hook would be clearer, certainly, though it also has its own > edge cases. Can you set something that has an init hook? What happens if > there's both a get and init hook? These probably have answers that could > be sorted out, but that's a different question from "why the <censored> > does a readonly class forbid me using even rudimentary hooks???" > > Not clearer. It would be the only thing that is semantically sound. > While it certainly needs careful consideration of semantics to ensure > there are no edge cases, figuring this out should be much easier than > intentionally introducing edge cases via a get hook. > > As to your questions: The init hook is triggered when reading from a > property that is in the uninitialized state. The return value of the > hook is stored in the property and returned as the result of the read > operation. Having an init hook implies the property is non-virtual. > > - Yes, you can set something that has an init hook. Setting means that > the property will no longer be uninitialized, which means that the init > hook will no longer be called. > - If there is both a get and an init hook, the init hook will be called > when the backing store is uninitialized. The result of the init hook > will then also go through the get hook. On subsequent reads only the get > hook will be called. > > Best regards > Tim Düsterhus > Hi Tim, The problem with allowing only set hooks is that readonly class won't be compatible with hooks, I think that is one of the main motivations behind this RFC. Faizan Akram Dar faizanakram.me