Хорошо, теперь, когда я лучше понимаю, что вы имеете в виду, позвольте мне попытаться объяснить здесь.
Реализация итератора (через интерфейс 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
.Обратите внимание, что вам не нужно определять итератор, чтобы использовать этот интерфейс.Все, что он делает - это обеспечивает возможность доступа к определенным элементам объекта с использованием массива, подобного синтаксису.
Примечание.Вы не можете рассматривать это как массив в целом.sort
функции никогда не будут работать.Однако есть несколько других интерфейсов, которые вы можете реализовать, чтобы сделать объект более похожим на массив.Одним из них является Countable
интерфейс , который позволяет использовать на объекте функцию count()
(count($obj)
).
В качестве примера объединения всех этих элементов в один класс, посмотрите ArrayObject
class .Каждый интерфейс в этом списке предоставляет возможность использовать определенную функцию ядра.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
), который зависит от состояния исходных объектов.Но обычно это не так