php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #71254 Allow limited recursion with __get().
Submitted: 2015-12-31 01:54 UTC Modified: 2015-12-31 02:11 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:0 (0.0%)
From: andreas at dqxtech dot net Assigned:
Status: Open Package: Class/Object related
PHP Version: 7.0.2RC1 OS:
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2015-12-31 01:54 UTC] andreas at dqxtech dot net
Description:
------------
Currently, magic __get() refuses to dive into recursion if the same key is repeated.
https://3v4l.org/00Kuv

It stops, and gives a "Notice: Undefined property: C::$x".

There are cases where the recursion is not a bug, and it is indeed legitimate to dive one level into the recursion.

As we can see, calling ->__get('x') explicitly instead of magic ->x, avoids the notice. But this is undesirable, because we want the IDE to recognize that we are calling @property $x.

The unlimited recursion can be avoided with stubs:
https://3v4l.org/Nb3VF

Proposal:
I want to propose that the recursion detection mechanic only gets active if the same key is used a 3rd time, not the 2nd time. This permits the legitimate case, but still prevents unlimited recursion.

Test script:
---------------
<?php
class C {
    private $buffer = array();
    function __get($key) {
        if (array_key_exists($key, $this->buffer)) {
            return $this->buffer[$key];
        }
        $f = 'calc_' . $key;
        return $this->buffer[$key] = $this->$f();
    }
    function calc_x() {
        $x = new stdClass;
        // Set a stub to avoid infinite recursion.
        $this->buffer['x'] = $x;
        $x->y = $this->y;
        return $x;
    }
    function calc_y() {
        $y = new stdClass;
        // Set a stub to avoid infinite recursion.
        $this->buffer['y'] = $y;
        $y->x = $this->x;
        return $y;
    }
}


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-12-31 02:11 UTC] andreas at dqxtech dot net
Hmm.. maybe the correct solution is not a stub, but a proxy (lazy instantiation).
And with that, every key in __get() is only called once.

A single proxy in the loop is sufficient to prevent the recursion and all problems caused by it.

A single stub in the loop is sufficient to prevent infinite recursion. But to also avoid duplicate values in the buffer, one needs one stub per calc_*() method..

There is a trick to only need one stub in the circle:
https://3v4l.org/nXTYJ
But I'm not sure if I really like this, or if proxy is generally better.
 [2016-03-05 23:04 UTC] yen1 at senam dot cz
I ran into a similar issue :(. I don't know a purpose why prevent recursion when magic __get / __set can be called manually and thus still fall into infinite recursion? It is also a bit confusing when the __get method gets called for the first time but not for the second time.
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Fri Apr 26 08:01:25 2019 UTC