1 | <?php |
||||
2 | namespace Turahe\Master; |
||||
3 | |||||
4 | use Illuminate\Contracts\Cache\Factory as FactoryContract; |
||||
5 | use Illuminate\Support\Arr; |
||||
6 | |||||
7 | class Currency |
||||
8 | { |
||||
9 | /** |
||||
10 | * Currency configuration. |
||||
11 | * |
||||
12 | * @var array |
||||
13 | */ |
||||
14 | protected $config = []; |
||||
15 | |||||
16 | /** |
||||
17 | * Laravel application |
||||
18 | * |
||||
19 | * @var \Illuminate\Contracts\Cache\Factory |
||||
20 | */ |
||||
21 | protected $cache; |
||||
22 | |||||
23 | /** |
||||
24 | * User's currency |
||||
25 | * |
||||
26 | * @var string |
||||
27 | */ |
||||
28 | protected $user_currency; |
||||
29 | |||||
30 | /** |
||||
31 | * Currency driver instance. |
||||
32 | * |
||||
33 | * @var Contracts\DriverInterface |
||||
34 | */ |
||||
35 | protected $driver; |
||||
36 | |||||
37 | /** |
||||
38 | * Formatter instance. |
||||
39 | * |
||||
40 | * @var Contracts\FormatterInterface |
||||
41 | */ |
||||
42 | protected $formatter; |
||||
43 | |||||
44 | /** |
||||
45 | * Cached currencies |
||||
46 | * |
||||
47 | * @var array |
||||
48 | */ |
||||
49 | protected $currencies_cache; |
||||
50 | |||||
51 | /** |
||||
52 | * Create a new instance. |
||||
53 | * |
||||
54 | * @param array $config |
||||
55 | * @param FactoryContract $cache |
||||
56 | */ |
||||
57 | public function __construct(array $config, FactoryContract $cache) |
||||
58 | { |
||||
59 | $this->config = $config; |
||||
60 | $this->cache = $cache->store($this->config('cache_driver')); |
||||
0 ignored issues
–
show
|
|||||
61 | } |
||||
62 | |||||
63 | /** |
||||
64 | * Format given number. |
||||
65 | * |
||||
66 | * @param float $amount |
||||
67 | * @param string $from |
||||
68 | * @param string $to |
||||
69 | * @param bool $format |
||||
70 | * |
||||
71 | * @return null|string |
||||
72 | */ |
||||
73 | public function convert(float $amount, string $from = null, string $to = null, bool $format = true) |
||||
74 | { |
||||
75 | // Get currencies involved |
||||
76 | $from = $from ?: $this->config('default'); |
||||
77 | $to = $to ?: $this->getUserCurrency(); |
||||
78 | |||||
79 | // Get exchange rates |
||||
80 | (float)$from_rate = $this->getCurrencyProp($from, 'exchange_rate'); |
||||
81 | (float)$to_rate = $this->getCurrencyProp($to, 'exchange_rate'); |
||||
82 | |||||
83 | // Skip invalid to currency rates |
||||
84 | if ($to_rate === null) { |
||||
0 ignored issues
–
show
|
|||||
85 | return null; |
||||
86 | } |
||||
87 | |||||
88 | try { |
||||
89 | // Convert amount |
||||
90 | if ($from === $to) { |
||||
91 | $value = $amount; |
||||
92 | } else { |
||||
93 | $value = ($amount * $to_rate) / $from_rate; |
||||
94 | } |
||||
95 | } catch (\Exception $e) { |
||||
0 ignored issues
–
show
catch (\Exception $e) is not reachable.
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed. Unreachable code is most often the result of function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last ![]() |
|||||
96 | // Prevent invalid conversion or division by zero errors |
||||
97 | return null; |
||||
98 | } |
||||
99 | |||||
100 | // Should the result be formatted? |
||||
101 | if ($format === true) { |
||||
102 | return $this->format($value, $to); |
||||
103 | } |
||||
104 | |||||
105 | // Return value |
||||
106 | return $value; |
||||
107 | } |
||||
108 | |||||
109 | /** |
||||
110 | * Format the value into the desired currency. |
||||
111 | * |
||||
112 | * @param float $value |
||||
113 | * @param string $code |
||||
114 | * @param bool $include_symbol |
||||
115 | * |
||||
116 | * @return string |
||||
117 | */ |
||||
118 | public function format($value, $code = null, $include_symbol = true) |
||||
119 | { |
||||
120 | // Get default currency if one is not set |
||||
121 | $code = $code ?: $this->config('default'); |
||||
122 | |||||
123 | // Remove unnecessary characters |
||||
124 | $value = preg_replace('/[\s\',!]/', '', $value); |
||||
125 | |||||
126 | // Check for a custom formatter |
||||
127 | if ($formatter = $this->getFormatter()) { |
||||
128 | return $formatter->format($value, $code); |
||||
129 | } |
||||
130 | |||||
131 | // Get the measurement format |
||||
132 | $format = $this->getCurrencyProp($code, 'format'); |
||||
133 | |||||
134 | // Value Regex |
||||
135 | $valRegex = '/([0-9].*|)[0-9]/'; |
||||
136 | |||||
137 | // Match decimal and thousand separators |
||||
138 | preg_match_all('/[\s\',.!]/', $format, $separators); |
||||
139 | |||||
140 | if ($thousand = Arr::get($separators, '0.0', null)) { |
||||
141 | if ($thousand == '!') { |
||||
142 | $thousand = ''; |
||||
143 | } |
||||
144 | } |
||||
145 | |||||
146 | $decimal = Arr::get($separators, '0.1', null); |
||||
147 | |||||
148 | // Match format for decimals count |
||||
149 | preg_match($valRegex, $format, $valFormat); |
||||
0 ignored issues
–
show
$format of type array is incompatible with the type string expected by parameter $subject of preg_match() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
150 | |||||
151 | $valFormat = Arr::get($valFormat, 0, 0); |
||||
152 | |||||
153 | // Count decimals length |
||||
154 | $decimals = $decimal ? strlen(substr(strrchr($valFormat, $decimal), 1)) : 0; |
||||
155 | |||||
156 | // Do we have a negative value? |
||||
157 | if ($negative = $value < 0 ? '-' : '') { |
||||
158 | $value = $value * -1; |
||||
159 | } |
||||
160 | |||||
161 | // Format the value |
||||
162 | $value = number_format($value, $decimals, $decimal, $thousand); |
||||
163 | |||||
164 | // Apply the formatted measurement |
||||
165 | if ($include_symbol) { |
||||
166 | $value = preg_replace($valRegex, $value, $format); |
||||
167 | } |
||||
168 | |||||
169 | // Return value |
||||
170 | return $negative . $value; |
||||
0 ignored issues
–
show
Are you sure
$value of type string|string[] can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
171 | } |
||||
172 | |||||
173 | /** |
||||
174 | * Set user's currency. |
||||
175 | * |
||||
176 | * @param string $code |
||||
177 | */ |
||||
178 | public function setUserCurrency($code) |
||||
179 | { |
||||
180 | $this->user_currency = strtoupper($code); |
||||
181 | } |
||||
182 | |||||
183 | /** |
||||
184 | * Return the user's currency code. |
||||
185 | * |
||||
186 | * @return string |
||||
187 | */ |
||||
188 | public function getUserCurrency() |
||||
189 | { |
||||
190 | return $this->user_currency ?: $this->config('default'); |
||||
191 | } |
||||
192 | |||||
193 | /** |
||||
194 | * Determine if the provided currency is valid. |
||||
195 | * |
||||
196 | * @param string $code |
||||
197 | * |
||||
198 | * @return null|array |
||||
199 | */ |
||||
200 | public function hasCurrency($code) |
||||
201 | { |
||||
202 | return array_key_exists(strtoupper($code), $this->getCurrencies()); |
||||
203 | } |
||||
204 | |||||
205 | /** |
||||
206 | * Determine if the provided currency is active. |
||||
207 | * |
||||
208 | * @param string $code |
||||
209 | * |
||||
210 | * @return bool |
||||
211 | */ |
||||
212 | public function isActive($code) |
||||
213 | { |
||||
214 | return $code && (bool) Arr::get($this->getCurrency($code), 'active', false); |
||||
215 | } |
||||
216 | |||||
217 | /** |
||||
218 | * Return the current currency if the |
||||
219 | * one supplied is not valid. |
||||
220 | * |
||||
221 | * @param string $code |
||||
222 | * |
||||
223 | * @return null|array |
||||
224 | */ |
||||
225 | public function getCurrency($code = null) |
||||
226 | { |
||||
227 | $code = $code ?: $this->getUserCurrency(); |
||||
228 | |||||
229 | return Arr::get($this->getCurrencies(), strtoupper($code)); |
||||
230 | } |
||||
231 | |||||
232 | /** |
||||
233 | * Return all currencies. |
||||
234 | * |
||||
235 | * @return array |
||||
236 | */ |
||||
237 | public function getCurrencies() |
||||
238 | { |
||||
239 | if ($this->currencies_cache === null) { |
||||
240 | if (config('app.debug', false) === true) { |
||||
241 | $this->currencies_cache = $this->getDriver()->all(); |
||||
242 | } else { |
||||
243 | $this->currencies_cache = $this->cache->rememberForever('torann.currency', function () { |
||||
244 | return $this->getDriver()->all(); |
||||
245 | }); |
||||
246 | } |
||||
247 | } |
||||
248 | |||||
249 | return $this->currencies_cache; |
||||
250 | } |
||||
251 | |||||
252 | /** |
||||
253 | * Return all active currencies. |
||||
254 | * |
||||
255 | * @return array |
||||
256 | */ |
||||
257 | public function getActiveCurrencies() |
||||
258 | { |
||||
259 | return array_filter($this->getCurrencies(), function ($currency) { |
||||
260 | return $currency['active'] == true; |
||||
261 | }); |
||||
262 | } |
||||
263 | |||||
264 | /** |
||||
265 | * Get storage driver. |
||||
266 | * |
||||
267 | */ |
||||
268 | public function getDriver() |
||||
269 | { |
||||
270 | if ($this->driver === null) { |
||||
271 | // Get driver configuration |
||||
272 | $config = $this->config('drivers.' . $this->config('driver'), []); |
||||
273 | |||||
274 | // Get driver class |
||||
275 | $driver = Arr::pull($config, 'class'); |
||||
276 | |||||
277 | // Create driver instance |
||||
278 | $this->driver = new $driver($config); |
||||
279 | } |
||||
280 | |||||
281 | return $this->driver; |
||||
282 | } |
||||
283 | |||||
284 | /** |
||||
285 | * Get formatter driver. |
||||
286 | * |
||||
287 | */ |
||||
288 | public function getFormatter() |
||||
289 | { |
||||
290 | if ($this->formatter === null && $this->config('formatter') !== null) { |
||||
291 | // Get formatter configuration |
||||
292 | $config = $this->config('formatters.' . $this->config('formatter'), []); |
||||
293 | |||||
294 | // Get formatter class |
||||
295 | $class = Arr::pull($config, 'class'); |
||||
296 | |||||
297 | // Create formatter instance |
||||
298 | $this->formatter = new $class(array_filter($config)); |
||||
299 | } |
||||
300 | |||||
301 | return $this->formatter; |
||||
302 | } |
||||
303 | |||||
304 | /** |
||||
305 | * Clear cached currencies. |
||||
306 | */ |
||||
307 | public function clearCache() |
||||
308 | { |
||||
309 | $this->cache->forget('torann.currency'); |
||||
310 | $this->currencies_cache = null; |
||||
311 | } |
||||
312 | |||||
313 | /** |
||||
314 | * Get configuration value. |
||||
315 | * |
||||
316 | * @param string $key |
||||
317 | * @param mixed $default |
||||
318 | * |
||||
319 | * @return mixed |
||||
320 | */ |
||||
321 | public function config($key = null, $default = null) |
||||
322 | { |
||||
323 | if ($key === null) { |
||||
324 | return $this->config; |
||||
325 | } |
||||
326 | |||||
327 | return Arr::get($this->config, $key, $default); |
||||
328 | } |
||||
329 | |||||
330 | /** |
||||
331 | * Get the given property value from provided currency. |
||||
332 | * |
||||
333 | * @param string $code |
||||
334 | * @param string $key |
||||
335 | * @param mixed $default |
||||
336 | * |
||||
337 | * @return array |
||||
338 | */ |
||||
339 | protected function getCurrencyProp($code, $key, $default = null) |
||||
340 | { |
||||
341 | return Arr::get($this->getCurrency($code), $key, $default); |
||||
342 | } |
||||
343 | |||||
344 | /** |
||||
345 | * Get a given value from the current currency. |
||||
346 | * |
||||
347 | * @param string $key |
||||
348 | * |
||||
349 | * @return mixed |
||||
350 | */ |
||||
351 | public function __get($key) |
||||
352 | { |
||||
353 | return Arr::get($this->getCurrency(), $key); |
||||
354 | } |
||||
355 | |||||
356 | /** |
||||
357 | * Dynamically call the default driver instance. |
||||
358 | * |
||||
359 | * @param string $method |
||||
360 | * @param array $parameters |
||||
361 | * |
||||
362 | * @return mixed |
||||
363 | */ |
||||
364 | public function __call($method, $parameters) |
||||
365 | { |
||||
366 | return call_user_func_array([$this->getDriver(), $method], $parameters); |
||||
367 | } |
||||
368 | } |
||||
369 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..