Completed
Branch master (f6fbf6)
by Nikola
04:10 queued 01:40
created

FileRepository::latest()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 14
ccs 0
cts 9
cp 0
rs 9.2
cc 4
eloc 5
nc 3
nop 2
crap 20
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
        $this->load();
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function save(array $rates)
59
    {
60
        /**
61
         * @var RateInterface $rate
62
         */
63
        foreach ($rates as $rate) {
64
            $this->rates[sprintf('%s_%s_%s', $rate->getCurrencyCode(), $rate->getDate()->format('Y-m-d'), $rate->getRateType())] = $rate;
65
        }
66
67
        usort($this->rates, function($rate1, $rate2) {
68
            /**
69
             * @var RateInterface $rate1
70
             * @var RateInterface $rate2
71
             */
72
            return ($rate1->getDate() > $rate2->getDate()) ? -1 : 1;
73
        });
74
75
        $data = '';
76
77
        /**
78
         * @var RateInterface $rate
79
         */
80
        foreach ($this->rates as $rate) {
81
            $data .= json_encode(array(
82
                    'sourceName' => $rate->getSourceName(),
83
                    'value' => $rate->getValue(),
84
                    'currencyCode' => $rate->getCurrencyCode(),
85
                    'rateType' => $rate->getRateType(),
86
                    'date' => $rate->getDate()->format('Y-m-d H:i:s'),
87
                    'baseCurrencyCode' => $rate->getBaseCurrencyCode()
88
                )) . "\n";
89
        }
90
91
        file_put_contents($this->pathToFile, $data, LOCK_EX);
92
93
        $this->load();
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function delete(array $rates)
100
    {
101
        /**
102
         * @var RateInterface $rate
103
         */
104
        foreach ($rates as $rate) {
105
            unset($this->rates[$this->getRateKey($rate)]);
106
        }
107
108
        $this->save(array());
109
    }
110
111
    /**
112
     * {@inheritdoc}
113
     */
114 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...
115
    {
116
        if (is_null($date)) {
117
            $date = new \DateTime('now');
118
        }
119
120
        return array_key_exists(str_replace(array(
121
            $currencyCode,
122
            $date->format('Y-m-d'),
123
            $rateType
124
        ), array(
125
            '%currency_code%',
126
            '%date%',
127
            '%rate_type%'
128
        ), self::RATE_KEY_FORMAT), $this->rates);
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 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...
135
    {
136
        if (is_null($date)) {
137
            $date = new \DateTime('now');
138
        }
139
140
        return $this->rates[str_replace(array(
141
            $currencyCode,
142
            $date->format('Y-m-d'),
143
            $rateType
144
        ), array(
145
            '%currency_code%',
146
            '%date%',
147
            '%rate_type%'
148
        ), self::RATE_KEY_FORMAT)];
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function latest($currencyCode, $rateType = 'default')
155
    {
156
        /**
157
         * @var RateInterface $rate
158
         */
159
        foreach ($this->rates as $rate) {
160
161
            if ($rate->getCurrencyCode() == $currencyCode && $rate->getRateType() == $rateType) {
162
                return $rate;
163
            }
164
        }
165
166
        throw new ExchangeRateException(sprintf('Could not fetch latest rate for rate currency code "%s" and rate type "%s".', $currencyCode, $rateType));
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172
    public function all(array $criteria = array())
173
    {
174
        if (count($criteria) == 0) {
175
            return $this->rates;
176
        } else {
177
            $result = array();
178
179
            /**
180
             * @var RateInterface $rate
181
             */
182
            foreach ($this->rates as $rate) {
183
184
                if (RateFilter::matches($rate, $criteria)) {
185
                    $result[] = $rate;
186
                }
187
            }
188
189
            return $result;
190
        }
191
    }
192
193
    /**
194
     * Load rates from file.
195
     *
196
     * @return RateInterface[]
197
     */
198
    protected function load()
199
    {
200
        $this->rates = array();
201
        $this->latest = array();
202
203
        $handle = fopen($this->pathToFile, 'r');
204
205
        if ($handle) {
206
207
            while (($line = fgets($handle)) !== false) {
208
                $data = json_decode($line, true);
209
210
                $rate = new Rate(
211
                    $data['sourceName'],
212
                    $data['value'],
213
                    $data['currencyCode'],
214
                    $data['rateType'],
215
                    \DateTime::createFromFormat('Y-m-d H:i:s', $data['date']),
216
                    $data['baseCurrencyCode']
217
                );
218
219
                $this->rates[$this->getRateKey($rate)] = $rate;
220
221
                $latestKey = sprintf('%s_%s', $rate->getCurrencyCode(), $rate->getRateType());
222
223
                if (!isset($this->latest[$latestKey]) || ($this->latest[$latestKey]->getDate() < $rate->getDate())) {
224
                    $this->latest[$latestKey] = $rate;
225
                }
226
            }
227
228
            fclose($handle);
229
230
        } else {
231
            throw new \RuntimeException(sprintf('Error opening file on path "%s".', $this->pathToFile));
232
        }
233
234
        return $this->rates;
235
    }
236
237
    protected function getRateKey(RateInterface $rate)
238
    {
239
        return str_replace(array(
240
            $rate->getCurrencyCode(),
241
            $rate->getDate()->format('Y-m-d'),
242
            $rate->getRateType()
243
        ), array(
244
            '%currency_code%',
245
            '%date%',
246
            '%rate_type%'
247
        ), self::RATE_KEY_FORMAT);
248
    }
249
}
250