php SimpleXML check if a child exists

PhpSimplexml

Php Problem Overview


A->b->c might exist but c might not exist. How do I check it?

Php Solutions


Solution 1 - Php

It might be better to wrap this in an isset()

if(isset($A->b->c)) { // c exists

That way if $A or $A->b don't exist... it doesn't blow up.

Solution 2 - Php

SimpleXML always return Object. If there is no child, empty object is returned.

if( !empty($a->b)){
  var_dump($a->b);
}

Solution 3 - Php

I solved it by using the children() function and doing a count() on it, ignoring an PHP error if there are no children by putting an @ before the count-call. This is stupid, but it works:

$identification = $xml->identification;
if (@count($identification->children()) == 0)
  $identification = $xml->Identification;

I hate this..

Solution 4 - Php

After some experimentation, I've discovered that the only reliable method of checking if a node exists is using count($xml->someNode).

Here's a test case: https://gist.github.com/Thinkscape/6262156

Solution 5 - Php

The answer by @null is correct - the simplest way to do this is with isset

if (isset($A->b->c)) { /* c exists */ }

However, the reasons for it are not obvious (to me at least), and there's a fair bit of misinformation on this page. It took me a while to understand it properly so I wanted to share what I learned.

As some people have pointed out, when you access a non-existent child of a SimpleXMLElement, what you get is actually an 'empty' SimpleXMLElement, as opposed to false or null.

So for example if b doesn't exist:

$b = $A->b; // $b is now an empty SimpleXMLElement
$b->getName(); // returns an empty string
isset($b); // returns true

So you might think that using isset to test for the existence of children is not going to work, because if the child doesn't exist, we still get an empty SimpleXMLObject, so isset is bound to return true.

But in fact it doesn't:

isset($A->b); // returns false

This was pretty surprising to me! The reason is that isset is not a regular function but a PHP language construct. When you call isset($A->b) PHP does not first calculate $A->b and then pass the result as an argument to isset(). Instead the behaviour when isset is called on an inaccessible object property is to call the __isset() overloading method on the class (as explained here: https://www.php.net/manual/en/language.oop5.overloading.php#object.isset)

So the author of the class can control the result of isset($A->b) independently from the result of $b = $A->b. In the case of SimpleXMLElement, they set it up so that isset($A->b) returns true if b exists and false otherwise - exactly what we need for testing the existence of a child element.

One further footnote to this - the original question asked about testing the existence of $A->b->c. Using isset($A->b->c) works perfectly for this as well, even if the intermediate b doesn't exist. I think what's happening here is that PHP first does $A->b, and if b doesn't exist, it gets an empty SimpleXMLElement, then it calls __isset('c') on that empty SimpleXMLElement to get the final result of isset($A->b->c).

Solution 6 - Php

If you have PHP 5.3, you can just use $a->count(). Otherwise, scippie's solution using @count($a->children()) works well. I find I don't need the @ but older PHP implementations may need it.

Solution 7 - Php

Method xpath returns array of matched elements or false

if(false !== $A->xpath('b/c')) { ...

http://www.php.net/manual/ru/simplexmlelement.xpath.php

Solution 8 - Php

Simply

var_dump(count($xml->node));

Solution 9 - Php

Using if(isset($A->b){ gave me issues, so I tried if($A->b){ and it worked!

Solution 10 - Php

Using xpath:

function has_child(\SimpleXMLElement $parent=null, string $xpathToChild)
{
	return isset($parent) && !empty($parent->xpath('('.$xpathToChild.')[1]'));
}

where $parent is an indirect or direct parent of the child node to check and $xpathToChild is an xpath of the child relative to $parent.

()[1] is because we don't want to select all the child nodes. One is enough.

To check if $a->b->c exists:

has_child($a,'b/c');

You can also check for attributes. To check if the node c has the t attribute.

has_child($a,'b/c/@t');

Solution 11 - Php

The 3 ways I can confirm work in PHP 5.5.23 were using isset() count() or empty()

Here is a script to show the results from each:

https://gist.github.com/mchelen/306f4f31f21c02cb0c24

Solution 12 - Php

I use a helper function to check if a node is a valid node provided as a parameter in function.

private static function isValidNode($node) {
  return isset($node) && $node instanceof SimpleXMLElement && !empty($node);
}

Usage example:

public function getIdFromNode($node) {
  if (!self::isValidNode($node)) {
    return 0;
  }
  return (int)$node['id'];
}

Solution 13 - Php

Thought I'd share my experience. Running on 5.4 I tried testing with 'isset' and 'empty' but neither worked for me. I ended up using is_null.

if(!is_null($xml->scheduler->outterList->innerList)) {
    //do something
}

Solution 14 - Php

Name Spaces

Be aware that if you are using name spaces in your XML file you will need to include those in your function calls when checking for children otherwise it will return ZERO every time:

if ($XMLelement->children($nameSpace,TRUE)->count()){
    //do something here 
}

Solution 15 - Php

I can't speak for earlier versions, but as at PHP 8.0, using the following one-line function:

function elementExists(?SimpleXMLElement $element) : bool {
  return is_null($element)?false:@count($element);
}

$A = new SimpleXMLElement('<A><b><c/></b></A>');  // doc contains c
$B = new SimpleXMLElement('<B><b/></B>');         // doc does not contain c 
$C = new SimpleXMLElement('<C><x><c/></x></C>');  // doc contains c but different heirarchy

print '$A contains ->b->c : ' . (elementExists($A->b->c)?"true":"false") . PHP_EOL;
print '$B contains ->b->c : ' . (elementExists($B->b->c)?"true":"false") . PHP_EOL;
print '$C contains ->b->c : ' . (elementExists($C->b->c)?"true":"false") . PHP_EOL;

returns

$A contains ->b->c : true
$B contains ->b->c : false
$C contains ->b->c : false

ie. correctly determines whether or not c exists and is in the required location.

Solution 16 - Php

You could try:

if($A->b->c && $A->b->c != '')

Solution 17 - Php

if($A->b->c != null) //c exists

If c does not exist, its value will be null (or, to be more precise, it will have no value). Note, however, that for this to work, both A and b need to not be null. Otherwise, PHP will throw an error (I think).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAn employeeView Question on Stackoverflow
Solution 1 - PhpnullView Answer on Stackoverflow
Solution 2 - PhpCedCannesView Answer on Stackoverflow
Solution 3 - PhpscippieView Answer on Stackoverflow
Solution 4 - PhpArtur BoderaView Answer on Stackoverflow
Solution 5 - PhpDaniel HowardView Answer on Stackoverflow
Solution 6 - PhpAlex MatulichView Answer on Stackoverflow
Solution 7 - Phpuser1391077View Answer on Stackoverflow
Solution 8 - PhpOğuz Can SertelView Answer on Stackoverflow
Solution 9 - PhpDaydahView Answer on Stackoverflow
Solution 10 - PhpCITBLView Answer on Stackoverflow
Solution 11 - PhpMike ChelenView Answer on Stackoverflow
Solution 12 - PhpphseView Answer on Stackoverflow
Solution 13 - Phpuser857276View Answer on Stackoverflow
Solution 14 - Phpg-manView Answer on Stackoverflow
Solution 15 - PhpPanchoView Answer on Stackoverflow
Solution 16 - PhpjgrundView Answer on Stackoverflow
Solution 17 - PhpPim JagerView Answer on Stackoverflow