note 65971 added to function.dom-domdocument-getelementsbytagname
| From: | captainbajoo at juno dot com | Date: | Thu, 11 May 2006 05:39:33 +0000 |
| Subject: | note 65971 added to function.dom-domdocument-getelementsbytagname | ||
| Groups: | php.notes | ||
| Request: | Send a blank email to php-notes+get-111107@lists.php.net to get a copy of this message | ||
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.
----
Server IP: 66.163.161.117
Probable Submitter: 71.240.241.169
----
X-Spam-Status: No, hits=3.1 required=5.0 tests=DATE_MISSING,FROM_NO_LOWER
autolearn=no version=2.64
----
Manual Page -- http://www.php.net/manual/en/function.dom-domdocument-getelementsbytagname.php
Edit -- http://master.php.net/note/edit/65971
Del: integrated -- http://master.php.net/note/delete/65971/integrated
Del: useless -- http://master.php.net/note/delete/65971/useless
Del: bad code -- http://master.php.net/note/delete/65971/bad+code
Del: spam -- http://master.php.net/note/delete/65971/spam
Del: non-english -- http://master.php.net/note/delete/65971/non-english
Del: in docs -- http://master.php.net/note/delete/65971/in+docs
Del: other reasons-- http://master.php.net/note/delete/65971
Reject -- http://master.php.net/note/reject/65971
Search -- http://master.php.net/manage/user-notes.php