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

CurrencyConverterComponent::_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\Controller\Component;
3
4
use Cake\Controller\Component;
5
use Cake\Datasource\ConnectionManager;
6
use Cake\ORM\TableRegistry;
7
use Cake\I18n\Time;
8
9
/**
10
 * CurrencyConverter Component to convert currency.
11
 *
12
 * Convert an amount number from one currency to an other currency
13
 * Return currency rate from one currency to an other
14
 * output type from HTML to JSON format.
15
 */
16
class CurrencyConverterComponent extends Component
17
{
18
    /**
19
     * Using database
20
     *
21
     * @var bool
22
     */
23
    private $database;
24
25
    /**
26
     * Time interval for refreshing database
27
     *
28
     * @var int
29
     */
30
    private $refresh;
31
32
    /**
33
     * Number of decimal to use for formatting converted price
34
     *
35
     * @var int
36
     */
37
    private $decimal;
38
39
    /**
40
     * Number to divise 1 and get the sup step to round price to
41
     *
42
     * @var float
43
     */
44
    private $round;
45
46
    /**
47
     * Session
48
     *
49
     * @var \Cake\Http\Session
50
     */
51
    private $session;
52
53
    /**
54
     * CurrencyratesTable Class
55
     * @var \Cake\ORM\Table
56
     */
57
    private $currencyratesTable;
58
59
    /**
60
     * Default CurrencyConverterComponent settings.
61
     *
62
     * When calling CurrencyConverterComponent() these settings will be merged with the configuration
63
     * you provide.
64
     *
65
     * - `database` - Mention if Component have to store currency rate in database
66
     * - `refresh` - Time interval for Component to refresh currency rate in database
67
     * - `decimal` - Number of decimal to use when formatting amount float number
68
     * - `round` - Number to divise 1 and get the sup step to round price to (eg: 4 for 0.25 step)
69
     *
70
     * @var array
71
     */
72
    protected $_defaultConfig = [
73
        'database' => true, // Mention if Component have to store currency rate in database
74
        'refresh' => 24, // Time interval for Component to refresh currency rate in database
75
        'decimal' => 2, // Number of decimal to use when formatting amount float number
76
        'round' => false, // Number to divise 1 and get the sup step to round price to (eg: 4 for 0.25 step)
77
    ];
78
79
    /**
80
     * @param array $config
81
     * @return void
82
     */
83 16
    public function initialize(array $config = []) {
84 16
        $config = $this->getConfig();
85
86 16
        $this->database = $config['database'];
87 16
        $this->refresh = $config['refresh'];
88 16
        $this->decimal = $config['decimal'];
89 16
        $this->round = $config['round'];
90 16
        if ($this->round == 0) {
91 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...
92 16
        }
93 16
        $this->session = $this->request->getSession();
0 ignored issues
show
Deprecated Code introduced by
The property Cake\Controller\Component::$request has been deprecated: 3.4.0 Storing references to the request is deprecated. Use Component::getController() or callback $event->getSubject() to access the controller & request instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

93
        $this->session = /** @scrutinizer ignore-deprecated */ $this->request->getSession();

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
94 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

94
        $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...
95 16
    }
96
97
    /**
98
     * Convert method take an amount as first parameter and convert it using $from currency and $to currency.
99
     *
100
     * @param float|string $amount the amount to convert.
101
     * @param string $from currency to convert from
102
     * @param string $to currency to convert to
103
     * @return string $amount converted
104
     */
105 9
    public function convert($amount, $from, $to)
106
    {
107 9
        $amount = floatval($amount);
108 9
        $rate = $this->_getRateToUse($from, $to);
109
110 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...
111
    }
112
113
    /**
114
     * Rate method return the rate of two currencies
115
     *
116
     * @param string $from currency to get the rate from
117
     * @param string $to currency to get the rate to
118
     * @return float|null $rate
119
     */
120 6
    public function rate($from, $to)
121
    {
122 6
        return $this->_getRateToUse($from, $to);
123
    }
124
125
    /**
126
     * getRateToUse return rate to use
127
     * Using $from and $to parameters representing currency to deal with and the configuration settings
128
     * This method save or update currencyrates Table if necesseray too.
129
     *
130
     * @param string $from currency to get the rate from
131
     * @param string $to currency to get the rate to
132
     * @return float|null $rate
133
     */
134 15
    private function _getRateToUse($from, $to)
135
    {
136 15
        $rate = 1;
137 15
        if ($from !== $to) {
138 12
            if ($this->database) {
139 10
                $rate = $this->_getRateFromSession($from, $to);
140 10
                if (!$rate) {
141 8
                    $rate = $this->_getRateFromDatabase($from, $to);
142 8
                }
143 10
            } else {
144 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\Contro...nent::_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...
145
            }
146 12
        }
147
148 15
        return $rate;
149
    }
150
151
    /**
152
     * Format number using configuration
153
     *
154
     * @param float number to format
155
     * @return string formatted number
156
     */
157 9
    private function _formatConvert($number)
158
    {
159 9
        if ($this->round) {
160 1
            $n = floor($number);
161 1
            $fraction = ($number - $n);
162 1
            if ($fraction != 0) {
163 1
                $step = 1/$this->round;
164 1
                $decimal = (((int)($fraction/$step) + 1) * $step);
165 1
                $number = $n + $decimal;
166 1
            }
167 1
        }
168 9
        $number = number_format($number, $this->decimal);
169
170 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

170
        return number_format(/** @scrutinizer ignore-type */ $number, $this->decimal);
Loading history...
171
    }
172
173
    /**
174
     * Check session to see if rate exists in.
175
     * 
176
     * @param  string $from currency to get the rate from.
177
     * @param  string $to currency to get the rate to.
178
     * @return float|null $rate.
179
     */
180 10
    private function _getRateFromSession($from, $to)
181
    {
182 10
        $session = $this->session->read('CurrencyConverter.' . $from . '-' . $to);        
183 10
        if ($session) {
184 4
            $modified = new Time($session['modified']);
185 4
            if ($modified->wasWithinLast($this->refresh . ' hours')) {
186 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...
187
            }
188 2
        }
189
190 8
        return null;
191
    }
192
193
    /**
194
     * Get a rate from database.
195
     *
196
     * It queries currencyratesTable and ...
197
     * if rate exists and has not to be modified, it returns this rate.
198
     * 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.
199
     * if rate does not exist, it call _getRateFromAPI to get a fresh rate, then create in table and store this rate.
200
     * 
201
     * @param  string $from currency to get the rate from
202
     * @param  string $to currency to get the rate to
203
     * @return float|null $rate
204
     */
205 8
    private function _getRateFromDatabase($from, $to)
206
    {
207 8
        $result = $this->currencyratesTable->find('all')->where(['from_currency' => $from, 'to_currency' => $to])->first();
208 8
        if ($result) {
209 6
            if ($result->get('modified')->wasWithinLast($this->refresh . ' hours')) {
210 2
                $rate = $result->get('rate');
211 2
            } else {
212 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\Contro...nent::_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...
213 4
                if ($rate) {
0 ignored issues
show
introduced by
$rate is of type null, thus it always evaluated to false.
Loading history...
214 4
                    $result->rate = $rate;
215 4
                    $this->currencyratesTable->save($result);
216 4
                    $this->_storeRateInSession($result);
217 4
                }
218
            }
219 6
        } else {
220 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\Contro...nent::_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...
221 2
            if ($rate) {
0 ignored issues
show
introduced by
$rate is of type null, thus it always evaluated to false.
Loading history...
222 2
                $result = $this->currencyratesTable->newEntity([
223 2
                    'from_currency' => $from,
224 2
                    'to_currency' => $to,
225
                    'rate' => $rate
226 2
                ]);
227 2
                $this->currencyratesTable->save($result);
228 2
                $this->_storeRateInSession($result);
229 2
            }
230
        }
231
232 8
        return $rate;
233
    }
234
235
    /**
236
     * Store in session a rate and his modified datetime
237
     * 
238
     * @param  \Cake\ORM\Entity $entity
239
     * @return void
240
     */
241 6
    private function _storeRateInSession($entity)
242
    {
243 6
        $this->session->write('CurrencyConverter', [
244 6
            $entity->get('from_currency') . '-' . $entity->get('to_currency') => [
245 6
                'rate' => $entity->get('rate'),
246 6
                'modified' => $entity->get('modified')
247 6
            ]
248 6
        ]);
249 6
    }
250
251
    /**
252
     * Call free.currencyconverterapi.com API to get a rate for one currency to an other one currency.
253
     *
254
     * @param string $from the currency.
255
     * @param string $to the currency.
256
     * @return int|null $rate.
257
     */
258 8
    private function _getRateFromAPI($from, $to)
259
    {
260 8
        $rate = null;
261
262 8
        $url = 'https://free.currencyconverterapi.com/api/v5/convert?q=' . $from . '_' . $to . '&compact=ultra';
263 8
        $request = @fopen($url, 'r');
264 8
        if ($request) {
0 ignored issues
show
introduced by
$request is of type false|resource, thus it always evaluated to false.
Loading history...
265 8
            $response = fgets($request, 4096);
266 8
            fclose($request);
267 8
            $response = json_decode($response, true);
268 8
            if (isset($response[$from . '_' . $to])) {
269 8
                $rate = $response[$from . '_' . $to];
270 8
            }
271 8
        }
272
        
273 8
        return $rate;
274
    }
275
}
276