Completed
Pull Request — master (#11)
by Eric
04:56
created

XmlClassMetadataLoader::loadClass()   C

Complexity

Conditions 7
Paths 64

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 7

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 34
ccs 23
cts 23
cp 1
rs 6.7272
cc 7
eloc 17
nc 64
nop 1
crap 7
1
<?php
2
3
/*
4
 * This file is part of the Ivory Serializer package.
5
 *
6
 * (c) Eric GELOEN <[email protected]>
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Ivory\Serializer\Mapping\Loader;
13
14
/**
15
 * @author GeLo <[email protected]>
16
 */
17
class XmlClassMetadataLoader extends AbstractFileClassMetadataLoader
18
{
19
    /**
20
     * {@inheritdoc}
21
     */
22 152
    protected function loadFile($file)
23
    {
24 152
        $data = [];
25 152
        $xml = simplexml_import_dom($this->loadDocument($file));
26
27 132
        foreach ($xml->class as $class) {
28 132
            $data[(string) $class['name']] = $this->loadClass($class);
29 66
        }
30
31 132
        return $data;
32
    }
33
34
    /**
35
     * @param \SimpleXMLElement $class
36
     *
37
     * @return mixed[]
38
     */
39 132
    private function loadClass(\SimpleXMLElement $class)
40
    {
41 132
        $definition = [];
42
43 132
        if (isset($class['exclusion-policy'])) {
44 84
            $definition['exclusion_policy'] = (string) $class['exclusion-policy'];
45 42
        }
46
47 132
        if (isset($class['order'])) {
48 92
            $definition['order'] = (string) $class['order'];
49 46
        }
50
51 132
        if (isset($class['readable'])) {
52 80
            $definition['readable'] = (string) $class['readable'] === 'true';
53 40
        }
54
55 132
        if (isset($class['writable'])) {
56 80
            $definition['writable'] = (string) $class['writable'] === 'true';
57 40
        }
58
59 132
        if (isset($class['xml-root'])) {
60 84
            $definition['xml_root'] = (string) $class['xml-root'];
61 42
        }
62
63 132
        $properties = [];
64
65 132
        foreach ($class->property as $property) {
66 128
            $properties[(string) $property['name']] = $this->loadProperty($property);
67 66
        }
68
69 132
        $definition['properties'] = $properties;
70
71 132
        return $definition;
72
    }
73
74
    /**
75
     * @param \SimpleXMLElement $element
76
     *
77
     * @return mixed[]
78
     */
79 128
    private function loadProperty(\SimpleXMLElement $element)
80
    {
81 128
        $property = [];
82
83 128
        if (isset($element['alias'])) {
84 84
            $property['alias'] = (string) $element['alias'];
85 42
        }
86
87 128
        if (isset($element['type'])) {
88 84
            $property['type'] = (string) $element['type'];
89 42
        }
90
91 128
        if (isset($element['exclude'])) {
92 84
            $property['exclude'] = (string) $element['exclude'] === 'true';
93 42
        }
94
95 128
        if (isset($element['expose'])) {
96 80
            $property['expose'] = (string) $element['expose'] === 'true';
97 40
        }
98
99 128
        if (isset($element['accessor'])) {
100 84
            $property['accessor'] = (string) $element['accessor'];
101 42
        }
102
103 128
        if (isset($element['readable'])) {
104 80
            $property['readable'] = (string) $element['readable'] === 'true';
105 40
        }
106
107 128
        if (isset($element['writable'])) {
108 80
            $property['writable'] = (string) $element['writable'] === 'true';
109 40
        }
110
111 128
        if (isset($element['mutator'])) {
112 84
            $property['mutator'] = (string) $element['mutator'];
113 42
        }
114
115 128
        if (isset($element['since'])) {
116 84
            $property['since'] = (string) $element['since'];
117 42
        }
118
119 128
        if (isset($element['until'])) {
120 84
            $property['until'] = (string) $element['until'];
121 42
        }
122
123 128
        if (isset($element['max-depth'])) {
124 80
            $property['max_depth'] = (string) $element['max-depth'];
125 40
        }
126
127 128
        foreach ($element->group as $group) {
128 84
            $property['groups'][] = (string) $group;
129 64
        }
130
131 128
        if (isset($element['xml-attribute'])) {
132 84
            $property['xml_attribute'] = (string) $element['xml-attribute'] === 'true';
133 42
        }
134
135 128
        if (isset($element['xml-value'])) {
136 80
            $property['xml_value'] = (string) $element['xml-value'] === 'true';
137 40
        }
138
139 128
        return $property;
140
    }
141
142
    /**
143
     * @param string $file
144
     *
145
     * @return \DOMDocument
146
     */
147 152
    private function loadDocument($file)
148
    {
149 152
        $data = trim(file_get_contents($file));
150
151 152
        if (empty($data)) {
152 4
            throw new \InvalidArgumentException();
153
        }
154
155 148
        $internalErrors = libxml_use_internal_errors();
156 148
        $disableEntities = libxml_disable_entity_loader();
157
158 148
        $this->setState(true, true);
159
160 148
        $document = new \DOMDocument();
161 148
        $document->validateOnParse = true;
162
163 148
        if (!$document->loadXML($data, LIBXML_NONET | LIBXML_COMPACT)) {
164 4
            $this->setState($internalErrors, $disableEntities);
165
166 4
            throw new \InvalidArgumentException();
167
        }
168
169 144
        $document->normalizeDocument();
170
171 144
        foreach ($document->childNodes as $child) {
172 144
            if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
173 74
                throw new \InvalidArgumentException('The document type is not allowed.');
174
            }
175 70
        }
176
177 140
        $this->validateDocument($document, $internalErrors, $disableEntities);
178 132
        $this->setState($internalErrors, $disableEntities);
179
180 132
        return $document;
181
    }
182
183
    /**
184
     * @param \DOMDocument $document
185
     * @param bool         $internalErrors
186
     * @param bool         $disableEntities
187
     */
188 140
    private function validateDocument(\DOMDocument $document, $internalErrors, $disableEntities)
189
    {
190 140
        if (@$document->schemaValidateSource(file_get_contents(__DIR__.'/../Resource/mapping.xsd'))) {
191 132
            return;
192
        }
193
194 8
        $errors = [];
195
196 8
        foreach (libxml_get_errors() as $error) {
197 8
            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
198 8
                LIBXML_ERR_WARNING === $error->level ? 'WARNING' : 'ERROR',
199 8
                $error->code,
200 8
                trim($error->message),
201 8
                $error->file ?: 'n/a',
202 8
                $error->line,
203 8
                $error->column
204 4
            );
205 4
        }
206
207 8
        $this->setState($internalErrors, $disableEntities);
208
209 8
        throw new \InvalidArgumentException(implode(PHP_EOL, $errors));
210
    }
211
212
    /**
213
     * @param bool $internalErrors
214
     * @param bool $disableEntities
215
     */
216 148
    private function setState($internalErrors, $disableEntities)
217
    {
218 148
        libxml_use_internal_errors($internalErrors);
219 148
        libxml_disable_entity_loader($disableEntities);
220 148
        libxml_clear_errors();
221 148
    }
222
}
223