source: trunk/lib/Currency.inc.php @ 720

Last change on this file since 720 was 550, checked in by anonymous, 8 years ago

Escaped quotes from email from names.
Changed logMsg string truncation method and added version to email log msg.
Better variable testing in carry queries.
Spelling errors.
Added runtime cache to Currency.
Added logging to form validation.
More robust form validation.
Added json serialization methond to Version.

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.