Search code examples
phpxmlxpathdomxpath

How to return additional node depending on current node attribute


We have a structure like this:

<class>
  <class-intro>
    <indication>Some content</indication>
  </class-intro>

  <article>
    <indication>Special content</indication>
  </article>

  <article includeclass="no">
    <indication>Different content</indication>
  </article>
</class>

I'm trying to select these with XQuery/XPath on a per article basis:

indication | node()[not(@includeclass) | @includeclass='yes']/ancestor::class/class-intro/indication

Note - I'm using PHP's http://php.net/manual/en/class.domxpath.php

// $xpath is a DOMXPath for the above document
$articles = $xpath->query("//article");

$indications = array();
foreach ($articles as $article) {
  $indications[] = $xpath->query(
    "indication | node()[not(@includeclass) | @includeclass='yes']/ancestor::class/class-intro/indication",
    $article
  );
}

var_dump($indications);

I'm expecting to get:

array(
  0 => array(
    0 => "Some content",
    1 => "Special content",
  ),
  1 => array(
    0 => "Different content",
  ),
);

But I am getting:

array(
  0 => array(
    0 => "Some content",
    1 => "Special content",
  ),
  1 => array(
    0 => "Some content",
    1 => "Different content",
  ),
);

Solution

  • The problem was because not(@includeclass) is always evaluates to true for every node() in this context, since none of child element of article has attribute includeclass.

    You should've used self axis to references current context node i.e use self::node() instead of node(), because includeclass attribute belongs to current context element article, not to the child node :

    self::node()[not(@includeclass) or @includeclass='yes']/ancestor::class/.....