darkpirate
darkpirate

Reputation: 722

DOM cannot append nodes to element from DOMNodeList (PHP 7.3)

I'm working with php 7.3 to write a function that loops over a set of nodes recieved in input and add them to a document.

public function appendChildren($nodes)
{
    foreach ($nodes as $node){

        $this->appendChild($node);
    }
}

It works but when the input value is not an array but a DOMNodeList, it only loops over the first element.

To reproduce the problem:

<?php

$doc = new DOMDocument();
$doc->formatOutput=true;

$root =  $doc->createElementNS('urn:oasis:names:tc:SAML:2.0:metadata', 'md:root');
$nodes[] = $doc->createElementNS('urn:oasis:names:tc:SAML:2.0:metadata', 'KeyInfo');
$nodes[] = $doc->createElementNS('urn:oasis:names:tc:SAML:2.0:metadata', 'KeyFile');
$nodes[] = $doc->createElementNS('urn:oasis:names:tc:SAML:2.0:metadata', 'KeyStory');
$nodes[] = $doc->createElementNS('urn:oasis:names:tc:SAML:2.0:metadata', 'KeyRole');

foreach($nodes as $node)
{
    $root->appendChild($node);
}
$nlist = $root->childNodes;
$newroot = $doc->createElementNS('urn:oasis:names:tc:SAML:2.0:metadata', 'md:newroot');


foreach($nlist as $node)
{
   $newroot->appendChild($node);
}

echo 'size of root: '.$root->childNodes->count()."\n";
echo 'size of newroot: '.$newroot->childNodes->count()."\n";

$doc->appendChild($newroot);
echo $doc->saveXML();

expected output:

size of root: 4
size of newroot: 4
<?xml version="1.0"?>
<md:newroot xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
  <md:KeyInfo/>
  <md:KeyFile/>
  <md:KeyStory/>
  <md:KeyRole/>
</md:root>

however i get:

size of root: 3
size of newroot: 1
<?xml version="1.0"?>
<md:newroot xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
  <md:KeyInfo/>
</md:newroot>

The loop is incomplete, and somehow the reference of the first element is removed from the former list and added to the latter (hence the size 3). Moreover, if i comment

 $newroot->appendChild($node);

the loop traverse the list as expected. How is it possible for the function appendChild() to stop the loop? And why does it happen after the first iteration?

Can someone shed some light on this behaviour ?

Upvotes: 1

Views: 137

Answers (1)

Markus Zeller
Markus Zeller

Reputation: 9135

The $nlist is changing while iterating due to appendChild. You are moving the content. You can see that with debugging

for($i = 0; $i < $nlist->length; $i++)
{
    $newroot->appendChild($nlist->item($i));
}

So you need to rewrite like this

while($nlist->length)
{
    $newroot->appendChild($nlist->item(0));
}

Upvotes: 1

Related Questions