“Indirect modification of overloaded element of SplFixedArray has no effect”

匿名 (未验证) 提交于 2019-12-03 01:57:01

问题:

Why the following

$a = new SplFixedArray(5); $a[0] = array(1, 2, 3); $a[0][0] = 12345; // here var_dump($a);

produces

Notice: Indirect modification of overloaded element of SplFixedArray has no effect in  on line 

Is it a bug? How do you deal with multidimensional SplFixedArrays then? Any workarounds?

回答1:

First, the problem is related to all classes which implement ArrayAccess it is not a special problem of SplFixedArray only.


When you accessing elements from SplFixedArray using the [] operator it behaves not exactly like an array. Internally it's offsetGet() method is called, and will return in your case an array - but not a reference to that array. This means all modifications you make on $a[0] will get lost unless you save it back:

Workaround:

$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);

Here is an example using a scalar which fails too - just to show you that it is not related to array elements only.



回答2:

This is actually fixable if you slap a & in front of offsetGet (assuming you have access to the internals of your ArrayAccess implementation):

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:

Adding my experience with the same error, in case it helps anyone:

I recently imported my code into a framework with a low error-tolerance (Laravel). As a result, my code now throws an exception when I try to retrieve a value from an associative array using a non-existent key. In order to deal with this I tried to implement my own dictionary using the ArrayAccess interface. This works fine, but the following syntax fails:

$myDict = new Dictionary(); $myDict[] = 123; $myDict[] = 456;

And in the case of a multimap:

$properties = new Dictionary(); $properties['colours'] = new Dictionary(); $properties['colours'][] = 'red'; $properties['colours'][] = 'blue';

I managed to fix the problem with the following implementation:

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]);     } }

Hope it helps.



回答4:

I guess SplFixedArray is incomplete/buggy.

If i wrote an own class and it works like a charm:

$a = new \myArrayClass(); $a[0] = array(1, 2, 3); $a[0][0] = 12345; var_dump($a->toArray());

Output (no notices/warnings here, in strict mode too):

array (size=1)     0 =>          array (size=3)             0 => int 12345             1 => int 2             2 => int 3

Using the [] operator is not a problem (for assoc/mixed arrays too). A right implementation of offsetSet should do the job:

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;            }         }     }     ...

But there is only one exception. Its not possible to use the [] operator for offset which does not exist. In our example:

$a[1][] ='value'; // Notice: Indirect modification of overloaded...

It would throw the warning above because ArrayAccess calls offsetGet and not offsetSet for [1] and the later [] fails. Maybe there is a solution out there, but i did not find it yet. But the following is working without probs:

$a[] ='value'; $a[0][] ='value';

I would write an own implementation instead of using SplFixedArray. Maybe its possible to overload some methods in SplFixedArray to fix it, but i am not sure because i never used and checked SplFixedArray.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!