РЕДАКТИРОВАТЬ: Я понял, что количество текста может быть пугающим. Суть этого вопроса:
Как реализовать ArrayAccess таким образом, чтобы сделать возможной настройку многомерных значений?
Я знаю, что это обсуждалось Вот уже, но я не могу правильно реализовать интерфейс ArrayAccess.
По сути, у меня есть класс для обработки конфигурации приложения с массивом и реализован ArrayAccess
, Получение значений работает нормально, даже значения из вложенных ключей ($port = $config['app']['port'];
). Настройка значений работает только для одномерных массивов, однако: как только я пытаюсь (не) установить значение (например, порт в предыдущем примере), я получаю следующее сообщение об ошибке:
Notice: Indirect modification of overloaded element <object name> has no effect in <file> on <line>
Похоже, общее мнение таково, что offsetGet()
метод должен возвращаться по ссылке (&offsetGet()
). Это, однако, не решает проблему, и я боюсь, что не знаю, как правильно реализовать этот метод — почему метод getter используется для установки значения? Php doc Вот тоже не очень помогает.
Чтобы напрямую скопировать это (PHP 5.4-5.6), пожалуйста, найдите образец кода, прикрепленный ниже:
<?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';
РЕДАКТИРОВАТЬ 2: Решение, как указал Райан, состояло в том, чтобы вместо этого использовать ArrayObject (который уже реализует ArrayAccess
, Countable
а также IteratorAggregate
).
Чтобы применить его к классу, содержащему массив, структурируйте его так:
<?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);
}
}
Я использовал это для моей библиотеки Config libconfig
который доступен на Github под лицензией MIT.
Я не уверен, будет ли это полезно. Я заметил, что ArrayObject
класс «интересный» …
Я не уверен, что это даже «ответ». Это скорее наблюдение за этим классом.
Он корректно обрабатывает «многомерный массив» как стандарт.
Возможно, вы сможете добавить методы, чтобы сделать больше того, что вы хотите?
<?php //
class Config extends \ArrayObject
{
// private $data = array();
public function __construct(array $data = array())
{
parent::__construct($data);
}
}
$conf = new Config(array('a' => 'foo', 'b' => 'bar', 'c' => array('sub' => 'baz')));
$conf['c']['sub'] = 'notbaz';
$conf['c']['sub2'] = 'notbaz2';
var_dump($conf, $conf['c'], $conf['c']['sub']);
unset($conf['c']['sub']);
var_dump('isset?: ', isset($conf['c']['sub']));
var_dump($conf, $conf['c'], $conf['c']['sub2']);
Выход:
object(Config)[1]
public 'a' => string 'foo' (length=3)
public 'b' => string 'bar' (length=3)
public 'c' =>
array
'sub' => string 'notbaz' (length=6)
'sub2' => string 'notbaz2' (length=7)
array
'sub' => string 'notbaz' (length=6)
'sub2' => string 'notbaz2' (length=7)
string 'notbaz' (length=6)
string 'isset?: ' (length=8)
boolean false
object(Config)[1]
public 'a' => string 'foo' (length=3)
public 'b' => string 'bar' (length=3)
public 'c' =>
array
'sub2' => string 'notbaz2' (length=7)
array
'sub2' => string 'notbaz2' (length=7)
string 'notbaz2' (length=7)
Других решений пока нет …