Completed
Push — master ( 17fbce...9c0ecb )
by Nikola
02:12
created

FileRepository::matches()   D

Complexity

Conditions 19
Paths 10

Size

Total Lines 40
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 40
rs 4.9142
cc 19
eloc 20
nc 10
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace RunOpenCode\ExchangeRate\Repository;
4
5
use RunOpenCode\ExchangeRate\Contract\RateInterface;
6
use RunOpenCode\ExchangeRate\Contract\RepositoryInterface;
7
use RunOpenCode\ExchangeRate\Exception\ExchangeRateException;
8
use RunOpenCode\ExchangeRate\Utils\RateFilter;
9
use RunOpenCode\ExchangeRateBundle\Model\Rate;
10
11
class FileRepository implements RepositoryInterface
12
{
13
    const RATE_KEY_FORMAT = '%currency_code%_%date%_%rate_type%';
14
15
    /**
16
     * File where all rates are persisted.
17
     *
18
     * @var string
19
     */
20
    protected $pathToFile;
21
22
    /**
23
     * Collection of loaded rates.
24
     *
25
     * @var array
26
     */
27
    protected $rates;
28
29
    /**
30
     * Collection of latest rates (to speed up search process).
31
     *
32
     * @var array
33
     */
34
    protected $latest;
35
36
    public function __construct($pathToFile)
37
    {
38
        $this->pathToFile = $pathToFile;
39
40
        if (!file_exists($this->pathToFile)) {
41
            touch($this->pathToFile);
42
        }
43
44
        if (!is_readable($this->pathToFile)) {
45
            throw new \RuntimeException(sprintf('File on path "%s" for storing rates must be readable.', $this->pathToFile));
46
        }
47
48
        if (!is_writable($this->pathToFile)) {
49
            throw new \RuntimeException(sprintf('File on path "%s" for storing rates must be writeable.', $this->pathToFile));
50
        }
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     */
56
    public function save(array $rates)
57
    {
58
        if (!$this->rates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->rates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
59
            $this->load();
60
        }
61
62
        /**
63
         * @var RateInterface $rate
64
         */
65
        foreach ($rates as $rate) {
66
            $this->rates[sprintf('%s_%s_%s', $rate->getCurrencyCode(), $rate->getDate()->format('Y-m-d'), $rate->getRateType())] = $rate;
67
        }
68
69
        usort($this->rates, function($rate1, $rate2) {
70
            /**
71
             * @var RateInterface $rate1
72
             * @var RateInterface $rate2
73
             */
74
            return ($rate1->getDate() > $rate2->getDate()) ? -1 : 1;
75
        });
76
77
        $data = '';
78
79
        /**
80
         * @var RateInterface $rate
81
         */
82
        foreach ($this->rates as $rate) {
83
            $data .= json_encode(array(
84
                    'sourceName' => $rate->getSourceName(),
85
                    'value' => $rate->getValue(),
86
                    'currencyCode' => $rate->getCurrencyCode(),
87
                    'rateType' => $rate->getRateType(),
88
                    'date' => $rate->getDate()->format('Y-m-d H:i:s'),
89
                    'baseCurrencyCode' => $rate->getBaseCurrencyCode()
90
                )) . "\n";
91
        }
92
93
        file_put_contents($this->pathToFile, $data, LOCK_EX);
94
95
        $this->load();
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function delete(array $rates)
102
    {
103
        if (!$this->rates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->rates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
104
            $this->load();
105
        }
106
107
        /**
108
         * @var RateInterface $rate
109
         */
110
        foreach ($rates as $rate) {
111
            unset($this->rates[$this->getRateKey($rate)]);
112
        }
113
114
        $this->save(array());
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 View Code Duplication
    public function has($currencyCode, \DateTime $date = null, $rateType = 'default')
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
    {
122
        if (!$this->rates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->rates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
123
            $this->load();
124
        }
125
126
        if (is_null($date)) {
127
            $date = new \DateTime('now');
128
        }
129
130
        return array_key_exists(str_replace(array(
131
            $currencyCode,
132
            $date->format('Y-m-d'),
133
            $rateType
134
        ), array(
135
            '%currency_code%',
136
            '%date%',
137
            '%rate_type%'
138
        ), self::RATE_KEY_FORMAT), $this->rates);
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144 View Code Duplication
    public function get($currencyCode, \DateTime $date = null, $rateType = 'default')
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145
    {
146
        if (!$this->rates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->rates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
147
            $this->load();
148
        }
149
150
        if (is_null($date)) {
151
            $date = new \DateTime('now');
152
        }
153
154
        return $this->rates[str_replace(array(
155
            $currencyCode,
156
            $date->format('Y-m-d'),
157
            $rateType
158
        ), array(
159
            '%currency_code%',
160
            '%date%',
161
            '%rate_type%'
162
        ), self::RATE_KEY_FORMAT)];
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168
    public function latest($currencyCode, $rateType = 'default')
169
    {
170
        if (!$this->rates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->rates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
171
            $this->load();
172
        }
173
174
        /**
175
         * @var RateInterface $rate
176
         */
177
        foreach ($this->rates as $rate) {
178
179
            if ($rate->getCurrencyCode() == $currencyCode && $rate->getRateType() == $rateType) {
180
                return $rate;
181
            }
182
        }
183
184
        throw new ExchangeRateException(sprintf('Could not fetch latest rate for rate currency code "%s" and rate type "%s".', $currencyCode, $rateType));
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     */
190
    public function all(array $criteria = array())
191
    {
192
        if (!$this->rates) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->rates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
193
            $this->load();
194
        }
195
196
        if (count($criteria) == 0) {
197
            return $this->rates;
198
        } else {
199
            $result = array();
200
201
            /**
202
             * @var RateInterface $rate
203
             */
204
            foreach ($this->rates as $rate) {
205
206
                if (RateFilter::matches($rate, $criteria)) {
207
                    $result[] = $rate;
208
                }
209
            }
210
211
            return $result;
212
        }
213
    }
214
215
    /**
216
     * Load rates from file.
217
     */
218
    protected function load()
219
    {
220
        $this->rates = array();
221
        $this->latest = array();
222
223
        $handle = fopen($this->pathToFile, 'r');
224
225
        if ($handle) {
226
227
            while (($line = fgets($handle)) !== false) {
228
                $data = json_decode($line, true);
229
230
                $rate = new Rate(
231
                    $data['sourceName'],
232
                    $data['value'],
233
                    $data['currencyCode'],
234
                    $data['rateType'],
235
                    \DateTime::createFromFormat('Y-m-d H:i:s', $data['date']),
236
                    $data['baseCurrencyCode']
237
                );
238
239
                $this->rates[$this->getRateKey($rate)] = $rate;
240
241
                $latestKey = sprintf('%s_%s', $rate->getCurrencyCode(), $rate->getRateType());
242
243
                if (!isset($this->latest[$latestKey]) || ($this->latest[$latestKey]->getDate() < $rate->getDate())) {
244
                    $this->latest[$latestKey] = $rate;
245
                }
246
            }
247
248
            fclose($handle);
249
250
        } else {
251
            throw new \RuntimeException(sprintf('Error opening file on path "%s".', $this->pathToFile));
252
        }
253
    }
254
255
    protected function getRateKey(RateInterface $rate)
256
    {
257
        return str_replace(array(
258
            $rate->getCurrencyCode(),
259
            $rate->getDate()->format('Y-m-d'),
260
            $rate->getRateType()
261
        ), array(
262
            '%currency_code%',
263
            '%date%',
264
            '%rate_type%'
265
        ), self::RATE_KEY_FORMAT);
266
    }
267
}
268