Completed
Push — master ( b659c5...c65770 )
by Michael
10:22
created

CacheRetriever   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 204
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 6
dl 0
loc 204
ccs 92
cts 92
cp 1
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A retrieveEveApi() 0 22 3
A setCachePath() 0 5 1
A setRetrieve() 0 5 1
A getCachePath() 0 8 3
B getXmlFileContents() 0 31 4
C isExpired() 0 48 8
A shouldRetrieve() 0 4 1
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains CacheRetriever class.
5
 *
6
 * PHP version 7.0+
7
 *
8
 * LICENSE:
9
 * This file is part of Yet Another Php Eve Api Library also know as Yapeal
10
 * which can be used to access the Eve Online API data and place it into a
11
 * database.
12
 * Copyright (C) 2014-2017 Michael Cummings
13
 *
14
 * This program is free software: you can redistribute it and/or modify it
15
 * under the terms of the GNU Lesser General Public License as published by the
16
 * Free Software Foundation, either version 3 of the License, or (at your
17
 * option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful, but WITHOUT
20
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
22
 * for more details.
23
 *
24
 * You should have received a copy of the GNU Lesser General Public License
25
 * along with this program. If not, see
26
 * <http://spdx.org/licenses/LGPL-3.0.html>.
27
 *
28
 * You should be able to find a copy of this license in the COPYING-LESSER.md
29
 * file. A copy of the GNU GPL should also be available in the COPYING.md file.
30
 *
31
 * @copyright 2014-2017 Michael Cummings
32
 * @license   LGPL-3.0+
33
 * @author    Michael Cummings <[email protected]>
34
 */
35
namespace Yapeal\FileSystem;
36
37
use Yapeal\Event\EveApiEventEmitterTrait;
38
use Yapeal\Event\EveApiEventInterface;
39
use Yapeal\Event\EveApiRetrieverInterface;
40
use Yapeal\Event\MediatorInterface;
41
use Yapeal\Log\Logger;
42
use Yapeal\Xml\EveApiReadWriteInterface;
43
use Yapeal\Xml\LibXmlChecksTrait;
44
45
/**
46
 * Class CacheRetriever
47
 */
48
class CacheRetriever implements EveApiRetrieverInterface
49
{
50
    use EveApiEventEmitterTrait;
51
    use LibXmlChecksTrait;
52
    use SafeFileHandlingTrait;
53
    /**
54
     * CacheRetriever constructor.
55
     *
56
     * @param string $cachePath
57
     */
58 13
    public function __construct(string $cachePath)
59
    {
60 13
        $this->setCachePath($cachePath);
61
    }
62
    /**
63
     * Method that is called for retrieve event.
64
     *
65
     * @param EveApiEventInterface $event
66
     * @param string               $eventName
67
     * @param MediatorInterface    $yem
68
     *
69
     * @return EveApiEventInterface
70
     * @throws \DomainException
71
     * @throws \InvalidArgumentException
72
     * @throws \LogicException
73
     * @throws \UnexpectedValueException
74
     */
75 12
    public function retrieveEveApi(
76
        EveApiEventInterface $event,
77
        string $eventName,
78
        MediatorInterface $yem
79
    ): EveApiEventInterface {
80 12
        if (!$this->shouldRetrieve()) {
81 1
            return $event;
82
        }
83 11
        $this->setYem($yem);
84 11
        $data = $event->getData();
85 11
        $yem->triggerLogEvent('Yapeal.Log.log',
86 11
            Logger::DEBUG,
87 11
            $this->getReceivedEventMessage($data, $eventName, __CLASS__));
88 11
        if (false === $xml = $this->getXmlFileContents($data)) {
89 8
            return $event;
90
        }
91 2
        $data->setEveApiXml($xml);
92 2
        $mess = 'Successfully retrieved the XML of';
93 2
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $this->createEveApiMessage($mess, $data));
94 2
        return $event->setData($data)
95 2
            ->setHandledSufficiently();
96
    }
97
    /**
98
     * Set cache path for Eve API XML.
99
     *
100
     * @param string $value Absolute path to cache/ directory.
101
     *
102
     * @return self Fluent interface.
103
     */
104 13
    public function setCachePath(string $value): self
105
    {
106 13
        $this->cachePath = $value;
107 13
        return $this;
108
    }
109
    /**
110
     * Turn on or off retrieving of Eve API data by this retriever.
111
     *
112
     * Allows class to stay registered for events but be enabled or disabled during runtime.
113
     *
114
     * @param bool $value
115
     *
116
     * @return self Fluent interface.
117
     */
118 13
    public function setRetrieve(bool $value = true): self
119
    {
120 13
        $this->retrieve = $value;
121 13
        return $this;
122
    }
123
    /**
124
     * Returns current cache path.
125
     *
126
     * @return string
127
     * @throws \LogicException
128
     */
129 11
    private function getCachePath(): string
130
    {
131 11
        if (null === $this->cachePath || '' === $this->cachePath) {
132 1
            $mess = 'Tried to access $cachePath before it was set';
133 1
            throw new \LogicException($mess);
134
        }
135 10
        return $this->cachePath;
136
    }
137
    /**
138
     * @param EveApiReadWriteInterface $data
139
     *
140
     * @return string|false
141
     * @throws \DomainException
142
     * @throws \InvalidArgumentException
143
     * @throws \LogicException
144
     * @throws \UnexpectedValueException
145
     */
146 11
    private function getXmlFileContents(EveApiReadWriteInterface $data)
147
    {
148
        // BaseSection/ApiHash.xml
149 11
        $cacheFile = sprintf('%s%s/%s%s.xml',
150 11
            $this->getCachePath(),
151 10
            ucfirst($data->getEveApiSectionName()),
152 10
            ucfirst($data->getEveApiName()),
153 10
            $data->getHash());
154 10
        if (false === $xml = $this->safeFileRead($cacheFile)) {
155 1
            $messagePrefix = sprintf('Failed to retrieve XML file %s during the retrieval of', $cacheFile);
156 1
            $this->getYem()
157 1
                ->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($messagePrefix, $data));
158 1
            return false;
159
        }
160 9
        if ('' === $xml) {
161 1
            $messagePrefix = sprintf('Received an empty XML file %s during the retrieval of', $cacheFile);
162 1
            $this->getYem()
163 1
                ->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
164 1
            return false;
165
        }
166 8
        $data->setEveApiXml($xml);
167 8
        if ($this->isExpired($data)) {
168 6
            $this->deleteWithRetry($cacheFile);
169 6
            $data->setEveApiXml('');
170 6
            return false;
171
        }
172 2
        $messagePrefix = sprintf('Using cached XML file %s during the retrieval of', $cacheFile);
173 2
        $this->getYem()
174 2
            ->triggerLogEvent('Yapeal.Log.log', Logger::DEBUG, $this->createEveApiMessage($messagePrefix, $data));
175 2
        return $xml;
176
    }
177
    /**
178
     * Enforces minimum 5 minute cache time and does some basic checks to see if XML DateTimes are valid.
179
     *
180
     * @param EveApiReadWriteInterface $data
181
     *
182
     * @return bool
183
     * @throws \DomainException
184
     * @throws \InvalidArgumentException
185
     * @throws \LogicException
186
     * @throws \UnexpectedValueException
187
     */
188 8
    private function isExpired(EveApiReadWriteInterface $data): bool
189
    {
190 8
        libxml_clear_errors();
191 8
        libxml_use_internal_errors(true);
192
        try {
193 8
            $simple = new \SimpleXMLElement($data->getEveApiXml());
194 1
        } catch (\Exception $exc) {
195 1
            $messagePrefix = 'The XML cause SimpleXMLElement exception during the retrieval of';
196 1
            $this->getYem()
197 1
                ->triggerLogEvent('Yapeal.Log.log',
198 1
                    Logger::WARNING,
199 1
                    $this->createEveApiMessage($messagePrefix, $data),
200 1
                    ['exception' => $exc]);
201 1
            $this->checkLibXmlErrors($data, $this->getYem());
202 1
            libxml_use_internal_errors(false);
203 1
            return true;
204
        }
205 7
        libxml_use_internal_errors(false);
206
        /** @noinspection PhpUndefinedFieldInspection */
207 7
        if (null === $currentTime = $simple->currentTime[0]) {
208 1
            $messagePrefix = 'Cached XML file missing required currentTime element during the retrieval of';
209 1
            $this->getYem()
210 1
                ->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
211 1
            return true;
212
        }
213
        /** @noinspection PhpUndefinedFieldInspection */
214 6
        if (null === $cachedUntil = $simple->cachedUntil[0]) {
215 1
            $messagePrefix = 'Cached XML file missing required cachedUntil element during the retrieval of';
216 1
            $this->getYem()
217 1
                ->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
218 1
            return true;
219
        }
220 5
        $eveFormat = 'Y-m-d H:i:sP';
221 5
        $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
222 5
        $current = \DateTimeImmutable::createFromFormat($eveFormat, $currentTime . '+00:00');
223 5
        $until = \DateTimeImmutable::createFromFormat($eveFormat, $cachedUntil . '+00:00');
224 5
        if (false === $now || false === $current || false === $until) {
225 1
            $messagePrefix = 'Failed to get DateTime instance for "now" or currentTime or cachedUntil during the retrieval of';
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 127 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
226 1
            $this->getYem()
227 1
                ->triggerLogEvent('Yapeal.Log.log', Logger::ERROR, $this->createEveApiMessage($messagePrefix, $data));
228 1
            return true;
229
        }
230
        // At minimum use cached XML for 5 minutes.
231 4
        if ($now <= $current->add(new \DateInterval('PT5M'))) {
232 1
            return false;
233
        }
234 3
        return ($until <= $now);
235
    }
236
    /**
237
     * @return bool
238
     */
239 12
    private function shouldRetrieve(): bool
240
    {
241 12
        return $this->retrieve;
242
    }
243
    /**
244
     * @var string $cachePath
245
     */
246
    private $cachePath;
247
    /**
248
     * @var bool $retrieve
249
     */
250
    private $retrieve = false;
251
}
252