【问题标题】:"Indirect modification of overloaded element of SplFixedArray has no effect"“间接修改 SplFixedArray 的重载元素无效”
【发布时间】:2013-12-01 22:14:40
【问题描述】:

为什么如下

$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 呢?有什么解决方法吗?

【问题讨论】:

  • 这可能对hakre.wordpress.com/2010/09/13/…有帮助
  • @hek2mgl 你能想到任何解决方法吗?
  • 我还在工作。认为需要一些时间才能找到解决方法。稍后(~1小时)会在这里看看
  • @NabinKunwar PHP 是 5.5

标签: php arrays spl


【解决方案1】:

我对这个问题做了一个解决方法,扩展 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-&gt;array[$offset]=$value; }

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

【讨论】:

    【解决方案2】:

    添加我对相同错误的经验,以防它帮助任何人:

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

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      如果您在offsetGet 前面加上&amp;,这实际上是可以解决的(假设您可以访问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)]);
          }
      }
      

      【讨论】:

      • 添加&amp; 到底有什么作用?
      • @Yani 它允许你改变返回的值(而不是复制它)。即,$dict[$obj] = 5; 会做你所期望的(更新字典),而不是什么都不做。
      【解决方案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
      

      使用 [] 运算符没有问题(对于 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 为 [1] 调用 offsetGet 而不是 offsetSet 并且后来的 [] 失败。也许那里有解决方案,但我还没有找到。但以下工作没有问题:

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

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

      【讨论】:

      • 这是 Stackoverflow,不是猜谜游戏!
      【解决方案5】:

      首先,这个问题与所有实现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 也失败了 - 只是为了向您展示它与数组元素无关。

      【讨论】:

      • Bitteschön :) 另一个可行的解决方法可能是子类 SplFixedArray 并覆盖 offsetGet() 以返回数组或标量的引用。但这可能会导致不需要的副作用..(因为真正的数组不会返回引用)
      猜你喜欢
      • 2017-11-16
      • 2015-03-07
      • 2021-08-18
      • 2013-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      相关资源
      最近更新 更多