无法为iteratorAggregate编写新元素?
题
我认为这是正确的吗 IteratorAggregate
仅提供类似阵列的 读 访问对象?如果我需要写入对象 - 阵列,那么我需要使用 Iterator
?
试图添加新元素时,iTeratorAggregate对象的演示导致致命错误,请参见:
<?
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
界面. 。请注意,您不必定义迭代器即可使用该接口。它所做的只是提供使用语法等数组访问对象的特定元素的能力。
请注意,我说的数组喜欢。您不能像整个数组一样对待它。 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
属性直接(就像常规OOP一样)。只需更换行 $objFoo['reeb'] = 'roob';
和 $objFoo->_array['reeb'] = 'roob';
. 。那将完成同一件事。
- 请注意,这是默认行为。您可以将内部迭代器侵犯(返回的迭代器
IteratorAggreagate::getIterator
)确实取决于原始对象状态。但这不是通常这样做的