Completed
Pull Request — master (#11)
by Eric
65:27
created

XmlClassMetadataLoader::dispatchErrors()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 19
Code Lines 12

Duplication

Lines 10
Ratio 52.63 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 10
loc 19
rs 9.2
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 4
eloc 12
nc 2
nop 2
crap 20
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 140
    protected function loadFile($file)
23
    {
24 140
        $data = [];
25 140
        $xml = simplexml_import_dom($this->loadDocument($file));
26
27 120
        foreach ($xml->class as $class) {
28 120
            $data[(string) $class['name']] = $this->loadClass($class);
29 60
        }
30
31 120
        return $data;
32
    }
33
34
    /**
35
     * @param \SimpleXMLElement $class
36
     *
37
     * @return mixed[]
38
     */
39 120
    private function loadClass(\SimpleXMLElement $class)
40
    {
41 120
        $definition = [];
42
43 120
        if (isset($class['exclusion-policy'])) {
44 76
            $definition['exclusion_policy'] = (string) $class['exclusion-policy'];
45 38
        }
46
47 120
        if (isset($class['order'])) {
48 84
            $definition['order'] = (string) $class['order'];
49 42
        }
50
51 120
        if (isset($class['readable'])) {
52 72
            $definition['readable'] = (string) $class['readable'] === 'true';
53 36
        }
54
55 120
        if (isset($class['writable'])) {
56 72
            $definition['writable'] = (string) $class['writable'] === 'true';
57 36
        }
58
59 120
        if (isset($class['xml-root'])) {
60
            $definition['xml_root'] = (string) $class['xml-root'];
61 120
        }
62 116
63 60
        $properties = [];
64
65 120
        foreach ($class->property as $property) {
66
            $properties[(string) $property['name']] = $this->loadProperty($property);
67 120
        }
68
69
        $definition['properties'] = $properties;
70
71
        return $definition;
72
    }
73
74
    /**
75 116
     * @param \SimpleXMLElement $element
76
     *
77 116
     * @return mixed[]
78
     */
79 116
    private function loadProperty(\SimpleXMLElement $element)
80 76
    {
81 38
        $property = [];
82
83 116
        if (isset($element['alias'])) {
84 76
            $property['alias'] = (string) $element['alias'];
85 38
        }
86
87 116
        if (isset($element['type'])) {
88 76
            $property['type'] = (string) $element['type'];
89 38
        }
90
91 116
        if (isset($element['exclude'])) {
92 72
            $property['exclude'] = (string) $element['exclude'] === 'true';
93 36
        }
94
95 116
        if (isset($element['expose'])) {
96 76
            $property['expose'] = (string) $element['expose'] === 'true';
97 38
        }
98
99 116
        if (isset($element['accessor'])) {
100 72
            $property['accessor'] = (string) $element['accessor'];
101 36
        }
102
103 116
        if (isset($element['readable'])) {
104 72
            $property['readable'] = (string) $element['readable'] === 'true';
105 36
        }
106
107 116
        if (isset($element['writable'])) {
108 76
            $property['writable'] = (string) $element['writable'] === 'true';
109 38
        }
110
111 116
        if (isset($element['mutator'])) {
112 76
            $property['mutator'] = (string) $element['mutator'];
113 38
        }
114
115 116
        if (isset($element['since'])) {
116 76
            $property['since'] = (string) $element['since'];
117 38
        }
118
119 116
        if (isset($element['until'])) {
120 72
            $property['until'] = (string) $element['until'];
121 36
        }
122
123 116
        if (isset($element['max-depth'])) {
124 76
            $property['max_depth'] = (string) $element['max-depth'];
125 58
        }
126
127 116
        foreach ($element->group as $group) {
128
            $property['groups'][] = (string) $group;
129
        }
130
131
        if (isset($element['xml-attribute'])) {
132
            $property['xml_attribute'] = (string) $element['xml-attribute'] === 'true';
133
        }
134
135 140
        if (isset($element['xml-value'])) {
136
            $property['xml_value'] = (string) $element['xml-value'] === 'true';
137 140
        }
138
139 140
        if (isset($element['xml-inline'])) {
140 4
            $property['xml_inline'] = (string) $element['xml-inline'] === 'true';
141
        }
142
143 136
        if (isset($element['xml-entry'])) {
144 136
            $property['xml_entry'] = (string) $element['xml-entry'];
145
        }
146 136
147
        if (isset($element['xml-entry-attribute'])) {
148 136
            $property['xml_entry_attribute'] = (string) $element['xml-entry-attribute'];
149 136
        }
150
151 136
        if (isset($element['xml-key-as-attribute'])) {
152 4
            $property['xml_key_as_attribute'] = (string) $element['xml-key-as-attribute'] === 'true';
153
        }
154 4
155
        if (isset($element['xml-key-as-node'])) {
156
            $property['xml_key_as_node'] = (string) $element['xml-key-as-node'] === 'true';
157 132
        }
158
159 132
        return $property;
160 132
    }
161 68
162
    /**
163 64
     * @param string $file
164
     *
165 128
     * @return \DOMDocument
166 120
     */
167
    private function loadDocument($file)
168 120
    {
169
        $data = trim(file_get_contents($file));
170
171
        if (empty($data)) {
172
            throw new \InvalidArgumentException();
173
        }
174
175
        $internalErrors = libxml_use_internal_errors();
176 128
        $disableEntities = libxml_disable_entity_loader();
177
178 128
        $this->setLibXmlState(true, true);
179 120
        $document = $this->createDocument($data, $internalErrors, $disableEntities);
180
        $this->setLibXmlState($internalErrors, $disableEntities);
181
182 8
        return $document;
183
    }
184 8
185 8
    /**
186 8
     * @param string $data
187 8
     * @param bool   $internalErrors
188 8
     * @param bool   $disableEntities
189 8
     *
190 8
     * @return \DOMDocument
191 8
     */
192 4
    private function createDocument($data, $internalErrors, $disableEntities)
193 4
    {
194
        $document = new \DOMDocument();
195 8
        $document->validateOnParse = true;
196
197 8
        if (!@$document->loadXML($data, LIBXML_NONET | LIBXML_COMPACT)) {
198
            $this->dispatchErrors($internalErrors, $disableEntities);
199
        }
200
201
        $document->normalizeDocument();
202
203
        if (!@$document->schemaValidateSource(file_get_contents(__DIR__.'/../Resource/mapping.xsd'))) {
204 136
            $this->dispatchErrors($internalErrors, $disableEntities);
205
        }
206 136
207 136
        foreach ($document->childNodes as $child) {
208 136
            if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
209 136
                throw new \InvalidArgumentException('The document type is not allowed.');
210
            }
211
        }
212
213
        return $document;
214
    }
215
216
    /**
217
     * @param bool $internalErrors
218
     * @param bool $disableEntities
219
     */
220
    private function dispatchErrors($internalErrors, $disableEntities)
221
    {
222
        $errors = [];
223
224 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...
225
            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
226
                $error->level === LIBXML_ERR_WARNING ? 'WARNING' : 'ERROR',
227
                $error->code,
228
                trim($error->message),
229
                $error->file ?: 'n/a',
230
                $error->line,
231
                $error->column
232
            );
233
        }
234
235
        $this->setLibXmlState($internalErrors, $disableEntities);
236
237
        throw new \InvalidArgumentException(implode(PHP_EOL, $errors));
238
    }
239
240
    /**
241
     * @param bool $internalErrors
242
     * @param bool $disableEntities
243
     */
244
    private function setLibXmlState($internalErrors, $disableEntities)
245
    {
246
        libxml_use_internal_errors($internalErrors);
247
        libxml_disable_entity_loader($disableEntities);
248
        libxml_clear_errors();
249
    }
250
}
251