Не могу написать новые элементы для ITERATORAGGREGREY?

StackOverflow https://stackoverflow.com/questions/3585164

  •  01-10-2019
  •  | 
  •  

Вопрос

Я правильно думаю, что IteratorAggregate только обеспечивает массив читать Iterator?

Демонстрация объекта ITERATORAGGRETY, вызывающая фатальную ошибку при попытке добавления нового элемента:

<?

class foo implements IteratorAggregate {

    public $_array = array('foo'=>'bar', 'baz'=>'quux');

    public function getIterator() {
        return new ArrayIterator($this->_array);
    }

} // end class declaration


$objFoo = & new foo();

foreach ( $objFoo as $key => $value ) {
        echo "key: $key, value: $value\n";
}

$objFoo['reeb'] = "roob";

foreach ( $objFoo as $key => $value ) {
        echo "key: $key, value: $value\n";
}

$ php test.php
key: foo, value: bar
key: baz, value: quux

Fatal error: Cannot use object of type foo as array in /var/www/test.php on line 20

Call Stack:
    0.0009      57956   1. {main}() /var/www/test.php:0
Это было полезно?

Решение

Для чего это стоит, учитывая ваш класс образца, вы могли бы только использовать ArrayObject (который реализует IteratorAggregate а также ArrayAccess Как мы хотим).

class Foo extends ArrayObject
{
    public function __construct()
    {
        parent::__construct(array('foo' => 'bar', 'baz' => 'quux'));
    }
}

$objFoo = new Foo();

foreach ( $objFoo as $key => $value ) {
    echo "$key => $value\n";
}

echo PHP_EOL;

$objFoo['foo'] = 'badger';
$objFoo['baz'] = 'badger';
$objFoo['bar'] = 'badger';

foreach ( $objFoo as $key => $value ) {
    echo "$key => $value\n";
}

С выходом, как и ожидалось:

foo => bar
baz => quux

foo => badger
baz => badger
bar => badger

Другие советы

Хорошо, теперь, когда я лучше понимаю, что вы имеете в виду, позвольте мне попытаться объяснить здесь.

Реализация итератора (либо через Iterator или IteratorAggregate Интерфейс) будет добавлять только функциональность при итерации. Что это значит, это только влияет на способность использовать foreach. Отказ Это не изменяется или не изменяет любое другое использование объекта. Ни один из которых непосредственно поддерживает «писать» итератору. Я говорю напрямую, так как вы все еще можете написать на базовый объект во время итерации, но не внутри Foreach.

Это означает, что:

foreach ($it as $value) {
    $it->foo = $value;
}

Будет работать нормально (так как он пишет на оригинальный объект).

Пока

foreach ($it as &$value) {
    $value = 'bar';
}

Скорее всего, не будет работать с тех пор, как он пытается писать через итератор (который не поддерживается напрямую. Это может быть сделано, но это не основной дизайн).

Чтобы увидеть, почему, давайте посмотрим на то, что происходит за кулисами с этим foreach ($obj as $value). Отказ Это в основном идентично:

Для Iterator Классы:

$obj->rewind();
while ($obj->valid()) {
    $value = $obj->current();
    // Inside of the loop here
    $obj->next();
}

Для IteratorAggregate Классы:

$it = $obj->getIterator();
$it->rewind();
while ($it->valid()) {
    $value = $it->current();
    // Inside of the loop here
    $it->next();
}

Теперь вы видите, почему запись не поддерживается? $iterator->current() не возвращает ссылку. Так что вы не можете написать ему напрямую, используя foreach ($it as &$value).

Теперь, если вы хотите сделать это, вам нужно изменить итератор, чтобы вернуть ссылку от current(). Отказ Однако это сломало бы интерфейс (так как он изменит методы подписи). Так что это невозможно. Так что это означает, что написание к итератор это невозможно.

Однако осознайте, что он только влияет на саму итерацию. Это не имеет ничего общего с доступом к объекту из любого другого контекста или в любом другом усадьбе. $obj->bar будет точно так же для объекта итератора, как и для объекта без итератора.

Теперь существует разница между Iterator а также IteratorAggregate. Отказ Оглянуться на while Эквивалена, и вы можете увидеть разницу. Предположим, мы сделали это:

foreach ($objFoo as $value) {
    $objFoo->_array = array();
    print $value . ' - ';
}

Что случилось бы? С Iterator, это только распечатало бы только первое значение. Это потому, что итератор работает непосредственно на объекте для каждой итерации. И поскольку вы изменили то, что объект илетет (внутренний массив), итерация изменяется.

Сейчас если $objFoo является IteratorAggregate, он будет распечатать все значения, существовавшие в начале итерации. Это потому, что вы сделали копию массива, когда вы вернули new ArrayIterator($this->_array);. Отказ Таким образом, вы можете продолжать повторять все объект, так как он был в начале итерации.

Обратите внимание, что ключевое различие. Каждая итерация Iterator будет зависеть от состояния объекта в этот момент во времени. Каждая итерация IteratorAggregate будет зависеть от состояния объекта в начале итерации. * Это имеет смысл?

Теперь, как для вашей конкретной ошибки. Это не имеет ничего общего с итераторами вообще. Вы пытаетесь получить доступ (писать на самом деле) переменную за пределами итерации. Таким образом, он вообще не требует итератора (за исключением, что вы пытаетесь проверить результаты, итерация).

Вы пытаетесь лечить объект как массив ($objFoo['reeb'] = 'roob';). Теперь, для нормальных объектов, это невозможно. Если вы хотите сделать это, вам нужно реализовать ArrayAccess интерфейс. Отказ Обратите внимание, что вам не нужно иметь итератор, определенный для того, чтобы использовать этот интерфейс. Все, что он делает, предоставляет возможность доступа к конкретным элементам объекта с использованием массива, такого как синтаксис.

Обратите внимание, что я сказал Array, как. Вы не можете относиться к этому как массив в целом. sort Функции никогда не будут работать. Тем не менее, существует несколько других интерфейсов, которые вы можете реализовать, чтобы сделать объект более массив. Один это то Countable интерфейс что позволяет использовать возможность использовать count() функция на объекте (count($obj)).

Для примера в ядре объединения всех них в один класс, проверьте ArrayObject сорт. Отказ Каждый интерфейс в этом списке предоставляет возможность использовать определенную особенность ядра. То IteratorAggregate обеспечивает возможность использовать foreach. Отказ То ArrayAccess Предоставляет возможность получить доступ к объекту с помощью синтаксиса подобного массивами. То Serializable интерфейс обеспечивает способность serialize а также deserialize данные в конкретном усадьбе. То Countable Интерфейс позволяет подсчитать объект так же, как массив.

Таким образом, эти интерфейсы не влияют на функциональность основных объектов в любом случае. Вы все еще можете сделать что-либо с ним, что вы можете сделать с нормальным объектом (например, $obj->property или $obj->method()). Однако они делают возможность использовать объект, такой как массив в определенных местах в ядре. Каждый предоставляет функциональные возможности в строгом объеме (что означает, что вы можете использовать любую комбинацию, которую вы хотели бы. Вам не нужно иметь возможность получить доступ к объекту, как массив, чтобы иметь возможность count() Это). Это истинная сила здесь.

О, и присвоение возвращаемого значения new по ссылке устарели, Так что нет необходимости делать это ...

Итак, как для вашей конкретной проблемы, вот один из них вокруг него. Я просто реализовал ArrayAccess интерфейс в свой класс, так что $objFoo['reeb'] = 'roob'; буду работать.

class foo implements ArrayAccess, IteratorAggregate {

    public $_array = array('foo'=>'bar', 'baz'=>'quux');

    public function getIterator() {
        return new ArrayIterator($this->_array);
    }

    public function offsetExists($offset) {
        return isset($this->_array[$offset]);
    }

    public function offsetGet($offset) {
        return isset($this->_array[$offset]) ? $this->_array[$offset] : null;
    }

    public function offsetSet($offset, $value) {
        $this->_array[$offset] = $value;
    }

    public function offsetUnset($offset) {
        if (isset($this->_array[$offset]) {
            unset($this->_array[$offset]);
        }
    }
}

Затем вы можете попробовать свой существующий код:

$objFoo = & new foo();

foreach ( $objFoo as $key => $value ) {
    echo "key: $key, value: $value\n";
}

$objFoo['reeb'] = "roob";

foreach ( $objFoo as $key => $value ) {
    echo "key: $key, value: $value\n";
}

И это должно работать нормально:

key: foo, value: bar
key: baz, value: quux
key: foo, value: bar
key: baz, value: quux
key: reeb, value: roob

Вы также можете «исправить», изменив $objFoo->_array недвижимость напрямую (как правильный ООП). Просто замените строку $objFoo['reeb'] = 'roob'; с участием $objFoo->_array['reeb'] = 'roob';. Отказ Это достигнет то же самое ....

  • Обратите внимание, что это поведение по умолчанию. Вы могли бы взломать внутренний итератор (итератор, возвращенный IteratorAggreagate::getIterator) Это зависит от исходных объектов состояния. Но это не то, как это обычно делается
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top