source: trunk/lib/Currency.inc.php

Last change on this file was 746, checked in by anonymous, 3 years ago

Add brackets to avoid ambiguity in return value

File size: 11.0 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/**
24 * Currency.inc.php
25 *
26 * Class to convert currency values.
27 *
28 * @author  Quinn Comendant <quinn@strangecode.com>
29 * @version 1.5
30 *
31 * Example of use:
32---------------------------------------------------------------------
33$currency = new Currency();
34$currency->setParam(array('cache_dir' => COMMON_BASE . '/tmp/xcache'));
35echo $currency->getRage('eur', 'usd');
36echo $currency->getValue(125.50, 'eur', 'usd');
37---------------------------------------------------------------------
38 */
39
40class Currency
41{
42    // Configuration parameters for this object.
43    protected $_params = array(
44        'cache_result' => true,
45        'cache_dir' => '',
46        'cache_age' => 86400, // 24 hours.
47        'api' => 'ecb', // 'xurrency' or 'ecb'. Add other APIs under the _performAPICall() method.
48        'api_key' => '', // Used only by xurrency API.
49    );
50
51    // Static runtime cache of values.
52    private static $rates = array();
53
54    /**
55     * Cart constructor.
56     */
57    public function __construct($params=array())
58    {
59        $app =& App::getInstance();
60
61        // Set custom parameters.
62        $this->setParam($params);
63
64        // Setup cache directory.
65        if ($this->getParam('cache_result')) {
66            if ('' == $this->getParam('cache_dir')) {
67                // Use a sane default cache directory.
68                $this->setParam(array('cache_dir' => '/tmp/xcache_' . md5(COMMON_BASE)));
69            }
70            if (!is_dir($this->getParam('cache_dir'))) {
71                $app->logMsg(sprintf('Creating cache_dir: %s', $this->getParam('cache_dir')), LOG_INFO, __FILE__, __LINE__);
72                if (!mkdir($this->getParam('cache_dir'))) {
73                    $app->logMsg(sprintf('Could not create cache_dir: %s', $this->getParam('cache_dir')), LOG_WARNING, __FILE__, __LINE__);
74                }
75            }
76        }
77    }
78
79    /**
80     * Set the params of this object.
81     *
82     * @param  array $params   Array of param keys and values to set.
83     */
84    public function setParam($params=null)
85    {
86        if (isset($params) && is_array($params)) {
87            // Merge new parameters with old overriding only those passed.
88            $this->_params = array_merge($this->_params, $params);
89        }
90    }
91
92    /**
93     * Return the value of a parameter, if it exists.
94     *
95     * @access public
96     * @param string $param        Which parameter to return.
97     * @return mixed               Configured parameter value.
98     */
99    public function getParam($param)
100    {
101        $app =& App::getInstance();
102
103        if (array_key_exists($param, $this->_params)) {
104            return $this->_params[$param];
105        } else {
106            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_DEBUG, __FILE__, __LINE__);
107            return null;
108        }
109    }
110
111    /*
112    * Return the exchange value between the two given currencies for given amount.
113    *
114    * @access   public
115    * @param    float   $amount Base amount to convert from.
116    * @param    string  $base   3-letter currency code to convert from.
117    * @param    string  $target 3-letter currency code to convert to.
118    * @return   mixed   Float converted currency value, or false on error.
119    * @author   Quinn Comendant <quinn@strangecode.com>
120    * @version  1.0
121    * @since    05 May 2008 23:50:59
122    */
123    public function getValue($amount, $base, $target)
124    {
125        if (false !== ($rate = $this->getRate($base, $target))) {
126            return abs($rate * $amount);
127        } else {
128            return false;
129        }
130    }
131
132    /*
133    * Return the currency conversion rate as a ratio.
134    *
135    * @access   public
136    * @param    string  $base   3-letter currency code to convert from.
137    * @param    string  $target 3-letter currency code to convert to.
138    * @return   mixed   Float exchange rate value, or false on error.
139    * @author   Quinn Comendant <quinn@strangecode.com>
140    * @version  1.0
141    * @since    25 May 2011 01:26:24
142    */
143    public function getRate($base, $target)
144    {
145        $app =& App::getInstance();
146
147        // If we've looked-up this rate during this run, return it from the runtime cache.
148        $rate_key = sprintf('%s-to-%s', $base, $target);
149        if (isset(self::$rates[$rate_key])) {
150            $app->logMsg(sprintf('Found %s in runtime cache: %s', $rate_key, self::$rates[$rate_key]), LOG_DEBUG, __FILE__, __LINE__);
151            return self::$rates[$rate_key];
152        }
153
154        $cache_file_path = sprintf('%s/%s-to-%s', $this->getParam('cache_dir'), $base, $target);
155        if (!is_dir(dirname($cache_file_path))) {
156            mkdir(dirname($cache_file_path));
157        }
158        $cache_file_mtime = @filemtime($cache_file_path);
159        if (!$this->getParam('cache_result') || !$cache_file_mtime || $cache_file_mtime < time() - $this->getParam('cache_age')) {
160            // Get fresh data and create cached file if missing or expired.
161            $app->logMsg(sprintf('Getting fresh currency exchange rate: %s-to-%s', $base, $target), LOG_DEBUG, __FILE__, __LINE__);
162            $value = $this->_performAPICall(array(
163                'amount' => '1',
164                'base' => $base,
165                'target' => $target
166            ));
167            if (false === $value || !is_numeric($value)) {
168                // Failed retrieving value. Use cached copy for now.
169                $app->logMsg(sprintf('Failed getting currency exchange rate: %s-to-%s, using cached copy', $base, $target), LOG_NOTICE, __FILE__, __LINE__);
170                if (!$value = @file_get_contents($cache_file_path)) {
171                    $app->logMsg(sprintf('Failed reading cached exchange rate file: %s', $cache_file_path), LOG_ERR, __FILE__, __LINE__);
172                    return false;
173                }
174            } else if ($this->getParam('cache_result') && !filePutContents($cache_file_path, $value)) {
175                $app->logMsg(sprintf('Failed writing to target rate file: %s', $cache_file_path), LOG_ERR, __FILE__, __LINE__);
176                return false;
177            }
178        } else {
179            $app->logMsg(sprintf('Getting cached currency exchange rate: %s-to-%s', $base, $target), LOG_DEBUG, __FILE__, __LINE__);
180            if (!$value = file_get_contents($cache_file_path)) {
181                $app->logMsg(sprintf('Failed reading target rate file: %s', $cache_file_path), LOG_ERR, __FILE__, __LINE__);
182                return false;
183            }
184        }
185        $app->logMsg(sprintf('Found currency exchange rate: %s-to-%s = %s', $base, $target, $value), LOG_DEBUG, __FILE__, __LINE__);
186
187        // Store the value in the runtime cache.
188        self::$rates[$rate_key] = trim($value);
189
190        return trim($value);
191    }
192
193    /**
194     * @param  string
195     * @param  array
196     * @return mixed
197     * @access private
198     */
199    protected function _performAPICall($parameters=null)
200    {
201        $app =& App::getInstance();
202
203        switch ($this->getParam('api')) {
204        case 'xurrency' :
205            $api_url = 'http://xurrency.com/api/%s/%s/%s';
206            if ('' != $this->getParam('api_key')) {
207                $api_url .= '?key=' . $this->getParam('api_key');
208            }
209            $json_response = file_get_contents(sprintf($api_url, $parameters['base'], $parameters['target'], $parameters['amount']));
210            $json = json_decode($json_response);
211            if (null === $json) {
212                $app->logMsg(sprintf('Could not decode JSON response: %s', getDump($json_response)), LOG_WARNING, __FILE__, __LINE__);
213                return false;
214            } else if ($json->status === 'fail') {
215                if ($json->code == 3) {
216                    $app->logMsg(sprintf('Xurrency error LimitReachedException: %s', $json->message), LOG_WARNING, __FILE__, __LINE__);
217                } elseif ($json->code == 2) {
218                    $app->logMsg(sprintf('Xurrency error InvalidCurrencies: %s', $json->message), LOG_WARNING, __FILE__, __LINE__);
219                } elseif ($json->code == 4 || $json->code == 5) {
220                    $app->logMsg(sprintf('Xurrency error InvalidKey: %s', $json->message), LOG_WARNING, __FILE__, __LINE__);
221                } else {
222                    $app->logMsg(sprintf('Xurrency unknown error: %s', $json->message), LOG_WARNING, __FILE__, __LINE__);
223                }
224                return false;
225            } else {
226                return $json->result->value;
227            }
228            break;
229
230        case 'ecb' :
231            // Fetch XML from ECB <http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml>
232            $api_url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';
233            if (false === ($sXML = file_get_contents($api_url))) {
234                $app->logMsg(sprintf('Failed to load ECB XML data from: %s', $api_url), LOG_WARNING, __FILE__, __LINE__);
235                return false;
236            }
237            if (false === ($oXML = simplexml_load_string($sXML))) {
238                $app->logMsg(sprintf('Failed to decode ECB XML data: %s', truncate($sXML, 200, 'end')), LOG_WARNING, __FILE__, __LINE__);
239                return false;
240            }
241            // Populate Array
242            foreach ($oXML->Cube->Cube->Cube as $oRate) {
243                foreach ($oRate->attributes() as $sKey => $sAttribute) {
244                    if ($sKey == 'currency') {
245                        $sCurrency = strtolower((string) $sAttribute);
246                    } else if ($sKey == 'rate') {
247                        $nRate = (string) $sAttribute;
248                    }
249                }
250                $aCurrencies['eur'][$sCurrency] = $nRate;
251                $aCurrencies[$sCurrency]['eur'] = 1 / $nRate;
252            }
253            // Check if requested rates are available.
254            if (isset($aCurrencies[$parameters['base']][$parameters['target']])) {
255                return (float) $aCurrencies[$parameters['base']][$parameters['target']];
256            } else {
257                $app->logMsg(sprintf('API %s does not have base %s or target %s', $this->getParam('api'), $parameters['base'], $parameters['target']), LOG_ALERT, __FILE__, __LINE__);
258                return false;
259            }
260
261        default :
262            $app->logMsg(sprintf('Unknown currency API: %s', $this->getParam('api')), LOG_ERR, __FILE__, __LINE__);
263            return false;
264        }
265    }
266}
267
268
Note: See TracBrowser for help on using the repository browser.