1
|
|
|
<?php |
2
|
|
|
namespace CurrencyConverter\View\Helper; |
3
|
|
|
|
4
|
|
|
use Cake\View\Helper; |
5
|
|
|
use Cake\ORM\TableRegistry; |
6
|
|
|
use Cake\I18n\Time; |
7
|
|
|
use CurrencyConverter\CurrencyConverter; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* @property \Cake\View\Helper\HtmlHelper $Html |
11
|
|
|
* @property \Cake\View\Helper\FormHelper $Form |
12
|
|
|
* @property \Cake\View\Helper\NumberHelper $Number |
13
|
|
|
*/ |
14
|
|
|
class CurrencyConverterHelper extends Helper |
15
|
|
|
{ |
16
|
|
|
/** |
17
|
|
|
* Using database |
18
|
|
|
* |
19
|
|
|
* @var bool |
20
|
|
|
*/ |
21
|
|
|
private $database; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Time interval for refreshing database |
25
|
|
|
* |
26
|
|
|
* @var int |
27
|
|
|
*/ |
28
|
|
|
private $refresh; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Number of decimal to use for formatting converted price |
32
|
|
|
* |
33
|
|
|
* @var int |
34
|
|
|
*/ |
35
|
|
|
private $decimal; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Number to divise 1 and get the sup step to round price to |
39
|
|
|
* |
40
|
|
|
* @var float |
41
|
|
|
*/ |
42
|
|
|
private $round; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Session |
46
|
|
|
* |
47
|
|
|
* @var \Cake\Http\Session |
48
|
|
|
*/ |
49
|
|
|
private $session; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* CurrencyratesTable Class |
53
|
|
|
* @var \Cake\ORM\Table |
54
|
|
|
*/ |
55
|
|
|
private $currencyratesTable; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Default CurrencyConverterComponent settings. |
59
|
|
|
* |
60
|
|
|
* When calling CurrencyConverterComponent() these settings will be merged with the configuration |
61
|
|
|
* you provide. |
62
|
|
|
* |
63
|
|
|
* - `database` - Mention if Component have to store currency rate in database |
64
|
|
|
* - `refresh` - Time interval for Component to refresh currency rate in database |
65
|
|
|
* - `decimal` - Number of decimal to use when formatting amount float number |
66
|
|
|
* - `round` - Number to divise 1 and get the sup step to round price to (eg: 4 for 0.25 step) |
67
|
|
|
* |
68
|
|
|
* @var array |
69
|
|
|
*/ |
70
|
|
|
protected $_defaultConfig = [ |
71
|
|
|
'database' => true, |
72
|
|
|
'refresh' => 24, |
73
|
|
|
'decimal' => 2, |
74
|
|
|
'round' => false, |
75
|
|
|
'apikey' => '', |
76
|
|
|
]; |
77
|
|
|
|
78
|
|
|
private $apiKey; |
79
|
|
|
private $currencyConverter; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @param array $config |
83
|
|
|
* @return void |
84
|
|
|
*/ |
85
|
16 |
|
public function initialize(array $config = []) { |
86
|
16 |
|
$this->database = $this->getConfig('database'); |
87
|
16 |
|
$this->refresh = $this->getConfig('refresh'); |
88
|
16 |
|
$this->decimal = $this->getConfig('decimal'); |
89
|
16 |
|
$this->round = ($this->getConfig('round') !== 0 ? $this->getConfig('round') : false); |
|
|
|
|
90
|
16 |
|
$this->apiKey = $this->getConfig('apikey'); |
91
|
|
|
|
92
|
16 |
|
$this->currencyConverter = new CurrencyConverter($this->apiKey); |
|
|
|
|
93
|
|
|
|
94
|
16 |
|
$this->session = $this->getView()->getRequest()->getSession(); |
95
|
16 |
|
$this->currencyratesTable = TableRegistry::get('CurrencyConverter.Currencyrates'); |
|
|
|
|
96
|
16 |
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Convert method take an amount as first parameter and convert it using $from currency and $to currency. |
100
|
|
|
* |
101
|
|
|
* @param float|string $amount the amount to convert. |
102
|
|
|
* @param string $from currency to convert from |
103
|
|
|
* @param string $to currency to convert to |
104
|
|
|
* @return string $amount converted |
105
|
|
|
*/ |
106
|
9 |
|
public function convert($amount, $from, $to) |
107
|
|
|
{ |
108
|
9 |
|
if (!isset($this->apiKey)) { |
109
|
|
|
throw new \Exception('Api Key not found'); |
110
|
|
|
} |
111
|
|
|
|
112
|
9 |
|
$amount = floatval($amount); |
113
|
9 |
|
$rate = $this->_getRateToUse($from, $to); |
114
|
|
|
|
115
|
9 |
|
return $convert = $this->_formatConvert($rate * $amount); |
|
|
|
|
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Rate method return the rate of two currencies |
120
|
|
|
* |
121
|
|
|
* @param string $from currency to get the rate from |
122
|
|
|
* @param string $to currency to get the rate to |
123
|
|
|
* @return float|null $rate |
124
|
|
|
*/ |
125
|
6 |
|
public function rate($from, $to) |
126
|
|
|
{ |
127
|
6 |
|
return $this->_getRateToUse($from, $to); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* getRateToUse return rate to use |
132
|
|
|
* Using $from and $to parameters representing currency to deal with and the configuration settings |
133
|
|
|
* This method save or update currencyrates Table if necesseray too. |
134
|
|
|
* |
135
|
|
|
* @param string $from currency to get the rate from |
136
|
|
|
* @param string $to currency to get the rate to |
137
|
|
|
* @return float|null $rate |
138
|
|
|
*/ |
139
|
15 |
|
private function _getRateToUse($from, $to) |
140
|
|
|
{ |
141
|
15 |
|
$rate = 1; |
142
|
15 |
|
if ($from !== $to) { |
143
|
12 |
|
if ($this->database) { |
144
|
10 |
|
$rate = $this->_getRateFromSession($from, $to); |
145
|
10 |
|
if (!$rate) { |
146
|
10 |
|
$rate = $this->_getRateFromDatabase($from, $to); |
147
|
|
|
} |
148
|
|
|
} else { |
149
|
2 |
|
$rate = $this->_getRateFromAPI($from, $to); |
150
|
|
|
} |
151
|
|
|
} |
152
|
|
|
|
153
|
15 |
|
return $rate; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Format number using configuration |
158
|
|
|
* |
159
|
|
|
* @param float number to format |
160
|
|
|
* @return string formatted number |
161
|
|
|
*/ |
162
|
9 |
|
private function _formatConvert($number) |
163
|
|
|
{ |
164
|
9 |
|
if ($this->round) { |
165
|
1 |
|
$n = floor($number); |
166
|
1 |
|
$fraction = ($number - $n); |
167
|
1 |
|
if ($fraction != 0) { |
168
|
1 |
|
$step = 1/$this->round; |
169
|
1 |
|
$decimal = (((int)($fraction/$step) + 1) * $step); |
170
|
1 |
|
$number = $n + $decimal; |
171
|
|
|
} |
172
|
|
|
} |
173
|
9 |
|
$number = number_format($number, $this->decimal); |
174
|
|
|
|
175
|
9 |
|
return number_format($number, $this->decimal); |
|
|
|
|
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Check session to see if rate exists in. |
180
|
|
|
* |
181
|
|
|
* @param string $from currency to get the rate from. |
182
|
|
|
* @param string $to currency to get the rate to. |
183
|
|
|
* @return float|null $rate. |
184
|
|
|
*/ |
185
|
10 |
|
private function _getRateFromSession($from, $to) |
186
|
|
|
{ |
187
|
10 |
|
$session = $this->session->read('CurrencyConverter.' . $from . '-' . $to); |
188
|
10 |
|
if ($session) { |
189
|
4 |
|
$modified = new Time($session['modified']); |
190
|
4 |
|
if ($modified->wasWithinLast($this->refresh . ' hours')) { |
191
|
2 |
|
return $rate = $session['rate']; |
|
|
|
|
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
|
195
|
8 |
|
return null; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Get a rate from database. |
200
|
|
|
* |
201
|
|
|
* It queries currencyratesTable and ... |
202
|
|
|
* if rate exists and has not to be modified, it returns this rate. |
203
|
|
|
* if rate exists and has to be modified, it call _getRateFromAPI method to get a fresh rate, then update in table and store in session this rate. |
204
|
|
|
* if rate does not exist, it call _getRateFromAPI to get a fresh rate, then create in table and store this rate. |
205
|
|
|
* |
206
|
|
|
* @param string $from currency to get the rate from |
207
|
|
|
* @param string $to currency to get the rate to |
208
|
|
|
* @return float|null $rate |
209
|
|
|
*/ |
210
|
8 |
|
private function _getRateFromDatabase($from, $to) |
211
|
|
|
{ |
212
|
8 |
|
$result = $this->currencyratesTable->find('all')->where(['from_currency' => $from, 'to_currency' => $to])->first(); |
213
|
8 |
|
if ($result) { |
214
|
6 |
|
if ($result->get('modified')->wasWithinLast($this->refresh . ' hours')) { |
215
|
2 |
|
$rate = $result->get('rate'); |
216
|
2 |
|
$this->_storeRateInSession($result); |
|
|
|
|
217
|
|
|
} else { |
218
|
4 |
|
$rate = $this->_getRateFromAPI($from, $to); |
219
|
4 |
|
if ($rate) { |
|
|
|
|
220
|
4 |
|
$result->rate = $rate; |
|
|
|
|
221
|
4 |
|
$this->currencyratesTable->save($result); |
|
|
|
|
222
|
6 |
|
$this->_storeRateInSession($result); |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
} else { |
226
|
2 |
|
$rate = $this->_getRateFromAPI($from, $to); |
227
|
2 |
|
if ($rate) { |
|
|
|
|
228
|
2 |
|
$result = $this->currencyratesTable->newEntity([ |
229
|
2 |
|
'from_currency' => $from, |
230
|
2 |
|
'to_currency' => $to, |
231
|
2 |
|
'rate' => $rate |
232
|
|
|
]); |
233
|
2 |
|
$this->currencyratesTable->save($result); |
234
|
2 |
|
$this->_storeRateInSession($result); |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
|
238
|
8 |
|
return $rate; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* Store in session a rate and his modified datetime |
243
|
|
|
* |
244
|
|
|
* @param \Cake\ORM\Entity $entity |
245
|
|
|
* @return void |
246
|
|
|
*/ |
247
|
8 |
|
private function _storeRateInSession($entity) |
248
|
|
|
{ |
249
|
8 |
|
$this->session->write('CurrencyConverter.' . $entity->get('from_currency') . '-' . $entity->get('to_currency'), [ |
250
|
8 |
|
'rate' => $entity->get('rate'), |
251
|
8 |
|
'modified' => $entity->get('modified')->i18nFormat('yyyy-MM-dd HH:mm:ss') |
252
|
|
|
]); |
253
|
8 |
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Call free.currencyconverterapi.com API to get a rate for one currency to an other one currency. |
257
|
|
|
* |
258
|
|
|
* @param string $from the currency. |
259
|
|
|
* @param string $to the currency. |
260
|
|
|
* @return int|null $rate. |
261
|
|
|
*/ |
262
|
8 |
|
private function _getRateFromAPI($from, $to) |
263
|
|
|
{ |
264
|
8 |
|
return $this->currencyConverter->getRates($from, $to); |
265
|
|
|
} |
266
|
|
|
|
267
|
11 |
|
public function setCurrencyConverter(CurrencyConverter $currencyConverter) |
268
|
|
|
{ |
269
|
11 |
|
$this->currencyConverter = $currencyConverter; |
270
|
11 |
|
} |
271
|
|
|
} |
272
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.