# unexpected behaviour in SimpleXML (php 5.2.2)

- Thread: https://externals.io/message/29654
- Original archive: https://news-web.php.net/php.internals/29654
- Started by Vesselin Kenashkov on 2007-05-22T08:23:02+00:00
- Messages: 6
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #29654 — Vesselin Kenashkov — 2007-05-22T08:23:02+00:00

URL: https://externals.io/message/29654#29654

Hello everybody,

Before writing to the dev-list I posted a message in the Zend dev forum (in
fact it was an year ago about the same issues), as well on PHP general list,
but without any reply or comment. I think two things in the SimpleXML are
inconsistent, or at least unexpected from the php developer.

Here is the first one - how one can find does a SimpleXMLElement has child
nodes?
Here is an example:
--------
$str = '<rootnode><subnode></subnode></rootnode>';
$x = new SimpleXMLElement($str);

if($x->subnode->children())
    print 'yes';
    else
        print 'no';
---------
will print 'no';

If the
$str='<rootnode><subnode><newnode></newnode></subnode></rootnode>';
it will print yes.
But the same will happen if the subnode has an attribute like:
$str = '<rootnode><subnode id="2"></subnode></rootnode>';

But if we use foreach($x->subnode->children() as $key=>$value)
in the latter example we will not get anything (which is correct).
I think is wrong the children() method to return object when there are no
child objects and the node has attributes.

The workaround I use is to extend the SimpleXMLElement
--------
class SimpleXMLElement2 extends SimpleXMLElement
{
public function has_children()
    {
    foreach($this->children() as $node)
        return true;
    return false;
    }

}
------
And then we can check with if($x->subnode->has_children())



Issue #2:
Xpath on nodes.
Let we have the example:
-----------------
$str =
'<rootnode><level1_node1><level2_node1></level2_node1></level1_node1><level1_node2></level1_node2></rootnode>';

$x = new SimpleXMLElement($str);

$r1 = $x->xpath('/*');
print $r1[0]->getName();//prints rootnode

$r2 = $x->level1_node1[0]->xpath('/*');
print $r2[0]->getName();//prints rootnode

$z = clone $x->level1_node1[0];
$r3 = $z->xpath('/*');
print $r3[0]->getName();//prints rootnode

//print $z->getName();//ok
------------------
I personally think that the xpath must be evaluated against the node which
method is called, not always against the rootnode.
So in the second example I would expect it to return level1_node1, and
especially in the thirds example.
Even in the third example the xpath is evaluated against the original XML
structure, not the subnode (level1_node1).
I think this is incorrect and leads to a confusion - for example we can pass
a node to a function like:
--------
function do_something($node)
    {
    //print $node->getName();//prints correct - the name of the supplied
node - level1_node1
    $r1 = $node->xpath('/*');
    print $r1[0]->getName();
    }

do_something(clone $x->level1_node1[0]);
--------
The do_something function is not aware at all about the full xml structure
and one could think that the expression will be evaluated just against the
supplied node, but it is not that the case.

Please give your comments - do you find this functionality OK, is it a bug,
or I miss something.


Vesselin Kenashkov

---

## #29655 — Christian Stocker — 2007-05-22T09:50:19+00:00

URL: https://externals.io/message/29654#29655 · In reply to: #29654

I can't answer on your first topic, but the second:



> Hello everybody,

> Issue #2:
> Xpath on nodes.
> Let we have the example:
> -----------------
> $str =
> '<rootnode><level1_node1><level2_node1></level2_node1></level1_node1><level1_node2></level1_node2></rootnode>';
> 
> 
> $x = new SimpleXMLElement($str);
> 
> $r1 = $x->xpath('/*');
> print $r1[0]->getName();//prints rootnode
> 
> $r2 = $x->level1_node1[0]->xpath('/*');
> print $r2[0]->getName();//prints rootnode
> 
> $z = clone $x->level1_node1[0];
> $r3 = $z->xpath('/*');
> print $r3[0]->getName();//prints rootnode
> 
> //print $z->getName();//ok
> ------------------

That's how it works also in ext/dom (and IMHO as it's supposed to work),
but the "workaround" is easy:

$r2 = $z->xpath('./*');

works as you would expect it.

chregu

> I personally think that the xpath must be evaluated against the node which
> method is called, not always against the rootnode.
> So in the second example I would expect it to return level1_node1, and
> especially in the thirds example.
> Even in the third example the xpath is evaluated against the original XML
> structure, not the subnode (level1_node1).
> I think this is incorrect and leads to a confusion - for example we can
> pass
> a node to a function like:
> --------
> function do_something($node)
>    {
>    //print $node->getName();//prints correct - the name of the supplied
> node - level1_node1
>    $r1 = $node->xpath('/*');
>    print $r1[0]->getName();
>    }
> 
> do_something(clone $x->level1_node1[0]);
> --------
> The do_something function is not aware at all about the full xml structure
> and one could think that the expression will be evaluated just against the
> supplied node, but it is not that the case.
> 
> Please give your comments - do you find this functionality OK, is it a bug,
> or I miss something.
> 
> 
> Vesselin Kenashkov
> 

-- 
christian stocker  |  Liip AG  |  schoeneggstrasse 5 |  ch-8004 zurich
phone +41 44 240 56 70 | mobile +41 76 561 88 60 | fax +41 1 240 56 71
http://www.liip.ch/  |  christian.stocker@liip.ch  |  GnuPG 0x5CE1DECB

---

## #29656 — Vesselin Kenashkov — 2007-05-22T10:11:41+00:00

URL: https://externals.io/message/29654#29656 · In reply to: #29655

>
> I can't answer on your first topic, but the second:
>
>
> That's how it works also in ext/dom (and IMHO as it's supposed to work),
> but the "workaround" is easy:
>
> $r2 = $z->xpath('./*');



Thank for looking and replying.

I have to clarify...

Actually I would expect at least the third example
-------------
$z = clone $x->level1_node1[0];
$r3 = $z->xpath('/*');
print $r3[0]->getName();//prints rootnode
-------------
to print level1_node1. But as well at least to me looks logical even the
second to print  level1_node1, because the xpath() method is acalled on the
level1_node1, not the rootnode.

I think that it is incorrect that the object (the xml strcuture) $z is still
treated like the object $x when it comes to Xpath. In fact they are
different object with different structures and the Xpath expressions must
return different results (IMHO).



works as you would expect it.
>
> chregu
>
> --
> christian stocker  |  Liip AG  |  schoeneggstrasse 5 |  ch-8004 zurich
> phone +41 44 240 56 70 | mobile +41 76 561 88 60 | fax +41 1 240 56 71
> http://www.liip.ch/  |  christian.stocker@liip.ch  |  GnuPG 0x5CE1DECB



Vesselin Kenashkov

P.S. sorry for the double post - reply issue.

---

## #29663 — kepbepos kepbepos — 2007-05-22T15:02:50+00:00

URL: https://externals.io/message/29654#29663 · In reply to: #29654

Hi,
I'm not an expert, but in your example

--------
> $str = '<rootnode><subnode></subnode></rootnode>';
> $x = new SimpleXMLElement($str);
>
> if($x->subnode->children())
>     print 'yes';
>     else
>         print 'no';
> ---------
> will print 'no';


ok no children

If the
> $str='<rootnode><subnode><newnode></newnode></subnode></rootnode>';
> it will print yes.


ok has  one child: <newnode>

But the same will happen if the subnode has an attribute like:
> $str = '<rootnode><subnode id="2"></subnode></rootnode>';


I think is correct, in fact (if I'm right) an attribute is a child.
Following DOM the tree is:

rootnode
     |
subnode
     |
    id

Here an example explaining that
http://www.w3schools.com/dom/nodetree.gif

Please someone else to confirm.

See you

---

## #29697 — Vesselin Kenashkov — 2007-05-23T17:06:55+00:00

URL: https://externals.io/message/29654#29697 · In reply to: #29663

The attribute is not a child XML node. For walking through attributes
SimpleXML has $node->attributes() method.
And if($node->attributes()) check works as expected.
So I think $node->children() must be just for child XML nodes, not for
attributes (there is no sense to give back results for attributes since we
have attributes() method).

Vesselin Kenashkov


>
> Hi,
> I'm not an expert, but in your example
>
> --------
> > […]
>
>
> ok no children
>
> If the
> > […]
>
>
> ok has  one child: <newnode>
>
> But the same will happen if the subnode has an attribute like:
> > […]
>
>
> I think is correct, in fact (if I'm right) an attribute is a child.
> Following DOM the tree is:
>
> rootnode
>      |
> subnode
>      |
>     id
>
> Here an example explaining that
> http://www.w3schools.com/dom/nodetree.gif
>
> Please someone else to confirm.
>
> See you

---

## #29717 — Caroline Maynard — 2007-05-24T13:06:42+00:00

URL: https://externals.io/message/29654#29717 · In reply to: #29654

Vesselin Kenashkov wrote:
> 
> Here is the first one - how one can find does a SimpleXMLElement has child
> nodes?
> Here is an example:
> --------
> $str = '<rootnode><subnode></subnode></rootnode>';
> $x = new SimpleXMLElement($str);
> 
> if($x->subnode->children())
>    print 'yes';
>    else
>        print 'no';
> ---------
> will print 'no';
> 
> If the
> $str='<rootnode><subnode><newnode></newnode></subnode></rootnode>';
> it will print yes.
> But the same will happen if the subnode has an attribute like:
> $str = '<rootnode><subnode id="2"></subnode></rootnode>';
> 
> But if we use foreach($x->subnode->children() as $key=>$value)
> in the latter example we will not get anything (which is correct).
> I think is wrong the children() method to return object when there are no
> child objects and the node has attributes.
> 
> The workaround I use is to extend the SimpleXMLElement
> --------
> class SimpleXMLElement2 extends SimpleXMLElement
> {
> public function has_children()
>    {
>    foreach($this->children() as $node)
>        return true;
>    return false;
>    }
> 
> }
> ------
> And then we can check with if($x->subnode->has_children())

That's how SimpleXML is, but you can ease your pain by using `count()`:

$x = new SimpleXMLElement($str);

if (count($x->subnode->children()))
     print 'yes';
else
     print 'no';
