«Непрямая модификация перегруженного элемента SplFixedArray не влияет»

Почему следующее

$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.