好的,现在我更好地理解了您的意思,让我在这里尝试解释一下。
实现迭代器(通过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);
. 因此,您可以继续迭代整个对象,就像它在迭代开始时一样。
请注意关键的区别。a 的每次迭代Iterator
都将取决于对象在该时间点的状态。a 的每次迭代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
直接更改属性来“修复”它(就像常规 OOP 一样)。只需将行替换$objFoo['reeb'] = 'roob';
为$objFoo->_array['reeb'] = 'roob';
. 这将完成同样的事情......
- 请注意,这是默认行为。您可以将一个内部迭代器(由 返回的迭代器
IteratorAggreagate::getIterator
)组合在一起,该迭代器确实取决于原始对象的状态。但这不是通常的做法