Completed
Push — master ( 5f00d3...4dc6dd )
by Gerben
01:58
created

Validator::isXMLValid()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2.003

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 15
ccs 10
cts 11
cp 0.9091
rs 9.9332
cc 2
nc 2
nop 2
crap 2.003
1
<?php declare(strict_types=1);
2
3
namespace Hyperized\Xml;
4
5
use DOMDocument;
6
use Exception;
7
use Hyperized\Xml\Constants\ErrorMessages;
8
use Hyperized\Xml\Constants\Strings;
9
use Hyperized\Xml\Exceptions\FileCouldNotBeOpenedException;
10
use Hyperized\Xml\Exceptions\InvalidXml;
11
use function is_string;
12
use LibXMLError;
13
14
/**
15
 * Class Validator
16
 *
17
 * @package Hyperized\Xml
18
 * Based on: http://stackoverflow.com/a/30058598/1757763
19
 */
20
final class Validator implements ValidatorInterface
21
{
22
    /**
23
     * @var string
24
     */
25
    private $version = Strings::VERSION;
26
    /**
27
     * @var string
28
     */
29
    private $encoding = Strings::UTF_8;
30
31
    /**
32
     * @param  string      $xmlPath
33
     * @param  string|null $xsdPath
34
     * @return bool
35
     * @throws FileCouldNotBeOpenedException
36
     * @throws InvalidXml
37
     */
38 8
    public function isXMLFileValid(string $xmlPath, string $xsdPath = null): bool
39
    {
40 8
        return $this->isXMLStringValid(self::getFileContent($xmlPath), $xsdPath);
41
    }
42
43
    /**
44
     * @param  string      $xml
45
     * @param  string|null $xsdPath
46
     * @return bool
47
     * @throws InvalidXml
48
     */
49 12
    public function isXMLStringValid(string $xml, string $xsdPath = null): bool
50
    {
51 12
        if (is_string($xsdPath)) {
52 2
            return $this->isXMLValid($xml, $xsdPath);
53
        }
54 10
        return $this->isXMLValid($xml);
55
    }
56
57
    /**
58
     * @param  string      $xmlContent
59
     * @param  string|null $xsdPath
60
     * @return bool
61
     * @throws InvalidXml
62
     */
63 12
    private function isXMLValid(string $xmlContent, string $xsdPath = null): bool
64
    {
65 12
        self::checkEmptyWhenTrimmed($xmlContent);
66
67 4
        libxml_use_internal_errors(true);
68
69 4
        $document = new DOMDocument($this->version, $this->encoding);
70 4
        $document->loadXML($xmlContent);
71 4
        if (isset($xsdPath)) {
72
            $document->schemaValidate($xsdPath);
73
        }
74 4
        $errors = libxml_get_errors();
75 4
        libxml_clear_errors();
76 4
        self::parseErrors($errors);
77 2
        return true;
78
    }
79
80
    /**
81
     * @param  string $xmlContent
82
     * @throws InvalidXml
83
     */
84 12
    private static function checkEmptyWhenTrimmed(string $xmlContent): void
85
    {
86 12
        if (trim($xmlContent) === '') {
87 8
            throw new InvalidXml(ErrorMessages::XML_EMPTY_TRIMMED);
88
        }
89 4
    }
90
91
    /**
92
     * @param  array|null $errors
93
     * @throws InvalidXml
94
     */
95 4
    private static function parseErrors(?array $errors): void
96
    {
97 4
        if (!empty($errors)) {
98 2
            $reduced = array_reduce(
99 2
                $errors,
100
                static function (
101
                    ?array $carry,
102
                    LibXMLError $item
103
                ): array {
104 2
                    $carry[] = trim($item->message);
105 2
                    return $carry;
106 2
                }
107
            );
108
109 2
            if (!empty($reduced)) {
110 2
                throw new InvalidXml(implode(Strings::NEW_LINE, $reduced));
111
            }
112
        }
113 2
    }
114
115
    /**
116
     * @param  string $fileName
117
     * @return string
118
     * @throws FileCouldNotBeOpenedException
119
     */
120 8
    private static function getFileContent(string $fileName): string
121
    {
122
        try {
123 8
            $contents = file_get_contents($fileName);
124 2
        } catch (Exception $exception) {
125 2
            throw new FileCouldNotBeOpenedException(ErrorMessages::NO_FILE_CONTENTS);
126
        }
127
128 6
        return !$contents ? $contents : '';
129
    }
130
131
    /**
132
     * @return string
133
     */
134 2
    public function getVersion(): string
135
    {
136 2
        return $this->version;
137
    }
138
139
    /**
140
     * @param string $version
141
     */
142 2
    public function setVersion(string $version): void
143
    {
144 2
        $this->version = $version;
145 2
    }
146
147
    /**
148
     * @return string
149
     */
150 2
    public function getEncoding(): string
151
    {
152 2
        return $this->encoding;
153
    }
154
155
    /**
156
     * @param string $encoding
157
     */
158 2
    public function setEncoding(string $encoding): void
159
    {
160 2
        $this->encoding = $encoding;
161 2
    }
162
}
163