note 65971 deleted from function.dom-domdocument-getelementsbytagname by didou
| From: | didou@php.net | Date: | Sat, 30 Dec 2006 03:14:24 +0000 |
| Subject: | note 65971 deleted from function.dom-domdocument-getelementsbytagname by didou | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-121049@lists.php.net to get a copy of this message | ||
Note Submitter: captainbajoo at juno dot com
----
Regarding the possibility of an infinite loop when altering the document tree while iterating:
We can see what really happens by investigating the keys of the NodeList. They are numeric indices
starting at 0. Suppose we call getElementsByTagName('*') and receive a NodeList like this:
$nodes[0] = <root>
$nodes[1] = <first>
Iteration via foreach:
foreach ($nodes as $node)
can be re-written like this:
for ($i=0; $i < count($nodes); $i++)
On the first iteration, $nodes[$i] refers to <root>
If we happen to insert a new node immediately before the current node:
insertBefore(<infinite>, $nodes[$i])
$nodes[$i] stops referring to <root> and now refers to <infinite>; the new element takes
the place of the old one (in the indexed ordering) and all subsequent nodes shift up by one.
On the next iteration, $i increments by one, so $nodes[$i] refers to <root> and the process
repeats.
Here is PHP code to demonstrate:
<?php
$dom = new DOMDocument();
$rootNode = $dom->createElement('root');
$dom->appendChild($rootNode);
$firstNode = $dom->createElement('first');
$rootNode->appendChild($firstNode);
$allnodes = $dom->getElementsByTagName('*');
for ($i=0; ; $i++) {
$node = $allnodes->item($i);
echo "before: {$node->nodeName} ";
$newNode = $dom->createElement('infinite');
$node->parentNode->insertBefore($newNode, $node);
$node = $allnodes->item($i);
echo "after: {$node->nodeName} <br/>";
if ($i > 4) break; # break arbitrarily so we can see the output
}
?>
Thus, if you are able to know, within the scope of the loop, that a node has been inserted, and use
for instead of foreach, you can explicitly increment the iterator once more and "skip" the
node that you already visited (if that's what you want; at any rate, that's what I
wanted).
"Can" doesn't mean "should," of course; this sounds like a kludge
(violating the sanctity of the iterator), but use your best judgment.