Completed
Pull Request — master (#787)
by
unknown
03:32
created

XmlDriver::loadMetadataFromFile()   F

Complexity

Conditions 85
Paths > 20000

Size

Total Lines 323
Code Lines 186

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 188
CRAP Score 142.7999

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 323
ccs 188
cts 235
cp 0.8
rs 2
cc 85
eloc 186
nc 4294967295
nop 2
crap 142.7999

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