我认为这是正确的吗 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 (实施 IteratorAggregateArrayAccess 就像我们想要的)。

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 对于迭代对象,将与非迭代对象完全相同。

现在,有区别 IteratorIteratorAggregate. 。回头看 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 接口提供了能力 serializedeserialize 特定庄园中的数据。这 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)确实取决于原始对象状态。但这不是通常这样做的
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top