Tips and Tricks for PHP SimpleXML

If you just started to work with PHP SimpleXML you migth find some deadends that are quite hard to get out of. Here are a couple of tips and tricks for you to use with SimpleXML and always have a 100% perfect XML manipulation.

Removing nodes from the XML:

If you search PHP site for all functions from SimpleXML there is one that you wont find, deleteChild (or any name like it), but there is a work around that. SimpleXML works very similar to Arrays, so if you need to delete a child node just use unset.

Consider the XML bellow:


<?xml version="1.0" encoding="UTF-8" ?>
<rss>
<channel>
<item>
<title><![CDATA[Tom & Jerry]]></title>
</item>
</channel>
</rss>

If you want to remove the channel node, just do the following:

unset($xml->channel);
$newXMLText = $xml->asXML();
$h = fopen($xmlFilePath,’w+’);
// fwrite($newXMLText,$h);
fwrite($h,$newXMLText);
fclose($h);

This will successfully remove any child node from the XML file. The only bad thing about this is that when you open the file you will see pretty much everything out of indent and line breaks. The file will not have format at all. To fix this you will need to use a second work around:

Before witting the file, do the following:

$dom = new DomDocument();
$dom->loadXML($newXMLText);
$dom->formatOutput = true;
$formatedXML = $dom->saveXML();

$h = fopen($xmlFilePath,’w+’);
// fwrite($formatedXML ,$h);
fwrite($h, $newXMLText);
fclose($h);

These are some hacks to not get too much involved with DOM, but if you are considering serious XML manipulation, then DOM XML is what you should be looking for.


About these ads

About mcloide

Making things simpler, just check: http://www.mcloide.com View all posts by mcloide

18 responses to “Tips and Tricks for PHP SimpleXML

  • Thomas

    So I am new to simpleXML and I am working with your example but I can’t seem to get it to work. I think I have copied everything exactly but apparently I am missing something because I can’t get it to work:


    channel);
    $newXMLText = $xml->asXML();
    $h = fopen('file.xml',’w+’);
    fwrite($newXMLText,$h);
    fclose($h);
    }

    ?>

    Any suggestions?

    • Thomas

      oops:

      channel);
      $newXMLText = $xml->asXML();
      $h = fopen('file.xml',’w+’);
      fwrite($newXMLText,$h);
      fclose($h);
      }

      ?>

      • Thomas

        Wow, I really wish wordpress would let you delete comments. Sigh.

        Here is my code:


        if( ! $xml = simplexml_load_file('file.xml') )
        {
        echo 'unable to load XML file';
        }
        else
        {

        unset($xml->channel);
        $newXMLText = $xml->asXML();
        $h = fopen('file.xml',’w+’);
        fwrite($newXMLText,$h);
        fclose($h);
        }

  • mcloide

    Hi Thomas,

    I believe that you are having difficulties because that is an example with only tips, so the full code is not there. Here is how it should look like;

    // Loading from a string
    // file xmlText.xml
    <?xml version=”1.0″ encoding=”UTF-8″ ?>
    <rss>
    <channel>
    <item>
    <title><![CDATA[Tom & Jerry]]></title>
    </item>
    </channel>
    <code>
    <codeItem>RST12345667</codeItem>
    <codeItem>RST12345668</codeItem>
    </code>
    </rss>

    // save it anywhere you think is good ;)

    $xml = simplexml_load_file(‘/path/to/your/file/xmlText.xml’);

    /** at this moment you already have the full xml loaded inside your xml object

    If you needed to Remove, let’s say for instance, the first item of the code node, with simpleXml you can do it by using */

    unset($xml->code->codeItem[0]);

    /** This will also work for any inner node, just correctly point the node that you want to unset, just like you would unset an array node **/

    /** Now that the code has been removed, the logical thing is to save the new xml, but if you do it just using simplexml functions, it will, for sure save it without formating. Anyway, for testing porpoises.. */

    $newXmlText = $xml->asXML();
    /** it will return the unformatted text from the object */

    $fp = fopen(‘/path/to/your/file/xmlText.xml’,w+);
    /** opening the file for writing, positioning the pointer at top and truncating the file */

    fwrite($fp, $newXmlText);

    fclose($fp);

    /** if you open the file now you will notice that it has no formating, which is kind of annoying to read, so a good way to format is to use the dom object to format the file before you save the text, so instead of doing the open and save text above you would do: */

    $dom = new DomDocument();
    $dom->loadXML($newXMLText); // same new xmlText as above
    $dom->formatOutput = true; // here is where the magic happens
    $formatedXML = $dom->saveXML();

    $fp = fopen(‘/path/to/your/file/xmlText.xml’,’w+’);
    fwrite($fp, $formatedXML);
    fclose($fp);

    BTW – I have just noticed that the fwrite on the code of the article is with the order of the elements inversed. I have fixed it on this comment, but just to keep track.

    Just a note, this is a great way to format the xml, but if you are working with a big xml file, consider using only the DOM object to handle this. It’s a bit more complex, but has a ton of features that can make your coding a bit simpler and faster.

  • Thomas

    Hey thanks for your help, its hard to find good resources on simpleXML. I failed to add single quotes around the w+ in the fopen line and thats why it was failing. Thanks!

    • mcloide

      No worries… I’m working on a series of articles that will go from the basic of PHP to the advanced. I’m still working on the basics, but soon enough I will start on the medium level and I will surely focus a full article on the xml features of PHP, including simpleXML.

      See you around.

  • Thomas

    Man, I hate to bother you with this but I have been looking at documentation all day and I can’t seem to figure it out-

    I am using the last example you post for removing nodes but now I would like to identify the nodes I want to remove by searching for a specific attribute.

    For example I want to remove the codeItem node with the id attribute set to ‘kill':


    RST12345668
    RST12345668

    I used your previous example to set my code up like this:

    code->codeItem->id['kill']);
    $newXmlText = $xml->asXML();
    $fp = fopen(‘file.xml’,’w+’);
    fwrite($fp, $newXmlText);
    fclose($fp);

    ?>

    Again, I am still not real clear on how to access attributes, so targeting a node with a specific attribute is really giving me a lot of trouble. Any suggestions?

    • Thomas

      Sorry, I screwed up the code tags again, the xml line I am trying to target looks like this:

      RST12345668

      • mcloide

        Thomas,

        The xml code tags did not appear no your comment, but anyway, to access, via simpleXml any node or value you will use something that looks like a mix of objects to array.

        Let’s say that you have the following node, inside your xml:

        <code>
        <codeItem id=”kill”>RS1234567987<codeItem>
        <code>

        The codeItem is the first (and only) item from your code node, so to access it you would use:

        $xml->code[0];

        If you needed it be sure that the one that you are accessing is really the node with the “kill” id, then you would use:

        $attr = $xml->code[0]->attributes();
        echo $attr['id']; // it would result kill

        So to unset (read delete) the node that have the id = kill you would use:

        if ($attr['id'] == ‘kill’)
        {
        unset($xml->code[0]);
        }

        Working with simpleXML is quite fun because you can loop on it like you are on an array, you can directly access it as you would access an object and you can use xpath to directly access an value from the xml (like a query string).

        Since it’s seems to be that you are starting, check out this article, from the Zend Developer Zone. It will help you to get an basic idea how to work with the simpleXML from PHP 5 – http://devzone.zend.com/article/688

        Keep in mind that the xml must always be well formed to be accessed by the simpleXML library.

        If you still in doubt, come back, ask and I will help you out as much as I can.

  • mcloide

    I have just realized that you might be trying to remove a node that have the value RST12345668. For that there are 3 ways:

    1. Go directly to it, let’s say that it is the second node from the code node, so accessing it would be $xml->code[1];

    2. Loop into all code subnodes and match the value
    $keyToUnset = ”;
    foreach($xml->code as $key => $codeItem)
    {
    $value = (string) $codeItem;
    if ($value == ‘RST12345668′)
    {
    $keyToUnset = $key;
    }
    }
    unset ($xml->code[$keyToUnset]);

    3. Use XPath
    unset ($xml->xpath(/code/coditem['RST12345668']));

    For performance like, using xpath is the way to go.

    Have fun.

  • Thomas

    I have multiple nodes named ‘codeItem’, so I am interested in picking out the node that has the attribute ‘id’ set to ‘kill’. Since I am so clumbsy with the wordpress comment declarations, checkout: http://cstang.hk/eater/demo.html for the php code and http://cstang.hk/eater/file.xml for the xml file I am working with.

    • mcloide

      Beautiful. Way better to read.

      What you are missing on that function is simply the part that you get to the attribute.

      $xml = simplexml_load_file(‘file.xml’);
      $keyToKill = null;
      foreach ( $xml->code as $key => $item)
      {
      $attr = $item->attributes();
      if ($attr['id'] == ‘kill’)
      $keyToKill = $key;
      }

      unset($xml->code->codeItem[$keyToKill]);

      $newXmlText = $xml->asXML();
      $fp = fopen(‘file.xml’,’w+’);
      fwrite($fp, $newXmlText);
      fclose($fp);

      You only don’t unset the key inside the foreach loop to avoid errors during the loop.

      Try this and let me know.

    • mcloide

      Thomas,

      I have gone through the code, made some simple modifications, but you will be able to see it working fully.

      Go to: http://www.dollartoolbox.com/simplexml/index.php

  • Thomas

    Hmm, still not working – I added a bracket to the ‘if’ statement but nothing changed. I have updated http://cstang.hk/eater/demo.html with the code I am currently using again. I also tried setting $key as a $xml->code->codeItem (instead of $xml->code) because I was worried that might be the problem but still nothing changed. I don’t think there is a permission problem on the server and I know the unset thing works with your earlier example so there must be some sort of small detail being overlooked here…

  • Thomas

    Wow, I think you may have just saved my sanity. Thank you – you didn’t have to help me, so I really appreciate it. Good luck with your blog!

    • mcloide

      Not a problem man … I know some times a second coder looking at the code helps …

      come back any time .. this is the place that I keep every single line of code that one day I have used as a library or in some way helped me with PHP

  • Marc

    Hi mcloide,

    This article is really useful. I was wondering if there’s a function in simpleXML to set a value; update a value.

    Marc

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 293 other followers

%d bloggers like this: