Completed
Pull Request — master (#888)
by Robert
03:06
created

AnnotationDriver   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 34

Test Coverage

Coverage 98.05%

Importance

Changes 0
Metric Value
wmc 62
lcom 1
cbo 34
dl 0
loc 205
ccs 151
cts 154
cp 0.9805
rs 1.3043
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 2
F loadMetadataForClass() 0 189 60

How to fix   Complexity   

Complex Class

Complex classes like AnnotationDriver 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 AnnotationDriver, 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 Doctrine\Common\Annotations\Reader;
22
use JMS\Serializer\Accessor\Updater\ClassAccessorUpdater;
23
use JMS\Serializer\Annotation\Accessor;
24
use JMS\Serializer\Annotation\AccessorOrder;
25
use JMS\Serializer\Annotation\AccessType;
26
use JMS\Serializer\Annotation\Discriminator;
27
use JMS\Serializer\Annotation\Exclude;
28
use JMS\Serializer\Annotation\ExcludeIf;
29
use JMS\Serializer\Annotation\ExclusionPolicy;
30
use JMS\Serializer\Annotation\Expose;
31
use JMS\Serializer\Annotation\Groups;
32
use JMS\Serializer\Annotation\HandlerCallback;
33
use JMS\Serializer\Annotation\Inline;
34
use JMS\Serializer\Annotation\MaxDepth;
35
use JMS\Serializer\Annotation\PostDeserialize;
36
use JMS\Serializer\Annotation\PostSerialize;
37
use JMS\Serializer\Annotation\PreSerialize;
38
use JMS\Serializer\Annotation\ReadOnly;
39
use JMS\Serializer\Annotation\SerializedName;
40
use JMS\Serializer\Annotation\Since;
41
use JMS\Serializer\Annotation\SkipWhenEmpty;
42
use JMS\Serializer\Annotation\Type;
43
use JMS\Serializer\Annotation\Until;
44
use JMS\Serializer\Annotation\VirtualProperty;
45
use JMS\Serializer\Annotation\XmlAttribute;
46
use JMS\Serializer\Annotation\XmlAttributeMap;
47
use JMS\Serializer\Annotation\XmlDiscriminator;
48
use JMS\Serializer\Annotation\XmlElement;
49
use JMS\Serializer\Annotation\XmlKeyValuePairs;
50
use JMS\Serializer\Annotation\XmlList;
51
use JMS\Serializer\Annotation\XmlMap;
52
use JMS\Serializer\Annotation\XmlNamespace;
53
use JMS\Serializer\Annotation\XmlRoot;
54
use JMS\Serializer\Annotation\XmlValue;
55
use JMS\Serializer\Exception\InvalidArgumentException;
56
use JMS\Serializer\GraphNavigator;
57
use JMS\Serializer\Metadata\ClassMetadata;
58
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
59
use JMS\Serializer\Metadata\PropertyMetadata;
60
use JMS\Serializer\Metadata\ClassMetadataUpdaterInterface;
61
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
62
use Metadata\Driver\DriverInterface;
63
use Metadata\MethodMetadata;
64
65
class AnnotationDriver implements DriverInterface
66
{
67
    private $reader;
68
69
    /**
70
     * @var ClassMetadataUpdaterInterface
71
     */
72
    private $classUpdater;
73
74 442
    public function __construct(Reader $reader, ClassMetadataUpdaterInterface $classUpdater = null)
75
    {
76 442
        $this->reader = $reader;
77 442
        $this->classUpdater = $classUpdater ?: new ClassAccessorUpdater();
78 442
    }
79
80 275
    public function loadMetadataForClass(\ReflectionClass $class)
81
    {
82 275
        $classMetadata = new ClassMetadata($name = $class->name);
83 275
        $classMetadata->fileResources[] = $class->getFilename();
84
85 275
        $propertiesMetadata = array();
86 275
        $propertiesAnnotations = array();
87
88 275
        $exclusionPolicy = 'NONE';
89 275
        $excludeAll = false;
90 275
        $readOnlyClass = false;
91 275
        foreach ($this->reader->getClassAnnotations($class) as $annot) {
92 157
            if ($annot instanceof ExclusionPolicy) {
93 36
                $exclusionPolicy = $annot->policy;
94 156
            } elseif ($annot instanceof XmlRoot) {
95 68
                $classMetadata->xmlRootName = $annot->name;
96 68
                $classMetadata->xmlRootNamespace = $annot->namespace;
97 121
            } elseif ($annot instanceof XmlNamespace) {
98 24
                $classMetadata->registerNamespace($annot->uri, $annot->prefix);
99 99
            } elseif ($annot instanceof Exclude) {
100
                $excludeAll = true;
101 99
            } elseif ($annot instanceof AccessType) {
102 6
                $classMetadata->accessType = $annot->type;
103 6
                $classMetadata->accessTypeNaming = $annot->naming;
104 95
            } elseif ($annot instanceof ReadOnly) {
105 5
                $readOnlyClass = true;
106 90
            } elseif ($annot instanceof AccessorOrder) {
107 47
                $classMetadata->setAccessorOrder($annot->order, $annot->custom);
108 43
            } elseif ($annot instanceof Discriminator) {
109 21
                if ($annot->disabled) {
110
                    $classMetadata->discriminatorDisabled = true;
111
                } else {
112 21
                    $classMetadata->setDiscriminator($annot->field, $annot->map, $annot->groups);
113
                }
114 27
            } elseif ($annot instanceof XmlDiscriminator) {
115 5
                $classMetadata->xmlDiscriminatorAttribute = (bool)$annot->attribute;
116 5
                $classMetadata->xmlDiscriminatorCData = (bool)$annot->cdata;
117 5
                $classMetadata->xmlDiscriminatorNamespace = $annot->namespace ? (string)$annot->namespace : null;
118 22
            } elseif ($annot instanceof VirtualProperty) {
119 8
                $virtualPropertyMetadata = new ExpressionPropertyMetadata($name, $annot->name, $annot->exp);
120 8
                $propertiesMetadata[] = $virtualPropertyMetadata;
121 157
                $propertiesAnnotations[] = $annot->options;
122
            }
123
        }
124
125 275
        foreach ($class->getMethods() as $method) {
126 215
            if ($method->class !== $name) {
127 18
                continue;
128
            }
129
130 214
            $methodAnnotations = $this->reader->getMethodAnnotations($method);
131
132 214
            foreach ($methodAnnotations as $annot) {
133 51
                if ($annot instanceof PreSerialize) {
134 3
                    $classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->name));
135 3
                    continue 2;
136 51
                } elseif ($annot instanceof PostDeserialize) {
137 6
                    $classMetadata->addPostDeserializeMethod(new MethodMetadata($name, $method->name));
138 6
                    continue 2;
139 48
                } elseif ($annot instanceof PostSerialize) {
140 3
                    $classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->name));
141 3
                    continue 2;
142 45
                } elseif ($annot instanceof VirtualProperty) {
143 41
                    $virtualPropertyMetadata = new VirtualPropertyMetadata($name, $method->name);
144 41
                    $propertiesMetadata[] = $virtualPropertyMetadata;
145 41
                    $propertiesAnnotations[] = $methodAnnotations;
146 41
                    continue 2;
147 7
                } elseif ($annot instanceof HandlerCallback) {
148 4
                    $classMetadata->addHandlerCallback(GraphNavigator::parseDirection($annot->direction), $annot->format, $method->name);
149 190
                    continue 2;
150
                }
151
            }
152
        }
153
154 275
        if (!$excludeAll) {
155 275
            foreach ($class->getProperties() as $property) {
156 254
                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...
157 20
                    continue;
158
                }
159 253
                $propertiesMetadata[] = new PropertyMetadata($name, $property->getName());
160 253
                $propertiesAnnotations[] = $this->reader->getPropertyAnnotations($property);
161
            }
162
163
            /** @var PropertyMetadata $propertyMetadata */
164 275
            foreach ($propertiesMetadata as $propertyKey => $propertyMetadata) {
165 262
                $isExclude = false;
166 262
                $isExpose = $propertyMetadata instanceof VirtualPropertyMetadata
167 262
                    || $propertyMetadata instanceof ExpressionPropertyMetadata;
168 262
                $propertyMetadata->readOnly = $propertyMetadata->readOnly || $readOnlyClass;
169 262
                $accessType = null;
170 262
                $accessTypeNaming = null;
171 262
                $accessor = array(null, null);
172
173 262
                $propertyAnnotations = $propertiesAnnotations[$propertyKey];
174
175 262
                foreach ($propertyAnnotations as $annot) {
176 254
                    if ($annot instanceof Since) {
177 7
                        $propertyMetadata->sinceVersion = $annot->version;
178 254
                    } elseif ($annot instanceof Until) {
179 7
                        $propertyMetadata->untilVersion = $annot->version;
180 254
                    } elseif ($annot instanceof SerializedName) {
181 86
                        $propertyMetadata->serializedName = $annot->name;
182 254
                    } elseif ($annot instanceof SkipWhenEmpty) {
183 7
                        $propertyMetadata->skipWhenEmpty = true;
184 253
                    } elseif ($annot instanceof Expose) {
185 38
                        $isExpose = true;
186 38
                        if (null !== $annot->if) {
187 38
                            $propertyMetadata->excludeIf = "!(" . $annot->if . ")";
188
                        }
189 253
                    } elseif ($annot instanceof Exclude) {
190 45
                        if (null !== $annot->if) {
191 22
                            $propertyMetadata->excludeIf = $annot->if;
192
                        } else {
193 45
                            $isExclude = true;
194
                        }
195 251
                    } elseif ($annot instanceof Type) {
196 209
                        $propertyMetadata->setType($annot->name);
197 167
                    } elseif ($annot instanceof XmlElement) {
198 21
                        $propertyMetadata->xmlAttribute = false;
199 21
                        $propertyMetadata->xmlElementCData = $annot->cdata;
200 21
                        $propertyMetadata->xmlNamespace = $annot->namespace;
201 161
                    } elseif ($annot instanceof XmlList) {
202 45
                        $propertyMetadata->xmlCollection = true;
203 45
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
204 45
                        $propertyMetadata->xmlEntryName = $annot->entry;
205 45
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
206 45
                        $propertyMetadata->xmlCollectionSkipWhenEmpty = $annot->skipWhenEmpty;
207 153
                    } elseif ($annot instanceof XmlMap) {
208 23
                        $propertyMetadata->xmlCollection = true;
209 23
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
210 23
                        $propertyMetadata->xmlEntryName = $annot->entry;
211 23
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
212 23
                        $propertyMetadata->xmlKeyAttribute = $annot->keyAttribute;
213 145
                    } elseif ($annot instanceof XmlKeyValuePairs) {
214 9
                        $propertyMetadata->xmlKeyValuePairs = true;
215 136
                    } elseif ($annot instanceof XmlAttribute) {
216 44
                        $propertyMetadata->xmlAttribute = true;
217 44
                        $propertyMetadata->xmlNamespace = $annot->namespace;
218 130
                    } elseif ($annot instanceof XmlValue) {
219 28
                        $propertyMetadata->xmlValue = true;
220 28
                        $propertyMetadata->xmlElementCData = $annot->cdata;
221 108
                    } elseif ($annot instanceof XmlElement) {
222
                        $propertyMetadata->xmlElementCData = $annot->cdata;
223 108
                    } elseif ($annot instanceof AccessType) {
224 4
                        $accessType = $annot->type;
225 4
                        $accessTypeNaming = $annot->naming;
226 108
                    } elseif ($annot instanceof ReadOnly) {
227 11
                        $propertyMetadata->readOnly = $annot->readOnly;
228 104
                    } elseif ($annot instanceof Accessor) {
229 11
                        $accessor = array($annot->getter, $annot->setter);
230 93
                    } elseif ($annot instanceof Groups) {
231 43
                        $propertyMetadata->groups = $annot->groups;
232 43
                        foreach ((array)$propertyMetadata->groups as $groupName) {
233 43
                            if (false !== strpos($groupName, ',')) {
234 3
                                throw new InvalidArgumentException(sprintf(
235 3
                                    'Invalid group name "%s" on "%s", did you mean to create multiple groups?',
236 3
                                    implode(', ', $propertyMetadata->groups),
237 43
                                    $propertyMetadata->class . '->' . $propertyMetadata->name
238
                                ));
239
                            }
240
                        }
241 68
                    } elseif ($annot instanceof Inline) {
242 7
                        $propertyMetadata->inline = true;
243 61
                    } elseif ($annot instanceof XmlAttributeMap) {
244 4
                        $propertyMetadata->xmlAttributeMap = true;
245 57
                    } elseif ($annot instanceof MaxDepth) {
246 251
                        $propertyMetadata->maxDepth = $annot->depth;
247
                    }
248
                }
249
250
251 259
                if ((ExclusionPolicy::NONE === $exclusionPolicy && !$isExclude)
252 259
                    || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)
253
                ) {
254 259
                    $propertyMetadata->setAccessor(
255 259
                        $accessType,
256 259
                        $accessor[0],
257 259
                        $accessor[1],
258 259
                        $accessTypeNaming
259
                    );
260 259
                    $classMetadata->addPropertyMetadata($propertyMetadata);
261
                }
262
            }
263
        }
264
265 272
        $this->classUpdater->update($classMetadata);
266
267 271
        return $classMetadata;
268
    }
269
}
270