Completed
Push — master ( 3e05ae...99069b )
by Asmir
12s
created

AnnotationDriver   F

Complexity

Total Complexity 68

Size/Duplication

Total Lines 206
Duplicated Lines 0 %

Test Coverage

Coverage 98.05%

Importance

Changes 0
Metric Value
dl 0
loc 206
ccs 151
cts 154
cp 0.9805
rs 2.9411
c 0
b 0
f 0
wmc 68

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 2
F loadMetadataForClass() 0 190 66

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