Re: Property Hooks Discussion
| From: | Larry Garfield | Date: | Fri, 31 Mar 2023 13:23:04 +0000 |
| Subject: | Re: Property Hooks Discussion | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-119812@lists.php.net to get a copy of this message | ||
On Fri, Mar 31, 2023, at 8:56 AM, Robert Landers wrote:
> Hello,
>
> I couldn't find the thread about Property Hooks
> (https://wiki.php.net/rfc/property-hooks) -- I think it got embedded
> in various places? Or maybe my search-fu in gmail is failing me.
>
> Anyway, I was reading it carefully and I had a couple of questions.
>
> What would happen with the following class:
>
> class BigNumbers {
> public \Gmp $myNumber {
> beforeSet(int|string $value) {
> return gmp_init($value);
> }
> }
> }
>
> Would $bigNumber->myNumber = gmp_init('25') throw an error because it
> isn't a
string|int? Can hooks only accept the type they
> are setting
> or any type?
>
> In other words, will it be possible for this to no longer be true (I'm
> not sure it is always true now, fwiw):
>
> $x = $y = $z = 1;
>
> $x === $y && $y === $z;
>
> This looks like a very powerful change to the language and I'm really
> looking forward to using it!
As Rowan said, this is a "known unknown" at the moment, and one of the reasons the RFC
hasn't been officially put forward yet. (Expect that soon.)
As it is right now, the value beforeSet receives MUST be the same as the property is typed as.
You're not allowed to declare a type yourself; the property type is implied. However, that
still would allow you to narrow a type yourself in practice. The RFC has an example or two of that
with strings -> UString objects.
We are considering (and I would favor, if it can be done cleanly) allowing the beforeSet hook to
accept a contravariant (wider) type than the property type, but still be required to return exactly
the type of the property. That would allow, for instance:
class Foo {
public int $val { beforeSet(int|string $new) => (int) $new; }
}
That is, you can accept a wider set of compatible types and then, in beforeSet, fold that down to
the type of the property. The type of the property is still guaranteed to be the only thing you get
on read.
That would mean the following as a side effect:
$f = new Foo();
$val = '5';
$f->val = $val;
$f->val === $val; // false, because different types!
Personally I think that's acceptable for the flexibility it gives; the point of hooks is
primarily to be "a thing you can add to a property in the future without having to precreate a
method just in case", you can already do the above behavior with a method quite easily. So
being able to do the same with hooks (and thus obviate another "just in case" reason to
add a verbose but pointless method on the off chance you need it in the future) seems reasonable.
There might be other edge cases we haven't thought of yet; right now that feature is pending on
Ilija making sure it could even work, which is a prerequisite for including it, obviously. :-)
--Larry Garfield