Re: [RFC] Readonly property hooks
| From: | Rob Landers | Date: | Fri, 18 Jul 2025 17:03:37 +0000 |
| Subject: | Re: [RFC] Readonly property hooks | ||
| References: | 1 2 3 4 5 6 7 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-128119@lists.php.net to get a copy of this message | ||
On Fri, Jul 18, 2025, at 18:48, Eric Norris wrote:
> On Fri, Jul 18, 2025 at 12:01 PM Rob Landers <rob@bottled.codes> wrote:
> >
> >
> >
> > On Fri, Jul 18, 2025, at 17:25, Tim Düsterhus wrote:
> >
> > Hi
> >
> > On 7/14/25 15:38, Larry Garfield wrote:
> > > Thanks, Ilija. You expressed my concerns as well. And yes, in practice, readonly
> > > classes over-reaching is the main use case; if you're marking individual properties readonly,
> > > then just don't mark the one that has a hook on it (use aviz if needed) and there's no
> > > issue.
> >
> > A readonly class is not just a convenience shortcut to mark each
> > individual property as readonly. It has important semantics of its own,
> > because it forces child classes to also be readonly. And even for final
> > classes it communicates to the user that "I won't be adding non-readonly
> > properties to the class".
> >
> >
> > Wasn’t that the entire point of readonly classes? Because it was painful to write
> > readonly for every property. Then if a property is readonly, the inherited property is also
> > readonly, so, by extension: a class extending a readonly class is also readonly.
> >
> > There’s no “communication” here; just logic.
> >
> >
> > Marking a class as readonly must therefore be a deliberate decision,
> > since it affects the public API of your class and in turn also user
> > expectations.
> >
> >
> > Not really. I can remove the readonly designation and manually mark every property as
> > readonly. The behavior of the class doesn’t magically change. Or, at least, I hope it doesn’t.
> >
> >
> > > Perhaps we're thinking about this the wrong way, though? So far we've
> > > talked as though readonly makes the property write-once. But... what if we think of it as applying
> > > to the field, aka the backing value?
> >
> > I think of readonly from the view of the public API surface of an
> > object. The property hooks RFC was very explicit in that property hooks
> > are intended to be “transparent to the user” and can be added without
> > breaking the public API. In other words: Whether or not a property is
> > implemented using a hook should be considered an implementation detail
> > and as a user of a class I do not care whether there is a backing value
> > or not.
> >
> > > So readonly doesn't limit calling the get hook, or even the set hook, multiple
> > > times. Only writing to the actual value in the object table. That gives the exact same set of
> > > guarantees that a getX()/setX() method would give. The methods can be called any number of times,
> > > but the stored value can only be written once.
> >
> > As a user of a class the "backing table" is mostly inaccessible to me
> > when interacting with objects. It's only exposed via var_dump() and
> > serialize(), the former of which is a debug functionality and the output
> > of latter not something I must touch.
> >
> > > It would not guarantee $foo->bar === $foo->bar in all cases (though that would
> > > likely hold in the 99% case in practice), but then, $foo->getBar() === $foo->getBar() has
> > > never been guaranteed either.
> >
> > Properties and methods are something different. For methods there a
> > reasonable expectation that *behavior* is associated with them, for
> > properties there is not.
> >
> >
> > Unless I missed something. Hooks are fancy methods? There is nothing intrinsic about
> > object properties. There is nothing that says two calls to the same property’s getters are going
> > to result in the same values. There is asynchronous php, declare ticks, etc. especially in the case
> > of globals, there is no guarantee you even have the same object. At the end of the day, it is up to
> > the programmer building that system / program to provide those guarantees— not the language.
>
> I do think that, without any additional information, it would be
> reasonable to assume that
$foo->bar === $foo->bar, i.e.
> there would
> not be side-effects until you've called a method or written to the
> object in some way. So I share Tim's opinion here, but I do agree that
> with hooks available this is not actually a guarantee. You could
> certainly have a $foo->random_value property and document
> that it
> will be different each time you call it.
>
> That said, once the programmer has added the readonly designation to a
> property, I do think that something says that two calls to the same
> property will result in the same values - the readonly designation. I
> disagree with the point that it's not up to the language - the
> language should provide an affordance for enforcing programmer intent,
> and I see no reason to even have a readonly designation if we're going
> to make it easily circumventable or otherwise just a "hint".
>
> It seems that one common counterpoint to the "let's not make it
> circumventable" argument is to point out that it's already
> circumventable via __get. I agree with Claude that this is not a
> justification for making it *easier* to circumvent. I would also like
> to note that the original RFC
> (https://wiki.php.net/rfc/readonly_properties_v2#unset) seems to allow
> this behavior *for the purpose of lazy initialization*. With an
> init
> hook, we'd have solved this problem, and could deprecate the
> __get
> hack for readonly properties / classes.
>
> Nicolas Grekas said "__get is certainly not legacy; removing it would
> break many use cases without proper alternatives.", but note that I'm
> only suggesting we could maybe deprecate __get for readonly
> properties once we had an init hook - I'm not proposing
> deprecating
> it generally. Without a counterexample, I don't think there would be
> another reason for __get to work with
> readonly properties.
>
I personally feel that making special restrictions and affordances to readonly classes is a bad
language design. It is a “class” and not something special or different like an enum. This is
just a class with its properties made readonly. The word says it all — read. Only.
Personally, I don’t use readonly much any more. The amount of restrictions and weird behavior just
makes it impossible for any real-world use except for narrow cases the original authors of the
feature dreamed up. With hooks and asymmetrical viz, it’s nearly an obsolete feature anyway.
— Rob