"Косвенная модификация перегруженного элемента 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? Есть обходные пути?
4 ответов
во-первых, проблема связана со всеми классами, которые реализуют 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
(предполагая, что у вас есть доступ к внутренним органам вашего 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)]);
}
}
добавление моего опыта с той же ошибкой, если это кому-то поможет:
недавно я импортировал свой код в фреймворк с низкой допускаемой ошибкой (Laravel). В результате мой код выдает исключение, когда я пытаюсь получить значение из ассоциативного массива, используя несуществующий ключ. Чтобы справиться с этим, я попытался реализовать свой собственный словарь с помощью интерфейса ArrayAccess. Это отлично работает, но следующий синтаксис не работает:
$myDict = new Dictionary();
$myDict[] = 123;
$myDict[] = 456;
и в случае контейнер multimap:
$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 (E.G. 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
использование оператора [] не является проблемой (для assoc/смешанных массивов тоже). Правильная реализация 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.