Completed
Pull Request — master (#771)
by
unknown
03:47
created

AnnotationDriver   F

Complexity

Total Complexity 61

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 32

Test Coverage

Coverage 97.52%

Importance

Changes 0
Metric Value
wmc 61
lcom 1
cbo 32
dl 0
loc 189
ccs 157
cts 161
cp 0.9752
rs 1.3043
c 0
b 0
f 0

2 Methods

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