tlorens
tlorens

Reputation: 532

PHP XPath to return parent nodes based on 'child' values

Is it possible to run just a single query to replace multiple queries?

 //host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]
 //host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]/@exclude
 //host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]/../problem[1]
 //host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]/../reference[1]
 //host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]/../impact[1]
 //host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]/../background[1]
 //host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]/../resolution[1]

For some reason this doesn't work in PHP's xpath() function.

//host_info[hostname="localhost" and vulnerability_id="remote_execution"]/(*, ../background, ../impact, ../problem)

I feel there has to be a better way here. Please note it's possible to have multiple <host_info> nodes which is why I'm targeting the hostname and vulnerability_id. But there are only one <background>, <resolution> nodes contained in the parent <vulnerability_id> node.

<report>
<vulnerability>
   <host_info>
      <hostname></hostname>
      <vulnerability_id></vulnerability_id>
   </host_info>
   <host_info>
      <hostname></hostname>
      <vulnerability_id></vulnerability_id>
   </host_info>
   <background></background>
   <resolution></resolution>
</vulnerability>
<vulnerability>
   <host_info></host_info>
   <host_info></host_info>
   <host_info></host_info>
   <host_info></host_info>
   <background></background>
   <resolution></resolution>
</vulnerability>
</report>

Upvotes: 1

Views: 29

Answers (1)

ThW
ThW

Reputation: 19492

A node list has only a single dimension, so it is not really useful to serialize details of multiple items into a single list.

Typically you would use one Xpath expression to identify your list nodes and then DOM methods and relative expressions to fetch data related to these nodes:

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMxpath($document);

foreach ($xpath->evaluate('//host_info[hostname="localhost" and vulnerability_id="remote_execution"]') as $hostInfo) {
    var_dump(
      [
          $hostInfo->getAttribute('exclude'),
          $xpath->evaluate('string(parent::*/background)', $hostInfo),
          $xpath->evaluate('string(parent::*/resolution)', $hostInfo)
        ]
    );
}

For a single item it is possible to fetch all the detail nodes into a single result list. However an expression like that will get complex fast and you then have to add logic that recognizes the different detail nodes.

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMxpath($document);

$expression = <<<'XPATH'
//host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]/@exclude|
(//host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]/parent::*/background)[1]|
(//host_info[hostname="localhost" and vulnerability_id="remote_execution"][1]/parent::*/resolution)[1]
XPATH;

foreach ($xpath->evaluate($expression) as $detail) {
    var_dump(
       $detail->localName, $detail->textContent
    );
} 

Upvotes: 1

Related Questions