loadMetadataForClass()   F
last analyzed

Complexity

Conditions 70
Paths > 20000

Size

Total Lines 225
Code Lines 167

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 70
eloc 167
c 2
b 0
f 0
nc 26400
nop 1
dl 0
loc 225
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer\Metadata\Driver;
6
7
use Doctrine\Common\Annotations\Reader;
8
use JMS\Serializer\Annotation\Accessor;
9
use JMS\Serializer\Annotation\AccessorOrder;
10
use JMS\Serializer\Annotation\AccessType;
11
use JMS\Serializer\Annotation\Discriminator;
12
use JMS\Serializer\Annotation\Exclude;
13
use JMS\Serializer\Annotation\ExclusionPolicy;
14
use JMS\Serializer\Annotation\Expose;
15
use JMS\Serializer\Annotation\Groups;
16
use JMS\Serializer\Annotation\Inline;
17
use JMS\Serializer\Annotation\MaxDepth;
18
use JMS\Serializer\Annotation\PostDeserialize;
19
use JMS\Serializer\Annotation\PostSerialize;
20
use JMS\Serializer\Annotation\PreSerialize;
21
use JMS\Serializer\Annotation\ReadOnlyProperty;
22
use JMS\Serializer\Annotation\SerializedName;
23
use JMS\Serializer\Annotation\SerializerAttribute;
24
use JMS\Serializer\Annotation\Since;
25
use JMS\Serializer\Annotation\SkipWhenEmpty;
26
use JMS\Serializer\Annotation\Type;
27
use JMS\Serializer\Annotation\UnionDiscriminator;
28
use JMS\Serializer\Annotation\Until;
29
use JMS\Serializer\Annotation\VirtualProperty;
30
use JMS\Serializer\Annotation\XmlAttribute;
31
use JMS\Serializer\Annotation\XmlAttributeMap;
32
use JMS\Serializer\Annotation\XmlDiscriminator;
33
use JMS\Serializer\Annotation\XmlElement;
34
use JMS\Serializer\Annotation\XmlKeyValuePairs;
35
use JMS\Serializer\Annotation\XmlList;
36
use JMS\Serializer\Annotation\XmlMap;
37
use JMS\Serializer\Annotation\XmlNamespace;
38
use JMS\Serializer\Annotation\XmlRoot;
39
use JMS\Serializer\Annotation\XmlValue;
40
use JMS\Serializer\Exception\InvalidMetadataException;
41
use JMS\Serializer\Expression\CompilableExpressionEvaluatorInterface;
42
use JMS\Serializer\Metadata\ClassMetadata;
43
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
44
use JMS\Serializer\Metadata\PropertyMetadata;
45
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
46
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
47
use JMS\Serializer\Type\Parser;
48
use JMS\Serializer\Type\ParserInterface;
49
use Metadata\ClassMetadata as BaseClassMetadata;
50
use Metadata\Driver\DriverInterface;
51
use Metadata\MethodMetadata;
52
53
class AnnotationOrAttributeDriver implements DriverInterface
54
{
55
    use ExpressionMetadataTrait;
56
57
    /**
58
     * @var ParserInterface
59
     */
60
    private $typeParser;
61
62
    /**
63
     * @var PropertyNamingStrategyInterface
64
     */
65
    private $namingStrategy;
66
67
    /**
68
     * @var Reader
69
     */
70
    private $reader;
71
72
    public function __construct(PropertyNamingStrategyInterface $namingStrategy, ?ParserInterface $typeParser = null, ?CompilableExpressionEvaluatorInterface $expressionEvaluator = null, ?Reader $reader = null)
73
    {
74
        $this->typeParser = $typeParser ?: new Parser();
75
        $this->namingStrategy = $namingStrategy;
76
        $this->expressionEvaluator = $expressionEvaluator;
77
        $this->reader = $reader;
78
    }
79
80
    public function loadMetadataForClass(\ReflectionClass $class): ?BaseClassMetadata
81
    {
82
        $configured = false;
83
84
        $classMetadata = new ClassMetadata($name = $class->name);
85
        $fileResource =  $class->getFilename();
86
87
        if (false !== $fileResource) {
88
            $classMetadata->fileResources[] = $fileResource;
89
        }
90
91
        $propertiesMetadata = [];
92
        $propertiesAnnotations = [];
93
94
        $exclusionPolicy = ExclusionPolicy::NONE;
95
        $excludeAll = false;
96
        $classAccessType = PropertyMetadata::ACCESS_TYPE_PROPERTY;
97
        $readOnlyClass = false;
98
99
        foreach ($this->getClassAnnotations($class) as $annot) {
100
            $configured = true;
101
102
            if ($annot instanceof ExclusionPolicy) {
103
                $exclusionPolicy = $annot->policy;
104
            } elseif ($annot instanceof XmlRoot) {
105
                $classMetadata->xmlRootName = $annot->name;
106
                $classMetadata->xmlRootNamespace = $annot->namespace;
107
                $classMetadata->xmlRootPrefix = $annot->prefix;
108
            } elseif ($annot instanceof XmlNamespace) {
109
                $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

109
                $classMetadata->registerNamespace(/** @scrutinizer ignore-type */ $annot->uri, $annot->prefix);
Loading history...
110
            } elseif ($annot instanceof Exclude) {
111
                if (null !== $annot->if) {
112
                    $classMetadata->excludeIf = $this->parseExpression($annot->if);
113
                } else {
114
                    $excludeAll = true;
115
                }
116
            } elseif ($annot instanceof AccessType) {
117
                $classAccessType = $annot->type;
118
            } elseif ($annot instanceof ReadOnlyProperty) {
119
                $readOnlyClass = true;
120
            } elseif ($annot instanceof AccessorOrder) {
121
                $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

121
                $classMetadata->setAccessorOrder(/** @scrutinizer ignore-type */ $annot->order, $annot->custom);
Loading history...
122
            } elseif ($annot instanceof Discriminator) {
123
                if ($annot->disabled) {
124
                    $classMetadata->discriminatorDisabled = true;
125
                } else {
126
                    $classMetadata->setDiscriminator($annot->field, $annot->map, $annot->groups);
127
                }
128
            } elseif ($annot instanceof XmlDiscriminator) {
129
                $classMetadata->xmlDiscriminatorAttribute = (bool) $annot->attribute;
130
                $classMetadata->xmlDiscriminatorCData = (bool) $annot->cdata;
131
                $classMetadata->xmlDiscriminatorNamespace = $annot->namespace ? (string) $annot->namespace : null;
132
            } elseif ($annot instanceof VirtualProperty) {
133
                $virtualPropertyMetadata = new ExpressionPropertyMetadata(
134
                    $name,
135
                    $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

135
                    /** @scrutinizer ignore-type */ $annot->name,
Loading history...
136
                    $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

136
                    $this->parseExpression(/** @scrutinizer ignore-type */ $annot->exp),
Loading history...
137
                );
138
                $propertiesMetadata[] = $virtualPropertyMetadata;
139
                $propertiesAnnotations[] = $annot->options;
140
            }
141
        }
142
143
        foreach ($class->getMethods() as $method) {
144
            if ($method->class !== $name) {
145
                continue;
146
            }
147
148
            $methodAnnotations = $this->getMethodAnnotations($method);
149
150
            foreach ($methodAnnotations as $annot) {
151
                $configured = true;
152
153
                if ($annot instanceof PreSerialize) {
154
                    $classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->name));
155
                    continue 2;
156
                } elseif ($annot instanceof PostDeserialize) {
157
                    $classMetadata->addPostDeserializeMethod(new MethodMetadata($name, $method->name));
158
                    continue 2;
159
                } elseif ($annot instanceof PostSerialize) {
160
                    $classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->name));
161
                    continue 2;
162
                } elseif ($annot instanceof VirtualProperty) {
163
                    $virtualPropertyMetadata = new VirtualPropertyMetadata($name, $method->name);
164
                    $propertiesMetadata[] = $virtualPropertyMetadata;
165
                    $propertiesAnnotations[] = $methodAnnotations;
166
                    continue 2;
167
                }
168
            }
169
        }
170
171
        if (!$excludeAll) {
172
            foreach ($class->getProperties() as $property) {
173
                if ($property->class !== $name || (isset($property->info) && $property->info['class'] !== $name)) {
174
                    continue;
175
                }
176
177
                $propertiesMetadata[] = new PropertyMetadata($name, $property->getName());
178
                $propertiesAnnotations[] = $this->getPropertyAnnotations($property);
179
            }
180
181
            foreach ($propertiesMetadata as $propertyKey => $propertyMetadata) {
182
                $isExclude = false;
183
                $isExpose = $propertyMetadata instanceof VirtualPropertyMetadata
184
                    || $propertyMetadata instanceof ExpressionPropertyMetadata;
185
                $propertyMetadata->readOnly = $propertyMetadata->readOnly || $readOnlyClass;
186
                $accessType = $classAccessType;
187
                $accessor = [null, null];
188
189
                $propertyAnnotations = $propertiesAnnotations[$propertyKey];
190
191
                foreach ($propertyAnnotations as $annot) {
192
                    $configured = true;
0 ignored issues
show
Unused Code introduced by
The assignment to $configured is dead and can be removed.
Loading history...
193
194
                    if ($annot instanceof Since) {
195
                        $propertyMetadata->sinceVersion = $annot->version;
196
                    } elseif ($annot instanceof Until) {
197
                        $propertyMetadata->untilVersion = $annot->version;
198
                    } elseif ($annot instanceof SerializedName) {
199
                        $propertyMetadata->serializedName = $annot->name;
200
                    } elseif ($annot instanceof SkipWhenEmpty) {
201
                        $propertyMetadata->skipWhenEmpty = true;
202
                    } elseif ($annot instanceof Expose) {
203
                        $isExpose = true;
204
                        if (null !== $annot->if) {
205
                            $propertyMetadata->excludeIf = $this->parseExpression('!(' . $annot->if . ')');
206
                        }
207
                    } elseif ($annot instanceof Exclude) {
208
                        if (null !== $annot->if) {
209
                            $propertyMetadata->excludeIf = $this->parseExpression($annot->if);
210
                        } else {
211
                            $isExclude = true;
212
                        }
213
                    } elseif ($annot instanceof Type) {
214
                        $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

214
                        $propertyMetadata->setType($this->typeParser->parse(/** @scrutinizer ignore-type */ $annot->name));
Loading history...
215
                    } elseif ($annot instanceof XmlElement) {
216
                        $propertyMetadata->xmlAttribute = false;
217
                        $propertyMetadata->xmlElementCData = $annot->cdata;
218
                        $propertyMetadata->xmlNamespace = $annot->namespace;
219
                    } elseif ($annot instanceof XmlList) {
220
                        $propertyMetadata->xmlCollection = true;
221
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
222
                        $propertyMetadata->xmlEntryName = $annot->entry;
223
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
224
                        $propertyMetadata->xmlCollectionSkipWhenEmpty = $annot->skipWhenEmpty;
225
                    } elseif ($annot instanceof XmlMap) {
226
                        $propertyMetadata->xmlCollection = true;
227
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
228
                        $propertyMetadata->xmlEntryName = $annot->entry;
229
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
230
                        $propertyMetadata->xmlKeyAttribute = $annot->keyAttribute;
231
                    } elseif ($annot instanceof XmlKeyValuePairs) {
232
                        $propertyMetadata->xmlKeyValuePairs = true;
233
                    } elseif ($annot instanceof XmlAttribute) {
234
                        $propertyMetadata->xmlAttribute = true;
235
                        $propertyMetadata->xmlNamespace = $annot->namespace;
236
                    } elseif ($annot instanceof XmlValue) {
237
                        $propertyMetadata->xmlValue = true;
238
                        $propertyMetadata->xmlElementCData = $annot->cdata;
239
                    } elseif ($annot instanceof AccessType) {
240
                        $accessType = $annot->type;
241
                    } elseif ($annot instanceof ReadOnlyProperty) {
242
                        $propertyMetadata->readOnly = $annot->readOnly;
243
                    } elseif ($annot instanceof Accessor) {
244
                        $accessor = [$annot->getter, $annot->setter];
245
                    } elseif ($annot instanceof Groups) {
246
                        $propertyMetadata->groups = $annot->groups;
247
                        foreach ((array) $propertyMetadata->groups as $groupName) {
248
                            if (false !== strpos($groupName, ',')) {
249
                                throw new InvalidMetadataException(sprintf(
250
                                    'Invalid group name "%s" on "%s", did you mean to create multiple groups?',
251
                                    implode(', ', $propertyMetadata->groups),
252
                                    $propertyMetadata->class . '->' . $propertyMetadata->name,
253
                                ));
254
                            }
255
                        }
256
                    } elseif ($annot instanceof Inline) {
257
                        $propertyMetadata->inline = true;
258
                    } elseif ($annot instanceof XmlAttributeMap) {
259
                        $propertyMetadata->xmlAttributeMap = true;
260
                    } elseif ($annot instanceof MaxDepth) {
261
                        $propertyMetadata->maxDepth = $annot->depth;
262
                    } elseif ($annot instanceof UnionDiscriminator) {
263
                        $propertyMetadata->setType([
264
                            'name' => 'union',
265
                            'params' => [null, $annot->field, $annot->map],
266
                        ]);
267
                    }
268
                }
269
270
                if ($propertyMetadata->inline) {
271
                    $classMetadata->isList = $classMetadata->isList || PropertyMetadata::isCollectionList($propertyMetadata->type);
272
                    $classMetadata->isMap = $classMetadata->isMap || PropertyMetadata::isCollectionMap($propertyMetadata->type);
273
274
                    if ($classMetadata->isMap && $classMetadata->isList) {
275
                        throw new InvalidMetadataException('Can not have an inline map and and inline map on the same class');
276
                    }
277
                }
278
279
                if (!$propertyMetadata->serializedName) {
280
                    $propertyMetadata->serializedName = $this->namingStrategy->translateName($propertyMetadata);
281
                }
282
283
                foreach ($propertyAnnotations as $annot) {
284
                    if ($annot instanceof VirtualProperty && null !== $annot->name) {
285
                        $propertyMetadata->name = $annot->name;
286
                    }
287
                }
288
289
                if (
290
                    (ExclusionPolicy::NONE === $exclusionPolicy && !$isExclude)
291
                    || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)
292
                ) {
293
                    $propertyMetadata->setAccessor($accessType, $accessor[0], $accessor[1]);
294
                    $classMetadata->addPropertyMetadata($propertyMetadata);
295
                }
296
            }
297
        }
298
299
        // if (!$configured) {
300
            // return null;
301
            // uncomment the above line afetr a couple of months
302
        // }
303
304
        return $classMetadata;
305
    }
306
307
    /**
308
     * @return list<SerializerAttribute>
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...
309
     */
310
    protected function getClassAnnotations(\ReflectionClass $class): array
311
    {
312
        $annotations = [];
313
314
        if (PHP_VERSION_ID >= 80000) {
315
            $annotations = array_map(
316
                static fn (\ReflectionAttribute $attribute): object => $attribute->newInstance(),
317
                $class->getAttributes(SerializerAttribute::class, \ReflectionAttribute::IS_INSTANCEOF),
318
            );
319
        }
320
321
        if (null !== $this->reader) {
322
            $annotations = array_merge($annotations, $this->reader->getClassAnnotations($class));
323
        }
324
325
        return $annotations;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $annotations returns the type array which is incompatible with the documented return type JMS\Serializer\Metadata\Driver\list.
Loading history...
326
    }
327
328
    /**
329
     * @return list<SerializerAttribute>
330
     */
331
    protected function getMethodAnnotations(\ReflectionMethod $method): array
332
    {
333
        $annotations = [];
334
335
        if (PHP_VERSION_ID >= 80000) {
336
            $annotations = array_map(
337
                static fn (\ReflectionAttribute $attribute): object => $attribute->newInstance(),
338
                $method->getAttributes(SerializerAttribute::class, \ReflectionAttribute::IS_INSTANCEOF),
339
            );
340
        }
341
342
        if (null !== $this->reader) {
343
            $annotations = array_merge($annotations, $this->reader->getMethodAnnotations($method));
344
        }
345
346
        return $annotations;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $annotations returns the type array which is incompatible with the documented return type JMS\Serializer\Metadata\Driver\list.
Loading history...
347
    }
348
349
    /**
350
     * @return list<SerializerAttribute>
351
     */
352
    protected function getPropertyAnnotations(\ReflectionProperty $property): array
353
    {
354
        $annotations = [];
355
356
        if (PHP_VERSION_ID >= 80000) {
357
            $annotations = array_map(
358
                static fn (\ReflectionAttribute $attribute): object => $attribute->newInstance(),
359
                $property->getAttributes(SerializerAttribute::class, \ReflectionAttribute::IS_INSTANCEOF),
360
            );
361
        }
362
363
        if (null !== $this->reader) {
364
            $annotations = array_merge($annotations, $this->reader->getPropertyAnnotations($property));
365
        }
366
367
        return $annotations;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $annotations returns the type array which is incompatible with the documented return type JMS\Serializer\Metadata\Driver\list.
Loading history...
368
    }
369
}
370