Failed Conditions
Push — master ( b07393...21bc80 )
by Guilherme
09:49
created

AnnotationDriver::attachPropertyOverrides()   C

Complexity

Conditions 13
Paths 11

Size

Total Lines 105
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 47
CRAP Score 13.0365

Importance

Changes 0
Metric Value
cc 13
eloc 53
c 0
b 0
f 0
nc 11
nop 4
dl 0
loc 105
ccs 47
cts 50
cp 0.94
crap 13.0365
rs 6.6166

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 /** @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 2297
    public function __construct(Reader $reader, $paths = null)
94
    {
95 2297
        $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 2297
        if ($paths) {
98 2211
            $this->addPaths((array) $paths);
99
        }
100 2297
    }
101
102
    /**
103
     * Appends lookup paths to metadata driver.
104
     *
105
     * @param string[] $paths
106
     */
107 2215
    public function addPaths(array $paths)
108
    {
109 2215
        $this->paths = array_unique(array_merge($this->paths, $paths));
110 2215
    }
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 193
    public function isTransient($className) : bool
184
    {
185 193
        $classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className));
186
187 193
        foreach ($classAnnotations as $annotation) {
188 188
            if (isset($this->entityAnnotationClasses[get_class($annotation)])) {
189 188
                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 379
    public function loadMetadataForClass(
276
        string $className,
277
        ?Mapping\ComponentMetadata $parent,
278
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
279
    ) : Mapping\ComponentMetadata {
280 379
        $reflectionClass  = new ReflectionClass($className);
281 379
        $classAnnotations = $this->getClassAnnotations($reflectionClass);
282 379
        $classBuilder     = new Builder\ClassMetadataBuilder($metadataBuildingContext);
283
        $classMetadata    = $classBuilder
284 379
            ->withClassName($reflectionClass->getName())
285 379
            ->withParentMetadata($parent)
286 379
            ->withEntityAnnotation($classAnnotations[Annotation\Entity::class] ?? null)
287 379
            ->withMappedSuperclassAnnotation($classAnnotations[Annotation\MappedSuperclass::class] ?? null)
288 379
            ->withEmbeddableAnnotation($classAnnotations[Annotation\Embeddable::class] ?? null)
289 379
            ->withTableAnnotation($classAnnotations[Annotation\Table::class] ?? null)
290 379
            ->withInheritanceTypeAnnotation($classAnnotations[Annotation\InheritanceType::class] ?? null)
291 379
            ->withDiscriminatorColumnAnnotation($classAnnotations[Annotation\DiscriminatorColumn::class] ?? null)
292 379
            ->withDiscriminatorMapAnnotation($classAnnotations[Annotation\DiscriminatorMap::class] ?? null)
293 379
            ->withChangeTrackingPolicyAnnotation($classAnnotations[Annotation\ChangeTrackingPolicy::class] ?? null)
294 379
            ->withCacheAnnotation($classAnnotations[Annotation\Cache::class] ?? null)
295 379
            ->build();
296
297 373
        if (! $classMetadata->isEmbeddedClass) {
298 373
            $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $classMetadata);
299 373
            $this->attachEntityListeners($classAnnotations, $classMetadata);
300
        }
301
302 373
        $this->attachProperties($classAnnotations, $reflectionClass, $classMetadata, $metadataBuildingContext);
303 370
        $this->attachPropertyOverrides($classAnnotations, $reflectionClass, $classMetadata, $metadataBuildingContext);
304
305 370
        return $classMetadata;
306
    }
307
308
    /**
309
     * @param Annotation\Annotation[] $classAnnotations
310
     */
311 373
    private function attachLifecycleCallbacks(
312
        array $classAnnotations,
313
        ReflectionClass $reflectionClass,
314
        Mapping\ClassMetadata $metadata
315
    ) : void {
316
        // Evaluate @HasLifecycleCallbacks annotation
317 373
        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 373
    }
341
342
    /**
343
     * @param Annotation\Annotation[] $classAnnotations
344
     *
345
     * @throws ReflectionException
346
     * @throws Mapping\MappingException
347
     */
348 373
    private function attachEntityListeners(
349
        array $classAnnotations,
350
        Mapping\ClassMetadata $metadata
351
    ) : void {
352
        // Evaluate @EntityListeners annotation
353 373
        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 373
    }
390
391
    /**
392
     * @param Annotation\Annotation[] $classAnnotations
393
     *
394
     * @throws Mapping\MappingException
395
     */
396 373
    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 373
        $propertyBuilder = new Builder\PropertyMetadataBuilder($metadataBuildingContext);
404
405
        /** @var ReflectionProperty $reflProperty */
406 373
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
407 373
            if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) {
408 74
                continue;
409
            }
410
411 373
            $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty);
412
            $propertyMetadata    = $propertyBuilder
413 372
                ->withComponentMetadata($metadata)
414 372
                ->withFieldName($reflectionProperty->getName())
415 372
                ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null)
416 372
                ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class] ?? null)
417 372
                ->withColumnAnnotation($propertyAnnotations[Annotation\Column::class] ?? null)
418 372
                ->withEmbeddedAnnotation($propertyAnnotations[Annotation\Embedded::class] ?? null)
419 372
                ->withOneToOneAnnotation($propertyAnnotations[Annotation\OneToOne::class] ?? null)
420 372
                ->withManyToOneAnnotation($propertyAnnotations[Annotation\ManyToOne::class] ?? null)
421 372
                ->withOneToManyAnnotation($propertyAnnotations[Annotation\OneToMany::class] ?? null)
422 372
                ->withManyToManyAnnotation($propertyAnnotations[Annotation\ManyToMany::class] ?? null)
423 372
                ->withJoinTableAnnotation($propertyAnnotations[Annotation\JoinTable::class] ?? null)
424 372
                ->withJoinColumnsAnnotation($propertyAnnotations[Annotation\JoinColumns::class] ?? null)
425 372
                ->withJoinColumnAnnotation($propertyAnnotations[Annotation\JoinColumn::class] ?? null)
426 372
                ->withOrderByAnnotation($propertyAnnotations[Annotation\OrderBy::class] ?? null)
427 372
                ->withVersionAnnotation($propertyAnnotations[Annotation\Version::class] ?? null)
428 372
                ->withGeneratedValueAnnotation($propertyAnnotations[Annotation\GeneratedValue::class] ?? null)
429 372
                ->withSequenceGeneratorAnnotation($propertyAnnotations[Annotation\SequenceGenerator::class] ?? null)
430 372
                ->withCustomIdGeneratorAnnotation($propertyAnnotations[Annotation\CustomIdGenerator::class] ?? null)
431 372
                ->build();
432
433 370
            $metadata->addProperty($propertyMetadata);
434
        }
435 370
    }
436
437
    /**
438
     * @param Annotation\Annotation[] $classAnnotations
439
     *
440
     * @throws Mapping\MappingException
441
     */
442 370
    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 370
        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\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 370
        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 370
    }
550
551
    /**
552
     * @return Annotation\Annotation[]
553
     */
554 379
    private function getClassAnnotations(ReflectionClass $reflectionClass) : array
555
    {
556 379
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
557
558 379
        foreach ($classAnnotations as $key => $annot) {
559 373
            if (! is_numeric($key)) {
560
                continue;
561
            }
562
563 373
            $classAnnotations[get_class($annot)] = $annot;
564
        }
565
566 379
        return $classAnnotations;
567
    }
568
569
    /**
570
     * @return Annotation\Annotation[]
571
     */
572 373
    private function getPropertyAnnotations(ReflectionProperty $reflectionProperty) : array
573
    {
574 373
        $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty);
575
576 372
        foreach ($propertyAnnotations as $key => $annot) {
577 372
            if (! is_numeric($key)) {
578
                continue;
579
            }
580
581 372
            $propertyAnnotations[get_class($annot)] = $annot;
582
        }
583
584 372
        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