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

Validator   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 207
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 96.72%

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 7
dl 0
loc 207
ccs 118
cts 122
cp 0.9672
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C validateEveApi() 0 64 8
B checkForValidDateTimes() 0 31 3
A getDom() 0 7 2
B getXsdFileContents() 0 52 6
1
<?php
2
declare(strict_types = 1);
3
/**
4
 * Contains Validator 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) 2015-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 2015-2017 Michael Cummings
32
 * @license   LGPL-3.0+
33
 * @author    Michael Cummings <[email protected]>
34
 */
35
namespace Yapeal\Xsd;
36
37
use Yapeal\Event\EveApiEventEmitterTrait;
38
use Yapeal\Event\EveApiEventInterface;
39
use Yapeal\Event\MediatorInterface;
40
use Yapeal\Event\YEMAwareInterface;
41
use Yapeal\Exception\YapealFileSystemException;
42
use Yapeal\FileSystem\RelativeFileSearchTrait;
43
use Yapeal\FileSystem\SafeFileHandlingTrait;
44
use Yapeal\Log\Logger;
45
use Yapeal\Xml\EveApiReadWriteInterface;
46
use Yapeal\Xml\LibXmlChecksTrait;
47
48
/**
49
 * Class Validator
50
 */
51
class Validator implements ValidatorInterface, YEMAwareInterface
52
{
53
    use EveApiEventEmitterTrait;
54
    use LibXmlChecksTrait;
55
    use RelativeFileSearchTrait;
56
    use SafeFileHandlingTrait;
57
    /**
58
     * Constructor.
59
     *
60
     * @param string $dir Base directory where Eve API XSD files can be found.
61
     */
62 12
    public function __construct(string $dir = __DIR__)
63
    {
64 12
        $this->setRelativeBaseDir($dir . '/');
65
    }
66
    /**
67
     * @param EveApiEventInterface $event
68
     * @param string               $eventName
69
     * @param MediatorInterface    $yem
70
     *
71
     * @return EveApiEventInterface
72
     * @throws \DomainException
73
     * @throws \InvalidArgumentException
74
     * @throws \LogicException
75
     * @throws \UnexpectedValueException
76
     */
77 11
    public function validateEveApi(
78
        EveApiEventInterface $event,
79
        string $eventName,
80
        MediatorInterface $yem
81
    ): EveApiEventInterface {
82 11
        $this->setYem($yem);
83 11
        $data = $event->getData();
84 11
        $yem->triggerLogEvent('Yapeal.Log.log',
85 11
            Logger::DEBUG,
86 11
            $this->getReceivedEventMessage($data, $eventName, __CLASS__));
87 11
        if ('' === $xml = $data->getEveApiXml()) {
88 1
            $messagePrefix = 'Given empty XML during the validation of';
89 1
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
90 1
            return $event;
91
        }
92 10
        if (false !== strpos($xml, '<!DOCTYPE html')) {
93 1
            $messagePrefix = 'Received HTML error doc instead of XML data during the validation of';
94 1
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
95
            // Cache received error html.
96 1
            $apiName = $data->getEveApiName();
97 1
            $data->setEveApiName('Invalid_' . $apiName);
98 1
            $this->emitEvents($data, 'preserve', 'Yapeal.Xml.Error');
99 1
            $data->setEveApiName($apiName);
100 1
            return $event;
101
        }
102 9
        if (false === $xsdContents = $this->getXsdFileContents($data)) {
103 3
            return $event;
104
        }
105 6
        libxml_clear_errors();
106 6
        libxml_use_internal_errors(true);
107 6
        $dom = $this->getDom();
108 6
        if (false === $dom->loadXML($xml)) {
109 1
            $messagePrefix = 'DOM could not load XML during the validation of';
110 1
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
111 1
            $this->checkLibXmlErrors($data, $yem);
112 1
            libxml_use_internal_errors(false);
113 1
            return $event;
114
        }
115
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
116 5
        if (false === @$dom->schemaValidateSource($xsdContents)) {
117 1
            $messagePrefix = 'DOM schema could not validate XML during the validation of';
118 1
            $yem->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
119
            // Cache error causing XML.
120 1
            $apiName = $data->getEveApiName();
121 1
            $data->setEveApiName('Invalid_' . $apiName);
122 1
            $this->emitEvents($data, 'preserve', 'Yapeal.Xml.Error');
123 1
            $data->setEveApiName($apiName);
124 1
            $this->checkLibXmlErrors($data, $yem);
125 1
            libxml_use_internal_errors(false);
126 1
            return $event;
127
        }
128 4
        if (false === $this->checkForValidDateTimes($dom, $data)) {
129 2
            return $event;
130
        }
131 2
        libxml_use_internal_errors(false);
132 2
        $messagePrefix = 'Successfully validated the XML during the validation of';
133 2
        $yem->triggerLogEvent('Yapeal.Log.log', Logger::INFO, $this->createEveApiMessage($messagePrefix, $data));
134
        // Check for XML error element.
135 2
        if (false !== strpos($xml, '<error ')) {
136 1
            $this->emitEvents($data, 'start', 'Yapeal.Xml.Error');
137 1
            return $event;
138
        }
139 1
        return $event->setHandledSufficiently();
140
    }
141
    /**
142
     * @param \DOMDocument             $dom
143
     * @param EveApiReadWriteInterface $data
144
     *
145
     * @return bool
146
     * @throws \DomainException
147
     * @throws \InvalidArgumentException
148
     * @throws \LogicException
149
     * @throws \UnexpectedValueException
150
     */
151 4
    private function checkForValidDateTimes(\DOMDocument $dom, EveApiReadWriteInterface $data)
152
    {
153 4
        libxml_clear_errors();
154 4
        libxml_use_internal_errors(true);
155 4
        $simple = simplexml_import_dom($dom);
156 4
        $eveFormat = 'Y-m-d H:i:sP';
157 4
        $current = \DateTimeImmutable::createFromFormat($eveFormat, $simple->currentTime[0] . '+00:00');
158 4
        $until = \DateTimeImmutable::createFromFormat($eveFormat, $simple->cachedUntil[0] . '+00:00');
159 4
        if ($until <= $current) {
160 1
            $messagePrefix = sprintf('CachedUntil is invalid was given %s and currentTime is %s during the validation of',
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 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...
161 1
                $until->format($eveFormat),
162 1
                $current->format($eveFormat));
163 1
            $this->getYem()
164 1
                ->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
165 1
            $mess = 'Please report the above logged error to CCP so they can fixed it';
166 1
            $this->getYem()
167 1
                ->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $mess);
168 1
            return false;
169
        }
170
        // Current plus a day.
171 3
        if ($until > $current->add(new \DateInterval('P1D'))) {
172 1
            $messagePrefix = sprintf('CachedUntil is excessively long was given %s and it is currently %s during the'
173 1
                . ' validation of',
174 1
                $until->format($eveFormat),
175 1
                $current->format($eveFormat));
176 1
            $this->getYem()
177 1
                ->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
178 1
            return false;
179
        }
180 2
        return true;
181
    }
182
    /**
183
     * @return \DOMDocument
184
     */
185 6
    private function getDom(): \DOMDocument
186
    {
187 6
        if (null === $this->dom) {
188 6
            $this->dom = new \DOMDocument();
189
        }
190 6
        return $this->dom;
191
    }
192
    /**
193
     * @param EveApiReadWriteInterface $data
194
     *
195
     * @return false|string
196
     * @throws \DomainException
197
     * @throws \InvalidArgumentException
198
     * @throws \LogicException
199
     * @throws \UnexpectedValueException
200
     */
201 9
    private function getXsdFileContents(EveApiReadWriteInterface $data)
202
    {
203
        try {
204 9
            $xsdFile = $this->findRelativeFileWithPath(ucfirst($data->getEveApiSectionName()),
205 9
                $data->getEveApiName(),
206 9
                'xsd');
207 1
        } catch (YapealFileSystemException $exc) {
208 1
            $messagePrefix = 'Failed to find accessible XSD file during the validation of';
209 1
            $this->getYem()
210 1
                ->triggerLogEvent('Yapeal.Log.log',
211 1
                    Logger::WARNING,
212 1
                    $this->createEveApiMessage($messagePrefix, $data),
213 1
                    ['exception' => $exc]);
214 1
            return false;
215
        }
216 8
        $contents = $this->safeFileRead($xsdFile);
217 8
        if (false === $contents) {
218
            $messagePrefix = sprintf('Failed to read XSD file %s during the validation of', $xsdFile);
219
            $this->getYem()
220
                ->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
221
            return false;
222
        }
223 8
        if ('' === $contents) {
224 1
            $messagePrefix = sprintf('Received an empty XSD file %s during the validation of', $xsdFile);
225 1
            $this->getYem()
226 1
                ->triggerLogEvent('Yapeal.Log.log', Logger::WARNING, $this->createEveApiMessage($messagePrefix, $data));
227 1
            return false;
228
        }
229 7
        libxml_clear_errors();
230 7
        libxml_use_internal_errors(true);
231
        try {
232 7
            if (false !== new \SimpleXMLElement($contents)) {
233 6
                $messagePrefix = sprintf('Using XSD file %s during the validation of', $xsdFile);
234 6
                $this->getYem()
235 6
                    ->triggerLogEvent('Yapeal.Log.log',
236 6
                        Logger::INFO,
237 6
                        $this->createEveApiMessage($messagePrefix, $data));
238 6
                return $contents;
239
            }
240 1
        } catch (\Exception $exc) {
241 1
            $messagePrefix = sprintf('SimpleXMLElement exception caused by XSD file %s during the validation of',
242
                $xsdFile);
243 1
            $this->getYem()
244 1
                ->triggerLogEvent('Yapeal.Log.log',
245 1
                    Logger::WARNING,
246 1
                    $this->createEveApiMessage($messagePrefix, $data),
247 1
                    ['exception' => $exc]);
248 1
            $this->checkLibXmlErrors($data, $this->getYem());
249
        }
250 1
        libxml_use_internal_errors(false);
251 1
        return false;
252
    }
253
    /**
254
     * @var \DOMDocument $dom
255
     */
256
    private $dom;
257
}
258