我試過這與普通數組和PHP迭代器。不幸的是PHP迭代器,因爲它們是對象,工作方式不同。對象通過引用傳遞,而數組是按值傳遞的。所以當嵌套的foreach到達迭代器的末尾時,第一個foreach不能恢復到它停止的地方,因爲內部指針被設置爲最後一個元素。
看看下面的例子使用純PHP陣列寫:
$test = [1, 2, 3];
foreach ($test as $i1 => $v1) {
echo "first loop: $i1\n";
foreach ($test as $i2 => $v2) {
echo "second loop: $i2\n";
}
}
上面的代碼產生以下輸出:
first loop: 0
second loop: 0
second loop: 1
second loop: 2
first loop: 1
second loop: 0
second loop: 1
second loop: 2
first loop: 2
second loop: 0
second loop: 1
second loop: 2
如果我們嘗試用一個迭代器同樣的事情,我們得到相當不同的結果。爲了避免混淆,我將使用ArrayIterator類,以便所有東西都已經由PHP人員實現,並且我們不會以錯誤的方式使用接口。所以容不得這裏的錯誤,這是迭代器是如何通過它們來實現:
$test = new ArrayIterator([1, 2, 3]);
foreach ($test as $i1 => $v1) {
echo "first loop: $i1\n";
foreach ($test as $i2 => $v2) {
echo "second loop: $i2\n";
}
}
輸出是:
first loop: 0
second loop: 0
second loop: 1
second loop: 2
正如你所看到的第一的foreach被執行一次。
解決方法可能是實施SeekableIterator接口。它會讓我們使用seek()方法將內部指針重置爲其正確的值。在我看來這是一個不好的做法,但如果PHP的人不解決這個問題,我不能說它是最好的。我從現在開始可能會避免使用迭代器,因爲它們看起來與陣列的行爲不同,我認爲這是人們首先假設的。因此,使用它們會使我的應用程序出錯,因爲可能是因爲我的團隊中的開發人員不知道這一點,並且隨代碼混淆。
遵循一個例子與SeekableIterator接口:
class MyIterator implements SeekableIterator
{
private $position = 0;
private $array = [1, 2, 3];
public function __construct()
{
$this->position = 0;
}
public function rewind()
{
$this->position = 0;
}
public function current()
{
return $this->array[$this->position];
}
public function key()
{
return $this->position;
}
public function next()
{
++$this->position;
}
public function valid()
{
return isset($this->array[$this->position]);
}
public function seek($position)
{
$this->position = $position;
}
}
$test = new MyIterator();
foreach ($test as $i1 => $v1) {
echo "first loop $i1\n";
foreach ($test as $i2 => $v2) {
echo "second loop $i2\n";
}
$test->seek($i1);
}
輸出是任何人會想到:這一切
first loop: 0
second loop: 0
second loop: 1
second loop: 2
first loop: 1
second loop: 0
second loop: 1
second loop: 2
first loop: 2
second loop: 0
second loop: 1
second loop: 2
是因爲每個的foreach工作在自己的數組的副本。迭代器,因爲它們是對象,通過引用傳遞。因此,每個foreach共享同一個對象。如果嘗試在嵌套的foreach中取消設置元素,也會發生同樣的情況。未設置將增加內部指針。然後執行到達嵌套foreach的末尾,內部指針再次增加。這意味着,如果未設置,我們會增加內部指針兩次。父親的foreach因此會跳過一個元素。
我的建議是,如果你不能避免迭代器,真的很小心。始終對它們進行單元測試。
注意:在PHP 5.6.14和PHP 7.0.0 RC5上測試的代碼。
另一種方法是實施['IteratorAggregate'](http://www.php.net/manual/en/class.iteratoraggregate.php)來代替。如果碰巧實現迭代器的對象封裝大量數據,克隆可能會很昂貴。 – Artefacto 2010-08-04 12:52:29
@Artee你應該根據你的評論寫一個答案。 – 2014-02-14 21:12:12