web-dev-qa-db-ja.com

「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をどのように扱いますか?回避策はありますか?

25
Desmond Hume

まず、問題は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);
_

これは スカラーを使用した例 であり、これも失敗します-配列要素のみに関連していないことを示すためです。

39
hek2mgl

これは、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)]);
    }
}
5
mpen

誰かに役立つ場合に備えて、同じエラーで私の経験を追加します:

最近、エラー許容度の低いフレームワーク(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]);
    }
}

それが役に立てば幸い。

0
mils