Completed
Pull Request — master (#743)
by Asmir
03:52
created

YamlDriver   D

Complexity

Total Complexity 91

Size/Duplication

Total Lines 309
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 76.97%

Importance

Changes 0
Metric Value
wmc 91
lcom 1
cbo 10
dl 0
loc 309
ccs 127
cts 165
cp 0.7697
rs 4.8717
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 2
F loadMetadataFromFile() 0 217 63
A getExtension() 0 4 1
F addClassProperties() 0 55 20
B getCallbackMetadata() 0 19 5

How to fix   Complexity   

Complex Class

Complex classes like YamlDriver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use YamlDriver, and based on these observations, apply Extract Interface, too.

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\Metadata\ClassMetadata;
25
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
26
use JMS\Serializer\Metadata\PropertyMetadata;
27
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
28
use JMS\Serializer\TypeParser;
29
use Metadata\Driver\AbstractFileDriver;
30
use Metadata\Driver\FileLocatorInterface;
31
use Metadata\MethodMetadata;
32
use Symfony\Component\Yaml\Yaml;
33
34
class YamlDriver extends AbstractFileDriver
35
{
36
    private $typeParser;
37
38 26
    public function __construct(FileLocatorInterface $locator, AbstractParser $typeParser = null)
39
    {
40 26
        parent::__construct($locator);
41 26
        $this->typeParser = $typeParser ?: new TypeParser();
42 26
    }
43
44 26
    protected function loadMetadataFromFile(\ReflectionClass $class, $file)
45
    {
46 26
        $config = Yaml::parse(file_get_contents($file));
47
48 26
        if (!isset($config[$name = $class->name])) {
49
            throw new RuntimeException(sprintf('Expected metadata for class %s to be defined in %s.', $class->name, $file));
50
        }
51
52 26
        $config = $config[$name];
53 26
        $metadata = new ClassMetadata($name);
54 26
        $metadata->fileResources[] = $file;
55 26
        $metadata->fileResources[] = $class->getFileName();
56 26
        $exclusionPolicy = isset($config['exclusion_policy']) ? strtoupper($config['exclusion_policy']) : 'NONE';
57 26
        $excludeAll = isset($config['exclude']) ? (Boolean)$config['exclude'] : false;
58 26
        $classAccessType = isset($config['access_type']) ? $config['access_type'] : PropertyMetadata::ACCESS_TYPE_PROPERTY;
59 26
        $readOnlyClass = isset($config['read_only']) ? (Boolean)$config['read_only'] : false;
60 26
        $this->addClassProperties($metadata, $config);
61
62 26
        $propertiesMetadata = array();
63 26
        if (array_key_exists('virtual_properties', $config)) {
64 4
            foreach ($config['virtual_properties'] as $methodName => $propertySettings) {
65 4
                if (isset($propertySettings['exp'])) {
66 2
                    $virtualPropertyMetadata = new ExpressionPropertyMetadata($name, $methodName, $propertySettings['exp']);
67 2
                    unset($propertySettings['exp']);
68
69
                } else {
70
71 3
                    if (!$class->hasMethod($methodName)) {
72
                        throw new RuntimeException('The method ' . $methodName . ' not found in class ' . $class->name);
73
                    }
74 3
                    $virtualPropertyMetadata = new VirtualPropertyMetadata($name, $methodName);
75
                }
76 4
                $propertiesMetadata[$methodName] = $virtualPropertyMetadata;
77 4
                $config['properties'][$methodName] = $propertySettings;
78
            }
79
        }
80
81 26
        if (!$excludeAll) {
82 26
            foreach ($class->getProperties() as $property) {
83 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...
84 2
                    continue;
85
                }
86
87 21
                $pName = $property->getName();
88 21
                $propertiesMetadata[$pName] = new PropertyMetadata($name, $pName);
89
            }
90
91 26
            foreach ($propertiesMetadata as $pName => $pMetadata) {
92 23
                $isExclude = false;
93 23
                $isExpose = $pMetadata instanceof VirtualPropertyMetadata
94 22
                    || $pMetadata instanceof ExpressionPropertyMetadata
95 23
                    || (isset($config['properties']) && array_key_exists($pName, $config['properties']));
96
97 23
                if (isset($config['properties'][$pName])) {
98 20
                    $pConfig = $config['properties'][$pName];
99
100 20
                    if (isset($pConfig['exclude'])) {
101 2
                        $isExclude = (Boolean)$pConfig['exclude'];
102
                    }
103
104 20
                    if ($isExclude) {
105 2
                        continue;
106
                    }
107
108 18
                    if (isset($pConfig['expose'])) {
109 3
                        $isExpose = (Boolean)$pConfig['expose'];
110
                    }
111
112 18
                    if (isset($pConfig['skip_when_empty'])) {
113 1
                        $pMetadata->skipWhenEmpty = (Boolean)$pConfig['skip_when_empty'];
114
                    }
115
116 18
                    if (isset($pConfig['since_version'])) {
117
                        $pMetadata->sinceVersion = (string)$pConfig['since_version'];
118
                    }
119
120 18
                    if (isset($pConfig['until_version'])) {
121
                        $pMetadata->untilVersion = (string)$pConfig['until_version'];
122
                    }
123
124 18
                    if (isset($pConfig['exclude_if'])) {
125 1
                        $pMetadata->excludeIf = (string)$pConfig['exclude_if'];
126
                    }
127
128 18
                    if (isset($pConfig['expose_if'])) {
129 1
                        $pMetadata->excludeIf = "!(" . $pConfig['expose_if'] . ")";
130
                    }
131
132 18
                    if (isset($pConfig['serialized_name'])) {
133 3
                        $pMetadata->serializedName = (string)$pConfig['serialized_name'];
134
                    }
135
136 18
                    if (isset($pConfig['type'])) {
137 13
                        $pMetadata->setType($this->typeParser->parse((string)$pConfig['type']));
138
                    }
139
140 18
                    if (isset($pConfig['groups'])) {
141 1
                        $pMetadata->groups = $pConfig['groups'];
142
                    }
143
144 18
                    if (isset($pConfig['xml_list'])) {
145 2
                        $pMetadata->xmlCollection = true;
146
147 2
                        $colConfig = $pConfig['xml_list'];
148 2
                        if (isset($colConfig['inline'])) {
149 2
                            $pMetadata->xmlCollectionInline = (Boolean)$colConfig['inline'];
150
                        }
151
152 2
                        if (isset($colConfig['entry_name'])) {
153 1
                            $pMetadata->xmlEntryName = (string)$colConfig['entry_name'];
154
                        }
155
156 2
                        if (isset($colConfig['skip_when_empty'])) {
157 1
                            $pMetadata->xmlCollectionSkipWhenEmpty = (Boolean)$colConfig['skip_when_empty'];
158
                        } else {
159 2
                            $pMetadata->xmlCollectionSkipWhenEmpty = true;
160
                        }
161
162 2
                        if (isset($colConfig['namespace'])) {
163
                            $pMetadata->xmlEntryNamespace = (string)$colConfig['namespace'];
164
                        }
165
                    }
166
167 18
                    if (isset($pConfig['xml_map'])) {
168
                        $pMetadata->xmlCollection = true;
169
170
                        $colConfig = $pConfig['xml_map'];
171
                        if (isset($colConfig['inline'])) {
172
                            $pMetadata->xmlCollectionInline = (Boolean)$colConfig['inline'];
173
                        }
174
175
                        if (isset($colConfig['entry_name'])) {
176
                            $pMetadata->xmlEntryName = (string)$colConfig['entry_name'];
177
                        }
178
179
                        if (isset($colConfig['namespace'])) {
180
                            $pMetadata->xmlEntryNamespace = (string)$colConfig['namespace'];
181
                        }
182
183
                        if (isset($colConfig['key_attribute_name'])) {
184
                            $pMetadata->xmlKeyAttribute = $colConfig['key_attribute_name'];
185
                        }
186
187
                    }
188
189 18
                    if (isset($pConfig['xml_element'])) {
190 4
                        $colConfig = $pConfig['xml_element'];
191 4
                        if (isset($colConfig['cdata'])) {
192 2
                            $pMetadata->xmlElementCData = (Boolean)$colConfig['cdata'];
193
                        }
194
195 4
                        if (isset($colConfig['namespace'])) {
196 3
                            $pMetadata->xmlNamespace = (string)$colConfig['namespace'];
197
                        }
198
                    }
199
200 18
                    if (isset($pConfig['xml_attribute'])) {
201 4
                        $pMetadata->xmlAttribute = (Boolean)$pConfig['xml_attribute'];
202
                    }
203
204 18
                    if (isset($pConfig['xml_attribute_map'])) {
205
                        $pMetadata->xmlAttributeMap = (Boolean)$pConfig['xml_attribute_map'];
206
                    }
207
208 18
                    if (isset($pConfig['xml_value'])) {
209 2
                        $pMetadata->xmlValue = (Boolean)$pConfig['xml_value'];
210
                    }
211
212 18
                    if (isset($pConfig['xml_key_value_pairs'])) {
213 1
                        $pMetadata->xmlKeyValuePairs = (Boolean)$pConfig['xml_key_value_pairs'];
214
                    }
215
216
                    //we need read_only before setter and getter set, because that method depends on flag being set
217 18
                    if (isset($pConfig['read_only'])) {
218 1
                        $pMetadata->readOnly = (Boolean)$pConfig['read_only'];
219
                    } else {
220 17
                        $pMetadata->readOnly = $pMetadata->readOnly || $readOnlyClass;
221
                    }
222
223 18
                    $pMetadata->setAccessor(
224 18
                        isset($pConfig['access_type']) ? $pConfig['access_type'] : $classAccessType,
225 18
                        isset($pConfig['accessor']['getter']) ? $pConfig['accessor']['getter'] : null,
226 18
                        isset($pConfig['accessor']['setter']) ? $pConfig['accessor']['setter'] : null
227
                    );
228
229 18
                    if (isset($pConfig['inline'])) {
230
                        $pMetadata->inline = (Boolean)$pConfig['inline'];
231
                    }
232
233 18
                    if (isset($pConfig['max_depth'])) {
234 1
                        $pMetadata->maxDepth = (int)$pConfig['max_depth'];
235
                    }
236
                }
237 23
                if ((ExclusionPolicy::NONE === $exclusionPolicy && !$isExclude)
238 23
                    || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)
239
                ) {
240 23
                    $metadata->addPropertyMetadata($pMetadata);
241
                }
242
            }
243
        }
244
245 26
        if (isset($config['callback_methods'])) {
246
            $cConfig = $config['callback_methods'];
247
248
            if (isset($cConfig['pre_serialize'])) {
249
                $metadata->preSerializeMethods = $this->getCallbackMetadata($class, $cConfig['pre_serialize']);
250
            }
251
            if (isset($cConfig['post_serialize'])) {
252
                $metadata->postSerializeMethods = $this->getCallbackMetadata($class, $cConfig['post_serialize']);
253
            }
254
            if (isset($cConfig['post_deserialize'])) {
255
                $metadata->postDeserializeMethods = $this->getCallbackMetadata($class, $cConfig['post_deserialize']);
256
            }
257
        }
258
259 26
        return $metadata;
260
    }
261
262 26
    protected function getExtension()
263
    {
264 26
        return 'yml';
265
    }
266
267 26
    private function addClassProperties(ClassMetadata $metadata, array $config)
268
    {
269 26
        if (isset($config['custom_accessor_order']) && !isset($config['accessor_order'])) {
270 1
            $config['accessor_order'] = 'custom';
271
        }
272
273 26
        if (isset($config['accessor_order'])) {
274 1
            $metadata->setAccessorOrder($config['accessor_order'], isset($config['custom_accessor_order']) ? $config['custom_accessor_order'] : array());
275
        }
276
277 26
        if (isset($config['xml_root_name'])) {
278 9
            $metadata->xmlRootName = (string)$config['xml_root_name'];
279
        }
280
281 26
        if (isset($config['xml_root_namespace'])) {
282 1
            $metadata->xmlRootNamespace = (string)$config['xml_root_namespace'];
283
        }
284
285 26
        if (array_key_exists('xml_namespaces', $config)) {
286
287 3
            foreach ($config['xml_namespaces'] as $prefix => $uri) {
288 3
                $metadata->registerNamespace($uri, $prefix);
289
            }
290
291
        }
292
293 26
        if (isset($config['discriminator'])) {
294 4
            if (isset($config['discriminator']['disabled']) && true === $config['discriminator']['disabled']) {
295
                $metadata->discriminatorDisabled = true;
296
            } else {
297 4
                if (!isset($config['discriminator']['field_name'])) {
298
                    throw new RuntimeException('The "field_name" attribute must be set for discriminators.');
299
                }
300
301 4
                if (!isset($config['discriminator']['map']) || !\is_array($config['discriminator']['map'])) {
302
                    throw new RuntimeException('The "map" attribute must be set, and be an array for discriminators.');
303
                }
304 4
                $groups = isset($config['discriminator']['groups']) ? $config['discriminator']['groups'] : array();
305 4
                $metadata->setDiscriminator($config['discriminator']['field_name'], $config['discriminator']['map'], $groups);
306
307 4
                if (isset($config['discriminator']['xml_attribute'])) {
308 1
                    $metadata->xmlDiscriminatorAttribute = (bool)$config['discriminator']['xml_attribute'];
309
                }
310 4
                if (isset($config['discriminator']['xml_element'])) {
311 2
                    if (isset($config['discriminator']['xml_element']['cdata'])) {
312 1
                        $metadata->xmlDiscriminatorCData = (bool)$config['discriminator']['xml_element']['cdata'];
313
                    }
314 2
                    if (isset($config['discriminator']['xml_element']['namespace'])) {
315 1
                        $metadata->xmlDiscriminatorNamespace = (string)$config['discriminator']['xml_element']['namespace'];
316
                    }
317
                }
318
319
            }
320
        }
321 26
    }
322
323
    private function getCallbackMetadata(\ReflectionClass $class, $config)
324
    {
325
        if (\is_string($config)) {
326
            $config = array($config);
327
        } elseif (!\is_array($config)) {
328
            throw new RuntimeException(sprintf('callback methods expects a string, or an array of strings that represent method names, but got %s.', json_encode($config['pre_serialize'])));
329
        }
330
331
        $methods = array();
332
        foreach ($config as $name) {
333
            if (!$class->hasMethod($name)) {
334
                throw new RuntimeException(sprintf('The method %s does not exist in class %s.', $name, $class->name));
335
            }
336
337
            $methods[] = new MethodMetadata($class->name, $name);
338
        }
339
340
        return $methods;
341
    }
342
}
343