Completed
Pull Request — master (#863)
by Omar
04:51
created

YamlDriver   F

Complexity

Total Complexity 92

Size/Duplication

Total Lines 310
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 9

Test Coverage

Coverage 76%

Importance

Changes 0
Metric Value
wmc 92
lcom 0
cbo 9
dl 0
loc 310
ccs 171
cts 225
cp 0.76
rs 1.5789
c 0
b 0
f 0

4 Methods

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