Completed
Push — master ( a11a2f...a1ae8b )
by Asmir
03:55
created

YamlDriver   F

Complexity

Total Complexity 91

Size/Duplication

Total Lines 306
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 9

Test Coverage

Coverage 75.78%

Importance

Changes 0
Metric Value
wmc 91
lcom 0
cbo 9
dl 0
loc 306
ccs 169
cts 223
cp 0.7578
rs 1.5789
c 0
b 0
f 0

4 Methods

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