Php Can';不写新元素来迭代或聚合?

Php Can';不写新元素来迭代或聚合?,php,arrays,iterator,spl,Php,Arrays,Iterator,Spl,我是否正确地认为,IteratorAggregate只提供对对象的类似数组的读取访问?如果我需要以数组的形式写入对象,那么我需要使用迭代器 尝试添加新元素时,IteratorAggregate对象导致致命错误的演示如下: <? class foo implements IteratorAggregate { public $_array = array('foo'=>'bar', 'baz'=>'quux'); public function getIte

我是否正确地认为,
IteratorAggregate
只提供对对象的类似数组的读取访问?如果我需要以数组的形式写入对象,那么我需要使用
迭代器

尝试添加新元素时,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";
}

好吧,既然我更好地理解了你的意思,让我试着在这里解释一下

实现迭代器(通过
iterator
IteratorAggregate
接口)只会在迭代时添加功能。这意味着,它只影响使用
foreach
的能力。它不会改变或改变对象的任何其他用途。它们都不直接支持向迭代器“写入”。我直接说,因为您仍然可以在迭代时写入基本对象,但不能在foreach内部写入

这意味着:

foreach ($it as $value) {
    $it->foo = $value;
}
将正常工作(因为它正在写入原始对象)

很可能无法工作,因为它试图通过迭代器进行编写(迭代器不受直接支持。它可能可以完成,但不是核心设计)

要了解原因,让我们看看foreach($obj as$value)的幕后情况。它基本上与:

对于
迭代器
类:

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

$obj->rewind();
while ($obj->valid()) {
    $value = $obj->current();
    // Inside of the loop here
    $obj->next();
}
$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 . ' - ';
}
会发生什么?使用
迭代器
,它将只打印第一个值。这是因为迭代器在每次迭代中直接对对象进行操作。由于您更改了对象迭代的对象(内部数组),因此迭代也会更改

现在,如果
$objFoo
是一个
IteratorAggregate
,它将打印迭代开始时存在的所有值。这是因为当您返回
新的ArrayIterator($this->\u数组)时,创建了数组的副本。因此,您可以继续迭代整个对象,就像迭代开始时一样

请注意关键的区别。
迭代器的每次迭代将取决于对象在该时间点的状态。
IteratorAggregate
的每次迭代将取决于迭代开始时对象的状态。*这有意义吗

现在,至于你的具体错误。它与迭代器完全无关。您正在尝试访问(实际写入)迭代之外的变量。因此它根本不涉及迭代器(除了您试图通过迭代检查结果)

您试图将对象视为数组(
$objFoo['reeb']='roob';
)。现在,对于普通对象,这是不可能的。如果您想这样做,您需要实现。请注意,使用该接口不必定义迭代器。它所做的只是提供使用类似数组的语法访问对象的特定元素的能力

注意我说的数组像。一般来说,不能将其视为数组<代码>排序
功能永远不会工作。但是,您可以实现一些其他接口,使对象更像数组。一个是允许在对象上使用
count()
函数(
count($obj)
)的

有关将所有这些合并到一个类中的核心示例,请查看。该列表中的每个接口都提供了使用核心特定功能的能力。
IteratorAggregate
提供了使用
foreach
的功能。
ArrayAccess
提供了使用类似数组的语法访问对象的功能。
Serializable
接口提供了对特定庄园中的数据进行
序列化
反序列化
的功能。
Countable
界面允许像数组一样对对象进行计数

因此,这些接口不会以任何方式影响对象的核心功能。您仍然可以使用它对普通对象执行任何操作(例如
$obj->property
$obj->method()
)。然而,它们所做的是提供在核心的某些位置像数组一样使用对象的能力。每个都在严格的范围内提供功能(这意味着您可以使用它们的任意组合。您不需要像数组一样访问对象,就可以
count()
it)。这才是真正的力量

哦,还有,所以没有必要这么做

所以,对于你的具体问题,这里有一个解决方法。我只是在类中实现了
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->\u array
属性来“修复”它(就像
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