SimpleXML remove nodes

2019-01-26 02:08发布

问题:

I've got a foreach loop that is only running once and it has me stumped.

1: I load an array of status values (either "request", "delete", or "purchased")

2: I then load an xml file and need to loop through the "code" nodes and update their status, BUT if the new code is "delete" I want to remove it before moving onto the next one

XML structure is....

<content>
.... lots of stuff
<codes>
<code date="xxx" status="request">xxxxx</code>
.. repeat ...
</codes>
</content>

and the php code is ...

$newstatus = $_POST['updates'];
$file = '../apps/templates/'.$folder.'/layout.xml';
$xml2 = simplexml_load_file($file);
foreach($xml2->codes->code as $code){
    if($code['status'] == "delete") {
        $dom=dom_import_simplexml($code);
        $dom->parentNode->removeChild($dom);
    }
}
$xml2->asXml($file);

I've temporarily removed the updating so I can debug the delete check. This all works BUT it only removes the 1st delete and leaves all the other deletes even though it's a foreach loop??. Any help greatly appreciated.

回答1:

Deleting multiple times in the same iteration is unstable. E.g. if you remove the second element, the third becomes the second and so on.

You can prevent that by storing the elements to delete into an array first:

$elementsToRemove = array();
foreach ($xml2->codes->code as $code) {
    if ($code['status'] == "delete") {
        $elementsToRemove[] = $code;
    }
}

And then you remove the element based on the array which is stable while you iterate over it:

foreach ($elementsToRemove as $code) {
    unset($code[0]);
}

You could also put the if-condition into an xpath query which does return the array directly (see the duplicate question for an example) or by making use of iterator_to_array().