4

With an example class such as this:

class Test{
    public function &__get($name){
        print_r($name);
    }
}

An instance of Test will kick back output as such:

$myTest = new Test;
$myTest->foo['bar']['hello'] = 'world';
//outputs only foo

Is there a way I can get more information about what dimension of the array is being accessed, showing me (from the previous example) that the bar element of foo, and the hello element of bar are being targeted?

Dan Lugg
  • 20,192
  • 19
  • 110
  • 174
  • 1
    @Jan probably not when he's doing things by reference... I have no idea whether this is possible, but my strong suspicion is it's not. I'm pretty sure the getter will be used to fetch the *whole* array, and accessing one of its members will take place outside the getter. – Pekka Dec 24 '10 at 16:06
  • @Jan; No typo, it is working by reference to permit multidimensional array element access for both setting and getting. @Pekka; I was afraid of that. I tried setting a temporary variable to an empty string using what `$name` provides, in order to traverse the temporary variable for information, but that doesn't work as I hoped, giving me a temporary variable with only a single element with the `foo` key (based on my previous example) – Dan Lugg Dec 24 '10 at 16:13

3 Answers3

3

You can't with the current implementation. In order for this to work, you will have to create an array object (i.e.: an object that implements ArrayAccess). Something like:

class SuperArray implements ArrayAccess {
    protected $_data = array();
    protected $_parents = array();

    public function __construct(array $data, array $parents = array()) {
        $this->_parents = $parents;
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $value = new SuperArray($value, array_merge($this->_parents, array($key)));
            }
            $this[$key] = $value;
        }
    }

    public function offsetGet($offset) {
        if (!empty($this->_parents)) echo "['".implode("']['", $this->_parents)."']";
        echo "['$offset'] is being accessed\n";
        return $this->_data[$offset];
    } 

    public function offsetSet($offset, $value) {
        if ($offset === '') $this->_data[] = $value;
        else $this->_data[$offset] = $value;
    } 

    public function offsetUnset($offset) {
        unset($this->_data[$offset]);
    } 

    public function offsetExists($offset) {
        return isset($this->_data[$offset]);
    } 
}

class Test{
    protected $foo;

    public function __construct() {
        $array['bar']['hello'] = 'world';
        $this->foo = new SuperArray($array); 
    }

    public function __get($name){
        echo $name.' is being accessed.'.PHP_EOL;
        return $this->$name;
    }
}

$test = new Test;
echo $test->foo['bar']['hello'];

Should output:

foo is being accessed.
['bar'] is being accessed
['bar']['hello'] is being accessed
world
netcoder
  • 66,435
  • 19
  • 125
  • 142
  • Thanks netcoder; Your comprehensive example was exactly what I was looking for. I'll play with that. – Dan Lugg Dec 24 '10 at 16:48
  • Your answer here saved me again in my forgetfulness. I wish I could **+100** this - it's invaluable. – Dan Lugg Jun 09 '11 at 05:21
1

No you can't. $myTest->foo['bar']['hello'] = 'world'; goes through the following translation $myTest->__get('foo')['bar']['hello'] = 'world'; breaking them in parts become

$tmp = $myTest->__get('foo')
$tmp['bar']['hello'] = 'world';

What you can do is to create an ArrayAccess Derived Object. Where you define your own offsetSet() and return that from __get()

Neel Basu
  • 12,638
  • 12
  • 82
  • 146
  • Thanks user256007; I'm guessing I can't pull the PHP generated temporary variable after it's been set to traverse in any way, can I? – Dan Lugg Dec 24 '10 at 16:19
  • 1
    Not only in PHP. in any language you can't pull a temporary variable generated by the `language` (Compiler|interpreter|processor). What happens in C++ is also the same we create an object and returns its reference or pointer. otherwise its not possible. – Neel Basu Dec 24 '10 at 16:22
1

Instead of returning an array, you could return an object that implements ArrayAccess. Objects are always returned and passed by reference. This pushes the problem at least on level down.

Oswald
  • 31,254
  • 3
  • 43
  • 68
  • Thanks Oswald; both you and user256007 suggest `ArrayAccess`. I'll have to look into it, as I'm unfamiliar. Any suggestions on where I'd want to start with incorporating `ArrayAccess`? – Dan Lugg Dec 24 '10 at 16:21