1

I'm trying to remove multiple elements in php using DOM Document, but it does not work as i wanted it to work.

<iPhone>
<enAttentePush>
  <UDID>dd6fc8032f0bf4c4b3a9c60f523900c9ad463b620a83b6473eda9a12b2c9d47f</UDID>
  <Message>someDataHere</Message>
</enAttentePush>

<enAttentePush>
  <UDID>dd6fc8032f0bf4c4b3a9c60f523900c9ad463b620a83b6473eda9a12b2c9d47f</UDID>
  <Message>someDataHere</Message>
</enAttentePush>
</iPhone>

I want to loop into my xml document and then erase all element to get an empty file.

Here is what i do in php :

$doc = new DOMDocument;

$doc->load('$myFile'); 
$domNodeList = $doc->getElementsByTagName('enAttentePush'); 
$arraySeen = array(); 
$arrayBug = array(); 

foreach ($domNodeList as $domElement)
{
        //echo('loop / ');
  $tempArray = array(); 

  $aps = $domElement->getElementsByTagName('Message')->item(0)->nodeValue;
  $udid = $domElement->getElementsByTagName('UDID')->item(0)->nodeValue; 
  if (!in_array($udid, $arraySeen))
  {         
      $tempArray[]=$aps; 
      $tempArray[]=$udid; 
      $arrayBug[]=$tempArray;
     $arraySeen[]=$udid; 
   }

   echo("-- removed -- ");
   $domElement->parentNode->removeChild($domElement); 
  }

It only delete only one node, the last one in general.

How can i do to loop thought my node and remove it each time i see a node ?

Thank you very much.

  • If I recall vaguely, deleting a node upsets the node query, which means that you have to re-query the DOM until all nodes are deleted. So instead of `foreach() { }` you should consider using `while() { }`. – pp19dd Jan 08 '14 at 16:19
  • Well i do not know how to make a while loop with dom... – Jacques Attali Jan 08 '14 at 16:22

3 Answers3

4

getElementsByTagName() returns "live" iterator, the list changes the moment you remove the elements from the list. You can convert the iterator into an array to get a copy of the list.

$xml = <<<'XML'
<iPhone>
<enAttentePush>
  <UDID>dd6fc8032f0bf4c4b3a9c60f523900c9ad463b620a83b6473eda9a12b2c9d47f</UDID>
  <Message>someDataHere</Message>
</enAttentePush>
<enAttentePush>
  <UDID>dd6fc8032f0bf4c4b3a9c60f523900c9ad463b620a83b6473eda9a12b2c9d47f</UDID>
  <Message>someDataHere</Message>
</enAttentePush>
</iPhone>
XML;

$dom = new DOMDocument();
$dom->loadXml($xml);

$nodes = iterator_to_array($dom->getElementsByTagName('enAttentePush'));
foreach ($nodes as $node) {
  $node->parentNode->removeChild($node);
}

echo $dom->saveXml();

Output:

<?xml version="1.0"?>
<iPhone>


</iPhone>

An easier way would be to copy the document element from the original document into a new one:

$xml = <<<'XML'
<iPhone>
<enAttentePush>
  <UDID>dd6fc8032f0bf4c4b3a9c60f523900c9ad463b620a83b6473eda9a12b2c9d47f</UDID>
  <Message>someDataHere</Message>
</enAttentePush>
<enAttentePush>
  <UDID>dd6fc8032f0bf4c4b3a9c60f523900c9ad463b620a83b6473eda9a12b2c9d47f</UDID>
  <Message>someDataHere</Message>
</enAttentePush>
</iPhone>
XML;

$source = new DOMDocument();
$source->loadXml($xml);

$target = new DOMDocument();
$target->appendChild(
  $target->importNode($source->documentElement, FALSE)
);

echo $target->saveXml();

Output:

<?xml version="1.0"?>
<iPhone/>

To remove specific nodes, select them with Xpath. This allows for complex conditions.

$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);

$nodes = $xpath->evaluate('/*/enAttentePush');
foreach ($nodes as $node) {
  $node->parentNode->removeChild($node);
}

echo $dom->saveXml();
ThW
  • 19,120
  • 3
  • 22
  • 44
  • Thank you very much ThW i'll use stckrboy solution which is more simple for a noob like me, nevertheless you answer helped me a lot, i'll take a look @ xPath. Thank you very much, really appreciated. – Jacques Attali Jan 09 '14 at 10:49
3

I had to do this just recently on my own site and found that you if you queue up your elements in an array, you can remove them all quite easily after that. Here is a little snippet that use, hopefully it helps you out:

$doc = new DOMDocument;

$doc->load('$myFile');
$domNodeList = $doc->getElementsByTagName('enAttentePush');

$domArray = array(); //set up an array to catch all our nodes

foreach($domNodeList as $dom) {
    $domArray[] = $dom;
}

// loop through the array and delete each node
foreach($domArray as $node){ 
    $doc->documentElement->removeChild($node);
}

$doc->save('$myFile');
stckrboy
  • 379
  • 10
  • 16
1

As already mentioned in comment, use while instead of foreach to iterate the DOMNodeList:

$i = $domNodeList->length;
while ($i > 0) {
    $i--;
    $domElement = $domNodeList->item($i);
    $domElement->parentNode->removeChild($domElement); 
}
nggit
  • 611
  • 8
  • 8