On 12/03/2024 22:43, Larry Garfield wrote:
It's slightly different, yes. The point is that the special behavior of a hook is
disabled if you are within the call stack of a hook, just like the special behavior of
__get/__set is disabled if you are within the call stack of __get/__set. What happens
when you hit an operation that would otherwise go into an infinite loop is a bit
different, but the "disable to avoid an infinite loop" logic is the same.
I guess I'm looking at it more from the user's point of view: it's very
rare with __get and __set to have a method that sometimes accesses the
"real" property, and sometimes goes through the "hook". Either there is
no real property, or the property has private/protected scope, so any
method on the classes sees the "real" property *regardless* of access
via the hook.
I think it would be more helpful to justify this design on its own
merits, particularly because it's a significant difference from other
languages (which either don't have a "real property" behind the hooks,
or in Kotlin's case allow access to it only *directly* inside the hook
definitions, via the "field" keyword).
The point is to give the user the option for full backwards compatibility when it makes
sense. This requires jumping through some hoops, which is the point. This is essentially
equivalent to creating a by-ref getter + a setter, exposing the underlying property. By
creating a virtual property, we are "accepting" that the two are detached. While we
could disallow this, we recognize that there may be valid use-cases that we'd like to enable.
It also parallels __get/__set, where using &__get means you can write to something
without going through __set.
I get the impression that to you, it's a given that a "virtual property"
is something clearly distinct from a "property with hooks", and that
users will consciously decide between one and the other.
This isn't my expectation; based on what people are used to from
existing features, and other languages, I expect users to see this as an
obvious starting point for defining a hooked property:
private int $_foo;
public int $foo { get => $this->_foo; set { $this->_foo = $value; } {
And this as a convenient short-hand for exactly the same thing:
public int $foo { get => $this->foo; set { $this->foo = $value; } }
Choosing one or the other won't feel like "jumping through a hoop", and
the ability to use an &get hook with one and not the other will simply
seem like a weird oddity.
In practice I expect it virtual properties with both hooks to be very rare.
Most virtual properties will, I expect, be lazy-computed get-only values.
I don't think this is true. Both of these are, in the terms of the RFC,
"virtual properties":
public Something $proxied { get => $this->otherObject->thing; set {
$this->otherObject->thing = $value; } };
public Money $price;
public int $pricePence { get => $this->price->asPence(); set {
$this->price = Money::fromPence($value); } }
I can also imagine generated classes with "virtual" properties which
call out to generic "getCached" and "setAndClearCache" methods doing the
job of this pair of __get and __set methods:
https://github.com/yiisoft/yii2/blob/master/framework/db/BaseActiveRecord.php#L274
With the change to allow &get in the absence of set, I believe that would
already work.
cf:https://3v4l.org/3Gnti/rfc#vrfc.property-hooks
Awesome! The RFC should probably highlight this, as it gives a
significant extra option for array properties.
(Also, good to know 3v4l has a copy of the branch; I hadn't thought to
check.)
Regards,
--
Rowan Tommins
[IMSoP]