Подтвердить что ты не робот

"Непрямая модификация перегруженного элемента 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? Любые обходные пути?

4b9b3361

Ответ 1

Во-первых, проблема связана со всеми классами, которые реализуют 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);

Вот пример с использованием скаляра, который тоже не работает - просто чтобы показать вам, что он не связан только с элементами массива.

Ответ 2

Это может быть исправлено, если вы нажмете & перед 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)]);
    }
}

Ответ 3

Добавление моего опыта с той же ошибкой, если это кому-то помогает:

Недавно я импортировал свой код в структуру с низкой погрешностью (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 (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]);
    }
}

Надеюсь, что это поможет.

Ответ 4

Я предполагаю, что 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.