EDIT: I realized the amount of text might be intimidating. The essence of this question:
How to implement ArrayAccess in a way that makes setting multidimensional values possible?
I am aware that this was discussed here already but I seem unable to implement the ArrayAccess interface correctly.
Basically, I've got a class to handle the app configuration with an array and implemented ArrayAccess
. Retrieving values works fine, even values from nested keys ($port = $config['app']['port'];
). Setting values works only for one-dimensional arrays, though: As soon as I try to (un)set a value (eg. the port in the previous example), i get the following error message:
Notice: Indirect modification of overloaded element <object name> has no effect in <file> on <line>
Now the general opinion seems to be that the offsetGet()
method has to return by reference (&offsetGet()
). That, however, does not solve the problem and I'm afraid I don't know how to implement that method correctly - why is a getter method used to set a value? The php doc here is not really helpful either.
To directly replicate this (PHP 5.4-5.6), please find a sample code attached below:
<?php
class Config implements \ArrayAccess
{
private $data = array();
public function __construct($data)
{
$this->data = $data;
}
/**
* ArrayAccess Interface
*
*/
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
public function &offsetGet($offset)
{
return isset($this->data[$offset]) ? $this->data[$offset] : null;
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetUnset($offset)
{
unset($this->data[$offset]);
}
}
$conf = new Config(array('a' => 'foo', 'b' => 'bar', 'c' => array('sub' => 'baz')));
$conf['c']['sub'] = 'notbaz';
EDIT 2: The solution, as Ryan pointed out, was to use ArrayObject instead (which already implements ArrayAccess
, Countable
and IteratorAggregate
).
To apply it to a class holding an array, structure it like so:
<?php
class Config extends \ArrayObject
{
private $data = array();
public function __construct($data)
{
$this->data = $data;
parent::__construct($this->data);
}
/**
* Iterator Interface
*
*/
public function getIterator() {
return new \ArrayIterator($this->data);
}
/**
* Count Interface
*
*/
public function count()
{
return count($this->data);
}
}
I used this for my Config library libconfig
which is available on Github under the MIT license.