Passed
Pull Request — master (#14)
by
unknown
02:41
created

CurrencyConverterHelper::_getRateFromDatabase()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 28
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 20
nc 5
nop 2
dl 0
loc 28
ccs 23
cts 23
cp 1
crap 5
rs 9.2888
c 0
b 0
f 0
1
<?php
2
namespace CurrencyConverter\View\Helper;
3
4
use Cake\View\Helper;
5
use Cake\ORM\TableRegistry;
6
use Cake\I18n\Time;
7
8
/**
9
 * @property \Cake\View\Helper\HtmlHelper $Html
10
 * @property \Cake\View\Helper\FormHelper $Form
11
 * @property \Cake\View\Helper\NumberHelper $Number
12
 */
13
class CurrencyConverterHelper extends Helper
14
{
15
    /**
16
     * Using database
17
     *
18
     * @var bool
19
     */
20
    private $database;
21
22
    /**
23
     * Time interval for refreshing database
24
     *
25
     * @var int
26
     */
27
    private $refresh;
28
29
    /**
30
     * Number of decimal to use for formatting converted price
31
     *
32
     * @var int
33
     */
34
    private $decimal;
35
36
    /**
37
     * Number to divise 1 and get the sup step to round price to
38
     *
39
     * @var float
40
     */
41
    private $round;
42
43
    /**
44
     * Session
45
     *
46
     * @var \Cake\Http\Session
47
     */
48
    private $session;
49
50
    /**
51
     * CurrencyratesTable Class
52
     * @var \Cake\ORM\Table
53
     */
54
    private $currencyratesTable;
55
56
    /**
57
     * Default CurrencyConverterComponent settings.
58
     *
59
     * When calling CurrencyConverterComponent() these settings will be merged with the configuration
60
     * you provide.
61
     *
62
     * - `database` - Mention if Component have to store currency rate in database
63
     * - `refresh` - Time interval for Component to refresh currency rate in database
64
     * - `decimal` - Number of decimal to use when formatting amount float number
65
     * - `round` - Number to divise 1 and get the sup step to round price to (eg: 4 for 0.25 step)
66
     *
67
     * @var array
68
     */
69
    protected $_defaultConfig = [
70
        'database' => true, // Mention if Component have to store currency rate in database
71
        'refresh' => 24, // Time interval for Component to refresh currency rate in database
72
        'decimal' => 2, // Number of decimal to use when formatting amount float number
73
        'round' => false, // Number to divise 1 and get the sup step to round price to (eg: 4 for 0.25 step)
74
    ];
75
76
    /**
77
     * @param array $config
78
     * @return void
79
     */
80 16
    public function initialize(array $config = []) {
81 16
        $config = $this->getConfig();
82
83 16
        $this->database = $config['database'];
84 16
        $this->refresh = $config['refresh'];
85 16
        $this->decimal = $config['decimal'];
86 16
        $this->round = $config['round'];
87 16
        if ($this->round == 0) {
88 16
            $this->round = false;
0 ignored issues
show
Documentation Bug introduced by
The property $round was declared of type double, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
89 16
        }
90 16
        $this->session = $this->request->getSession();
91 16
        $this->currencyratesTable = TableRegistry::get('CurrencyConverter.Currencyrates');
0 ignored issues
show
Deprecated Code introduced by
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 ignore-deprecated  annotation

91
        $this->currencyratesTable = /** @scrutinizer ignore-deprecated */ TableRegistry::get('CurrencyConverter.Currencyrates');

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.

Loading history...
92 16
    }
93
94
    /**
95
     * Convert method take an amount as first parameter and convert it using $from currency and $to currency.
96
     *
97
     * @param float|string $amount the amount to convert.
98
     * @param string $from currency to convert from
99
     * @param string $to currency to convert to
100
     * @return string $amount converted
101
     */
102 9
    public function convert($amount, $from, $to)
103
    {
104 9
        $amount = floatval($amount);
105 9
        $rate = $this->_getRateToUse($from, $to);
106
107 9
        return $convert = $this->_formatConvert($rate * $amount);
0 ignored issues
show
Unused Code introduced by
The assignment to $convert is dead and can be removed.
Loading history...
108
    }
109
110
    /**
111
     * Rate method return the rate of two currencies
112
     *
113
     * @param string $from currency to get the rate from
114
     * @param string $to currency to get the rate to
115
     * @return float|null $rate
116
     */
117 6
    public function rate($from, $to)
118
    {
119 6
        return $this->_getRateToUse($from, $to);
120
    }
121
122
    /**
123
     * getRateToUse return rate to use
124
     * Using $from and $to parameters representing currency to deal with and the configuration settings
125
     * This method save or update currencyrates Table if necesseray too.
126
     *
127
     * @param string $from currency to get the rate from
128
     * @param string $to currency to get the rate to
129
     * @return float|null $rate
130
     */
131 15
    private function _getRateToUse($from, $to)
132
    {
133 15
        $rate = 1;
134 15
        if ($from !== $to) {
135 12
            if ($this->database) {
136 10
                $rate = $this->_getRateFromSession($from, $to);
137 10
                if (!$rate) {
138 8
                    $rate = $this->_getRateFromDatabase($from, $to);
139 8
                }
140 10
            } else {
141 2
                $rate = $this->_getRateFromAPI($from, $to);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rate is correct as $this->_getRateFromAPI($from, $to) targeting CurrencyConverter\View\H...lper::_getRateFromAPI() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
142
            }
143 12
        }
144
145 15
        return $rate;
146
    }
147
148
    /**
149
     * Format number using configuration
150
     *
151
     * @param float number to format
152
     * @return string formatted number
153
     */
154 9
    private function _formatConvert($number)
155
    {
156 9
        if ($this->round) {
157 1
            $n = floor($number);
158 1
            $fraction = ($number - $n);
159 1
            if ($fraction != 0) {
160 1
                $step = 1/$this->round;
161 1
                $decimal = (((int)($fraction/$step) + 1) * $step);
162 1
                $number = $n + $decimal;
163 1
            }
164 1
        }
165 9
        $number = number_format($number, $this->decimal);
166
167 9
        return number_format($number, $this->decimal);
0 ignored issues
show
Bug introduced by
$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 ignore-type  annotation

167
        return number_format(/** @scrutinizer ignore-type */ $number, $this->decimal);
Loading history...
168
    }
169
170
    /**
171
     * Check session to see if rate exists in.
172
     * 
173
     * @param  string $from currency to get the rate from.
174
     * @param  string $to currency to get the rate to.
175
     * @return float|null $rate.
176
     */
177 10
    private function _getRateFromSession($from, $to)
178
    {
179 10
        $session = $this->session->read('CurrencyConverter.' . $from . '-' . $to);        
180 10
        if ($session) {
181 4
            $modified = new Time($session['modified']);
182 4
            if ($modified->wasWithinLast($this->refresh . ' hours')) {
183 2
                return $rate = $session['rate'];
0 ignored issues
show
Unused Code introduced by
The assignment to $rate is dead and can be removed.
Loading history...
184
            }
185 2
        }
186
187 8
        return null;
188
    }
189
190
    /**
191
     * Get a rate from database.
192
     *
193
     * It queries currencyratesTable and ...
194
     * if rate exists and has not to be modified, it returns this rate.
195
     * 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.
196
     * if rate does not exist, it call _getRateFromAPI to get a fresh rate, then create in table and store this rate.
197
     * 
198
     * @param  string $from currency to get the rate from
199
     * @param  string $to currency to get the rate to
200
     * @return float|null $rate
201
     */
202 8
    private function _getRateFromDatabase($from, $to)
203
    {
204 8
        $result = $this->currencyratesTable->find('all')->where(['from_currency' => $from, 'to_currency' => $to])->first();
205 8
        if ($result) {
206 6
            if ($result->get('modified')->wasWithinLast($this->refresh . ' hours')) {
207 2
                $rate = $result->get('rate');
208 2
            } else {
209 4
                $rate = $this->_getRateFromAPI($from, $to);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rate is correct as $this->_getRateFromAPI($from, $to) targeting CurrencyConverter\View\H...lper::_getRateFromAPI() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
210 4
                if ($rate) {
0 ignored issues
show
introduced by
$rate is of type null, thus it always evaluated to false.
Loading history...
211 4
                    $result->rate = $rate;
212 4
                    $this->currencyratesTable->save($result);
213 4
                    $this->_storeRateInSession($result);
214 4
                }
215
            }
216 6
        } else {
217 2
            $rate = $this->_getRateFromAPI($from, $to);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $rate is correct as $this->_getRateFromAPI($from, $to) targeting CurrencyConverter\View\H...lper::_getRateFromAPI() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
218 2
            if ($rate) {
0 ignored issues
show
introduced by
$rate is of type null, thus it always evaluated to false.
Loading history...
219 2
                $result = $this->currencyratesTable->newEntity([
220 2
                    'from_currency' => $from,
221 2
                    'to_currency' => $to,
222
                    'rate' => $rate
223 2
                ]);
224 2
                $this->currencyratesTable->save($result);
225 2
                $this->_storeRateInSession($result);
226 2
            }
227
        }
228
229 8
        return $rate;
230
    }
231
232
    /**
233
     * Store in session a rate and his modified datetime
234
     * 
235
     * @param  \Cake\ORM\Entity $entity
236
     * @return void
237
     */
238 6
    private function _storeRateInSession($entity)
239
    {
240 6
        $this->session->write('CurrencyConverter', [
241 6
            $entity->get('from_currency') . '-' . $entity->get('to_currency') => [
242 6
                'rate' => $entity->get('rate'),
243 6
                'modified' => $entity->get('modified')
244 6
            ]
245 6
        ]);
246 6
    }
247
248
    /**
249
     * Call free.currencyconverterapi.com API to get a rate for one currency to an other one currency.
250
     *
251
     * @param string $from the currency.
252
     * @param string $to the currency.
253
     * @return int|null $rate.
254
     */
255 8
    private function _getRateFromAPI($from, $to)
256
    {
257 8
        $rate = null;
258
259 8
        $url = 'https://free.currencyconverterapi.com/api/v5/convert?q=' . $from . '_' . $to . '&compact=ultra';
260 8
        $request = @fopen($url, 'r');
261 8
        if ($request) {
0 ignored issues
show
introduced by
$request is of type false|resource, thus it always evaluated to false.
Loading history...
262 8
            $response = fgets($request, 4096);
263 8
            fclose($request);
264 8
            $response = json_decode($response, true);
265 8
            if (isset($response[$from . '_' . $to])) {
266 8
                $rate = $response[$from . '_' . $to];
267 8
            }
268 8
        }
269
        
270 8
        return $rate;
271
    }
272
}
273