Cirrostratus Cirrostratus - 2 months ago 8
PHP Question

With DOMDocument, is it possible to filter the output of getElementsByTagName based on an additional parameter?

Take the following example of this xml:

<xml>
<siblings>
<brother>Derek</brother>
<sister>Elaine</sister>
<sister>Flora</sister>
</siblings>
<siblings>
<brother>Gary</brother>
<sister>Hannah</sister>
</siblings>
</xml>


If I were to use the following code:

$xmlDoc=new DOMDocument();
$xmlDoc->load("Family.xml");
$siblings = $xmlDoc->getElementsByTagName('Siblings');
$sister = $xmlDoc->getElementsByTagName('Sister');


This would normally return all instances of the tag "Sister", in this case "Elaine", "Flora" and "Hannah". Would it be possible to change it so that you could filter the tagnames by the name of one of the other nodes? For instance, using the name "Derek" to change the output to "Elaine" and "Flora" only.

ThW ThW
Answer

Xpath expressions allow you to use conditions to fetch nodes from a DOM.

$xml = <<<'XML'
<xml>
<siblings>
    <brother>Derek</brother>
    <sister>Elaine</sister> 
    <sister>Flora</sister>
</siblings>
<siblings>
    <brother>Gary</brother>
    <sister>Hannah</sister> 
</siblings> 
</xml>
XML;

$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);

$expression = '/xml/siblings[brother = "Derek"]/*[not(self::brother = "Derek")]';

foreach ($xpath->evaluate($expression) as $sibling) {
  echo $sibling->textContent, "\n";
}

Output:

Elaine
Flora

The Xpath Expression

  • Fetch the siblings elements ...
    /xml/siblings
  • ... if they have a child element brother with the value Derek ...
    /xml/siblings[brother = "Derek"]
  • ... and fetch their child elements...
    /xml/siblings[brother = "Derek"]/*
  • ... if they are not a brother element node with the value Derek.
    /xml/siblings[brother = "Derek"]/*[not(self::brother = "Derek")]