Passed
Pull Request — master (#12)
by Stanislau
03:07
created

XmlReader::__construct()   A

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: Temporary solution. MVP
20
 * @TODO: Get XSD api version
21
 */
22
class XmlReader implements ReaderInterface
23
{
24
    /**
25
     * @param iterable<string> $classDefinitionFilesCollection
26
     */
27 6
    public function __construct(
28
        private iterable $classDefinitionFilesCollection,
29
        private MergerFactoryInterface $mergerFactory
30
    ) {
31 6
    }
32
33 6
    public function read(): iterable
34
    {
35 6
        $classCollection = [];
36 6
        foreach ($this->classDefinitionFilesCollection as $filePath) {
37 6
            $xml = $this->createDom($filePath);
38
39 4
            foreach ($xml->getElementsByTagName(self::TAG_CLASS_DEFINITION) as $classDef) {
40 4
                $classCollection[] = $this->parse($classDef);
41
            }
42
        }
43
44 4
        return $this->mergerFactory->create($classCollection)->merge();
45
    }
46
47
    /**
48
     * @param \DOMDocument $document
49
     *
50
     * @return string[]
51
     */
52 5
    protected function lookupXsd(\DOMDocument $document): array
53
    {
54 5
        $schemaLocation = $document->getElementsByTagName('dto')[0]->getAttribute('xsi:schemaLocation');
55 5
        if (!$schemaLocation) {
56
            throw new \RuntimeException('XSD Scheme should be declared on <dto xsi:schemaLocation="">');
57
        }
58
59 5
        $location = explode(' ', $schemaLocation);
60 5
        if (2 !== \count($location)) {
61
            throw new \RuntimeException(sprintf('XSD Scheme declaration failed <dto xsi:schemaLocation="%s">', $schemaLocation));
62
        }
63
64 5
        return $location;
65
    }
66
67
    /**
68
     * @param \DOMNode $node
69
     * @return array
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 4
            foreach ($node->attributes as $tmpAttribute) {
80 4
                $attributes[$tmpAttribute->nodeName] = $tmpAttribute->nodeValue;
81
            }
82
        }
83
84 4
        if ($node->hasChildNodes()) {
85
            /** @var \DOMNode $child */
86 4
            foreach ($node->childNodes as $child) {
87 4
                $childName = $child->nodeName;
88 4
                if ('#text' === $childName) {
89 4
                    continue;
90
                }
91
92 4
                $attributes[$childName][] = $this->parse($child);
93
            }
94
        }
95
96 4
        return $attributes;
97
    }
98
99 6
    protected function createDom(string $filePath): \DOMDocument
100
    {
101 6
        if (!file_exists($filePath)) {
102 1
            throw new \RuntimeException(sprintf('File %s is not found', $filePath));
103
        }
104
105 5
        if (!is_readable($filePath)) {
106
            throw new \RuntimeException(sprintf('Has no access to read the file %s', $filePath));
107
        }
108
109 5
        $xml = new \DOMDocument();
110 5
        $xml->load($filePath);
111
112 5
        $xsdSchemaCfg = $this->lookupXsd($xml);
113 5
        $xsdSchemaLocation = $xsdSchemaCfg[1];
114
115 5
        libxml_use_internal_errors(true);
116
117 5
        if ($xml->schemaValidate($xsdSchemaLocation)) {
118 4
            return $xml;
119
        }
120
121 1
        $errs = [];
122
123 1
        foreach (libxml_get_errors() as $error) {
124 1
            $errs[] = sprintf('%s in file `%s` on line %d', $error->message, $error->file, $error->line);
125
        }
126
127 1
        $errorMessage = implode("\n ", $errs);
128
129 1
        libxml_use_internal_errors(false);
130
131 1
        throw new \RuntimeException(sprintf("Schema validation exception: \r\n %s\r", $errorMessage));
132
    }
133
}
134