Completed
Pull Request — master (#743)
by Asmir
03:52
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\Parser\AbstractParser;
22
use JMS\Serializer\Annotation\ExclusionPolicy;
23
use JMS\Serializer\Exception\RuntimeException;
24
use JMS\Serializer\Exception\XmlErrorException;
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 JMS\Serializer\TypeParser;
30
use Metadata\Driver\AbstractFileDriver;
31
use Metadata\Driver\FileLocatorInterface;
32
use Metadata\MethodMetadata;
33
34
class XmlDriver extends AbstractFileDriver
35
{
36
    private $typeParser;
37
38 27
    public function __construct(FileLocatorInterface $locator, AbstractParser $typeParser = null)
39
    {
40 27
        parent::__construct($locator);
41 27
        $this->typeParser = $typeParser ?: new TypeParser();
42 27
    }
43
44 27
    protected function loadMetadataFromFile(\ReflectionClass $class, $path)
45
    {
46 27
        $previous = libxml_use_internal_errors(true);
47 27
        libxml_clear_errors();
48
49 27
        $elem = simplexml_load_file($path);
50 27
        libxml_use_internal_errors($previous);
51
52 27
        if (false === $elem) {
53 1
            throw new XmlErrorException(libxml_get_last_error());
54
        }
55
56 26
        $metadata = new ClassMetadata($name = $class->name);
57 26
        if (!$elems = $elem->xpath("./class[@name = '" . $name . "']")) {
58
            throw new RuntimeException(sprintf('Could not find class %s inside XML element.', $name));
59
        }
60 26
        $elem = reset($elems);
61
62 26
        $metadata->fileResources[] = $path;
63 26
        $metadata->fileResources[] = $class->getFileName();
64 26
        $exclusionPolicy = strtoupper($elem->attributes()->{'exclusion-policy'}) ?: 'NONE';
65 26
        $excludeAll = null !== ($exclude = $elem->attributes()->exclude) ? 'true' === strtolower($exclude) : false;
66 26
        $classAccessType = (string)($elem->attributes()->{'access-type'} ?: PropertyMetadata::ACCESS_TYPE_PROPERTY);
67
68 26
        $propertiesMetadata = array();
69 26
        $propertiesNodes = array();
70
71 26
        if (null !== $accessorOrder = $elem->attributes()->{'accessor-order'}) {
72
            $metadata->setAccessorOrder((string)$accessorOrder, preg_split('/\s*,\s*/', (string)$elem->attributes()->{'custom-accessor-order'}));
73
        }
74
75 26
        if (null !== $xmlRootName = $elem->attributes()->{'xml-root-name'}) {
76 7
            $metadata->xmlRootName = (string)$xmlRootName;
77
        }
78
79 26
        if (null !== $xmlRootNamespace = $elem->attributes()->{'xml-root-namespace'}) {
80 1
            $metadata->xmlRootNamespace = (string)$xmlRootNamespace;
81
        }
82
83 26
        $readOnlyClass = 'true' === strtolower($elem->attributes()->{'read-only'});
84
85 26
        $discriminatorFieldName = (string)$elem->attributes()->{'discriminator-field-name'};
86 26
        $discriminatorMap = array();
87 26
        foreach ($elem->xpath('./discriminator-class') as $entry) {
88 4
            if (!isset($entry->attributes()->value)) {
89
                throw new RuntimeException('Each discriminator-class element must have a "value" attribute.');
90
            }
91
92 4
            $discriminatorMap[(string)$entry->attributes()->value] = (string)$entry;
93
        }
94
95 26
        if ('true' === (string)$elem->attributes()->{'discriminator-disabled'}) {
96
            $metadata->discriminatorDisabled = true;
97 26
        } elseif (!empty($discriminatorFieldName) || !empty($discriminatorMap)) {
98
99 4
            $discriminatorGroups = array();
100 4
            foreach ($elem->xpath('./discriminator-groups/group') as $entry) {
101 1
                $discriminatorGroups[] = (string)$entry;
102
            }
103 4
            $metadata->setDiscriminator($discriminatorFieldName, $discriminatorMap, $discriminatorGroups);
104
        }
105
106 26
        foreach ($elem->xpath('./xml-namespace') as $xmlNamespace) {
107 3
            if (!isset($xmlNamespace->attributes()->uri)) {
108
                throw new RuntimeException('The prefix attribute must be set for all xml-namespace elements.');
109
            }
110
111 3
            if (isset($xmlNamespace->attributes()->prefix)) {
112 3
                $prefix = (string)$xmlNamespace->attributes()->prefix;
113
            } else {
114 2
                $prefix = null;
115
            }
116
117 3
            $metadata->registerNamespace((string)$xmlNamespace->attributes()->uri, $prefix);
118
        }
119
120 26
        foreach ($elem->xpath('./xml-discriminator') as $xmlDiscriminator) {
121 2
            if (isset($xmlDiscriminator->attributes()->attribute)) {
122 1
                $metadata->xmlDiscriminatorAttribute = (string)$xmlDiscriminator->attributes()->attribute === 'true';
123
            }
124 2
            if (isset($xmlDiscriminator->attributes()->cdata)) {
125 1
                $metadata->xmlDiscriminatorCData = (string)$xmlDiscriminator->attributes()->cdata === 'true';
126
            }
127 2
            if (isset($xmlDiscriminator->attributes()->namespace)) {
128 2
                $metadata->xmlDiscriminatorNamespace = (string)$xmlDiscriminator->attributes()->namespace;
129
            }
130
        }
131
132 26
        foreach ($elem->xpath('./virtual-property') as $method) {
133
134 6
            if (isset($method->attributes()->expression)) {
135 2
                $virtualPropertyMetadata = new ExpressionPropertyMetadata($name, (string)$method->attributes()->name, (string)$method->attributes()->expression);
136
            } else {
137 5
                if (!isset($method->attributes()->method)) {
138
                    throw new RuntimeException('The method attribute must be set for all virtual-property elements.');
139
                }
140 5
                $virtualPropertyMetadata = new VirtualPropertyMetadata($name, (string)$method->attributes()->method);
141
            }
142
143 6
            $propertiesMetadata[] = $virtualPropertyMetadata;
144 6
            $propertiesNodes[] = $method;
145
        }
146
147 26
        if (!$excludeAll) {
148
149 26
            foreach ($class->getProperties() as $property) {
150 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...
151 2
                    continue;
152
                }
153
154 21
                $propertiesMetadata[] = new PropertyMetadata($name, $pName = $property->getName());
155 21
                $pElems = $elem->xpath("./property[@name = '" . $pName . "']");
156
157 21
                $propertiesNodes[] = $pElems ? reset($pElems) : null;
158
            }
159
160 26
            foreach ($propertiesMetadata as $propertyKey => $pMetadata) {
161
162 23
                $isExclude = false;
163 23
                $isExpose = $pMetadata instanceof VirtualPropertyMetadata
164 23
                    || $pMetadata instanceof ExpressionPropertyMetadata;
165
166 23
                $pElem = $propertiesNodes[$propertyKey];
167 23
                if (!empty($pElem)) {
168
169 23
                    if (null !== $exclude = $pElem->attributes()->exclude) {
170 2
                        $isExclude = 'true' === strtolower($exclude);
171
                    }
172
173 23
                    if ($isExclude) {
174 2
                        continue;
175
                    }
176
177 21
                    if (null !== $expose = $pElem->attributes()->expose) {
178 2
                        $isExpose = 'true' === strtolower($expose);
179
                    }
180
181 21
                    if (null !== $excludeIf = $pElem->attributes()->{'exclude-if'}) {
182 1
                        $pMetadata->excludeIf = $excludeIf;
183
                    }
184
185 21
                    if (null !== $skip = $pElem->attributes()->{'skip-when-empty'}) {
186 1
                        $pMetadata->skipWhenEmpty = 'true' === strtolower($skip);
187
                    }
188
189 21
                    if (null !== $excludeIf = $pElem->attributes()->{'expose-if'}) {
190 1
                        $pMetadata->excludeIf = "!(" . $excludeIf . ")";
191 1
                        $isExpose = true;
192
                    }
193
194 21
                    if (null !== $version = $pElem->attributes()->{'since-version'}) {
195
                        $pMetadata->sinceVersion = (string)$version;
196
                    }
197
198 21
                    if (null !== $version = $pElem->attributes()->{'until-version'}) {
199
                        $pMetadata->untilVersion = (string)$version;
200
                    }
201
202 21
                    if (null !== $serializedName = $pElem->attributes()->{'serialized-name'}) {
203 5
                        $pMetadata->serializedName = (string)$serializedName;
204
                    }
205
206 21
                    if (null !== $type = $pElem->attributes()->type) {
207 15
                        $pMetadata->setType($this->typeParser->parse((string)$type));
208 9
                    } elseif (isset($pElem->type)) {
209 2
                        $pMetadata->setType($this->typeParser->parse((string)$pElem->type));
210
                    }
211
212 21
                    if (null !== $groups = $pElem->attributes()->groups) {
213 2
                        $pMetadata->groups = preg_split('/\s*,\s*/', (string) trim($groups));
214 20
                    } elseif (isset($pElem->groups)) {
215 1
                        $pMetadata->groups = (array) $pElem->groups->value;
216
                    }
217
218 21
                    if (isset($pElem->{'xml-list'})) {
219
220 2
                        $pMetadata->xmlCollection = true;
221
222 2
                        $colConfig = $pElem->{'xml-list'};
223 2
                        if (isset($colConfig->attributes()->inline)) {
224 1
                            $pMetadata->xmlCollectionInline = 'true' === (string)$colConfig->attributes()->inline;
225
                        }
226
227 2
                        if (isset($colConfig->attributes()->{'entry-name'})) {
228 1
                            $pMetadata->xmlEntryName = (string)$colConfig->attributes()->{'entry-name'};
229
                        }
230
231 2
                        if (isset($colConfig->attributes()->{'skip-when-empty'})) {
232 1
                            $pMetadata->xmlCollectionSkipWhenEmpty = 'true' === (string)$colConfig->attributes()->{'skip-when-empty'};
233
                        } else {
234 1
                            $pMetadata->xmlCollectionSkipWhenEmpty = true;
235
                        }
236
237 2
                        if (isset($colConfig->attributes()->namespace)) {
238
                            $pMetadata->xmlEntryNamespace = (string)$colConfig->attributes()->namespace;
239
                        }
240
                    }
241
242 21
                    if (isset($pElem->{'xml-map'})) {
243
                        $pMetadata->xmlCollection = true;
244
245
                        $colConfig = $pElem->{'xml-map'};
246
                        if (isset($colConfig->attributes()->inline)) {
247
                            $pMetadata->xmlCollectionInline = 'true' === (string)$colConfig->attributes()->inline;
248
                        }
249
250
                        if (isset($colConfig->attributes()->{'entry-name'})) {
251
                            $pMetadata->xmlEntryName = (string)$colConfig->attributes()->{'entry-name'};
252
                        }
253
254
                        if (isset($colConfig->attributes()->namespace)) {
255
                            $pMetadata->xmlEntryNamespace = (string)$colConfig->attributes()->namespace;
256
                        }
257
258
                        if (isset($colConfig->attributes()->{'key-attribute-name'})) {
259
                            $pMetadata->xmlKeyAttribute = (string)$colConfig->attributes()->{'key-attribute-name'};
260
                        }
261
                    }
262
263 21
                    if (isset($pElem->{'xml-element'})) {
264 4
                        $colConfig = $pElem->{'xml-element'};
265 4
                        if (isset($colConfig->attributes()->cdata)) {
266 2
                            $pMetadata->xmlElementCData = 'true' === (string)$colConfig->attributes()->cdata;
267
                        }
268
269 4
                        if (isset($colConfig->attributes()->namespace)) {
270 3
                            $pMetadata->xmlNamespace = (string)$colConfig->attributes()->namespace;
271
                        }
272
                    }
273
274 21
                    if (isset($pElem->attributes()->{'xml-attribute'})) {
275 4
                        $pMetadata->xmlAttribute = 'true' === (string)$pElem->attributes()->{'xml-attribute'};
276
                    }
277
278 21
                    if (isset($pElem->attributes()->{'xml-attribute-map'})) {
279
                        $pMetadata->xmlAttributeMap = 'true' === (string)$pElem->attributes()->{'xml-attribute-map'};
280
                    }
281
282 21
                    if (isset($pElem->attributes()->{'xml-value'})) {
283 2
                        $pMetadata->xmlValue = 'true' === (string)$pElem->attributes()->{'xml-value'};
284
                    }
285
286 21
                    if (isset($pElem->attributes()->{'xml-key-value-pairs'})) {
287 1
                        $pMetadata->xmlKeyValuePairs = 'true' === (string)$pElem->attributes()->{'xml-key-value-pairs'};
288
                    }
289
290 21
                    if (isset($pElem->attributes()->{'max-depth'})) {
291 1
                        $pMetadata->maxDepth = (int)$pElem->attributes()->{'max-depth'};
292
                    }
293
294
                    //we need read-only before setter and getter set, because that method depends on flag being set
295 21
                    if (null !== $readOnly = $pElem->attributes()->{'read-only'}) {
296 1
                        $pMetadata->readOnly = 'true' === strtolower($readOnly);
297
                    } else {
298 20
                        $pMetadata->readOnly = $pMetadata->readOnly || $readOnlyClass;
299
                    }
300
301 21
                    $getter = $pElem->attributes()->{'accessor-getter'};
302 21
                    $setter = $pElem->attributes()->{'accessor-setter'};
303 21
                    $pMetadata->setAccessor(
304 21
                        (string)($pElem->attributes()->{'access-type'} ?: $classAccessType),
305 21
                        $getter ? (string)$getter : null,
306 21
                        $setter ? (string)$setter : null
307
                    );
308
309 21
                    if (null !== $inline = $pElem->attributes()->inline) {
310
                        $pMetadata->inline = 'true' === strtolower($inline);
311
                    }
312
313
                }
314
315 23
                if ((ExclusionPolicy::NONE === (string)$exclusionPolicy && !$isExclude)
316 23
                    || (ExclusionPolicy::ALL === (string)$exclusionPolicy && $isExpose)
317
                ) {
318
319 23
                    $metadata->addPropertyMetadata($pMetadata);
320
                }
321
            }
322
        }
323
324 26
        foreach ($elem->xpath('./callback-method') as $method) {
325
            if (!isset($method->attributes()->type)) {
326
                throw new RuntimeException('The type attribute must be set for all callback-method elements.');
327
            }
328
            if (!isset($method->attributes()->name)) {
329
                throw new RuntimeException('The name attribute must be set for all callback-method elements.');
330
            }
331
332
            switch ((string)$method->attributes()->type) {
333
                case 'pre-serialize':
334
                    $metadata->addPreSerializeMethod(new MethodMetadata($name, (string)$method->attributes()->name));
335
                    break;
336
337
                case 'post-serialize':
338
                    $metadata->addPostSerializeMethod(new MethodMetadata($name, (string)$method->attributes()->name));
339
                    break;
340
341
                case 'post-deserialize':
342
                    $metadata->addPostDeserializeMethod(new MethodMetadata($name, (string)$method->attributes()->name));
343
                    break;
344
345
                case 'handler':
346
                    if (!isset($method->attributes()->format)) {
347
                        throw new RuntimeException('The format attribute must be set for "handler" callback methods.');
348
                    }
349
                    if (!isset($method->attributes()->direction)) {
350
                        throw new RuntimeException('The direction attribute must be set for "handler" callback methods.');
351
                    }
352
353
                    break;
354
355
                default:
356
                    throw new RuntimeException(sprintf('The type "%s" is not supported.', $method->attributes()->name));
357
            }
358
        }
359
360 26
        return $metadata;
361
    }
362
363 26
    protected function getExtension()
364
    {
365 26
        return 'xml';
366
    }
367
}
368