8

イデオン

サンプルコード:

<?php
$a = new ArrayObject();
$a['b'] = array('c'=>array('d'));
print_r($a);
unset($a['b']['c']);
print_r($a);

出力

ArrayObject Object
(
    [b] => Array
        (
            [c] => Array
                (
                    [0] => d
                )
        )
)
ArrayObject Object
(
    [b] => Array
        (
            [c] => Array
                (
                    [0] => d
                )
        )
)

$a['b']['c']設定を解除した後でも、まだそこにあることに気付きます。値が 1 つしか残っていないことが予想$aされます ( b)。

私の実際のアプリでは、次の警告が表示されます。

MyClass のオーバーロードされた要素の間接的な変更は効果がありません

MyClass拡張する場所ArrayObject。このようにネストされた要素の設定を解除できることに依存するコードがたくさんありますが、どうすればこれを機能させることができますか?

4

4 に答える 4

11

それを行う1つの方法

<?php
$a      = new ArrayObject();
$a['b'] = array('c' => array('d'));
$d      =& $a['b'];

unset($d['c']);
print_r($a['b']);

プリント:

Array
(
)

最初に使用した構文が要素を削除しない理由についての説明については、もう少し考える必要があります。

編集:動作の説明

何が起こっているかは、への呼び出しが次のようにunset($a['b']['c']);変換されることです。

$temp = $a->offsetGet('b');
unset($temp['c']);

は参照ではなく の$tempコピーである$aため、PHP は内部でコピー オン ライトを使用し、 を持たない 2 番目の配列を作成し$tempます。['b']['c']$a

別の編集: 再利用可能なコード

したがって、どのようにスライスしても、オーバーロードしようとすると問題が発生function offsetGet($index)するようfunction &offsetGet($index)です。だからここに私が思いついた最短のヘルパーメソッドがあります/ArrayObjectあなたのボートを浮かせるものは何でものサブクラスの静的またはインスタンスメソッドとして追加できます:

function unsetNested(ArrayObject $oArrayObject, $sIndex, $sNestedIndex)
{
    if(!$oArrayObject->offSetExists($sIndex))
        return;

    $aValue =& $oArrayObject[$sIndex];

    if(!array_key_exists($sNestedIndex, $aValue))
        return;

    unset($aValue[$sNestedIndex]);
}

したがって、元のコードは次のようになります

$a      = new ArrayObject();
$a['b'] = array('c' => array('d'));

// instead of unset($a['b']['c']);
unsetNested($a, 'b', 'c');
print_r($a['b']);

まだ別の編集: OO ソリューション

わかりました - だから私は今朝スクランブルしていたにちがいありません.b / cコードにエラーが見つかりました.修正されたら、OOに基づいて解決策を実装できます.

私が試したことを知っているように、拡張機能のセグメンテーション違反..:

/// XXX This does not work, posted for illustration only
class BadMoxuneArrayObject extends ArrayObject
{
    public function &offsetGet($index)
    {   
        $var =& $this[$index];
        return $var;
    }   
}

一方、Decorator の実装は魅力的に機能します。

class MoxuneArrayObject implements IteratorAggregate, ArrayAccess, Serializable, Countable
{
    private $_oArrayObject;  // Decorated ArrayObject instance

    public function __construct($mInput=null, $iFlags=0, $sIteratorClass='')
    {
        if($mInput === null)
            $mInput = array();

        if($sIteratorClass === '')
            $this->_oArrayObject = new ArrayObject($mInput, $iFlags);
        else
            $this->_oArrayObject = new ArrayObject($mInput, $iFlags, $sIteratorClass);
    } 

    // -----------------------------------------
    // override offsetGet to return by reference
    // -----------------------------------------
    public function &offsetGet($index)
    {
        $var =& $this->_oArrayObject[$index];
        return $var;
    }

    // ------------------------------------------------------------
    // everything else is passed through to the wrapped ArrayObject
    // ------------------------------------------------------------
    public function append($value)
    {
        return $this->_oArrayObject->append($value);
    }

    public function asort()
    {
        return $this->_oArrayObject->asort();
    }

    public function count()
    {
        return $this->_oArrayObject->count();
    }

    public function exchangeArray($mInput)
    {
        return $this->_oArrayObject->exchangeArray($mInput);
    }

    public function getArrayCopy()
    {
        return $this->_oArrayObject->getArrayCopy();
    }

    public function getFlags()
    {
        return $this->_oArrayObject->getFlags();
    }

    public function getIterator()
    {
        return $this->_oArrayObject->getIterator();
    }

    public function getIteratorClass()
    {
        return $this->_oArrayObject->getIteratorClass();
    }

    public function ksort()
    {
        return $this->_oArrayObject->ksort();
    }

    public function natcassesort()
    {
        return $this->_oArrayObject->natcassesort();
    }

    public function offsetExists($index)
    {
        return $this->_oArrayObject->offsetExists($index);
    }

    public function offsetSet($index, $value)
    {
        return $this->_oArrayObject->offsetSet($index, $value);
    }

    public function offsetUnset($index)
    {
        return $this->_oArrayObject->offsetUnset($index);
    }

    public function serialize()
    {
        return $this->_oArrayObject->serialize();
    }

    public function setFlags($iFlags)
    {
        return $this->_oArrayObject->setFlags($iFlags);
    }

    public function setIteratorClass($iterator_class)
    {
        return $this->_oArrayObject->setIteratorClass($iterator_class);
    }

    public function uasort($cmp_function)
    {
        return $this->_oArrayObject->uasort($cmp_function);
    }

    public function uksort($cmp_function)
    {
        return $this->_oArrayObject->uksort($cmp_function);
    }

    public function unserialize($serialized)
    {
        return $this->_oArrayObject->unserialize($serialized);
    }
}

これで、このコードは希望どおりに機能します。

$a      = new MoxuneArrayObject();
$a['b'] = array('c' => array('d'));
unset($a['b']['c']);
var_dump($a);

ただし、いくつかのコードを変更する必要があります..; 私はそれを回避する方法がわかりません。

于 2012-04-02T18:48:15.033 に答える
4

の「オーバーロードされた」ブラケット演算子はArrayObject、ネストされた配列のコピーを返しているように思えますが、元の配列への参照ではありません。したがって、 を呼び出すと、データの格納に使用され$a['b']ている内部配列のコピーが取得ArrayObjectされます。さらに解決すると$a['b']['c']、コピー内の要素「c」が提供されるだけなので、それを呼び出しunset()ても、元の要素「c」の設定が解除されません。

ArrayObjectinterfaceを実装ArrayAccessます。これは、実際にブラケット演算子がオブジェクトで機能することを可能にします。のドキュメントは、PHP 5.3.4ArrayAccess::offsetGetの時点で、の内部配列の元のデータへの参照は、彼の例で示されているように、演算子ArrayObjectを使用して取得できることを示しています。=&

于 2012-04-02T19:06:54.760 に答える
1

プロジェクト内のすべての同じ状況でそのような置換を行うのに大きな問題がない場合は、unset($a->b['c']);代わりに使用できますunset($a['b']['c']);

于 2014-03-13T21:33:19.183 に答える
0

私は部分的な解決策を持っているようです。unsetネストされたすべての配列が のインスタンスである場合に機能するようですArrayObject。ネストされたすべての配列も ArrayObjects であることを確認するために、代わりにこのクラスから派生できます。

class ArrayWrapper extends ArrayObject {
    public function __construct($input=array(), $flags=ArrayObject::STD_PROP_LIST, $iterator_class='ArrayIterator') {
        foreach($input as $key=>$value) {
            if(is_array($value)) {
                $input[$key] = new self($value, $flags, $iterator_class);
            }
        }
        parent::__construct($input, $flags, $iterator_class);
    }

    public function offsetSet($offset, $value) {
        parent::offsetSet($offset, is_array($value) ? new ArrayWrapper($value) : $value);
    }
}

(再帰性のために更新; 未テスト)

入れ子になった配列を追加しようとすると、自動的にArrayWrapper代わりに に変換されます。

残念ながら、ArrayObjects など、他の配列関数の多くは機能しarray_key_existsません。

于 2012-04-02T23:09:07.093 に答える