Completed
Push — master ( e1c4bf...7b4b35 )
by Johannes
10s
created

YamlDriver   F

Complexity

Total Complexity 79

Size/Duplication

Total Lines 274
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 8

Test Coverage

Coverage 68.88%

Importance

Changes 12
Bugs 2 Features 4
Metric Value
wmc 79
c 12
b 2
f 4
lcom 0
cbo 8
dl 0
loc 274
ccs 135
cts 196
cp 0.6888
rs 2.0547

4 Methods

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