Passed
Push — master ( 12666c...576d22 )
by Asmir
14:13 queued 11:54
created

AnnotationOrAttributeDriver   F

Complexity

Total Complexity 71

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 166
c 1
b 0
f 0
dl 0
loc 244
rs 2.7199
wmc 71

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 2
F loadMetadataForClass() 0 207 69

How to fix   Complexity   

Complex Class

Complex classes like AnnotationOrAttributeDriver 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 AnnotationOrAttributeDriver, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer\Metadata\Driver;
6
7
use JMS\Serializer\Annotation\Accessor;
8
use JMS\Serializer\Annotation\AccessorOrder;
9
use JMS\Serializer\Annotation\AccessType;
10
use JMS\Serializer\Annotation\Discriminator;
11
use JMS\Serializer\Annotation\Exclude;
12
use JMS\Serializer\Annotation\ExclusionPolicy;
13
use JMS\Serializer\Annotation\Expose;
14
use JMS\Serializer\Annotation\Groups;
15
use JMS\Serializer\Annotation\Inline;
16
use JMS\Serializer\Annotation\MaxDepth;
17
use JMS\Serializer\Annotation\PostDeserialize;
18
use JMS\Serializer\Annotation\PostSerialize;
19
use JMS\Serializer\Annotation\PreSerialize;
20
use JMS\Serializer\Annotation\ReadOnlyProperty;
21
use JMS\Serializer\Annotation\SerializedName;
22
use JMS\Serializer\Annotation\Since;
23
use JMS\Serializer\Annotation\SkipWhenEmpty;
24
use JMS\Serializer\Annotation\Type;
25
use JMS\Serializer\Annotation\Until;
26
use JMS\Serializer\Annotation\VirtualProperty;
27
use JMS\Serializer\Annotation\XmlAttribute;
28
use JMS\Serializer\Annotation\XmlAttributeMap;
29
use JMS\Serializer\Annotation\XmlDiscriminator;
30
use JMS\Serializer\Annotation\XmlElement;
31
use JMS\Serializer\Annotation\XmlKeyValuePairs;
32
use JMS\Serializer\Annotation\XmlList;
33
use JMS\Serializer\Annotation\XmlMap;
34
use JMS\Serializer\Annotation\XmlNamespace;
35
use JMS\Serializer\Annotation\XmlRoot;
36
use JMS\Serializer\Annotation\XmlValue;
37
use JMS\Serializer\Exception\InvalidMetadataException;
38
use JMS\Serializer\Expression\CompilableExpressionEvaluatorInterface;
39
use JMS\Serializer\Metadata\ClassMetadata;
40
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
41
use JMS\Serializer\Metadata\PropertyMetadata;
42
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
43
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
44
use JMS\Serializer\Type\Parser;
45
use JMS\Serializer\Type\ParserInterface;
46
use Metadata\ClassMetadata as BaseClassMetadata;
47
use Metadata\Driver\DriverInterface;
48
use Metadata\MethodMetadata;
49
50
abstract class AnnotationOrAttributeDriver implements DriverInterface
51
{
52
    use ExpressionMetadataTrait;
53
54
    /**
55
     * @var ParserInterface
56
     */
57
    private $typeParser;
58
59
    /**
60
     * @var PropertyNamingStrategyInterface
61
     */
62
    private $namingStrategy;
63
64
    public function __construct(PropertyNamingStrategyInterface $namingStrategy, ?ParserInterface $typeParser = null, ?CompilableExpressionEvaluatorInterface $expressionEvaluator = null)
65
    {
66
        $this->typeParser = $typeParser ?: new Parser();
67
        $this->namingStrategy = $namingStrategy;
68
        $this->expressionEvaluator = $expressionEvaluator;
69
    }
70
71
    public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadata
72
    {
73
        $classMetadata = new ClassMetadata($name = $class->name);
74
        $fileResource =  $class->getFilename();
75
76
        if (false !== $fileResource) {
77
            $classMetadata->fileResources[] = $fileResource;
78
        }
79
80
        $propertiesMetadata = [];
81
        $propertiesAnnotations = [];
82
83
        $exclusionPolicy = ExclusionPolicy::NONE;
84
        $excludeAll = false;
85
        $classAccessType = PropertyMetadata::ACCESS_TYPE_PROPERTY;
86
        $readOnlyClass = false;
87
88
        foreach ($this->getClassAnnotations($class) as $annot) {
89
            if ($annot instanceof ExclusionPolicy) {
90
                $exclusionPolicy = $annot->policy;
91
            } elseif ($annot instanceof XmlRoot) {
92
                $classMetadata->xmlRootName = $annot->name;
93
                $classMetadata->xmlRootNamespace = $annot->namespace;
94
                $classMetadata->xmlRootPrefix = $annot->prefix;
95
            } elseif ($annot instanceof XmlNamespace) {
96
                $classMetadata->registerNamespace($annot->uri, $annot->prefix);
0 ignored issues
show
Bug introduced by
It seems like $annot->uri can also be of type null; however, parameter $uri of JMS\Serializer\Metadata\...ta::registerNamespace() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

96
                $classMetadata->registerNamespace(/** @scrutinizer ignore-type */ $annot->uri, $annot->prefix);
Loading history...
97
            } elseif ($annot instanceof Exclude) {
98
                if (null !== $annot->if) {
99
                    $classMetadata->excludeIf = $this->parseExpression($annot->if);
100
                } else {
101
                    $excludeAll = true;
102
                }
103
            } elseif ($annot instanceof AccessType) {
104
                $classAccessType = $annot->type;
105
            } elseif ($annot instanceof ReadOnlyProperty) {
106
                $readOnlyClass = true;
107
            } elseif ($annot instanceof AccessorOrder) {
108
                $classMetadata->setAccessorOrder($annot->order, $annot->custom);
0 ignored issues
show
Bug introduced by
It seems like $annot->order can also be of type null; however, parameter $order of JMS\Serializer\Metadata\...ata::setAccessorOrder() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

108
                $classMetadata->setAccessorOrder(/** @scrutinizer ignore-type */ $annot->order, $annot->custom);
Loading history...
109
            } elseif ($annot instanceof Discriminator) {
110
                if ($annot->disabled) {
111
                    $classMetadata->discriminatorDisabled = true;
112
                } else {
113
                    $classMetadata->setDiscriminator($annot->field, $annot->map, $annot->groups);
114
                }
115
            } elseif ($annot instanceof XmlDiscriminator) {
116
                $classMetadata->xmlDiscriminatorAttribute = (bool) $annot->attribute;
117
                $classMetadata->xmlDiscriminatorCData = (bool) $annot->cdata;
118
                $classMetadata->xmlDiscriminatorNamespace = $annot->namespace ? (string) $annot->namespace : null;
119
            } elseif ($annot instanceof VirtualProperty) {
120
                $virtualPropertyMetadata = new ExpressionPropertyMetadata(
121
                    $name,
122
                    $annot->name,
0 ignored issues
show
Bug introduced by
It seems like $annot->name can also be of type null; however, parameter $fieldName of JMS\Serializer\Metadata\...Metadata::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

122
                    /** @scrutinizer ignore-type */ $annot->name,
Loading history...
123
                    $this->parseExpression($annot->exp)
0 ignored issues
show
Bug introduced by
It seems like $annot->exp can also be of type null; however, parameter $expression of JMS\Serializer\Metadata\...iver::parseExpression() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

123
                    $this->parseExpression(/** @scrutinizer ignore-type */ $annot->exp)
Loading history...
124
                );
125
                $propertiesMetadata[] = $virtualPropertyMetadata;
126
                $propertiesAnnotations[] = $annot->options;
127
            }
128
        }
129
130
        foreach ($class->getMethods() as $method) {
131
            if ($method->class !== $name) {
132
                continue;
133
            }
134
135
            $methodAnnotations = $this->getMethodAnnotations($method);
136
137
            foreach ($methodAnnotations as $annot) {
138
                if ($annot instanceof PreSerialize) {
139
                    $classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->name));
140
                    continue 2;
141
                } elseif ($annot instanceof PostDeserialize) {
142
                    $classMetadata->addPostDeserializeMethod(new MethodMetadata($name, $method->name));
143
                    continue 2;
144
                } elseif ($annot instanceof PostSerialize) {
145
                    $classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->name));
146
                    continue 2;
147
                } elseif ($annot instanceof VirtualProperty) {
148
                    $virtualPropertyMetadata = new VirtualPropertyMetadata($name, $method->name);
149
                    $propertiesMetadata[] = $virtualPropertyMetadata;
150
                    $propertiesAnnotations[] = $methodAnnotations;
151
                    continue 2;
152
                }
153
            }
154
        }
155
156
        if (!$excludeAll) {
157
            foreach ($class->getProperties() as $property) {
158
                if ($property->class !== $name || (isset($property->info) && $property->info['class'] !== $name)) {
159
                    continue;
160
                }
161
162
                $propertiesMetadata[] = new PropertyMetadata($name, $property->getName());
163
                $propertiesAnnotations[] = $this->getPropertyAnnotations($property);
164
            }
165
166
            foreach ($propertiesMetadata as $propertyKey => $propertyMetadata) {
167
                $isExclude = false;
168
                $isExpose = $propertyMetadata instanceof VirtualPropertyMetadata
169
                    || $propertyMetadata instanceof ExpressionPropertyMetadata;
170
                $propertyMetadata->readOnly = $propertyMetadata->readOnly || $readOnlyClass;
171
                $accessType = $classAccessType;
172
                $accessor = [null, null];
173
174
                $propertyAnnotations = $propertiesAnnotations[$propertyKey];
175
176
                foreach ($propertyAnnotations as $annot) {
177
                    if ($annot instanceof Since) {
178
                        $propertyMetadata->sinceVersion = $annot->version;
179
                    } elseif ($annot instanceof Until) {
180
                        $propertyMetadata->untilVersion = $annot->version;
181
                    } elseif ($annot instanceof SerializedName) {
182
                        $propertyMetadata->serializedName = $annot->name;
183
                    } elseif ($annot instanceof SkipWhenEmpty) {
184
                        $propertyMetadata->skipWhenEmpty = true;
185
                    } elseif ($annot instanceof Expose) {
186
                        $isExpose = true;
187
                        if (null !== $annot->if) {
188
                            $propertyMetadata->excludeIf = $this->parseExpression('!(' . $annot->if . ')');
189
                        }
190
                    } elseif ($annot instanceof Exclude) {
191
                        if (null !== $annot->if) {
192
                            $propertyMetadata->excludeIf = $this->parseExpression($annot->if);
193
                        } else {
194
                            $isExclude = true;
195
                        }
196
                    } elseif ($annot instanceof Type) {
197
                        $propertyMetadata->setType($this->typeParser->parse($annot->name));
0 ignored issues
show
Bug introduced by
It seems like $annot->name can also be of type null; however, parameter $type of JMS\Serializer\Type\ParserInterface::parse() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

197
                        $propertyMetadata->setType($this->typeParser->parse(/** @scrutinizer ignore-type */ $annot->name));
Loading history...
198
                    } elseif ($annot instanceof XmlElement) {
199
                        $propertyMetadata->xmlAttribute = false;
200
                        $propertyMetadata->xmlElementCData = $annot->cdata;
201
                        $propertyMetadata->xmlNamespace = $annot->namespace;
202
                    } elseif ($annot instanceof XmlList) {
203
                        $propertyMetadata->xmlCollection = true;
204
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
205
                        $propertyMetadata->xmlEntryName = $annot->entry;
206
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
207
                        $propertyMetadata->xmlCollectionSkipWhenEmpty = $annot->skipWhenEmpty;
208
                    } elseif ($annot instanceof XmlMap) {
209
                        $propertyMetadata->xmlCollection = true;
210
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
211
                        $propertyMetadata->xmlEntryName = $annot->entry;
212
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
213
                        $propertyMetadata->xmlKeyAttribute = $annot->keyAttribute;
214
                    } elseif ($annot instanceof XmlKeyValuePairs) {
215
                        $propertyMetadata->xmlKeyValuePairs = true;
216
                    } elseif ($annot instanceof XmlAttribute) {
217
                        $propertyMetadata->xmlAttribute = true;
218
                        $propertyMetadata->xmlNamespace = $annot->namespace;
219
                    } elseif ($annot instanceof XmlValue) {
220
                        $propertyMetadata->xmlValue = true;
221
                        $propertyMetadata->xmlElementCData = $annot->cdata;
222
                    } elseif ($annot instanceof AccessType) {
223
                        $accessType = $annot->type;
224
                    } elseif ($annot instanceof ReadOnlyProperty) {
225
                        $propertyMetadata->readOnly = $annot->readOnly;
226
                    } elseif ($annot instanceof Accessor) {
227
                        $accessor = [$annot->getter, $annot->setter];
228
                    } elseif ($annot instanceof Groups) {
229
                        $propertyMetadata->groups = $annot->groups;
230
                        foreach ((array) $propertyMetadata->groups as $groupName) {
231
                            if (false !== strpos($groupName, ',')) {
232
                                throw new InvalidMetadataException(sprintf(
233
                                    'Invalid group name "%s" on "%s", did you mean to create multiple groups?',
234
                                    implode(', ', $propertyMetadata->groups),
235
                                    $propertyMetadata->class . '->' . $propertyMetadata->name
236
                                ));
237
                            }
238
                        }
239
                    } elseif ($annot instanceof Inline) {
240
                        $propertyMetadata->inline = true;
241
                    } elseif ($annot instanceof XmlAttributeMap) {
242
                        $propertyMetadata->xmlAttributeMap = true;
243
                    } elseif ($annot instanceof MaxDepth) {
244
                        $propertyMetadata->maxDepth = $annot->depth;
245
                    }
246
                }
247
248
                if ($propertyMetadata->inline) {
249
                    $classMetadata->isList = $classMetadata->isList || PropertyMetadata::isCollectionList($propertyMetadata->type);
250
                    $classMetadata->isMap = $classMetadata->isMap || PropertyMetadata::isCollectionMap($propertyMetadata->type);
251
252
                    if ($classMetadata->isMap && $classMetadata->isList) {
253
                        throw new InvalidMetadataException('Can not have an inline map and and inline map on the same class');
254
                    }
255
                }
256
257
                if (!$propertyMetadata->serializedName) {
258
                    $propertyMetadata->serializedName = $this->namingStrategy->translateName($propertyMetadata);
259
                }
260
261
                foreach ($propertyAnnotations as $annot) {
262
                    if ($annot instanceof VirtualProperty && null !== $annot->name) {
263
                        $propertyMetadata->name = $annot->name;
264
                    }
265
                }
266
267
                if (
268
                    (ExclusionPolicy::NONE === $exclusionPolicy && !$isExclude)
269
                    || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)
270
                ) {
271
                    $propertyMetadata->setAccessor($accessType, $accessor[0], $accessor[1]);
272
                    $classMetadata->addPropertyMetadata($propertyMetadata);
273
                }
274
            }
275
        }
276
277
        return $classMetadata;
278
    }
279
280
    /**
281
     * @return list<object>
0 ignored issues
show
Bug introduced by
The type JMS\Serializer\Metadata\Driver\list 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...
282
     */
283
    abstract protected function getClassAnnotations(\ReflectionClass $class): array;
284
285
    /**
286
     * @return list<object>
287
     */
288
    abstract protected function getMethodAnnotations(\ReflectionMethod $method): array;
289
290
    /**
291
     * @return list<object>
292
     */
293
    abstract protected function getPropertyAnnotations(\ReflectionProperty $property): array;
294
}
295