Hi Rob

On Thu, Jul 31, 2025 at 10:48 AM Rob Landers <rob@bottled.codes> wrote:
>
> Currently, this is not allowed by PHP:
>
> class Foo {
>   public function __construct(
>       public string &$ref { get => $this->ref ?? 'null'; set => $this->ref = 
> $value; }
>   ) {}
> }
>
> After the release, I'd like to propose an RFC that allows using references 
> with hooks:

As we've considered references extensively during the hooks RFC, allow
me to add some context.

> this will probably necessitate a new zval type, determined at runtime (it 
> will not be exposed to users).

The impact for new zval types is gigantic, and it's been avoided for
decades. I don't think it's technically necessary either. We're
already recording zend_reference.sources, which is a list of
properties that store the reference. This is used for type coercion
when assigning to a reference that may be stored in a typed property.

    class C {
        public int $prop;
    }

    $c = new C();
    $prop = 42;
    $c->prop = &$prop;
    $prop = '43';
    var_dump($prop); // int(43)

You may be able to use the same mechanism for set hooks. This would
need to be extended to store the actual object containing the
reference, so that the set hook may be invoked. However, there are
some issues. For example, what do you do when a reference is assigned
to multiple hooked properties?

    class C {
        public int $prop1 { set => $value + 1; }
        public int $prop2 { set => $value + 2; }
    }

    $c = new C();
    $prop = 42;
    $c->prop1 = &$prop;
    $c->prop2 = &$prop;
    $prop = 43; // What happens now?

Typed properties handle type conflicts gracefully [^1], but graceful
handling for hooks is less clear because each hook can transform the
assigned value. You might allow assignment only when the reference is
bound to a single hooked property, but then this isn't a general
solution. I suppose you may also require all set hooks to assign the
reference to an identical value.

Another current limitation is reference assignment. `$c->prop1 =
&$prop;` from above doesn't actually work. That's because hooks
currently allow &get, i.e. retrieving the reference stored in a hooked
property (`$prop = &$c->prop1`), but they don't allow setting the
property reference, which is semantically distinct. set(&value) is not
sufficient because the hook implementation must know whether to assign
to the backing store through a direct assignment, or =&. set(&value,
bool $byRef) would work, but is quite clunky.

You also mention arrays. This approach does not solve the issues we've
outlined in the RFC [^2]. In particular, indirect modification may
still hide mutations from set hooks. Even if we can fix this and make
every mutation call the set hook, the significant ergonomic issues of
understanding _how_ the array changed remain, along with performance
issues due to array separation.

Disregarding arrays, this approach might not be too complex. However,
given that arrays are by far the most used type when it comes to
references, them being exempt makes this feature far less useful.
That's the main reason we didn't pursue this idea further, and I
remain quite convinced that arrays are unworkable.

Ilija

[^1]: https://3v4l.org/defVI
[^2]: https://wiki.php.net/rfc/property-hooks#arrays

Reply via email to