Adding currency conversion to Zend_Currency

Currency

I recently needed to do currency conversion for a Zend Framework project, so a naturally turned to Zend_Currency. Sadly, Zend_Currency doesn’t feature currency conversion, rather it focuses on the localization aspects of currency. This is perfectly understandable as offering conversion would make this component dependant on potentially unreliable third parties that Zend would not be able to support.

But that aside, sometimes a project just needs some basic currency conversion. It generally doesn’t have to be up-to-the-minute information, and this is where the data source from the European Central Bank comes in useful. The ECB provides a simple XML data file for many common currencies, which is updated every day:

http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml

I figured this best way to use this would be to extend Zend_Currency to offer currency conversion. And here it is:

<?php
class OW_Currency extends Zend_Currency {

	/**
	 * URL to conversions provided by the European Central Bank
	 *
	 * This data is updated every 24 hours
	 *
	 * @var string
	 */
	protected $_conversionUrl = 'http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml';

	/**
	 * Convert a quantity from this currency to another
	 *
	 * @param string $toCurrency The three-characeter destination currency code
	 * @param float $quantity What value of this currency should be converted to $toCurrency
	 * @return float
	 */
	public function convert($toCurrency, $quantity) {
		$cache = self::getCache();
		if(!$cache) {
			$conversions = $this->getConversions();
		} else {
			//Cache the result for 24 hours if we can
			$cache = Zend_Cache::factory('Class', $cache->getBackend(), array('cached_entity' => $this, 'lifetime' => 60*60*24));
			$conversions = $cache->getConversions();
		}

		$fromCurrency = $this->getShortName();

		if(!isset($conversions[$fromCurrency]) && !isset($conversions[$toCurrency])) {
			return false;
		}

		$inEuros = $quantity / $conversions[$fromCurrency];
		$inDestinationCurrency = $conversions[$toCurrency] * $inEuros;

		return $inDestinationCurrency;
	}

	/**
	 * Get the conversions table
	 *
	 * @return array An array indexed by currency code. Values are the conversion rate against the Euro.
	 */
	public function getConversions() {
		$client = new Zend_Http_Client($this->_conversionUrl);
		$response = $client->request(Zend_Http_Client::GET);

		if($response->getStatus() != 200) {
			return array();
		}

		$xml = $response->getBody();

		$regex = '#[\'"]([A-Z]{3})[\'"].+[\'"]([0-9\.]+)[\'"]#';
		$matches = array();
		if(!preg_match_all($regex, $xml, $matches)) {
			return array();
		}

		$conversions = array_combine($matches[1], $matches[2]);
		$conversions['EUR'] = 1;

		foreach ($conversions as $k => $v) {
			$conversions[$k] = (float)$v;
		}

		return $conversions;
	}
}

The above class will use Zend_Currency’s cache (if you have set it) to cache the data for 24 hours.

Example usage:

$from = 'GBP';
$to = 'USD';
$britishPounds = 100;

$currency = new OW_Currency($from);
$usDollars = $currency->convert($to, $britishPounds);

2 comments so far

  1. tuzvbhi on

    Even if don’t use the currency-functionality of the zf i have to admit, that this class is quite useful and should be part of the normal Zend_Currency-class (perhaps with the ability to pass an cached object of the currency-list

    • Adam Charnock on

      Hey, I’m pleased you liked it. It would be great if it was included in ZF, but I think the dependancy on the ECB data source would need to be moved outside of Zend_Currency. Maybe some sort of Zend_Currency_Provider_Ecb type of arrangement?


Leave a reply