1

Why do I get parent elements when I call the SimpleXMLElement::xpath() method on a child element?

Example:

$xml = '<root>
  <node>
    <tag v="foo"/>
    <tag v="bar"/>
  </node>
  <node>
    <tag v="foo"/>
  </node>
  <node>
    <tag v="foo"/>
    <tag v="bar"/>
  </node>
</root>';

$simpleXML = new \SimpleXMLElement($xml);

$nodeList = $simpleXML->xpath('//node');

foreach ($nodeList as $node) {
    $node->xpath('//tag');
}

Here $node->xpath('//tag') returns all the <tag> xml tags of the document, at each iteration, instead of returning only the child <tag> elements of the <node> tag.

2 Answers2

2

Differences between //tag, .//tag, and //descendent::tag:

  • //tag retrieves all tag elements anywhere in the document.

  • .//tag retrieves all tag elements at or beneath the context node.

  • descendant::tag retrieves all tag elements beneath the context node.

See also

kjhughes
  • 106,133
  • 27
  • 181
  • 240
1

In XPath, when you use //, you are saying a node in any position relative to the current node. With XPath this also includes nodes higher in the document as well as enclosed in this element.

There are several ways to solve it in this particular scenario...

With reference to What's the difference between //node and /descendant::node in xpath?, you can use the descendant axis...

foreach ($nodeList as $node) {
    $node->xpath('descendant::tag');
}

which only uses nodes inside the base node (in this case $node).

Or if the hierarchy in your document is exactly as you have in your document, a simpler way is to use SimpleXML object notation (with a loop to show each one)...

foreach ($nodeList as $node) {
    // For each enclosed tag
    foreach ( $node->tag as $tag )  {
        // Echo the v attribute
        echo $tag['v'].PHP_EOL;
    }
}
Nigel Ren
  • 56,122
  • 11
  • 43
  • 55
  • Thanks, `$node->xpath('descendant::tag');` works for me! But I don't understand how a child object accesses data outside its borders? `$node->xpath()` should return a single object that knows nothing about external tags. Does this object store a reference to the parent element? – Максим Nov 01 '20 at 21:25
  • An XML object has links to parent elements to know how it fits into the structure. With XPath you can use all sorts of axes to find related data - https://developer.mozilla.org/en-US/docs/Web/XPath/Axes. – Nigel Ren Nov 01 '20 at 21:43