php “间接修改 SplFixedArray 的重载元素无效”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20053269/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 02:49:31  来源:igfitidea点击:

"Indirect modification of overloaded element of SplFixedArray has no effect"

phparraysspl

提问by Desmond Hume

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 <file> on line <indicated>

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

这是一个错误吗?那么你如何处理多维 SplFixedArrays 呢?任何解决方法?

回答by hek2mgl

First, the problem is related to all classes which implement ArrayAccessit is not a special problem of SplFixedArrayonly.

首先,该问题与实现ArrayAccess它的所有类有关,而不仅仅是一个特殊问题SplFixedArray



When you accessing elements from SplFixedArrayusing 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 referenceto that array. This means all modifications you make on $a[0]will get lost unless you save it back:

当您SplFixedArray使用[]运算符访问元素时,它的行为与数组不完全相同。在内部offsetGet()调用它的方法,并在您的情况下返回一个数组 -但不是对该数组的引用。这意味着您所做的所有修改都$a[0]将丢失,除非您将其保存回来:

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 scalarwhich fails too - just to show you that it is not related to array elements only.

这是一个使用标量示例,它也失败了 - 只是为了向您展示它仅与数组元素无关。

回答by mpen

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

这实际上是可以修复的,如果你&在前面打一个耳光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)]);
    }
}

回答by mils

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:

我最近将我的代码导入到一个低容错框架 (Laravel) 中。因此,当我尝试使用不存在的键从关联数组中检索值时,我的代码现在会引发异常。为了解决这个问题,我尝试使用 ArrayAccess 接口实现我自己的字典。这工作正常,但以下语法失败:

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

我设法通过以下实现解决了这个问题:

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

Hope it helps.

希望能帮助到你。

回答by Leopoldo Sanczyk

I did a workaround to this problem extending SplFixedArrayand overriding offsetGet()to return a reference* But as this hek2mglmentioned, it could lead to side effects.

我对这个问题进行了解决方法扩展SplFixedArray和覆盖offsetGet()以返回引用* 但是正如这个hek2mgl提到的那样,它可能会导致副作用。

I shared the code to do it, because I couldn't find it in other place. Note is not serious implementation, because I'm not even checking the offset exists (I will be glad if someone propose enhancements), but it works:

我分享了代码来做到这一点,因为我在其他地方找不到它。注意不是认真的实现,因为我什至没有检查偏移量是否存在(如果有人提出改进,我会很高兴),但它有效:

class mySplFixedArray extends SplFixedArray{
        public function &offsetGet($offset) {
            return $this->array[$offset];
        }       
    }

I was changing native PHP hash like arrays for these less memory consuming fixed length arrays, and some of the other things I have to change too (either as a consequence of the lazy extending of the class SplFixedArray, or just for not using native arrays) were:

我正在为这些较少内存消耗的固定长度数组更改原生 PHP 散列,如数组,还有一些我必须更改的其他内容(要么是 SplFixedArray 类的延迟扩展的结果,要么只是因为不使用原生数组)是:

  • Creating a manual method for copying my class objects property by property. clonedidn't worked anymore.
  • Use a[i]!==NULLto check if the element exists, because isset()didn't worked anymore.
  • Add an offsetSet()method to the extended class too:
    public function offsetSet($offset,$value) { $this->array[$offset]=$value; }
  • 创建一个按属性复制我的类对象属性的手动方法。clone没有工作了。
  • 使用a[i]!==NULL来检查元素存在,因为isset()没有了工作。
  • offsetSet()向扩展类添加一个方法:
    public function offsetSet($offset,$value) { $this->array[$offset]=$value; }

(*) I think this overriding is only possible after some PHP version between 5.2.6 and 5.3.4. I couldn't find too much info or code about this problem, but I want to share the solution for other people anyway.

(*) 我认为这种覆盖只有在 5.2.6 和 5.3.4 之间的某些 PHP 版本之后才有可能。我找不到关于这个问题的太多信息或代码,但无论如何我想为其他人分享解决方案。

回答by Tom

I guess SplFixedArray is incomplete/buggy.

我猜 SplFixedArray 不完整/有问题。

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:

使用 [] 运算符不是问题(对于关联/混合数组也是如此)。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;
           }
        }
    }
    ...

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:

它会抛出上面的警告,因为 ArrayAccess 为 [1] 调用 offsetGet 而不是 offsetSet 并且后面的 [] 失败。也许那里有解决方案,但我还没有找到。但以下工作没有问题:

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

我会编写自己的实现而不是使用 SplFixedArray。也许可以重载 SplFixedArray 中的某些方法来修复它,但我不确定,因为我从未使用和检查过 SplFixedArray。