I'm trying perform an xpath query on the result of an earlier xpath query. I can write the xpath query to extract the child elements and then iterate over the resulting elements, passing them off to another function for further processing. My problems come when I want to apply another xpath each of those resulting elements.
Given the XML
<grandfather>
<mother>
<son>1</son>
<son>2</son>
</mother>
<mother>
<daughter>3</daughter>
</mother>
<mother>
<daughter>4</daughter>
<son>5</son>
</mother>
<grandfather>
I can get all the mothers with the xpath /grandfather/mother
, but can I then query those mother nodes, in a separate function, using the passed mother node as the relative root of the next query?
The problem is compounded when the XML has a namespace which you need to register to get the xpath to work. With SimpleXML, the namespace prefix registered before the first xpath query is not retained in the query result, so you have to register it again for subsequent queries.
The second argument for DOMXpath::evaluate()
is the context for the expression. Here is a small example working on your xml:
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
foreach ($xpath->evaluate('//mother') as $mother) {
var_dump(
$xpath->evaluate('count(daughter)', $mother),
$xpath->evaluate('count(son)', $mother)
);
}
You have to provide the Xpath instance into a function to keep the namespace registration.
It is possible to extend DOMDocument and the other DOMNode classes to allow the registration on the object level. I implemented that in FluentDOM.