Completed
Push — master ( e097f5...045570 )
by Eric
06:56
created

XmlClassMetadataLoader::loadDocument()   B

Complexity

Conditions 6
Paths 13

Size

Total Lines 36
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 6.027

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 36
ccs 20
cts 22
cp 0.9091
rs 8.439
cc 6
eloc 19
nc 13
nop 1
crap 6.027
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 160
    protected function loadFile($file)
23
    {
24 160
        $data = [];
25 160
        $xml = simplexml_import_dom($this->loadDocument($file));
26
27 140
        foreach ($xml->class as $class) {
28 140
            $data[(string) $class['name']] = $this->loadClass($class);
29 70
        }
30
31 140
        return $data;
32
    }
33
34
    /**
35
     * @param \SimpleXMLElement $class
36
     *
37
     * @return mixed[]
38
     */
39 140
    private function loadClass(\SimpleXMLElement $class)
40
    {
41 140
        $definition = [];
42
43 140
        if (isset($class['exclusion-policy'])) {
44 84
            $definition['exclusion_policy'] = (string) $class['exclusion-policy'];
45 42
        }
46
47 140
        if (isset($class['order'])) {
48 92
            $definition['order'] = (string) $class['order'];
49 46
        }
50
51 140
        if (isset($class['readable'])) {
52 80
            $definition['readable'] = (string) $class['readable'] === 'true';
53 40
        }
54
55 140
        if (isset($class['writable'])) {
56 80
            $definition['writable'] = (string) $class['writable'] === 'true';
57 40
        }
58
59 140
        if (isset($class['xml-root'])) {
60 84
            $definition['xml_root'] = (string) $class['xml-root'];
61 42
        }
62
63 140
        $properties = [];
64
65 140
        foreach ($class->property as $property) {
66 136
            $properties[(string) $property['name']] = $this->loadProperty($property);
67 70
        }
68
69 140
        $definition['properties'] = $properties;
70
71 140
        return $definition;
72
    }
73
74
    /**
75
     * @param \SimpleXMLElement $element
76
     *
77
     * @return mixed[]
78
     */
79 136
    private function loadProperty(\SimpleXMLElement $element)
80
    {
81 136
        $property = [];
82
83 136
        if (isset($element['alias'])) {
84 84
            $property['alias'] = (string) $element['alias'];
85 42
        }
86
87 136
        if (isset($element['type'])) {
88 84
            $property['type'] = (string) $element['type'];
89 42
        }
90
91 136
        if (isset($element['exclude'])) {
92 84
            $property['exclude'] = (string) $element['exclude'] === 'true';
93 42
        }
94
95 136
        if (isset($element['expose'])) {
96 80
            $property['expose'] = (string) $element['expose'] === 'true';
97 40
        }
98
99 136
        if (isset($element['accessor'])) {
100 84
            $property['accessor'] = (string) $element['accessor'];
101 42
        }
102
103 136
        if (isset($element['readable'])) {
104 80
            $property['readable'] = (string) $element['readable'] === 'true';
105 40
        }
106
107 136
        if (isset($element['writable'])) {
108 80
            $property['writable'] = (string) $element['writable'] === 'true';
109 40
        }
110
111 136
        if (isset($element['mutator'])) {
112 84
            $property['mutator'] = (string) $element['mutator'];
113 42
        }
114
115 136
        if (isset($element['since'])) {
116 84
            $property['since'] = (string) $element['since'];
117 42
        }
118
119 136
        if (isset($element['until'])) {
120 84
            $property['until'] = (string) $element['until'];
121 42
        }
122
123 136
        if (isset($element['max-depth'])) {
124 80
            $property['max_depth'] = (string) $element['max-depth'];
125 40
        }
126
127 136
        foreach ($element->group as $group) {
128 84
            $property['groups'][] = (string) $group;
129 68
        }
130
131 136
        if (isset($element['xml-attribute'])) {
132 80
            $property['xml_attribute'] = (string) $element['xml-attribute'] === 'true';
133 40
        }
134
135 136
        if (isset($element['xml-value'])) {
136 80
            $property['xml_value'] = (string) $element['xml-value'] === 'true';
137 40
        }
138
139 136
        if (isset($element['xml-inline'])) {
140 80
            $property['xml_inline'] = (string) $element['xml-inline'] === 'true';
141 40
        }
142
143 136
        if (isset($element['xml-entry'])) {
144 84
            $property['xml_entry'] = (string) $element['xml-entry'];
145 42
        }
146
147 136
        if (isset($element['xml-entry-attribute'])) {
148 84
            $property['xml_entry_attribute'] = (string) $element['xml-entry-attribute'];
149 42
        }
150
151 136
        if (isset($element['xml-key-as-attribute'])) {
152 80
            $property['xml_key_as_attribute'] = (string) $element['xml-key-as-attribute'] === 'true';
153 40
        }
154
155 136
        if (isset($element['xml-key-as-node'])) {
156 80
            $property['xml_key_as_node'] = (string) $element['xml-key-as-node'] === 'true';
157 40
        }
158
159 136
        return $property;
160
    }
161
162
    /**
163
     * @param string $file
164
     *
165
     * @return \DOMDocument
166
     */
167 160
    private function loadDocument($file)
168
    {
169 160
        $data = trim(@file_get_contents($file));
170
171 160
        if (empty($data)) {
172 4
            throw new \InvalidArgumentException(sprintf('The XML mapping file "%s" is not valid.', $file));
173
        }
174
175 156
        $internalErrors = libxml_use_internal_errors();
176 156
        $disableEntities = libxml_disable_entity_loader();
177
178 156
        $this->setLibXmlState(true, true);
179
180 156
        $document = new \DOMDocument();
181 156
        $document->validateOnParse = true;
182
183 156
        if (!@$document->loadXML($data, LIBXML_NONET | LIBXML_COMPACT)) {
184 4
            $this->dispatchErrors($file, $internalErrors, $disableEntities);
185
        }
186
187 152
        $document->normalizeDocument();
188
189 152
        if (!@$document->schemaValidateSource(file_get_contents(__DIR__.'/../Resource/mapping.xsd'))) {
190 12
            $this->dispatchErrors($file, $internalErrors, $disableEntities);
191
        }
192
193 140
        foreach ($document->childNodes as $child) {
194 140
            if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
195 70
                throw new \InvalidArgumentException('The document type is not allowed.');
196
            }
197 70
        }
198
199 140
        $this->setLibXmlState($internalErrors, $disableEntities);
200
201 140
        return $document;
202
    }
203
204
    /**
205
     * @param string $file
206
     * @param bool  $internalErrors
207
     * @param bool  $disableEntities
208
     */
209 16
    private function dispatchErrors($file, $internalErrors, $disableEntities)
210
    {
211 16
        $errors = [];
212
213 16 View Code Duplication
        foreach (libxml_get_errors() as $error) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
214 16
            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
215 16
                $error->level === LIBXML_ERR_WARNING ? 'WARNING' : 'ERROR',
216 16
                $error->code,
217 16
                trim($error->message),
218 8
                $file,
219 16
                $error->line,
220 16
                $error->column
221 8
            );
222 8
        }
223
224 16
        $this->setLibXmlState($internalErrors, $disableEntities);
225
226 16
        throw new \InvalidArgumentException(implode(PHP_EOL, $errors));
227
    }
228
229
    /**
230
     * @param bool $internalErrors
231
     * @param bool $disableEntities
232
     */
233 156
    private function setLibXmlState($internalErrors, $disableEntities)
234
    {
235 156
        libxml_use_internal_errors($internalErrors);
236 156
        libxml_disable_entity_loader($disableEntities);
237 156
        libxml_clear_errors();
238 156
    }
239
}
240