neubert
neubert

Reputation: 16802

accessing multi dimensional arrays via ArrayAccess

Say I have the following code:

<?php

class test implements ArrayAccess {
    var $var;

    function __construct()
    {
        $this->var = array(
            'a' => array('b' => 'c'),
            'd' => array('e' => 'f'),
            'g' => array('h' => 'i')
        );
    }

    function offsetExists($offset)
    {
        return isset($this->var);
    }

    function offsetGet($offset)
    {
        return isset($this->var[$offset]) ? $this->var[$offset] : null;
    }

    function offsetSet($offset, $value)
    {
        if (is_null($offset)) {
            $this->var[] = $value;
        } else {
            $this->var[$offset] = $value;
        }
    }

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

$test = new test();
$test['a']['b'] = 'zzz';

print_r($test->var);

What I'd want that to do is to display something like this:

Array
(
    [a] => Array
        (
            [b] => zzz
        )

    [d] => Array
        (
            [e] => f
        )

    [g] => Array
        (
            [h] => i
        )

)

What it actually displays is more like this:

Array
(
    [a] => Array
        (
            [b] => c
        )

    [d] => Array
        (
            [e] => f
        )

    [g] => Array
        (
            [h] => i
        )

)

ie. $test['a']['b'] is unchanged.

Any idea how I can make it changeable using that syntax? I could assign $test['a'] to a variable and then do $temp['b'] = 'zzz'; and then do $test['a'] = $temp; but idk - that seems excessive.

Upvotes: 4

Views: 102

Answers (2)

Fabian Schmengler
Fabian Schmengler

Reputation: 24576

The problem is that offsetGet returns an array by value, i.e. a copy of the internal value. $test['a']['b'] = 'zzz' operates on this copy, returned by $test['a'].

But you can make offsetGet return a reference instead:

    function &offsetGet($offset)
    {
        $null = null;
        if (isset($this->var[$offset])) {
            return $this->var[$offset];
        }
        return $null;
    }

Note that I had to modify the method body as well, so that return is always followed by a variable, not an expression.

Demo:

https://3v4l.org/4jGMN

Output for 5.4.7 - 7.0.0rc2, hhvm-3.6.1 - 3.9.0

Array
(
    [a] => Array
        (
            [b] => zzz
        )

    [d] => Array
        (
            [e] => f
        )

    [g] => Array
        (
            [h] => i
        )

)

Update

You can even simplify it to:

function &offsetGet($offset)
{
    return $this->v[$offset];
}

Because if you return non-existing variables by reference, they are implicitly created. And this way, you can create new nested elements like this:

$test['new']['nested'] = 'xxx';

Demo: https://3v4l.org/OSvuA

Upvotes: 1

mdamia
mdamia

Reputation: 4557

You are printing out the array from the class. Try this

$test = new test();
$data = $test -> var;
$data['a']['b']= 'ssss';

print_r($data) ;

Hope this help.

Upvotes: 0

Related Questions