Completed
Pull Request — master (#944)
by Asmir
02:43
created

AnnotationDriver   F

Complexity

Total Complexity 67

Size/Duplication

Total Lines 204
Duplicated Lines 0 %

Test Coverage

Coverage 98.03%

Importance

Changes 0
Metric Value
dl 0
loc 204
ccs 149
cts 152
cp 0.9803
rs 3.0612
c 0
b 0
f 0
wmc 67

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 2
F loadMetadataForClass() 0 188 65

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.

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
declare(strict_types=1);
4
5
/*
6
 * Copyright 2016 Johannes M. Schmitt <[email protected]>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
namespace JMS\Serializer\Metadata\Driver;
22
23
use Doctrine\Common\Annotations\Reader;
24
use JMS\Serializer\Annotation\Accessor;
25
use JMS\Serializer\Annotation\AccessorOrder;
26
use JMS\Serializer\Annotation\AccessType;
27
use JMS\Serializer\Annotation\Discriminator;
28
use JMS\Serializer\Annotation\Exclude;
29
use JMS\Serializer\Annotation\ExcludeIf;
0 ignored issues
show
Bug introduced by
The type JMS\Serializer\Annotation\ExcludeIf was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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