PHP DOM 为什么使用 removeChild 删除元素的子节点会中断对其子节点的 foreach 循环?

PHP DOM Why does removing a child node of an element with removeChild interrupt a foreach loop over its child nodes?

我遇到了 DOM 方法 removeChild 的一个令人费解的行为。当遍历 DOMElement 的子节点时,沿途删除这些节点之一会中断循环,即循环不会迭代剩余的子节点。

这是一个最小的例子:

$test_string = <<<XML
<test>
<text>A sample text with <i>mixed content</i> of <b>various sorts</b></text>
</test>
XML;

$test_DOMDocument = new DOMDocument();
$test_DOMDocument->loadXML($test_string);
$test_DOMNode = $test_DOMDocument->getElementsByTagName("text");

foreach ($test_DOMNode as $text) {
  foreach ($text->childNodes as $node) {
    if (preg_match("/text/", $node->nodeValue)) {
      echo $node->nodeValue;
      $node->parentNode->removeChild($node);
    } else {
      echo $node->nodeValue;
    }
  }
}

如果我注释掉行 $node->parentNode->removeChild($node);,那么输出就是整个测试字符串,即 A sample text with mixed content of various sorts,正如预期的那样。但是,在该行中,仅输出第一个子节点,即 A sample text with。也就是说,当循环经过它时删除第一个子节点显然会中断循环;剩余的子节点不被处理。这是为什么?

在此先感谢您的帮助!

根据对我的问题的评论的建议,我想出了以下解决方案:

$test_string = <<<XML
<test>
<text>A sample text with <i>mixed content</i> of <b>various sorts</b></text>
</test>
XML;

$test_DOMDocument = new DOMDocument();
$test_DOMDocument->loadXML($test_string);
$test_DOMNode = $test_DOMDocument->getElementsByTagName("text");

foreach ($test_DOMNode as $text) {
  $child_nodes = $text->childNodes;
  for($n = $child_nodes->length-1; $n >= 0; --$n) {
    $node = $child_nodes->item($n);
    if (preg_match("/text/", $node->nodeValue)) {
      echo $node->nodeValue;
      $node->parentNode->removeChild($node);
    } else {
      echo $node->nodeValue;
    }
  }
}

也就是说,我使用 another posting 中建议的方法以相反的顺序遍历子节点。这样,所有的节点都处理完了:输出为various sorts of mixed contentA sample text with。注意文本片段的相反顺序。在我的具体用例中,这种反转并不重要,因为我实际上并没有回显文本节点,而是对它们执行另一种操作。