Completed
Pull Request — master (#708)
by Asmir
11:37
created

YamlDriver   F

Complexity

Total Complexity 88

Size/Duplication

Total Lines 297
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 9

Test Coverage

Coverage 74.88%

Importance

Changes 0
Metric Value
wmc 88
lcom 0
cbo 9
dl 0
loc 297
ccs 161
cts 215
cp 0.7488
rs 1.5789
c 0
b 0
f 0

4 Methods

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