“间接修改SplFixedArray的重载元素没有任何影响”

时间:2013-11-18 16:53:34

标签: php arrays spl

为什么以下

$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呢?任何解决方法?

5 个答案:

答案 0 :(得分:38)

首先,问题与实施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);

这是一个example using a scalar也失败了 - 只是为了告诉你它与数组元素无关。

答案 1 :(得分:5)

如果您在&前面拍了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)]);
    }
}

答案 2 :(得分:0)

添加我遇到相同错误的经验,以防万一:

我最近将我的代码导入了一个具有低容错性的框架(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]);
    }
}

希望它有所帮助。

答案 3 :(得分:0)

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

我共享了执行此操作的代码,因为在其他地方找不到它。请注意,这并不是一个认真的实现,因为我什至不检查偏移量是否存在(如果有人提出增强功能,我会很高兴的),但它的确有效:

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

我正在为这些内存消耗较少的固定长度数组更改本机PHP哈希,例如数组,我也必须更改其他一些内容(由于类SplFixedArray的延迟扩展,或者只是因为不使用)本机数组)是:

  • 创建一个手动方法来按属性复制我的类对象的属性。 clone不再起作用。
  • 使用a[i]!==NULL来检查元素是否存在,因为isset()不再起作用。
  • 也向扩展类添加offsetSet()方法:
    public function offsetSet($offset,$value) { $this->array[$offset]=$value; }

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

答案 4 :(得分:-3)

我猜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 / mixed数组也是如此)。 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而不调用[1]的offsetSet而后面的[]失败。也许有一个解决方案,但我还没有找到它。但以下是没有probs的工作:

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

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