Search code examples
phpxpathsimplexml

php xpath returns Array(0)


I read the following XML: http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml

controller:

$ECB_rates = array();
$currencies = explode(',', 'GBP,USD,RUB,AUD');
foreach($currencies as $currency) {
    $ECB_rates[$currency] = $ECB_XML->xpath('//Cube/Cube/Cube[@currency="' . $currency . '"]/@rate');
}
$this->set('ECB_rates', $ECB_rates);

view:

var_dump($ECB_rates);

and I get the following:

array(4) { ["GBP"]=> array(0) { } ["USD"]=> array(0) { } ["RUB"]=> array(0) { } ["AUD"]=> array(0) { } } 

I can't figure out why the rates are returned as empty array.


Solution

  • The document has a default namespace, which can commonly confuse XPath expressions if not done correctly. The other part is that xpath() returns an array of SimpleXMLElements which also doesn't help your cause.

    The following code first registers the default namespace with the prefix 'def' and then (I've simplified the expression as well) uses this as a prefix to find the <Cube> element with the currency you want. Then it takes the first result (there should only be one anyway) and casts it to a string to make it more useful.

    $ECB_XML->registerXPathNamespace("def", "http://www.ecb.int/vocabulary/2002-08-01/eurofxref");
    $ECB_rates = array();
    $currencies = explode(',', 'GBP,USD,RUB,AUD');
    foreach($currencies as $currency) {
        $ECB_rates[$currency] = (string)$ECB_XML->xpath('//def:Cube[@currency="' . $currency . '"]/@rate')[0];
    }
    var_dump($ECB_rates);
    

    Which gives...

    array(4) {
      'GBP' =>
      string(7) "0.87295"
      'USD' =>
      string(6) "1.2234"
      'RUB' =>
      string(7) "70.8270"
      'AUD' =>
      string(6) "1.5934"
    }
    

    Update:

    You could (if the XML format is stable) just use SimpleXML element/attribute access. Checking if the currency is in the array of currencies your after...

    foreach($ECB_XML->Cube->Cube->Cube as $rate) {
        $currency = (string)$rate["currency"];
        if ( in_array($currency, $currencies))   {
            $ECB_rates[$currency] = (string)$rate["rate"];
        }
    }
    

    This may give you the items in a different order, but this might not be an issue.