Completed
Pull Request — master (#743)
by Asmir
07:41
created

XmlDriver::loadMetadataFromFile()   F

Complexity

Conditions 84
Paths > 20000

Size

Total Lines 318
Code Lines 181

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 134
CRAP Score 179.845

Importance

Changes 0
Metric Value
dl 0
loc 318
ccs 134
cts 176
cp 0.7614
rs 2
c 0
b 0
f 0
cc 84
eloc 181
nc 4294967295
nop 2
crap 179.845

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * Copyright 2016 Johannes M. Schmitt <[email protected]>
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace JMS\Serializer\Metadata\Driver;
20
21
use JMS\Serializer\Annotation\ExclusionPolicy;
22
use JMS\Serializer\Exception\RuntimeException;
23
use JMS\Serializer\Exception\XmlErrorException;
24
use JMS\Serializer\GraphNavigatorInterface;
25
use JMS\Serializer\Metadata\ClassMetadata;
26
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
27
use JMS\Serializer\Metadata\PropertyMetadata;
28
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
29
use Metadata\Driver\AbstractFileDriver;
30
use Metadata\MethodMetadata;
31
32
class XmlDriver extends AbstractFileDriver
33
{
34 27
    protected function loadMetadataFromFile(\ReflectionClass $class, $path)
35
    {
36 27
        $previous = libxml_use_internal_errors(true);
37 27
        libxml_clear_errors();
38
39 27
        $elem = simplexml_load_file($path);
40 27
        libxml_use_internal_errors($previous);
41
42 27
        if (false === $elem) {
43 1
            throw new XmlErrorException(libxml_get_last_error());
44
        }
45
46 26
        $metadata = new ClassMetadata($name = $class->name);
47 26
        if (!$elems = $elem->xpath("./class[@name = '" . $name . "']")) {
48
            throw new RuntimeException(sprintf('Could not find class %s inside XML element.', $name));
49
        }
50 26
        $elem = reset($elems);
51
52 26
        $metadata->fileResources[] = $path;
53 26
        $metadata->fileResources[] = $class->getFileName();
54 26
        $exclusionPolicy = strtoupper($elem->attributes()->{'exclusion-policy'}) ?: 'NONE';
55 26
        $excludeAll = null !== ($exclude = $elem->attributes()->exclude) ? 'true' === strtolower($exclude) : false;
56 26
        $classAccessType = (string)($elem->attributes()->{'access-type'} ?: PropertyMetadata::ACCESS_TYPE_PROPERTY);
57
58 26
        $propertiesMetadata = array();
59 26
        $propertiesNodes = array();
60
61 26
        if (null !== $accessorOrder = $elem->attributes()->{'accessor-order'}) {
62
            $metadata->setAccessorOrder((string)$accessorOrder, preg_split('/\s*,\s*/', (string)$elem->attributes()->{'custom-accessor-order'}));
63
        }
64
65 26
        if (null !== $xmlRootName = $elem->attributes()->{'xml-root-name'}) {
66 7
            $metadata->xmlRootName = (string)$xmlRootName;
67
        }
68
69 26
        if (null !== $xmlRootNamespace = $elem->attributes()->{'xml-root-namespace'}) {
70 1
            $metadata->xmlRootNamespace = (string)$xmlRootNamespace;
71
        }
72
73 26
        $readOnlyClass = 'true' === strtolower($elem->attributes()->{'read-only'});
74
75 26
        $discriminatorFieldName = (string)$elem->attributes()->{'discriminator-field-name'};
76 26
        $discriminatorMap = array();
77 26
        foreach ($elem->xpath('./discriminator-class') as $entry) {
78 4
            if (!isset($entry->attributes()->value)) {
79
                throw new RuntimeException('Each discriminator-class element must have a "value" attribute.');
80
            }
81
82 4
            $discriminatorMap[(string)$entry->attributes()->value] = (string)$entry;
83
        }
84
85 26
        if ('true' === (string)$elem->attributes()->{'discriminator-disabled'}) {
86
            $metadata->discriminatorDisabled = true;
87 26
        } elseif (!empty($discriminatorFieldName) || !empty($discriminatorMap)) {
88
89 4
            $discriminatorGroups = array();
90 4
            foreach ($elem->xpath('./discriminator-groups/group') as $entry) {
91 1
                $discriminatorGroups[] = (string)$entry;
92
            }
93 4
            $metadata->setDiscriminator($discriminatorFieldName, $discriminatorMap, $discriminatorGroups);
94
        }
95
96 26
        foreach ($elem->xpath('./xml-namespace') as $xmlNamespace) {
97 3
            if (!isset($xmlNamespace->attributes()->uri)) {
98
                throw new RuntimeException('The prefix attribute must be set for all xml-namespace elements.');
99
            }
100
101 3
            if (isset($xmlNamespace->attributes()->prefix)) {
102 3
                $prefix = (string)$xmlNamespace->attributes()->prefix;
103
            } else {
104 2
                $prefix = null;
105
            }
106
107 3
            $metadata->registerNamespace((string)$xmlNamespace->attributes()->uri, $prefix);
108
        }
109
110 26
        foreach ($elem->xpath('./xml-discriminator') as $xmlDiscriminator) {
111 2
            if (isset($xmlDiscriminator->attributes()->attribute)) {
112 1
                $metadata->xmlDiscriminatorAttribute = (string)$xmlDiscriminator->attributes()->attribute === 'true';
113
            }
114 2
            if (isset($xmlDiscriminator->attributes()->cdata)) {
115 1
                $metadata->xmlDiscriminatorCData = (string)$xmlDiscriminator->attributes()->cdata === 'true';
116
            }
117 2
            if (isset($xmlDiscriminator->attributes()->namespace)) {
118 2
                $metadata->xmlDiscriminatorNamespace = (string)$xmlDiscriminator->attributes()->namespace;
119
            }
120
        }
121
122 26
        foreach ($elem->xpath('./virtual-property') as $method) {
123
124 6
            if (isset($method->attributes()->expression)) {
125 2
                $virtualPropertyMetadata = new ExpressionPropertyMetadata($name, (string)$method->attributes()->name, (string)$method->attributes()->expression);
126
            } else {
127 5
                if (!isset($method->attributes()->method)) {
128
                    throw new RuntimeException('The method attribute must be set for all virtual-property elements.');
129
                }
130 5
                $virtualPropertyMetadata = new VirtualPropertyMetadata($name, (string)$method->attributes()->method);
131
            }
132
133 6
            $propertiesMetadata[] = $virtualPropertyMetadata;
134 6
            $propertiesNodes[] = $method;
135
        }
136
137 26
        if (!$excludeAll) {
138
139 26
            foreach ($class->getProperties() as $property) {
140 22
                if ($property->class !== $name || (isset($property->info) && $property->info['class'] !== $name)) {
0 ignored issues
show
Bug introduced by
The property info does not seem to exist in ReflectionProperty.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
141 2
                    continue;
142
                }
143
144 21
                $propertiesMetadata[] = new PropertyMetadata($name, $pName = $property->getName());
145 21
                $pElems = $elem->xpath("./property[@name = '" . $pName . "']");
146
147 21
                $propertiesNodes[] = $pElems ? reset($pElems) : null;
148
            }
149
150 26
            foreach ($propertiesMetadata as $propertyKey => $pMetadata) {
151
152 23
                $isExclude = false;
153 23
                $isExpose = $pMetadata instanceof VirtualPropertyMetadata
154 23
                    || $pMetadata instanceof ExpressionPropertyMetadata;
155
156 23
                $pElem = $propertiesNodes[$propertyKey];
157 23
                if (!empty($pElem)) {
158
159 23
                    if (null !== $exclude = $pElem->attributes()->exclude) {
160 2
                        $isExclude = 'true' === strtolower($exclude);
161
                    }
162
163 23
                    if ($isExclude) {
164 2
                        continue;
165
                    }
166
167 21
                    if (null !== $expose = $pElem->attributes()->expose) {
168 2
                        $isExpose = 'true' === strtolower($expose);
169
                    }
170
171 21
                    if (null !== $excludeIf = $pElem->attributes()->{'exclude-if'}) {
172 1
                        $pMetadata->excludeIf = $excludeIf;
173
                    }
174
175 21
                    if (null !== $skip = $pElem->attributes()->{'skip-when-empty'}) {
176 1
                        $pMetadata->skipWhenEmpty = 'true' === strtolower($skip);
177
                    }
178
179 21
                    if (null !== $excludeIf = $pElem->attributes()->{'expose-if'}) {
180 1
                        $pMetadata->excludeIf = "!(" . $excludeIf . ")";
181 1
                        $isExpose = true;
182
                    }
183
184 21
                    if (null !== $version = $pElem->attributes()->{'since-version'}) {
185
                        $pMetadata->sinceVersion = (string)$version;
186
                    }
187
188 21
                    if (null !== $version = $pElem->attributes()->{'until-version'}) {
189
                        $pMetadata->untilVersion = (string)$version;
190
                    }
191
192 21
                    if (null !== $serializedName = $pElem->attributes()->{'serialized-name'}) {
193 5
                        $pMetadata->serializedName = (string)$serializedName;
194
                    }
195
196 21
                    if (null !== $type = $pElem->attributes()->type) {
197 15
                        $pMetadata->setType((string)$type);
198 9
                    } elseif (isset($pElem->type)) {
199 2
                        $pMetadata->setType((string)$pElem->type);
200
                    }
201
202 21
                    if (null !== $groups = $pElem->attributes()->groups) {
203 2
                        $pMetadata->groups = preg_split('/\s*,\s*/', (string) trim($groups));
204 20
                    } elseif (isset($pElem->groups)) {
205 1
                        $pMetadata->groups = (array) $pElem->groups->value;
206
                    }
207
208 21
                    if (isset($pElem->{'xml-list'})) {
209
210 2
                        $pMetadata->xmlCollection = true;
211
212 2
                        $colConfig = $pElem->{'xml-list'};
213 2
                        if (isset($colConfig->attributes()->inline)) {
214 1
                            $pMetadata->xmlCollectionInline = 'true' === (string)$colConfig->attributes()->inline;
215
                        }
216
217 2
                        if (isset($colConfig->attributes()->{'entry-name'})) {
218 1
                            $pMetadata->xmlEntryName = (string)$colConfig->attributes()->{'entry-name'};
219
                        }
220
221 2
                        if (isset($colConfig->attributes()->{'skip-when-empty'})) {
222 1
                            $pMetadata->xmlCollectionSkipWhenEmpty = 'true' === (string)$colConfig->attributes()->{'skip-when-empty'};
223
                        } else {
224 1
                            $pMetadata->xmlCollectionSkipWhenEmpty = true;
225
                        }
226
227 2
                        if (isset($colConfig->attributes()->namespace)) {
228
                            $pMetadata->xmlEntryNamespace = (string)$colConfig->attributes()->namespace;
229
                        }
230
                    }
231
232 21
                    if (isset($pElem->{'xml-map'})) {
233
                        $pMetadata->xmlCollection = true;
234
235
                        $colConfig = $pElem->{'xml-map'};
236
                        if (isset($colConfig->attributes()->inline)) {
237
                            $pMetadata->xmlCollectionInline = 'true' === (string)$colConfig->attributes()->inline;
238
                        }
239
240
                        if (isset($colConfig->attributes()->{'entry-name'})) {
241
                            $pMetadata->xmlEntryName = (string)$colConfig->attributes()->{'entry-name'};
242
                        }
243
244
                        if (isset($colConfig->attributes()->namespace)) {
245
                            $pMetadata->xmlEntryNamespace = (string)$colConfig->attributes()->namespace;
246
                        }
247
248
                        if (isset($colConfig->attributes()->{'key-attribute-name'})) {
249
                            $pMetadata->xmlKeyAttribute = (string)$colConfig->attributes()->{'key-attribute-name'};
250
                        }
251
                    }
252
253 21
                    if (isset($pElem->{'xml-element'})) {
254 4
                        $colConfig = $pElem->{'xml-element'};
255 4
                        if (isset($colConfig->attributes()->cdata)) {
256 2
                            $pMetadata->xmlElementCData = 'true' === (string)$colConfig->attributes()->cdata;
257
                        }
258
259 4
                        if (isset($colConfig->attributes()->namespace)) {
260 3
                            $pMetadata->xmlNamespace = (string)$colConfig->attributes()->namespace;
261
                        }
262
                    }
263
264 21
                    if (isset($pElem->attributes()->{'xml-attribute'})) {
265 4
                        $pMetadata->xmlAttribute = 'true' === (string)$pElem->attributes()->{'xml-attribute'};
266
                    }
267
268 21
                    if (isset($pElem->attributes()->{'xml-attribute-map'})) {
269
                        $pMetadata->xmlAttributeMap = 'true' === (string)$pElem->attributes()->{'xml-attribute-map'};
270
                    }
271
272 21
                    if (isset($pElem->attributes()->{'xml-value'})) {
273 2
                        $pMetadata->xmlValue = 'true' === (string)$pElem->attributes()->{'xml-value'};
274
                    }
275
276 21
                    if (isset($pElem->attributes()->{'xml-key-value-pairs'})) {
277 1
                        $pMetadata->xmlKeyValuePairs = 'true' === (string)$pElem->attributes()->{'xml-key-value-pairs'};
278
                    }
279
280 21
                    if (isset($pElem->attributes()->{'max-depth'})) {
281 1
                        $pMetadata->maxDepth = (int)$pElem->attributes()->{'max-depth'};
282
                    }
283
284
                    //we need read-only before setter and getter set, because that method depends on flag being set
285 21
                    if (null !== $readOnly = $pElem->attributes()->{'read-only'}) {
286 1
                        $pMetadata->readOnly = 'true' === strtolower($readOnly);
287
                    } else {
288 20
                        $pMetadata->readOnly = $pMetadata->readOnly || $readOnlyClass;
289
                    }
290
291 21
                    $getter = $pElem->attributes()->{'accessor-getter'};
292 21
                    $setter = $pElem->attributes()->{'accessor-setter'};
293 21
                    $pMetadata->setAccessor(
294 21
                        (string)($pElem->attributes()->{'access-type'} ?: $classAccessType),
295 21
                        $getter ? (string)$getter : null,
296 21
                        $setter ? (string)$setter : null
297
                    );
298
299 21
                    if (null !== $inline = $pElem->attributes()->inline) {
300
                        $pMetadata->inline = 'true' === strtolower($inline);
301
                    }
302
303
                }
304
305 23
                if ((ExclusionPolicy::NONE === (string)$exclusionPolicy && !$isExclude)
306 23
                    || (ExclusionPolicy::ALL === (string)$exclusionPolicy && $isExpose)
307
                ) {
308
309 23
                    $metadata->addPropertyMetadata($pMetadata);
310
                }
311
            }
312
        }
313
314 26
        foreach ($elem->xpath('./callback-method') as $method) {
315
            if (!isset($method->attributes()->type)) {
316
                throw new RuntimeException('The type attribute must be set for all callback-method elements.');
317
            }
318
            if (!isset($method->attributes()->name)) {
319
                throw new RuntimeException('The name attribute must be set for all callback-method elements.');
320
            }
321
322
            switch ((string)$method->attributes()->type) {
323
                case 'pre-serialize':
324
                    $metadata->addPreSerializeMethod(new MethodMetadata($name, (string)$method->attributes()->name));
325
                    break;
326
327
                case 'post-serialize':
328
                    $metadata->addPostSerializeMethod(new MethodMetadata($name, (string)$method->attributes()->name));
329
                    break;
330
331
                case 'post-deserialize':
332
                    $metadata->addPostDeserializeMethod(new MethodMetadata($name, (string)$method->attributes()->name));
333
                    break;
334
335
                case 'handler':
336
                    if (!isset($method->attributes()->format)) {
337
                        throw new RuntimeException('The format attribute must be set for "handler" callback methods.');
338
                    }
339
                    if (!isset($method->attributes()->direction)) {
340
                        throw new RuntimeException('The direction attribute must be set for "handler" callback methods.');
341
                    }
342
343
                    break;
344
345
                default:
346
                    throw new RuntimeException(sprintf('The type "%s" is not supported.', $method->attributes()->name));
347
            }
348
        }
349
350 26
        return $metadata;
351
    }
352
353 26
    protected function getExtension()
354
    {
355 26
        return 'xml';
356
    }
357
}
358