Почему следующее
$a = new SplFixedArray(5); $a[0] = array(1, 2, 3); $a[0][0] = 12345; // here var_dump($a);
производит
Notice: Indirect modification of overloaded element of SplFixedArray has no effect in <file> on line <indicated>
Это ошибка? Как тогда вы имеете дело с многомерными SplFixedArrays? Любые обходные пути?
Во-первых, проблема связана со всеми классами, которые реализуют ArrayAccess
это не особая проблема только SplFixedArray
.
Когда вы обращаетесь к элементам из SplFixedArray
с помощью оператора []
он ведет себя не так, как массив. Внутри это метод offsetGet()
вызывается и возвращает в вашем случае массив – но не ссылку на этот массив. Это означает, что все изменения, сделанные вами на $a[0]
будут потеряны, если вы не сохраните их:
Временное решение:
$a = new SplFixedArray(5); $a[0] = array(1, 2, 3); // get element $element = $a[0]; // modify it $element[0] = 12345; // store the element again $a[0] = $element; var_dump($a);
Вот пример использования скаляра, который тоже не работает – просто для того, чтобы показать вам, что он не связан только с элементами массива.
Это фактически можно устранить, если вы offsetGet
&
перед offsetGet
(если у вас есть доступ к внутренним элементам вашей реализации ArrayAccess
):
class Dict implements IDict { private $_data = []; /** * @param mixed $offset * @return bool */ public function offsetExists($offset) { return array_key_exists(self::hash($offset), $this->_data); } /** * @param mixed $offset * @return mixed */ public function &offsetGet($offset) { return $this->_data[self::hash($offset)]; } /** * @param mixed $var * @return string */ private static function hash($var) { return is_object($var) ? spl_object_hash($var) : json_encode($var,JSON_UNESCAPED_SLASHES); } /** * @param mixed $offset * @param mixed $value */ public function offsetSet($offset, $value) { $this->_data[self::hash($offset)] = $value; } /** * @param mixed $offset */ public function offsetUnset($offset) { unset($this->_data[self::hash($offset)]); } }
сclass Dict implements IDict { private $_data = []; /** * @param mixed $offset * @return bool */ public function offsetExists($offset) { return array_key_exists(self::hash($offset), $this->_data); } /** * @param mixed $offset * @return mixed */ public function &offsetGet($offset) { return $this->_data[self::hash($offset)]; } /** * @param mixed $var * @return string */ private static function hash($var) { return is_object($var) ? spl_object_hash($var) : json_encode($var,JSON_UNESCAPED_SLASHES); } /** * @param mixed $offset * @param mixed $value */ public function offsetSet($offset, $value) { $this->_data[self::hash($offset)] = $value; } /** * @param mixed $offset */ public function offsetUnset($offset) { unset($this->_data[self::hash($offset)]); } }
Добавление моего опыта с той же ошибкой, если это кому-то помогает:
Недавно я импортировал свой код в структуру с низкой погрешностью (Laravel). В результате мой код теперь генерирует исключение, когда я пытаюсь получить значение из ассоциативного массива с использованием несуществующего ключа. Чтобы справиться с этим, я попытался реализовать свой собственный словарь, используя интерфейс ArrayAccess. Это отлично работает, но следующий синтаксис не выполняется:
$myDict = new Dictionary(); $myDict[] = 123; $myDict[] = 456;
А в случае мультимары:
$properties = new Dictionary(); $properties['colours'] = new Dictionary(); $properties['colours'][] = 'red'; $properties['colours'][] = 'blue';
Мне удалось решить эту проблему со следующей реализацией:
<?php use ArrayAccess; /** * Class Dictionary * * DOES NOT THROW EXCEPTIONS, RETURNS NULL IF KEY IS EMPTY * * @package fnxProdCrawler */ class Dictionary implements ArrayAccess { // FOR MORE INFO SEE: http://alanstorm.com/php_array_access protected $dict; function __construct() { $this->dict = []; } // INTERFACE IMPLEMENTATION - ArrayAccess public function offsetExists($key) { return array_key_exists($key, $this->dict); } public function offsetGet($key) { if ($this->offsetExists($key)) return $this->dict[$key]; else return null; } public function offsetSet($key, $value) { // NOTE: THIS IS THE FIX FOR THE ISSUE "Indirect modification of overloaded element of SplFixedArray has no effect" // NOTE: WHEN APPENDING AN ARRAY (EG myArr[] = 5) THE KEY IS NULL, SO WE TEST FOR THIS CONDITION BELOW, AND VOILA if (is_null($key)) { $this->dict[] = $value; } else { $this->dict[$key] = $value; } } public function offsetUnset($key) { unset($this->dict[$key]); } }
с<?php use ArrayAccess; /** * Class Dictionary * * DOES NOT THROW EXCEPTIONS, RETURNS NULL IF KEY IS EMPTY * * @package fnxProdCrawler */ class Dictionary implements ArrayAccess { // FOR MORE INFO SEE: http://alanstorm.com/php_array_access protected $dict; function __construct() { $this->dict = []; } // INTERFACE IMPLEMENTATION - ArrayAccess public function offsetExists($key) { return array_key_exists($key, $this->dict); } public function offsetGet($key) { if ($this->offsetExists($key)) return $this->dict[$key]; else return null; } public function offsetSet($key, $value) { // NOTE: THIS IS THE FIX FOR THE ISSUE "Indirect modification of overloaded element of SplFixedArray has no effect" // NOTE: WHEN APPENDING AN ARRAY (EG myArr[] = 5) THE KEY IS NULL, SO WE TEST FOR THIS CONDITION BELOW, AND VOILA if (is_null($key)) { $this->dict[] = $value; } else { $this->dict[$key] = $value; } } public function offsetUnset($key) { unset($this->dict[$key]); } }
Надеюсь, поможет.
Я предполагаю, что SplFixedArray является неполным / багги.
Если я написал собственный класс, и он работает как шарм:
$a = new \myArrayClass(); $a[0] = array(1, 2, 3); $a[0][0] = 12345; var_dump($a->toArray());
Выход (здесь нет предупреждений / предупреждений, в строгом режиме):
array (size=1) 0 => array (size=3) 0 => int 12345 1 => int 2 2 => int 3
Использование оператора [] не является проблемой (для ассоциативно-смешанных массивов тоже). Правильная реализация offsetSet должна выполнить эту работу:
public function offsetSet($offset, $value) { if ($offset === null) { $offset = 0; if (\count($this->array)) { $keys = \preg_grep( '#^(0|([1-9][0-9]*))$#', \array_keys($this->array)); if (\count($keys)) { $offset = \max($keys) + 1; } } } ...
Но есть только одно исключение. Невозможно использовать оператор [] для смещения, которого не существует. В нашем примере:
$a[1][] ='value'; // Notice: Indirect modification of overloaded...
Он выкинул бы предупреждение выше, потому что ArrayAccess вызывает offsetGet, а не offsetSet для [1], а более поздняя [] терпит неудачу. Может быть, есть решение, но я еще не нашел его. Но работает без проблем:
$a[] ='value'; $a[0][] ='value';
Я бы написал собственную реализацию вместо использования SplFixedArray. Возможно, его можно перегрузить некоторые методы в SplFixedArray, чтобы исправить это, но я не уверен, потому что я никогда не использовал и не проверял SplFixedArray.