Как сбросить вложенный массив с ArrayObject? - PullRequest
8 голосов
/ 02 апреля 2012

ideone

Пример кода:

<?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'] все еще там, даже послепри снятии с охраны.Я ожидал бы, что $a будет иметь только одно оставленное значение (b).

В моем реальном приложении я получаю следующее предупреждение:

Косвенная модификация перегруженного элементаMyClass не имеет никакого эффекта

Где MyClass расширяется ArrayObject.У меня много кода, который зависит от того, можно ли сбрасывать вложенные элементы, как это, так как я могу заставить это работать?

Ответы [ 4 ]

11 голосов
/ 02 апреля 2012

Один из способов сделать это

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

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

печатает:

Array
(
)

Придется немного подумать, чтобы объяснить, почему синтаксис, который вы изначально использовали, не 't удалить элемент.

РЕДАКТИРОВАТЬ: объяснение поведения

Что происходит, вызов unset($a['b']['c']); переводится в:

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

поскольку $temp является копией $a вместо ссылки на нее, PHP использует внутреннее копирование при записи и создает второй массив, в котором $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 Решение

ОК - Итак, я, должно быть, сегодня утром карабкался, потому что я обнаружил ошибку в своем коде, и когда пересмотрел,мы можем реализовать решение, основанное на ОО.

Просто чтобы вы знали, что я пробовал, расширение segfaults ..:

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

Реализация декоратора с другой стороны работает как шарм:

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);

Тем не менее, придется изменить какой-то код, хотя ..;Я не вижу никакого способа обойти это.

4 голосов
/ 02 апреля 2012

Мне кажется, что «перегруженный» оператор скобки ArrayObject возвращает копию вложенного массива, а не ссылку на оригинал.Таким образом, когда вы вызываете $a['b'], вы получаете копию внутреннего массива, который ArrayObject использует для хранения данных.Дальнейшее разрешение его до $a['b']['c'] просто дает вам элемент "c" внутри копии, поэтому вызов unset() для него не отменяет отмену элемента "c" в оригинале.

ArrayObject внедряет интерфейс ArrayAccess , который фактически позволяет оператору скобок работать с объектом.Документация для ArrayAccess::offsetGet указывает, что, начиная с PHP 5.3.4, ссылки на исходные данные во внутреннем массиве ArrayObject могут быть получены с помощью оператора =&, так как quickshiftin указан в егопример.

1 голос
/ 14 марта 2014

Вы можете использовать unset($a->b['c']); вместо unset($a['b']['c']); в случае, если не будет огромной проблемы сделать такую ​​замену для всех тех же ситуаций в вашем проекте

0 голосов
/ 03 апреля 2012

Кажется, у меня есть частичное решение . unset, кажется, работает, если все вложенные массивы являются экземплярами ArrayObject. Чтобы гарантировать, что все вложенные массивы также являются объектами ArrayObject, мы можем извлечь их из этого класса:

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.

К сожалению, многие другие функции массива, такие как array_key_exists, не работают с объектами ArrayObjects.

...