XmlReader::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 1
eloc 0
c 3
b 0
f 0
nc 1
nop 2
dl 0
loc 4
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 *  This file is part of the Micro framework package.
7
 *
8
 *  (c) Stanislau Komar <[email protected]>
9
 *
10
 *  For the full copyright and license information, please view the LICENSE
11
 *  file that was distributed with this source code.
12
 */
13
14
namespace Micro\Library\DTO\Reader;
15
16
use Micro\Library\DTO\Merger\MergerFactoryInterface;
17
18
/**
19
 * @TODO: Get XSD api version
20
 */
21
class XmlReader implements ReaderInterface
22
{
23
    /**
24
     * @param iterable<string> $classDefinitionFilesCollection
25
     */
26 6
    public function __construct(
27
        private iterable $classDefinitionFilesCollection,
28
        private MergerFactoryInterface $mergerFactory
29
    ) {
30 6
    }
31
32 6
    public function read(): iterable
33
    {
34 6
        $classCollection = [];
35 6
        foreach ($this->classDefinitionFilesCollection as $filePath) {
36 6
            $xml = $this->createDom($filePath);
37
38 4
            foreach ($xml->getElementsByTagName(self::TAG_CLASS_DEFINITION) as $classDef) {
39 4
                $classCollection[] = $this->parse($classDef);
40
            }
41
        }
42
43 4
        return $this->mergerFactory->create($classCollection)->merge();
44
    }
45
46
    /**
47
     * @param \DOMDocument $document
48
     *
49
     * @return string[]
50
     */
51 5
    protected function lookupXsd(\DOMDocument $document): array
52
    {
53 5
        $schemaLocation = $document->getElementsByTagName('dto')[0]->getAttribute('xsi:schemaLocation');
54 5
        if (!$schemaLocation) {
55
            throw new \RuntimeException('XSD Scheme should be declared on <dto xsi:schemaLocation="">');
56
        }
57
58 5
        $location = explode(' ', $schemaLocation);
59 5
        if (2 !== \count($location)) {
60
            throw new \RuntimeException(sprintf('XSD Scheme declaration failed <dto xsi:schemaLocation="%s">', $schemaLocation));
61
        }
62
63 5
        return $location;
64
    }
65
66
    /**
67
     * @param \DOMNode $node
68
     *
69
     * @return array<mixed, mixed>
70
     */
71 4
    protected function parse(\DOMNode $node): array
72
    {
73 4
        $attributes = [];
74
75 4
        if ($node->hasAttributes()) {
76
            /**
77
             * @var \DOMAttr $tmpAttribute
78
             *
79
             * Ignored psalm because hasAttribute() checks `attributes` on NULL
80
             *
81
             * @psalm-suppress PossiblyNullIterator
82
             */
83 4
            foreach ($node->attributes as $tmpAttribute) {
84 4
                $attributes[$tmpAttribute->nodeName] = $tmpAttribute->nodeValue;
85
            }
86
        }
87
88 4
        if ($node->hasChildNodes()) {
89
            /** @var \DOMNode $child */
90 4
            foreach ($node->childNodes as $child) {
91 4
                $childName = $child->nodeName;
92 4
                if ('#text' === $childName) {
93 4
                    continue;
94
                }
95
96 4
                if (!isset($attributes[$childName]) || !\is_array($attributes[$childName])) {
97 4
                    $attributes[$childName] = [];
98
                }
99
100 4
                $attributes[$childName][] = $this->parse($child);
101
            }
102
        }
103
104 4
        return $attributes;
105
    }
106
107 6
    protected function createDom(string $filePath): \DOMDocument
108
    {
109 6
        if (!file_exists($filePath)) {
110 1
            throw new \RuntimeException(sprintf('File %s is not found', $filePath));
111
        }
112
113 5
        if (!is_readable($filePath)) {
114
            throw new \RuntimeException(sprintf('Has no access to read the file %s', $filePath));
115
        }
116
117 5
        $xml = new \DOMDocument();
118 5
        $xml->load($filePath);
119
120 5
        $xsdSchemaCfg = $this->lookupXsd($xml);
121 5
        $xsdSchemaLocation = $xsdSchemaCfg[1];
122
123 5
        libxml_use_internal_errors(true);
124
125 5
        if ($xml->schemaValidate($xsdSchemaLocation)) {
126 4
            return $xml;
127
        }
128
129 1
        $errs = [];
130
131 1
        foreach (libxml_get_errors() as $error) {
132 1
            $errs[] = sprintf('%s in file `%s` on line %d', $error->message, $error->file, $error->line);
133
        }
134
135 1
        $errorMessage = implode("\n ", $errs);
136
137 1
        libxml_use_internal_errors(false);
138
139 1
        throw new \RuntimeException(sprintf("Schema validation exception: \r\n %s\r", $errorMessage));
140
    }
141
}
142