phpxmlxpathphpquery

How to query a xml file using xpath (php) ?


I am trying to query an XML file using XPath. But as return I get nothing. I think I formatted the query false.

XML

<subject id="Tom">
   <relation unit="ITSupport" role="ITSupporter" />
  </subject>

PHP

$xpath = new DOMXpath($doc);
            $role = 'ITSupporter';
           $elements = $xpath-> query("//subject/@id[../relation/@role='".$role."']");              
           foreach ($elements as $element) {    
              $name = $element -> nodeValue;
              $arr[$i] = $name;
              $i = $i + 1;
           }    

How can I get the id TOM? I want to save it to for example $var


Solution

  • Building up the Xpath expression:

    Additionally the source could be cleaned up. PHP arrays can use the $array[] syntax to push new elements into them.

    Put together:

    $xml = <<<'XML'
    <subject id="Tom">
      <relation unit="ITSupport" role="ITSupporter" />
    </subject>
    XML;
    
    $role = 'ITSupporter';
    
    $document = new DOMDocument();
    $document->loadXML($xml);
    $xpath = new DOMXpath($document);
    
    $ids = [];
    foreach ($xpath->evaluate("//subject[relation/@role='".$role."']/@id") as $idAttribute) {
      $ids[] = $idAttribute->value;
    }
    var_dump($ids);
    

    Output:

    array(1) { 
      [0]=> 
      string(3) "Tom" 
    }
    

    If you expect only a single result you can cast the it in Xpath:

    $id = $xpath->evaluate(
      "string(//subject[relation/@role='".$role."']/@id)"
    );
    var_dump($id);
    

    Output:

    string(3) "Tom"
    

    XML Namespaces

    Looking at the example posted in the comment your XML uses the namespace http://cpee.org/ns/organisation/1.0 without a prefix. The XML parser will resolve it so you can read the nodes as {http://cpee.org/ns/organisation/1.0}subject. Here are 3 examples that all resolve to this:

    The same has to happen for the Xpath expression. However Xpath does not have a default namespace. You need to register an use an prefix of your choosing. This allows the Xpath engine to resolve something like //org:subject to //{http://cpee.org/ns/organisation/1.0}subject.

    The PHP does not need to change much:

    $xml = <<<'XML'
    <subject id="Tom" xmlns="http://cpee.org/ns/organisation/1.0">
      <relation unit="ITSupport" role="ITSupporter" />
    </subject>
    XML;
    
    $role = 'ITSupporter';
    
    $document = new DOMDocument();
    $document->loadXML($xml);
    $xpath = new DOMXpath($document);
    // register a prefix for the namespace
    $xpath->registerNamespace('org', 'http://cpee.org/ns/organisation/1.0');
    
    $ids = [];
    // address the elements using the registered prefix
    $idAttributes = $xpath->evaluate("//org:subject[org:relation/@role='".$role."']/@id");
    foreach ($idAttributes as $idAttribute) {
      $ids[] = $idAttribute->value;
    }
    var_dump($ids);