Completed
Push — master ( 95b25b...5c8a6e )
by Nikola
04:30
created

FileRepository::initStorage()   B

Complexity

Conditions 8
Paths 5

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

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