Completed
Push — master ( 0c820c...9912c8 )
by Asmir
9s
created

XmlDriver::loadMetadataFromFile()   F

Complexity

Conditions 78
Paths > 20000

Size

Total Lines 303
Code Lines 172

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 173
CRAP Score 131.4956

Importance

Changes 0
Metric Value
dl 0
loc 303
ccs 173
cts 218
cp 0.7936
rs 2
c 0
b 0
f 0
cc 78
eloc 172
nc 4294967295
nop 2
crap 131.4956

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