AnnotationDriver   F
last analyzed

Complexity

Total Complexity 63

Size/Duplication

Total Lines 560
Duplicated Lines 0 %

Test Coverage

Coverage 87.89%

Importance

Changes 0
Metric Value
eloc 213
c 0
b 0
f 0
dl 0
loc 560
rs 3.36
wmc 63
ccs 196
cts 223
cp 0.8789

18 Methods

Rating   Name   Duplication   Size   Complexity  
A setFileExtension() 0 3 1
C getAllClassNames() 0 63 12
A addPaths() 0 3 1
A isTransient() 0 11 3
A getExcludePaths() 0 3 1
A getPaths() 0 3 1
A getReader() 0 3 1
A attachProperties() 0 38 3
B attachEntityListeners() 0 36 7
A __construct() 0 6 2
A getFileExtension() 0 3 1
A attachLifecycleCallbacks() 0 25 5
C attachPropertyOverrides() 0 105 13
A getPropertyAnnotations() 0 13 3
A getClassAnnotations() 0 13 3
A addExcludePaths() 0 3 1
A getMethodAnnotations() 0 13 3
A loadMetadataForClass() 0 31 2

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 /** @noinspection ALL */
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping\Driver;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Annotations\Reader;
9
use Doctrine\ORM\Annotation;
10
use Doctrine\ORM\Cache\Exception\CacheException;
11
use Doctrine\ORM\Events;
12
use Doctrine\ORM\Mapping;
13
use Doctrine\ORM\Mapping\Builder;
14
use FilesystemIterator;
15
use RecursiveDirectoryIterator;
16
use RecursiveIteratorIterator;
17
use RecursiveRegexIterator;
18
use ReflectionClass;
19
use ReflectionException;
20
use ReflectionMethod;
21
use ReflectionProperty;
22
use RegexIterator;
23
use RuntimeException;
24
use UnexpectedValueException;
25
use function array_merge;
26
use function array_unique;
27
use function class_exists;
28
use function constant;
29
use function get_class;
30
use function get_declared_classes;
31
use function in_array;
32
use function is_dir;
33
use function is_numeric;
34
use function preg_match;
35
use function preg_quote;
36
use function realpath;
37
use function str_replace;
38
use function strpos;
39
40
/**
41
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
42
 */
43
class AnnotationDriver implements MappingDriver
44
{
45
    /** @var int[] */
46
    protected $entityAnnotationClasses = [
47
        Annotation\Entity::class           => 1,
48
        Annotation\MappedSuperclass::class => 2,
49
    ];
50
51
    /**
52
     * The AnnotationReader.
53
     *
54
     * @var AnnotationReader
55
     */
56
    protected $reader;
57
58
    /**
59
     * The paths where to look for mapping files.
60
     *
61
     * @var string[]
62
     */
63
    protected $paths = [];
64
65
    /**
66
     * The paths excluded from path where to look for mapping files.
67
     *
68
     * @var string[]
69
     */
70
    protected $excludePaths = [];
71
72
    /**
73
     * The file extension of mapping documents.
74
     *
75
     * @var string
76
     */
77
    protected $fileExtension = '.php';
78
79
    /**
80
     * Cache for AnnotationDriver#getAllClassNames().
81
     *
82
     * @var string[]|null
83
     */
84
    protected $classNames;
85
86
    /**
87
     * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
88
     * docblock annotations.
89
     *
90
     * @param Reader               $reader The AnnotationReader to use, duck-typed.
91
     * @param string|string[]|null $paths  One or multiple paths where mapping classes can be found.
92
     */
93 2302
    public function __construct(Reader $reader, $paths = null)
94
    {
95 2302
        $this->reader = $reader;
0 ignored issues
show
Documentation Bug introduced by
$reader is of type Doctrine\Common\Annotations\Reader, but the property $reader was declared to be of type Doctrine\Common\Annotations\AnnotationReader. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
96
97 2302
        if ($paths) {
98 2216
            $this->addPaths((array) $paths);
99
        }
100 2302
    }
101
102
    /**
103
     * Appends lookup paths to metadata driver.
104
     *
105
     * @param string[] $paths
106
     */
107 2220
    public function addPaths(array $paths)
108
    {
109 2220
        $this->paths = array_unique(array_merge($this->paths, $paths));
110 2220
    }
111
112
    /**
113
     * Retrieves the defined metadata lookup paths.
114
     *
115
     * @return string[]
116
     */
117
    public function getPaths()
118
    {
119
        return $this->paths;
120
    }
121
122
    /**
123
     * Append exclude lookup paths to metadata driver.
124
     *
125
     * @param string[] $paths
126
     */
127
    public function addExcludePaths(array $paths)
128
    {
129
        $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths));
130
    }
131
132
    /**
133
     * Retrieve the defined metadata lookup exclude paths.
134
     *
135
     * @return string[]
136
     */
137
    public function getExcludePaths()
138
    {
139
        return $this->excludePaths;
140
    }
141
142
    /**
143
     * Retrieve the current annotation reader
144
     *
145
     * @return Reader
146
     */
147 1
    public function getReader()
148
    {
149 1
        return $this->reader;
150
    }
151
152
    /**
153
     * Gets the file extension used to look for mapping files under.
154
     *
155
     * @return string
156
     */
157
    public function getFileExtension()
158
    {
159
        return $this->fileExtension;
160
    }
161
162
    /**
163
     * Sets the file extension used to look for mapping files under.
164
     *
165
     * @param string $fileExtension The file extension to set.
166
     */
167
    public function setFileExtension($fileExtension)
168
    {
169
        $this->fileExtension = $fileExtension;
170
    }
171
172
    /**
173
     * Returns whether the class with the specified name is transient. Only non-transient
174
     * classes, that is entities and mapped superclasses, should have their metadata loaded.
175
     *
176
     * A class is non-transient if it is annotated with an annotation
177
     * from the {@see AnnotationDriver::entityAnnotationClasses}.
178
     *
179
     * @param string $className
180
     *
181
     * @throws ReflectionException
182
     */
183 194
    public function isTransient($className) : bool
184
    {
185 194
        $classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className));
186
187 194
        foreach ($classAnnotations as $annotation) {
188 189
            if (isset($this->entityAnnotationClasses[get_class($annotation)])) {
189 189
                return false;
190
            }
191
        }
192
193 12
        return true;
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     *
199
     * @throws ReflectionException
200
     */
201 60
    public function getAllClassNames() : array
202
    {
203 60
        if ($this->classNames !== null) {
204 45
            return $this->classNames;
205
        }
206
207 60
        if (! $this->paths) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->paths of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
208
            throw Mapping\MappingException::pathRequired();
209
        }
210
211 60
        $classes       = [];
212 60
        $includedFiles = [];
213
214 60
        foreach ($this->paths as $path) {
215 60
            if (! is_dir($path)) {
216
                throw Mapping\MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
217
            }
218
219 60
            $iterator = new RegexIterator(
220 60
                new RecursiveIteratorIterator(
221 60
                    new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
222 60
                    RecursiveIteratorIterator::LEAVES_ONLY
223
                ),
224 60
                '/^.+' . preg_quote($this->fileExtension) . '$/i',
225 60
                RecursiveRegexIterator::GET_MATCH
226
            );
227
228 60
            foreach ($iterator as $file) {
229 60
                $sourceFile = $file[0];
230
231 60
                if (! preg_match('(^phar:)i', $sourceFile)) {
232 60
                    $sourceFile = realpath($sourceFile);
233
                }
234
235 60
                foreach ($this->excludePaths as $excludePath) {
236
                    $exclude = str_replace('\\', '/', realpath($excludePath));
237
                    $current = str_replace('\\', '/', $sourceFile);
238
239
                    if (strpos($current, $exclude) !== false) {
240
                        continue 2;
241
                    }
242
                }
243
244 60
                require_once $sourceFile;
245
246 60
                $includedFiles[] = $sourceFile;
247
            }
248
        }
249
250 60
        $declared = get_declared_classes();
251
252 60
        foreach ($declared as $className) {
253 60
            $reflectionClass = new ReflectionClass($className);
254 60
            $sourceFile      = $reflectionClass->getFileName();
255
256 60
            if (in_array($sourceFile, $includedFiles, true) && ! $this->isTransient($className)) {
257 60
                $classes[] = $className;
258
            }
259
        }
260
261 60
        $this->classNames = $classes;
262
263 60
        return $classes;
264
    }
265
266
    /**
267
     * {@inheritDoc}
268
     *
269
     * @throws CacheException
270
     * @throws Mapping\MappingException
271
     * @throws ReflectionException
272
     * @throws RuntimeException
273
     * @throws UnexpectedValueException
274
     */
275 380
    public function loadMetadataForClass(
276
        string $className,
277
        ?Mapping\ComponentMetadata $parent,
278
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
279
    ) : Mapping\ComponentMetadata {
280 380
        $reflectionClass  = new ReflectionClass($className);
281 380
        $classAnnotations = $this->getClassAnnotations($reflectionClass);
282 380
        $classBuilder     = new Builder\ClassMetadataBuilder($metadataBuildingContext);
283
        $classMetadata    = $classBuilder
284 380
            ->withClassName($reflectionClass->getName())
285 380
            ->withParentMetadata($parent)
286 380
            ->withEntityAnnotation($classAnnotations[Annotation\Entity::class] ?? null)
287 380
            ->withMappedSuperclassAnnotation($classAnnotations[Annotation\MappedSuperclass::class] ?? null)
288 380
            ->withEmbeddableAnnotation($classAnnotations[Annotation\Embeddable::class] ?? null)
289 380
            ->withTableAnnotation($classAnnotations[Annotation\Table::class] ?? null)
290 380
            ->withInheritanceTypeAnnotation($classAnnotations[Annotation\InheritanceType::class] ?? null)
291 380
            ->withDiscriminatorColumnAnnotation($classAnnotations[Annotation\DiscriminatorColumn::class] ?? null)
292 380
            ->withDiscriminatorMapAnnotation($classAnnotations[Annotation\DiscriminatorMap::class] ?? null)
293 380
            ->withChangeTrackingPolicyAnnotation($classAnnotations[Annotation\ChangeTrackingPolicy::class] ?? null)
294 380
            ->withCacheAnnotation($classAnnotations[Annotation\Cache::class] ?? null)
295 380
            ->build();
296
297 374
        if (! $classMetadata->isEmbeddedClass) {
298 374
            $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $classMetadata);
299 374
            $this->attachEntityListeners($classAnnotations, $classMetadata);
300
        }
301
302 374
        $this->attachProperties($classAnnotations, $reflectionClass, $classMetadata, $metadataBuildingContext);
303 371
        $this->attachPropertyOverrides($classAnnotations, $reflectionClass, $classMetadata, $metadataBuildingContext);
304
305 371
        return $classMetadata;
306
    }
307
308
    /**
309
     * @param Annotation\Annotation[] $classAnnotations
310
     */
311 374
    private function attachLifecycleCallbacks(
312
        array $classAnnotations,
313
        ReflectionClass $reflectionClass,
314
        Mapping\ClassMetadata $metadata
315
    ) : void {
316
        // Evaluate @HasLifecycleCallbacks annotation
317 374
        if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) {
318
            $eventMap = [
319 14
                Events::prePersist  => Annotation\PrePersist::class,
320 14
                Events::postPersist => Annotation\PostPersist::class,
321 14
                Events::preUpdate   => Annotation\PreUpdate::class,
322 14
                Events::postUpdate  => Annotation\PostUpdate::class,
323 14
                Events::preRemove   => Annotation\PreRemove::class,
324 14
                Events::postRemove  => Annotation\PostRemove::class,
325 14
                Events::postLoad    => Annotation\PostLoad::class,
326 14
                Events::preFlush    => Annotation\PreFlush::class,
327
            ];
328
329
            /** @var ReflectionMethod $reflectionMethod */
330 14
            foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
331 13
                $annotations = $this->getMethodAnnotations($reflectionMethod);
332
333 13
                foreach ($eventMap as $eventName => $annotationClassName) {
334 13
                    if (isset($annotations[$annotationClassName])) {
335 12
                        $metadata->addLifecycleCallback($eventName, $reflectionMethod->getName());
336
                    }
337
                }
338
            }
339
        }
340 374
    }
341
342
    /**
343
     * @param Annotation\Annotation[] $classAnnotations
344
     *
345
     * @throws ReflectionException
346
     * @throws Mapping\MappingException
347
     */
348 374
    private function attachEntityListeners(
349
        array $classAnnotations,
350
        Mapping\ClassMetadata $metadata
351
    ) : void {
352
        // Evaluate @EntityListeners annotation
353 374
        if (isset($classAnnotations[Annotation\EntityListeners::class])) {
354
            /** @var Annotation\EntityListeners $entityListenersAnnot */
355 8
            $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class];
356
            $eventMap             = [
357 8
                Events::prePersist  => Annotation\PrePersist::class,
358 8
                Events::postPersist => Annotation\PostPersist::class,
359 8
                Events::preUpdate   => Annotation\PreUpdate::class,
360 8
                Events::postUpdate  => Annotation\PostUpdate::class,
361 8
                Events::preRemove   => Annotation\PreRemove::class,
362 8
                Events::postRemove  => Annotation\PostRemove::class,
363 8
                Events::postLoad    => Annotation\PostLoad::class,
364 8
                Events::preFlush    => Annotation\PreFlush::class,
365
            ];
366
367 8
            foreach ($entityListenersAnnot->value as $listenerClassName) {
368 8
                if (! class_exists($listenerClassName)) {
369
                    throw Mapping\MappingException::entityListenerClassNotFound(
370
                        $listenerClassName,
371
                        $metadata->getClassName()
372
                    );
373
                }
374
375 8
                $listenerClass = new ReflectionClass($listenerClassName);
376
377
                /** @var ReflectionMethod $reflectionMethod */
378 8
                foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
379 8
                    $annotations = $this->getMethodAnnotations($reflectionMethod);
380
381 8
                    foreach ($eventMap as $eventName => $annotationClassName) {
382 8
                        if (isset($annotations[$annotationClassName])) {
383 6
                            $metadata->addEntityListener($eventName, $listenerClassName, $reflectionMethod->getName());
384
                        }
385
                    }
386
                }
387
            }
388
        }
389 374
    }
390
391
    /**
392
     * @param Annotation\Annotation[] $classAnnotations
393
     *
394
     * @throws Mapping\MappingException
395
     */
396 374
    private function attachProperties(
397
        array $classAnnotations,
0 ignored issues
show
Unused Code introduced by
The parameter $classAnnotations is not used and could be removed. ( Ignorable by Annotation )

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

397
        /** @scrutinizer ignore-unused */ array $classAnnotations,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
398
        ReflectionClass $reflectionClass,
399
        Mapping\ClassMetadata $metadata,
400
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
401
    ) : void {
402
        // Evaluate annotations on properties/fields
403 374
        $propertyBuilder = new Builder\PropertyMetadataBuilder($metadataBuildingContext);
404
405
        /** @var ReflectionProperty $reflProperty */
406 374
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
407 374
            if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) {
408 75
                continue;
409
            }
410
411 374
            $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty);
412
            $propertyMetadata    = $propertyBuilder
413 373
                ->withComponentMetadata($metadata)
414 373
                ->withFieldName($reflectionProperty->getName())
415 373
                ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null)
416 373
                ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class] ?? null)
417 373
                ->withColumnAnnotation($propertyAnnotations[Annotation\Column::class] ?? null)
418 373
                ->withEmbeddedAnnotation($propertyAnnotations[Annotation\Embedded::class] ?? null)
419 373
                ->withOneToOneAnnotation($propertyAnnotations[Annotation\OneToOne::class] ?? null)
420 373
                ->withManyToOneAnnotation($propertyAnnotations[Annotation\ManyToOne::class] ?? null)
421 373
                ->withOneToManyAnnotation($propertyAnnotations[Annotation\OneToMany::class] ?? null)
422 373
                ->withManyToManyAnnotation($propertyAnnotations[Annotation\ManyToMany::class] ?? null)
423 373
                ->withJoinTableAnnotation($propertyAnnotations[Annotation\JoinTable::class] ?? null)
424 373
                ->withJoinColumnsAnnotation($propertyAnnotations[Annotation\JoinColumns::class] ?? null)
425 373
                ->withJoinColumnAnnotation($propertyAnnotations[Annotation\JoinColumn::class] ?? null)
426 373
                ->withOrderByAnnotation($propertyAnnotations[Annotation\OrderBy::class] ?? null)
427 373
                ->withVersionAnnotation($propertyAnnotations[Annotation\Version::class] ?? null)
428 373
                ->withGeneratedValueAnnotation($propertyAnnotations[Annotation\GeneratedValue::class] ?? null)
429 373
                ->withSequenceGeneratorAnnotation($propertyAnnotations[Annotation\SequenceGenerator::class] ?? null)
430 373
                ->withCustomIdGeneratorAnnotation($propertyAnnotations[Annotation\CustomIdGenerator::class] ?? null)
431 373
                ->build();
432
433 371
            $metadata->addProperty($propertyMetadata);
434
        }
435 371
    }
436
437
    /**
438
     * @param Annotation\Annotation[] $classAnnotations
439
     *
440
     * @throws Mapping\MappingException
441
     */
442 371
    private function attachPropertyOverrides(
443
        array $classAnnotations,
444
        ReflectionClass $reflectionClass,
0 ignored issues
show
Unused Code introduced by
The parameter $reflectionClass is not used and could be removed. ( Ignorable by Annotation )

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

444
        /** @scrutinizer ignore-unused */ ReflectionClass $reflectionClass,

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
445
        Mapping\ClassMetadata $metadata,
446
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
447
    ) : void {
448
        // Evaluate AssociationOverrides annotation
449 371
        if (isset($classAnnotations[Annotation\AssociationOverrides::class])) {
450 5
            $associationOverridesAnnot = $classAnnotations[Annotation\AssociationOverrides::class];
451
452 5
            foreach ($associationOverridesAnnot->value as $associationOverrideAnnotation) {
0 ignored issues
show
Bug introduced by
Accessing value on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
453 5
                $fieldName = $associationOverrideAnnotation->name;
454 5
                $property  = $metadata->getProperty($fieldName);
455
456 5
                if (! $property) {
457
                    throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName);
458
                }
459
460 5
                $override = clone $property;
461
462
                // Check for JoinColumn/JoinColumns annotations
463 5
                if ($associationOverrideAnnotation->joinColumns) {
464 3
                    $joinColumnBuilder = new Builder\JoinColumnMetadataBuilder($metadataBuildingContext);
465
466
                    $joinColumnBuilder
467 3
                        ->withComponentMetadata($metadata)
468 3
                        ->withFieldName($fieldName);
469
470 3
                    $joinColumns = [];
471
472 3
                    foreach ($associationOverrideAnnotation->joinColumns as $joinColumnAnnotation) {
473 3
                        $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnotation);
474
475 3
                        $joinColumnMetadata = $joinColumnBuilder->build();
476 3
                        $columnName         = $joinColumnMetadata->getColumnName();
0 ignored issues
show
Unused Code introduced by
The assignment to $columnName is dead and can be removed.
Loading history...
477
478
                        // @todo guilhermeblanco Open an issue to discuss making this scenario impossible.
479
                        //if ($metadata->checkPropertyDuplication($columnName)) {
480
                        //    throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName);
481
                        //}
482
483 3
                        $joinColumns[] = $joinColumnMetadata;
484
                    }
485
486 3
                    $override->setJoinColumns($joinColumns);
0 ignored issues
show
Bug introduced by
The method setJoinColumns() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\ToOneAssociationMetadata. ( Ignorable by Annotation )

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

486
                    $override->/** @scrutinizer ignore-call */ 
487
                               setJoinColumns($joinColumns);
Loading history...
487
                }
488
489
                // Check for JoinTable annotations
490 5
                if ($associationOverrideAnnotation->joinTable) {
491 2
                    $joinTableBuilder = new Builder\JoinTableMetadataBuilder($metadataBuildingContext);
492
493
                    $joinTableBuilder
494 2
                        ->withComponentMetadata($metadata)
495 2
                        ->withFieldName($fieldName)
496 2
                        ->withTargetEntity($property->getTargetEntity())
0 ignored issues
show
Bug introduced by
The method getTargetEntity() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\EmbeddedMetadata or Doctrine\ORM\Mapping\AssociationMetadata. ( Ignorable by Annotation )

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

496
                        ->withTargetEntity($property->/** @scrutinizer ignore-call */ getTargetEntity())
Loading history...
497 2
                        ->withJoinTableAnnotation($associationOverrideAnnotation->joinTable);
498
499 2
                    $override->setJoinTable($joinTableBuilder->build());
0 ignored issues
show
Bug introduced by
The method setJoinTable() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\ManyToManyAssociationMetadata. ( Ignorable by Annotation )

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

499
                    $override->/** @scrutinizer ignore-call */ 
500
                               setJoinTable($joinTableBuilder->build());
Loading history...
500
                }
501
502
                // Check for inversedBy
503 5
                if ($associationOverrideAnnotation->inversedBy) {
504 1
                    $override->setInversedBy($associationOverrideAnnotation->inversedBy);
0 ignored issues
show
Bug introduced by
The method setInversedBy() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\AssociationMetadata. ( Ignorable by Annotation )

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

504
                    $override->/** @scrutinizer ignore-call */ 
505
                               setInversedBy($associationOverrideAnnotation->inversedBy);
Loading history...
505
                }
506
507
                // Check for fetch
508 5
                if ($associationOverrideAnnotation->fetch) {
509 1
                    $override->setFetchMode(constant(Mapping\FetchMode::class . '::' . $associationOverrideAnnotation->fetch));
0 ignored issues
show
Bug introduced by
The method setFetchMode() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\AssociationMetadata. ( Ignorable by Annotation )

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

509
                    $override->/** @scrutinizer ignore-call */ 
510
                               setFetchMode(constant(Mapping\FetchMode::class . '::' . $associationOverrideAnnotation->fetch));
Loading history...
510
                }
511
512 5
                $metadata->setPropertyOverride($override);
513
            }
514
        }
515
516
        // Evaluate AttributeOverrides annotation
517 371
        if (isset($classAnnotations[Annotation\AttributeOverrides::class])) {
518 3
            $attributeOverridesAnnot = $classAnnotations[Annotation\AttributeOverrides::class];
519 3
            $fieldBuilder            = new Builder\FieldMetadataBuilder($metadataBuildingContext);
520
521
            $fieldBuilder
522 3
                ->withComponentMetadata($metadata)
523 3
                ->withIdAnnotation(null)
524 3
                ->withVersionAnnotation(null);
525
526 3
            foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnotation) {
527 3
                $fieldName = $attributeOverrideAnnotation->name;
528 3
                $property  = $metadata->getProperty($fieldName);
529
530 3
                if (! $property) {
531
                    throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName);
532
                }
533
534
                $fieldBuilder
535 3
                    ->withFieldName($fieldName)
536 3
                    ->withColumnAnnotation($attributeOverrideAnnotation->column);
537
538 3
                $fieldMetadata = $fieldBuilder->build();
539 3
                $columnName    = $fieldMetadata->getColumnName();
540
541
                // Prevent column duplication
542 3
                if ($metadata->checkPropertyDuplication($columnName)) {
0 ignored issues
show
Bug introduced by
It seems like $columnName can also be of type null; however, parameter $columnName of Doctrine\ORM\Mapping\Cla...ckPropertyDuplication() 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

542
                if ($metadata->checkPropertyDuplication(/** @scrutinizer ignore-type */ $columnName)) {
Loading history...
543
                    throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName);
544
                }
545
546 3
                $metadata->setPropertyOverride($fieldMetadata);
547
            }
548
        }
549 371
    }
550
551
    /**
552
     * @return Annotation\Annotation[]
553
     */
554 380
    private function getClassAnnotations(ReflectionClass $reflectionClass) : array
555
    {
556 380
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
557
558 380
        foreach ($classAnnotations as $key => $annot) {
559 374
            if (! is_numeric($key)) {
560
                continue;
561
            }
562
563 374
            $classAnnotations[get_class($annot)] = $annot;
564
        }
565
566 380
        return $classAnnotations;
567
    }
568
569
    /**
570
     * @return Annotation\Annotation[]
571
     */
572 374
    private function getPropertyAnnotations(ReflectionProperty $reflectionProperty) : array
573
    {
574 374
        $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty);
575
576 373
        foreach ($propertyAnnotations as $key => $annot) {
577 373
            if (! is_numeric($key)) {
578
                continue;
579
            }
580
581 373
            $propertyAnnotations[get_class($annot)] = $annot;
582
        }
583
584 373
        return $propertyAnnotations;
585
    }
586
587
    /**
588
     * @return Annotation\Annotation[]
589
     */
590 21
    private function getMethodAnnotations(ReflectionMethod $reflectionMethod) : array
591
    {
592 21
        $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod);
593
594 21
        foreach ($methodAnnotations as $key => $annot) {
595 18
            if (! is_numeric($key)) {
596
                continue;
597
            }
598
599 18
            $methodAnnotations[get_class($annot)] = $annot;
600
        }
601
602 21
        return $methodAnnotations;
603
    }
604
}
605