Search code examples
phpxmlvariablesforeachunset

XML if unset node cant get variable value


So I unset each element from XML string

$xml = <<< XML
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<cars>  
<car_id>26395593</car_id>
<standart>0</standart>
<model>2</model> 
</cars>
</xml>
XML;

//Load XML into variable;

$concerts = simplexml_load_string($xml);

foreach ($concerts->xpath('/*/cars/*') as $child) {
    $chil = $child;

    echo "Before= " .$chil ."\n";
    unset( $child[0] );
    echo "After= " .$chil ."\n";
}

Now Result is like this

Before= 26395593
After= 
Before= 0
After= 
Before= 2
After=

Why $chil variable is unsetting too? How to save $child value to variable?


Solution

  • SimpleXML is an abstraction for the DOM. $child and $child[0] are separate SimpleXMLElement objects, but access the same DOM node. The unset() not just deletes the SimpleXMLElement object, but removes the node from the DOM, too.

    So after that the second SimpleXMLElement objects refers to a removed DOM node.

    With a little modification to your example, you can get an warning for it:

    $concerts = simplexml_load_string($xml);
    
    foreach ($concerts->xpath('/*/cars/*') as $child) {
        echo "Before= " .$child->asXml() ."\n";
        unset( $child[0] );
        echo "After= " .$child->asXml() ."\n";
    }
    

    Output:

    Before= <car_id>26395593</car_id>
    
    Warning: SimpleXMLElement::asXML(): Node no longer exists in /tmp/e... on line 19
    After= 
    Before= <standart>0</standart>
    
    Warning: SimpleXMLElement::asXML(): Node no longer exists in /tmp/e... on line 19
    After= 
    Before= <model>2</model>
    
    Warning: SimpleXMLElement::asXML(): Node no longer exists in /tmp/e... on line 19
    After= 
    

    You should avoid unsetting SimpleXMLElement objects. Keep the original document the same, read values from it and create a new XML document if you need to store the data in another format.

    To "disconnect" the value from the XML node, cast the SimpleXMLElement object into a scalar:

    $concerts = simplexml_load_string($xml);
    foreach ($concerts->xpath('/*/cars/*') as $child) {
        $value = (string)$child;    
        echo "Before= " .$value."\n";
        unset( $child[0] );
        echo "After= " .$value ."\n";
    }
    

    Output:

    Before= 26395593
    After= 26395593
    Before= 0
    After= 0
    Before= 2
    After= 2