On Tue, Nov 29, 2022, at 11:25 PM, Stephen Reay wrote:

> Hi Larry,
>
> Thank you for clarifying the setter behaviour in more explicit terms, 
> but I have to say I’m quite disappointed in this continued “use the 
> logic of readonly to apply to something that is explicitly not 
> readonly” - this is even more stark now that you’ve explicitly made 
> them mutually exclusive behaviours.
>
> I’m generally very in favour of maintaining consistency, but this seems 
> like it’s using technical consistency as an excuse to justify 
> unintuitive behaviour that breaks consistency in another, much more 
> obvious way.
>
>
> Can you explain why it makes more sense to maintain consistency with 
> “readonly” than it does to maintain consistency with the existing 
> “__set()” behaviour for properties, particularly now that you’ve 
> indicated these features (asymmetric visibility and readonly) are 
> mutually exclusive? 
>
> While it’s stated multiple times that “readonly” introduced a limited 
> form of asymmetric visibility, and thus this is a continuation, in 
> terms of intuitiveness, the existing __set() rules are very easy to 
> comprehend even with readonly:
>
> - if the property is declared as public, __set() is never called; if 
> it’s declared as protected, __set is called when the property is 
> accessed from outside that class or it’s hierarchy. Yes, I know that 
> readonly imposes an implicit visibility difference - but that is 
> essentially an implementation detail, from the point of view of the 
> userland developer, it’s not a clear statement of intended behaviour on 
> their part, expressed through the code as written.
>
> For example, with `public readonly int $foo` it’s quite obvious why 
> __set() isn’t called, using the exiting well-understood logic: it’s a 
> public property. PHP applies a kind of asymmetric visibility to the 
> property behind the scenes, but that isn’t what the developer declared, 
> it’s the implementation. This behaviour matches that of regular, 
> non-readonly fields: when the field is declared public (or has implicit 
> public visibility) __set() is never called.
>
> If we make that field protected, __set() will be called when the 
> property is written to from outside the class, regardless of whether 
> it’s readonly or not.
>
>
> What you’re proposing changes that, in a way that is completely 
> unintuitive: when attempting to *write* data to a property that is 
> marked as protected(set), the __set() method will not be called.
>
>
> So please, can you explain to me why consistency with an implementation 
> detail of readonly properties is more important than consistency with 
> declared developer intention for regular properties via the magic 
> setter method?

There's a couple of reasons.

One, and arguably the most important, readonly and aviz being incompatible is, 
hopefully, a temporary situation.  There's some fiddly bits to work out 
design-wise, and based on earlier comments in the thread we're going to punt on 
that for now to avoid that dragging down the whole RFC.  I believe we 
absolutely should allow them together in the future (maybe in a later 8.3 RFC, 
maybe a future version, TBD), which means ensuring, now, that they are 
compatible in the future.  This approach involves the fewest future BC breaks.

Second, I wouldn't call the current behavior of readonly a mere implementation 
detail.  It's weird and unexpected, I'd agree, but only as a side effect of 
previous design decisions, some of which are even older than readonly.  But 
it's an observed behavior that code can rely on, and in some cases does.  For 
example:

https://peakd.com/hive-168588/@crell/php-tricks-lazy-public-readonly-properties

The "unset a declared property to force it through __get once" is a trick that 
some ORMs use extensively.  readonly just inherited that, leading to the 
current behavior: __set depends on the write/set visibility of the property and 
its settedness.  This RFC doesn't change anything there.  

The alternative would be to have __set called always for a non-public-set 
property.  However, that is a place for bugs, as you then can't not have a 
back-door way to publicly set a property even if it's declared private(set).  
(Or, you have to be very careful in your __set to avoid it.)  That is both 
inconsistent with the language today, and error prone.

Finally, we're planning to work in the near-future on property hooks (aka 
property accessors), which would allow per-property custom set routines.  That 
would largely remove the issue entirely, as the use of __set would go way down 
and you'd basically never have to use it with a declared property, fancy tricks 
or no, so this issue would never come up at all.

--Larry Garfield

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php

Reply via email to