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

- 4,073
- 6
- 31
- 43

- 6,188
- 9
- 33
- 43
-
1Please select a new answer – Tim Ogilvy Apr 27 '17 at 09:58
17 Answers
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.

- 7,432
- 4
- 26
- 28
-
14This seemed neater than the other answers, but actually SimpleXMLElement returns and empty SimpleXMLElement object for any requested node that doesn't exist. So the empty() method seems like the best route. – spikyjt Jan 22 '13 at 11:22
-
also, `c` may be optional but `A` and `b` may be required, so I may actually want to get an exception if they are not defined - easy way to check document's integrity – davka Feb 28 '13 at 10:50
-
@spikyjt `isset()` is the best route to check if the node exists. You can use `empty()` if you need to check whether the node exists AND has something inside it, i.e. between `>` and `<` (either text or child nodes). If you need just to check for the existence of the node then `empty()` won't do. For example, `empty()` will return you `true` for this node `` – CITBL Apr 18 '17 at 15:42
-
2@CITBL you are correct, `empty()` is not much use. It is important to be aware however, that if there is no `c` element, `isset($A->c) === false`, but `$A->c` returns an empty SimpleXMLElement. So with `$c = $A->c;`, then `isset($c) === true` (i.e. `$c !== null` even though that might be expected). – spikyjt Apr 18 '17 at 22:14
SimpleXML always return Object. If there is no child, empty object is returned.
if( !empty($a->b)){
var_dump($a->b);
}

- 427
- 4
- 2
-
Apparently this is a feature and not a bug. And it is very important to note this. Accessing a child of the object will create it if it does not exist. – cosmin Apr 25 '13 at 14:50
-
3This should be the accepted answer, would have saved me some time and frustration :-) – Niek Klein Kromhof May 24 '16 at 14:09
-
2Wrong answer. Because `empty()` returns `true` even if the node exists but has no content. For example, in your code sample, if `$a` contains this: `
` then `empty($a->b)` will return `true`. Therefore that's not a reliable way to check if a node exists. You can use `empty()` though if you need something inside a node, i.e. between `>` and `<`, and not interested in the node without content – CITBL Apr 28 '17 at 20:04
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)
.

- 4,330
- 3
- 30
- 23
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

- 1,662
- 22
- 20
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..

- 14,870
- 3
- 10
- 25

- 2,011
- 1
- 26
- 42
-
You are completely right (that's why I said 'I hate this...'), but when you know that whatever error happens, you want it to be ignored, then this is fine. – scippie Jan 28 '20 at 08:10
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.

- 57,710
- 92
- 283
- 453

- 101
- 1
- 1
-
This should be the accepted answer, the currently accepted one is wrong. – Robbert van den Bogerd Dec 20 '18 at 12:48
Using if(isset($A->b){
gave me issues, so I tried
if($A->b){
and it worked!

- 372
- 7
- 20
-
2`count` and cast to boolean are the two ways that makes sense. Using `if` as you describe is the shortest way to cast to boolean. – Brilliand Jan 06 '15 at 18:56
Method xpath returns array of matched elements or false
if(false !== $A->xpath('b/c')) { ...

- 63
- 1
- 1
-
4No, the docs indicate it returns FALSE if there's an error, not if the path returned no results. – Brian Mar 14 '17 at 15:24
-
Tested this, @Brian is correct, it will only return false when an error occurs. Do not use this check, it will always return a (empty) node. – Robbert van den Bogerd Dec 20 '18 at 12:46
Simply
var_dump(count($xml->node));

- 749
- 2
- 11
- 26
-
In PHP7.2 they added a warning when counting something that does not implement Countable interface. So, this should be taken into account and better use isset() over count() – shirkkan Feb 18 '19 at 08:48
Using xpath:
function has_child(?\SimpleXMLElement $parent, 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:
if(has_child($a,'b/c')) { //...
You can also check for attributes. To check if the node c
has the t
attribute.
if(has_child($a,'b/c/@t')) { //...

- 1,587
- 3
- 21
- 36
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:

- 402
- 4
- 9
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'];
}

- 2,681
- 2
- 20
- 14
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
}

- 1,407
- 1
- 14
- 19
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
}

- 64
- 4
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.

- 2,043
- 24
- 39
You could try:
if($A->b->c && $A->b->c != '')

- 57,710
- 92
- 283
- 453

- 337
- 1
- 3
- 2
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).
-
22This isn't a great idea. c will be an empty object if the node does not exist, which isn't the same as NULL. – spikyjt Jan 22 '13 at 11:15
-
5To add to what @spikyjt said, if node c doesn't exist in $A->b, an empty SimpleXMLElement is returned. The valid instance of SimpleXMLElement is not null; that expression always evaluates true. – Matt Nov 19 '13 at 19:17