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); |
|||
0 ignored issues
–
show
|
|||||
90 | 16 | $this->apiKey = $this->getConfig('apikey'); |
|||
91 | |||||
92 | 16 | $this->currencyConverter = new CurrencyConverter($this->apiKey); |
|||
0 ignored issues
–
show
It seems like
$this->apiKey can also be of type null ; however, parameter $apiKey of CurrencyConverter\CurrencyConverter::__construct() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
93 | |||||
94 | 16 | $this->session = $this->getView()->getRequest()->getSession(); |
|||
95 | 16 | $this->currencyratesTable = TableRegistry::get('CurrencyConverter.Currencyrates'); |
|||
0 ignored issues
–
show
The function
Cake\ORM\TableRegistry::get() has been deprecated: 3.6.0 Use \Cake\ORM\Locator\TableLocator::get() instead.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||
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); |
|||
0 ignored issues
–
show
|
|||||
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); |
|||
0 ignored issues
–
show
$number of type string is incompatible with the type double expected by parameter $number of number_format() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
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']; |
|||
0 ignored issues
–
show
|
|||||
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); |
|||
0 ignored issues
–
show
It seems like
$result can also be of type array ; however, parameter $entity of CurrencyConverter\View\H...::_storeRateInSession() does only seem to accept Cake\ORM\Entity , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
217 | } else { |
||||
218 | 4 | $rate = $this->_getRateFromAPI($from, $to); |
|||
219 | 4 | if ($rate) { |
|||
0 ignored issues
–
show
The expression
$rate of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||||
220 | 4 | $result->rate = $rate; |
|||
0 ignored issues
–
show
|
|||||
221 | 4 | $this->currencyratesTable->save($result); |
|||
0 ignored issues
–
show
It seems like
$result can also be of type array ; however, parameter $entity of Cake\ORM\Table::save() does only seem to accept Cake\Datasource\EntityInterface , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
222 | 6 | $this->_storeRateInSession($result); |
|||
223 | } |
||||
224 | } |
||||
225 | } else { |
||||
226 | 2 | $rate = $this->_getRateFromAPI($from, $to); |
|||
227 | 2 | if ($rate) { |
|||
0 ignored issues
–
show
The expression
$rate of type integer|null is loosely compared to true ; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For 0 == false // true
0 == null // true
123 == false // false
123 == null // false
// It is often better to use strict comparison
0 === false // false
0 === null // false
![]() |
|||||
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.