Detecting empty XML node with PHP's SimpleXML

Brittany Layne Rapheal picture Brittany Layne Rapheal · Oct 30, 2014 · Viewed 8.7k times · Source

Let me start by saying I'm not well versed in parsing XML and/or writing PHP. I've been researching and piecing things together for what I'm working on, but I'm stuck.

I'm trying to create a basic if/else statement: if the node isn't empty, write the content of the node.

Here's a snippet of the XML I'm calling:

<channel>
    <item>
      <title>This is a test</title>
      <link />
      <description>Test description</description>
      <category>Test category</category>
      <guid />
    </item>
  </channel>

and here's the PHP I have so far:

<?php
    $alerts = simplexml_load_file('example.xml');
    $guid = $alerts->channel->item->guid;

    if ($guid->count() == 0) {
    print "// No alert";
}
    else {
        echo "<div class='emergency-alert'>".$guid."</div>";
}

?>

Obviously, "guid" is an empty node, but it's returning:

<div class="emergency-alert"> </div>

What am I doing wrong? :(

PS, I've tried hasChildren() and that didn't work either.

Answer

hakre picture hakre · Nov 1, 2014

In PHP a SimpleXMLElement which represents an empty XML element (self-closing tag or empty open/close tag pair with no content) casts to boolean FALSE. This is perhaps a bit unexpected, as normally every object in PHP casts to boolean TRUE:

var_dump((bool) new SimpleXMLElement("<guid/>")); # bool(false)

var_dump((bool) new SimpleXMLElement("<guid></guid>")); # bool(false)

var_dump((bool) new SimpleXMLElement("<guid> </guid>")); # bool(true)

This special rule is documented in the PHP manual on Converting to boolean.

You can make use of that to check whether or not the <guid> element you have is empty. However it's important here, you ask for the element specifically. In your existing code:

$guid = $alerts->channel->item->guid;

you're not asking for a specific <guid> element, but for all which are children of the parent <item> element. These kind of SimpleXMLElement objects cast to boolean true unless they contain zero elements (compare with your use of SimpleXMLElement::count()).

Different to that, if you obtain the first <guid> element by index, you will get a SimpleXMLElement of that index or NULL in case the element does not exist (that means, there is no <guid> element).

Both - non-existing element as NULL or the existing, empty one - will cast to boolean false which can be easily used in your if/else statement:

$guid = $alerts->channel->item->guid[0];
                                    ### zero is the index of the first element
if ((bool) $guid) {
    # non-empty, existing <guid> element
} else {
    # empty or non-existing
}

This then answers your question.