Completed
Push — master ( 7bb02e...95b25b )
by Nikola
04:06
created

FileRepository::__construct()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 18
ccs 0
cts 14
cp 0
rs 8.8571
cc 6
eloc 9
nc 4
nop 1
crap 42
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) && !(mkdir(dirname($this->pathToFile), 0777, true) && touch($this->pathToFile))) {
41
            throw new \RuntimeException(sprintf('Could not create storage file on path "%s".', $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(RateInterface $rate1, RateInterface $rate2) {
68
            return ($rate1->getDate() > $rate2->getDate()) ? -1 : 1;
69
        });
70
71
        $data = '';
72
73
        /**
74
         * @var RateInterface $rate
75
         */
76
        foreach ($this->rates as $rate) {
77
            $data .= json_encode(array(
78
                    'sourceName' => $rate->getSourceName(),
79
                    'value' => $rate->getValue(),
80
                    'currencyCode' => $rate->getCurrencyCode(),
81
                    'rateType' => $rate->getRateType(),
82
                    'date' => $rate->getDate()->format('Y-m-d H:i:s'),
83
                    'baseCurrencyCode' => $rate->getBaseCurrencyCode(),
84
                    'createdAt' => $rate->getCreatedAt()->format('Y-m-d H:i:s'),
85
                    'modifiedAt' => $rate->getModifiedAt()->format('Y-m-d H:i:s')
86
                )) . "\n";
87
        }
88
89
        file_put_contents($this->pathToFile, $data, LOCK_EX);
90
91
        $this->load();
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function delete(array $rates)
98
    {
99
        /**
100
         * @var RateInterface $rate
101
         */
102
        foreach ($rates as $rate) {
103
            unset($this->rates[$this->getRateKey($rate)]);
104
        }
105
106
        $this->save(array());
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112
    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...
113
    {
114
        if (is_null($date)) {
115
            $date = new \DateTime('now');
116
        }
117
118
        return array_key_exists(str_replace(array(
119
            $currencyCode,
120
            $date->format('Y-m-d'),
121
            $rateType
122
        ), array(
123
            '%currency_code%',
124
            '%date%',
125
            '%rate_type%'
126
        ), self::RATE_KEY_FORMAT), $this->rates);
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132
    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...
133
    {
134
        if (is_null($date)) {
135
            $date = new \DateTime('now');
136
        }
137
138
        return $this->rates[str_replace(array(
139
            $currencyCode,
140
            $date->format('Y-m-d'),
141
            $rateType
142
        ), array(
143
            '%currency_code%',
144
            '%date%',
145
            '%rate_type%'
146
        ), self::RATE_KEY_FORMAT)];
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152
    public function latest($currencyCode, $rateType = 'default')
153
    {
154
        /**
155
         * @var RateInterface $rate
156
         */
157
        foreach ($this->rates as $rate) {
158
159
            if ($rate->getCurrencyCode() == $currencyCode && $rate->getRateType() == $rateType) {
160
                return $rate;
161
            }
162
        }
163
164
        throw new ExchangeRateException(sprintf('Could not fetch latest rate for rate currency code "%s" and rate type "%s".', $currencyCode, $rateType));
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170
    public function all(array $criteria = array())
171
    {
172
        if (count($criteria) == 0) {
173
            return $this->rates;
174
        } else {
175
            $result = array();
176
177
            /**
178
             * @var RateInterface $rate
179
             */
180
            foreach ($this->rates as $rate) {
181
182
                if (RateFilter::matches($rate, $criteria)) {
183
                    $result[] = $rate;
184
                }
185
            }
186
187
            return $result;
188
        }
189
    }
190
191
    /**
192
     * Load rates from file.
193
     *
194
     * @return RateInterface[]
195
     */
196
    protected function load()
197
    {
198
        $this->rates = array();
199
        $this->latest = array();
200
201
        $handle = fopen($this->pathToFile, 'r');
202
203
        if ($handle) {
204
205
            while (($line = fgets($handle)) !== false) {
206
                $data = json_decode($line, true);
207
208
                $rate = new Rate(
209
                    $data['sourceName'],
210
                    $data['value'],
211
                    $data['currencyCode'],
212
                    $data['rateType'],
213
                    \DateTime::createFromFormat('Y-m-d H:i:s', $data['date']),
214
                    $data['baseCurrencyCode'],
215
                    \DateTime::createFromFormat('Y-m-d H:i:s', $data['createdAt']),
216
                    \DateTime::createFromFormat('Y-m-d H:i:s', $data['modifiedAt'])
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