* Copyright 2001-2012 Strangecode, LLC * * This file is part of The Strangecode Codebase. * * The Strangecode Codebase is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your option) * any later version. * * The Strangecode Codebase is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * The Strangecode Codebase. If not, see . */ /** * Currency.inc.php * * Class to convert currency values. * * @author Quinn Comendant * @version 1.5 * * Example of use: --------------------------------------------------------------------- $currency = new Currency(); $currency->setParam(array('cache_dir' => COMMON_BASE . '/tmp/xcache')); echo $currency->getRage('eur', 'usd'); echo $currency->getValue(125.50, 'eur', 'usd'); --------------------------------------------------------------------- */ class Currency { // Configuration parameters for this object. protected $_params = array( 'cache_result' => true, 'cache_dir' => '', 'cache_age' => 86400, // 24 hours. 'api' => 'ecb', // 'xurrency' or 'ecb'. Add other APIs under the _performAPICall() method. 'api_key' => '', // Used only by xurrency API. ); // Static runtime cache of values. private static $rates = array(); /** * Cart constructor. */ public function __construct($params=array()) { $app =& App::getInstance(); // Set custom parameters. $this->setParam($params); // Setup cache directory. if ($this->getParam('cache_result')) { if ('' == $this->getParam('cache_dir')) { // Use a sane default cache directory. $this->setParam(array('cache_dir' => '/tmp/xcache_' . md5(COMMON_BASE))); } if (!is_dir($this->getParam('cache_dir'))) { $app->logMsg(sprintf('Creating cache_dir: %s', $this->getParam('cache_dir')), LOG_INFO, __FILE__, __LINE__); if (!mkdir($this->getParam('cache_dir'))) { $app->logMsg(sprintf('Could not create cache_dir: %s', $this->getParam('cache_dir')), LOG_WARNING, __FILE__, __LINE__); } } } } /** * Set the params of this object. * * @param array $params Array of param keys and values to set. */ public function setParam($params=null) { if (isset($params) && is_array($params)) { // Merge new parameters with old overriding only those passed. $this->_params = array_merge($this->_params, $params); } } /** * Return the value of a parameter, if it exists. * * @access public * @param string $param Which parameter to return. * @return mixed Configured parameter value. */ public function getParam($param) { $app =& App::getInstance(); if (array_key_exists($param, $this->_params)) { return $this->_params[$param]; } else { $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__); return null; } } /* * Return the exchange value between the two given currencies for given amount. * * @access public * @param float $amount Base amount to convert from. * @param string $base 3-letter currency code to convert from. * @param string $target 3-letter currency code to convert to. * @return mixed Float converted currency value, or false on error. * @author Quinn Comendant * @version 1.0 * @since 05 May 2008 23:50:59 */ public function getValue($amount, $base, $target) { if (false !== ($rate = $this->getRate($base, $target))) { return abs($rate * $amount); } else { return false; } } /* * Return the currency conversion rate as a ratio. * * @access public * @param string $base 3-letter currency code to convert from. * @param string $target 3-letter currency code to convert to. * @return mixed Float exchange rate value, or false on error. * @author Quinn Comendant * @version 1.0 * @since 25 May 2011 01:26:24 */ public function getRate($base, $target) { $app =& App::getInstance(); // If we've looked-up this rate during this run, return it from the runtime cache. $rate_key = sprintf('%s-to-%s', $base, $target); if (isset(self::$rates[$rate_key])) { $app->logMsg(sprintf('Found %s in runtime cache: %s', $rate_key, self::$rates[$rate_key]), LOG_DEBUG, __FILE__, __LINE__); return self::$rates[$rate_key]; } $cache_file_path = sprintf('%s/%s-to-%s', $this->getParam('cache_dir'), $base, $target); if (!is_dir(dirname($cache_file_path))) { mkdir(dirname($cache_file_path)); } $cache_file_mtime = @filemtime($cache_file_path); if (!$this->getParam('cache_result') || !$cache_file_mtime || $cache_file_mtime < time() - $this->getParam('cache_age')) { // Get fresh data and create cached file if missing or expired. $app->logMsg(sprintf('Getting fresh currency exchange rate: %s-to-%s', $base, $target), LOG_DEBUG, __FILE__, __LINE__); $value = $this->_performAPICall(array( 'amount' => '1', 'base' => $base, 'target' => $target )); if (false === $value || !is_numeric($value)) { // Failed retrieving value. Use cached copy for now. $app->logMsg(sprintf('Failed getting currency exchange rate: %s-to-%s, using cached copy', $base, $target), LOG_NOTICE, __FILE__, __LINE__); if (!$value = @file_get_contents($cache_file_path)) { $app->logMsg(sprintf('Failed reading cached exchange rate file: %s', $cache_file_path), LOG_ERR, __FILE__, __LINE__); return false; } } else if ($this->getParam('cache_result') && !filePutContents($cache_file_path, $value)) { $app->logMsg(sprintf('Failed writing to target rate file: %s', $cache_file_path), LOG_ERR, __FILE__, __LINE__); return false; } } else { $app->logMsg(sprintf('Getting cached currency exchange rate: %s-to-%s', $base, $target), LOG_DEBUG, __FILE__, __LINE__); if (!$value = file_get_contents($cache_file_path)) { $app->logMsg(sprintf('Failed reading target rate file: %s', $cache_file_path), LOG_ERR, __FILE__, __LINE__); return false; } } $app->logMsg(sprintf('Found currency exchange rate: %s-to-%s = %s', $base, $target, $value), LOG_DEBUG, __FILE__, __LINE__); // Store the value in the runtime cache. self::$rates[$rate_key] = trim($value); return trim($value); } /** * @param string * @param array * @return mixed * @access private */ protected function _performAPICall($parameters=null) { $app =& App::getInstance(); switch ($this->getParam('api')) { case 'xurrency' : $api_url = 'http://xurrency.com/api/%s/%s/%s'; if ('' != $this->getParam('api_key')) { $api_url .= '?key=' . $this->getParam('api_key'); } $json_response = file_get_contents(sprintf($api_url, $parameters['base'], $parameters['target'], $parameters['amount'])); $json = json_decode($json_response); if (null === $json) { $app->logMsg(sprintf('Could not decode JSON response: %s', getDump($json_response)), LOG_WARNING, __FILE__, __LINE__); return false; } else if ($json->status === 'fail') { if ($json->code == 3) { $app->logMsg(sprintf('Xurrency error LimitReachedException: %s', $json->message), LOG_WARNING, __FILE__, __LINE__); } elseif ($json->code == 2) { $app->logMsg(sprintf('Xurrency error InvalidCurrencies: %s', $json->message), LOG_WARNING, __FILE__, __LINE__); } elseif ($json->code == 4 || $json->code == 5) { $app->logMsg(sprintf('Xurrency error InvalidKey: %s', $json->message), LOG_WARNING, __FILE__, __LINE__); } else { $app->logMsg(sprintf('Xurrency unknown error: %s', $json->message), LOG_WARNING, __FILE__, __LINE__); } return false; } else { return $json->result->value; } break; case 'ecb' : // Fetch XML from ECB $api_url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'; if (false === ($sXML = file_get_contents($api_url))) { $app->logMsg(sprintf('Failed to load ECB XML data from: %s', $api_url), LOG_WARNING, __FILE__, __LINE__); return false; } if (false === ($oXML = simplexml_load_string($sXML))) { $app->logMsg(sprintf('Failed to decode ECB XML data: %s', truncate($sXML, 200, 'end')), LOG_WARNING, __FILE__, __LINE__); return false; } // Populate Array foreach ($oXML->Cube->Cube->Cube as $oRate) { foreach ($oRate->attributes() as $sKey => $sAttribute) { if ($sKey == 'currency') { $sCurrency = strtolower((string) $sAttribute); } else if ($sKey == 'rate') { $nRate = (string) $sAttribute; } } $aCurrencies['eur'][$sCurrency] = $nRate; $aCurrencies[$sCurrency]['eur'] = 1 / $nRate; } // Check if requested rates are available. if (isset($aCurrencies[$parameters['base']][$parameters['target']])) { return (float) $aCurrencies[$parameters['base']][$parameters['target']]; } else { $app->logMsg(sprintf('API %s does not have base %s or target %s', $this->getParam('api'), $parameters['base'], $parameters['target']), LOG_ALERT, __FILE__, __LINE__); return false; } default : $app->logMsg(sprintf('Unknown currency API: %s', $this->getParam('api')), LOG_ERR, __FILE__, __LINE__); return false; } } }