Automated Exchange Rates for your web application

September 7th 2013

Every developer knows that when it comes to e-commerce, getting exchange rates to work as expected is the biggest hurdle.

Thankfully, the ECB (European Central Bank) has a great little XML feed (would prefer JSON) to give you the most recent exchange rates.

I've slapped together this code for some calculations I need to make, and figured it might be useful.

PHP Code:
<?php
/**
 * EcbExchangeRates
 *
 * @author      Roger Thomas <roger.thomas@rogerethomas.com>
 * @copyright   2013 Roger Thomas
 * @link        http://www.rogerethomas.com
 * @license     MIT License
 * @version     1.0.0
 * @package     Commerce
 *
 * MIT LICENSE
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
class Commerce_Currency_EcbExchangeRates
{
    
/**
     * Retrieve the most recent Exchange Rates from the European Central Bank
     *
     * @return array|boolean false on failure.
     */
    
public static function getRates()
    {
        try {
            
$client = new Zend_Http_Client('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml');
            
$response $client->request(Zend_Http_Client::GET);

            if (
$response->extractCode($response) != 200) {
                return 
false;
            }

            
$body $response->getBody();
            
$domDocument = new DomDocument();
            
$validXml $domDocument->loadXML(
                
$response->getBody()
            );
            if (!
$validXml) {

                return 
false;
            }

            
$currencies = array();
            
$cube $domDocument->getElementsByTagName("Cube");

            foreach (
$cube as $singleNode) {

                
/* @var $singleNode DOMElement */
                
$name $singleNode->getAttribute('currency'); // USD, GBP etc
                
$rate $singleNode->getAttribute('rate'); // 0.81220, 1.6412 etc

                
if (!self::validCurrencyName($name) || !self::validRate($rate)) {
                    continue; 
// skip due to failed validation.
                
}

                
$name strtoupper($name);
                
$currencies[$name] = (float) $rate;
            }

            if (!empty(
$currencies)) {
                return 
$currencies;
            }

        } catch (
Exception $e) {

        }

        return 
false;
    }

    
/**
     * Validate a currency name is valid based solely on length.
     * @param string $name
     * @return boolean
     */
    
protected static function validCurrencyName($name)
    {
        return (
strlen($name) == 3);
    }

    
/**
     * Validate a rate is a valid float. Passed value will be a string.
     * @param string $rate
     * @return boolean
     */
    
protected static function validRate($rate)
    {
        if (empty(
$rate)) {
            return 
false;
        }
        
$validator = new Zend_Validate_Float();

        return 
$validator->isValid($rate);
    }
}

This particular class is used in a Zend Framework application, so if you're using it in less awesome framework, you'll need to make some changes.

The output is quite easy. You'll either get an array, or boolean false. It'd be worth caching it though. I'm relatively certain that hammering the ECB's servers is a bad idea.

For example:

Call the exchange rates like this:

PHP Code:
<?php
Commerce_Currency_EcbExchangeRates
::getRates()

And you should get back something like...

Text Snippet:
Array
(
    [USD] => 1.3117
    [JPY] => 130.71
    [BGN] => 1.9558
    [CZK] => 25.765
    [DKK] => 7.4588
    [GBP] => 0.842
    [HUF] => 300.67
    [LTL] => 3.4528
    [LVL] => 0.7026
    [PLN] => 4.295
    [RON] => 4.4735
    [SEK] => 8.7248
    [CHF] => 1.2382
    [NOK] => 8.002
    [HRK] => 7.5878
    [RUB] => 43.826
    [TRY] => 2.7031
    [AUD] => 1.4309
    [BRL] => 3.0395
    [CAD] => 1.3682
    [CNY] => 8.0269
    [HKD] => 10.1732
    [IDR] => 15161.44
    [ILS] => 4.7862
    [INR] => 85.582
    [KRW] => 1433.5
    [MXN] => 17.4784
    [MYR] => 4.3666
    [NZD] => 1.6505
    [PHP] => 58.328
    [SGD] => 1.677
    [THB] => 42.44
    [ZAR] => 13.3552
)

;)