Failed Conditions
Pull Request — master (#7095)
by Benjamin
15:14
created

convertClassAnnotationsToClassMetadata()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4.1755

Importance

Changes 0
Metric Value
cc 4
eloc 20
nc 4
nop 4
dl 0
loc 33
ccs 14
cts 18
cp 0.7778
crap 4.1755
rs 8.5806
c 0
b 0
f 0
1
<?php
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\DBAL\Types\Type;
10
use Doctrine\ORM\Annotation;
11
use Doctrine\ORM\Cache\CacheException;
12
use Doctrine\ORM\Events;
13
use Doctrine\ORM\Mapping;
14
use function array_diff;
15
use function array_intersect;
16
use function array_map;
17
use function array_merge;
18
use function array_unique;
19
use function class_exists;
20
use function constant;
21
use function count;
22
use function defined;
23
use function get_class;
24
use function get_declared_classes;
25
use function in_array;
26
use function is_dir;
27
use function is_numeric;
28
use function preg_match;
29
use function preg_quote;
30
use function realpath;
31
use function sprintf;
32
use function str_replace;
33
use function strpos;
34
use function strtolower;
35
use function strtoupper;
36
37
/**
38
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
39
 *
40
 */
41
class AnnotationDriver implements MappingDriver
42
{
43
    /**
44
     * @var int[]
45
     */
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 2271
    public function __construct(Reader $reader, $paths = null)
94
    {
95 2271
        $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 2271
        if ($paths) {
97 2185
            $this->addPaths((array) $paths);
98
        }
99 2271
    }
100
101
    /**
102
     * Appends lookup paths to metadata driver.
103
     *
104
     * @param string[] $paths
105
     */
106 2189
    public function addPaths(array $paths)
107
    {
108 2189
        $this->paths = array_unique(array_merge($this->paths, $paths));
109 2189
    }
110
111
    /**
112
     * Retrieves the defined metadata lookup paths.
113
     *
114
     * @return string[]
115
     */
116
    public function getPaths()
117
    {
118
        return $this->paths;
119
    }
120
121
    /**
122
     * Append exclude lookup paths to metadata driver.
123
     *
124
     * @param string[] $paths
125
     */
126
    public function addExcludePaths(array $paths)
127
    {
128
        $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths));
129
    }
130
131
    /**
132
     * Retrieve the defined metadata lookup exclude paths.
133
     *
134
     * @return string[]
135
     */
136
    public function getExcludePaths()
137
    {
138
        return $this->excludePaths;
139
    }
140
141
    /**
142
     * Retrieve the current annotation reader
143
     *
144
     * @return AnnotationReader
145
     */
146 1
    public function getReader()
147
    {
148 1
        return $this->reader;
149
    }
150
151
    /**
152
     * Gets the file extension used to look for mapping files under.
153
     *
154
     * @return string
155
     */
156
    public function getFileExtension()
157
    {
158
        return $this->fileExtension;
159
    }
160
161
    /**
162
     * Sets the file extension used to look for mapping files under.
163
     *
164
     * @param string $fileExtension The file extension to set.
165
     *
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
     * @return bool
182
     */
183 193
    public function isTransient($className)
184
    {
185 193
        $classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className));
186
187 193
        foreach ($classAnnotations as $annot) {
188 188
            if (isset($this->entityAnnotationClasses[get_class($annot)])) {
189 188
                return false;
190
            }
191
        }
192 12
        return true;
193
    }
194
195
    /**
196
     * {@inheritDoc}
197
     */
198 61
    public function getAllClassNames()
199
    {
200 61
        if ($this->classNames !== null) {
201 46
            return $this->classNames;
202
        }
203
204 61
        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...
205
            throw Mapping\MappingException::pathRequired();
206
        }
207
208 61
        $classes       = [];
209 61
        $includedFiles = [];
210
211 61
        foreach ($this->paths as $path) {
212 61
            if (! is_dir($path)) {
213
                throw Mapping\MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
214
            }
215
216 61
            $iterator = new \RegexIterator(
217 61
                new \RecursiveIteratorIterator(
218 61
                    new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
219 61
                    \RecursiveIteratorIterator::LEAVES_ONLY
220
                ),
221 61
                '/^.+' . preg_quote($this->fileExtension) . '$/i',
222 61
                \RecursiveRegexIterator::GET_MATCH
223
            );
224
225 61
            foreach ($iterator as $file) {
226 61
                $sourceFile = $file[0];
227
228 61
                if (! preg_match('(^phar:)i', $sourceFile)) {
229 61
                    $sourceFile = realpath($sourceFile);
230
                }
231
232 61
                foreach ($this->excludePaths as $excludePath) {
233
                    $exclude = str_replace('\\', '/', realpath($excludePath));
234
                    $current = str_replace('\\', '/', $sourceFile);
235
236
                    if (strpos($current, $exclude) !== false) {
237
                        continue 2;
238
                    }
239
                }
240
241 61
                require_once $sourceFile;
242
243 61
                $includedFiles[] = $sourceFile;
244
            }
245
        }
246
247 61
        $declared = get_declared_classes();
248
249 61
        foreach ($declared as $className) {
250 61
            $rc         = new \ReflectionClass($className);
251 61
            $sourceFile = $rc->getFileName();
252 61
            if (in_array($sourceFile, $includedFiles, true) && ! $this->isTransient($className)) {
253 61
                $classes[] = $className;
254
            }
255
        }
256
257 61
        $this->classNames = $classes;
258
259 61
        return $classes;
260
    }
261
262
    /**
263
     * {@inheritDoc}
264
     *
265
     * @throws CacheException
266
     * @throws Mapping\MappingException
267
     * @throws \ReflectionException
268
     * @throws \RuntimeException
269
     */
270 369
    public function loadMetadataForClass(
271
        string $className,
272
        Mapping\ClassMetadata $metadata,
273
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
274
    ) : Mapping\ClassMetadata {
275 369
        $reflectionClass = $metadata->getReflectionClass();
276
277 369
        if (! $reflectionClass) {
278
            // this happens when running annotation driver in combination with
279
            // static reflection services. This is not the nicest fix
280
            $reflectionClass = new \ReflectionClass($metadata->getClassName());
281
        }
282
283 369
        $classAnnotations = $this->getClassAnnotations($reflectionClass);
284 369
        $classMetadata    = $this->convertClassAnnotationsToClassMetadata(
285 369
            $classAnnotations,
286 369
            $reflectionClass,
287 369
            $metadata,
288 369
            $metadataBuildingContext
289
        );
290
291
        // Evaluate @Cache annotation
292 366
        if (isset($classAnnotations[Annotation\Cache::class])) {
293 18
            $cacheAnnot = $classAnnotations[Annotation\Cache::class];
294 18
            $cache      = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $metadata);
295
296 18
            $classMetadata->setCache($cache);
297
        }
298
299
        // Evaluate annotations on properties/fields
300
        /* @var $reflProperty \ReflectionProperty */
301 366
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
302 366
            if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) {
303 76
                continue;
304
            }
305
306 366
            $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty);
307 365
            $property            = $this->convertPropertyAnnotationsToProperty(
308 365
                $propertyAnnotations,
309 365
                $reflectionProperty,
310 365
                $classMetadata
311
            );
312
313 365
            if ($classMetadata->isMappedSuperclass &&
314 365
                $property instanceof Mapping\ToManyAssociationMetadata &&
315 365
                ! $property->isOwningSide()) {
316 1
                throw Mapping\MappingException::illegalToManyAssociationOnMappedSuperclass(
317 1
                    $classMetadata->getClassName(),
318 1
                    $property->getName()
319
                );
320
            }
321
322 364
            if (! $property) {
323 1
                continue;
324
            }
325
326 364
            $metadata->addProperty($property);
327
        }
328
329 363
        $this->attachPropertyOverrides($classAnnotations, $reflectionClass, $metadata);
330
331 363
        return $classMetadata;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $classMetadata returns the type Doctrine\ORM\Mapping\ClassMetadata which is incompatible with the return type mandated by Doctrine\ORM\Mapping\Dri...:loadMetadataForClass() of void.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
332
    }
333
334
    /**
335
     * @param Annotation\Annotation[] $classAnnotations
336
     *
337
     * @throws Mapping\MappingException
338
     */
339 369
    private function convertClassAnnotationsToClassMetadata(
340
        array $classAnnotations,
341
        \ReflectionClass $reflectionClass,
342
        Mapping\ClassMetadata $metadata,
343
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
344
    ) : Mapping\ClassMetadata {
345
        switch (true) {
346 369
            case isset($classAnnotations[Annotation\Entity::class]):
347 365
                return $this->convertClassAnnotationsToEntityClassMetadata(
348 365
                    $classAnnotations,
349 365
                    $reflectionClass,
350 365
                    $metadata,
351 365
                    $metadataBuildingContext
352
                );
353
354
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
355
356 27
            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
357 24
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
358 24
                    $classAnnotations,
359 24
                    $reflectionClass,
360 24
                    $metadata
361
                );
362
363 3
            case isset($classAnnotations[Annotation\Embeddable::class]):
364
                return $this->convertClassAnnotationsToEmbeddableClassMetadata(
365
                    $classAnnotations,
366
                    $reflectionClass,
367
                    $metadata
368
                );
369
370
            default:
371 3
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
372
        }
373
    }
374
375
    /**
376
     * @param Annotation\Annotation[] $classAnnotations
377
     *
378
     * @return Mapping\ClassMetadata
379
     *
380
     * @throws Mapping\MappingException
381
     * @throws \UnexpectedValueException
382
     */
383 365
    private function convertClassAnnotationsToEntityClassMetadata(
384
        array $classAnnotations,
385
        \ReflectionClass $reflectionClass,
386
        Mapping\ClassMetadata $metadata,
387
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
388
    ) {
389
        /** @var Annotation\Entity $entityAnnot */
390 365
        $entityAnnot = $classAnnotations[Annotation\Entity::class];
391
392 365
        if ($entityAnnot->repositoryClass !== null) {
393 3
            $metadata->setCustomRepositoryClassName($entityAnnot->repositoryClass);
394
        }
395
396 365
        if ($entityAnnot->readOnly) {
397 1
            $metadata->asReadOnly();
398
        }
399
400 365
        $metadata->isMappedSuperclass = false;
401 365
        $metadata->isEmbeddedClass    = false;
402
403 365
        $this->attachTable($classAnnotations, $reflectionClass, $metadata, $metadataBuildingContext);
404
405
        // Evaluate @ChangeTrackingPolicy annotation
406 365
        if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) {
407 5
            $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class];
408
409 5
            $metadata->setChangeTrackingPolicy(
410 5
                constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value))
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...
411
            );
412
        }
413
414
        // Evaluate @InheritanceType annotation
415 365
        if (isset($classAnnotations[Annotation\InheritanceType::class])) {
416 79
            $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class];
417
418 79
            $metadata->setInheritanceType(
419 79
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value))
420
            );
421
422 79
            if ($metadata->inheritanceType !== Mapping\InheritanceType::NONE) {
423 79
                $this->attachDiscriminatorColumn($classAnnotations, $reflectionClass, $metadata);
424
            }
425
        }
426
427 365
        $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata);
428 365
        $this->attachEntityListeners($classAnnotations, $reflectionClass, $metadata);
429
430 365
        return $metadata;
431
    }
432
433
    /**
434
     * @param Annotation\Annotation[] $classAnnotations
435
     */
436 24
    private function convertClassAnnotationsToMappedSuperClassMetadata(
437
        array $classAnnotations,
438
        \ReflectionClass $reflectionClass,
439
        Mapping\ClassMetadata $metadata
440
    ) : Mapping\ClassMetadata {
441
        /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */
442 24
        $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class];
443
444 24
        if ($mappedSuperclassAnnot->repositoryClass !== null) {
445 2
            $metadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass);
446
        }
447
448 24
        $metadata->isMappedSuperclass = true;
449 24
        $metadata->isEmbeddedClass    = false;
450
451 24
        $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata);
452 24
        $this->attachEntityListeners($classAnnotations, $reflectionClass, $metadata);
453
454 24
        return $metadata;
455
    }
456
457
    /**
458
     * @param Annotation\Annotation[] $classAnnotations
459
     */
460
    private function convertClassAnnotationsToEmbeddableClassMetadata(
461
        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

461
        /** @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...
462
        \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

462
        /** @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...
463
        Mapping\ClassMetadata $metadata
464
    ) : Mapping\ClassMetadata {
465
        $metadata->isMappedSuperclass = false;
466
        $metadata->isEmbeddedClass    = true;
467
468
        return $metadata;
469
    }
470
471
    /**
472
     * @param Annotation\Annotation[] $propertyAnnotations
473
     *
474
     * @todo guilhermeblanco Remove nullable typehint once embeddables are back
475
     */
476 365
    private function convertPropertyAnnotationsToProperty(
477
        array $propertyAnnotations,
478
        \ReflectionProperty $reflectionProperty,
479
        Mapping\ClassMetadata $metadata
480
    ) : ?Mapping\Property {
481
        switch (true) {
482 365
            case isset($propertyAnnotations[Annotation\Column::class]):
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
483 360
                return $this->convertReflectionPropertyToFieldMetadata(
484 360
                    $reflectionProperty,
485 360
                    $propertyAnnotations,
486 360
                    $metadata
487
                );
488
489 253
            case isset($propertyAnnotations[Annotation\OneToOne::class]):
490 111
                return $this->convertReflectionPropertyToOneToOneAssociationMetadata(
491 111
                    $reflectionProperty,
492 111
                    $propertyAnnotations,
493 111
                    $metadata
494
                );
495
496 201
            case isset($propertyAnnotations[Annotation\ManyToOne::class]):
497 138
                return $this->convertReflectionPropertyToManyToOneAssociationMetadata(
498 138
                    $reflectionProperty,
499 138
                    $propertyAnnotations,
500 138
                    $metadata
501
                );
502
503 163
            case isset($propertyAnnotations[Annotation\OneToMany::class]):
504 107
                return $this->convertReflectionPropertyToOneToManyAssociationMetadata(
505 107
                    $reflectionProperty,
506 107
                    $propertyAnnotations,
507 107
                    $metadata
508
                );
509
510 106
            case isset($propertyAnnotations[Annotation\ManyToMany::class]):
511 89
                return $this->convertReflectionPropertyToManyToManyAssociationMetadata(
512 89
                    $reflectionProperty,
513 89
                    $propertyAnnotations,
514 89
                    $metadata
515
                );
516
517 31
            case isset($propertyAnnotations[Annotation\Embedded::class]):
518 1
                return null;
519
520
            default:
521 30
                return new Mapping\TransientMetadata($reflectionProperty->getName());
522
        }
523
    }
524
525
    /**
526
     * @param Annotation\Annotation[] $propertyAnnotations
527
     *
528
     * @throws Mapping\MappingException
529
     */
530 360
    private function convertReflectionPropertyToFieldMetadata(
531
        \ReflectionProperty $reflProperty,
532
        array $propertyAnnotations,
533
        Mapping\ClassMetadata $metadata
534
    ) : Mapping\FieldMetadata {
535 360
        $className   = $metadata->getClassName();
536 360
        $fieldName   = $reflProperty->getName();
537 360
        $isVersioned = isset($propertyAnnotations[Annotation\Version::class]);
538 360
        $columnAnnot = $propertyAnnotations[Annotation\Column::class];
539
540 360
        if ($columnAnnot->type === null) {
0 ignored issues
show
Bug introduced by
Accessing type on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
541
            throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName);
542
        }
543
544 360
        $fieldMetadata = $this->convertColumnAnnotationToFieldMetadata($columnAnnot, $fieldName, $isVersioned);
545
546
        // Check for Id
547 360
        if (isset($propertyAnnotations[Annotation\Id::class])) {
548 356
            $fieldMetadata->setPrimaryKey(true);
549
        }
550
551
        // Check for GeneratedValue strategy
552 360
        if (isset($propertyAnnotations[Annotation\GeneratedValue::class])) {
553 306
            $generatedValueAnnot = $propertyAnnotations[Annotation\GeneratedValue::class];
554 306
            $strategy            = strtoupper($generatedValueAnnot->strategy);
0 ignored issues
show
Bug introduced by
Accessing strategy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
555 306
            $idGeneratorType     = constant(sprintf('%s::%s', Mapping\GeneratorType::class, $strategy));
556
557 306
            if ($idGeneratorType !== Mapping\GeneratorType::NONE) {
558 286
                $idGeneratorDefinition = [];
559
560
                // Check for CustomGenerator/SequenceGenerator/TableGenerator definition
561
                switch (true) {
562 286
                    case isset($propertyAnnotations[Annotation\SequenceGenerator::class]):
563 9
                        $seqGeneratorAnnot = $propertyAnnotations[Annotation\SequenceGenerator::class];
564
565
                        $idGeneratorDefinition = [
566 9
                            'sequenceName' => $seqGeneratorAnnot->sequenceName,
0 ignored issues
show
Bug introduced by
Accessing sequenceName on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
567 9
                            'allocationSize' => $seqGeneratorAnnot->allocationSize,
0 ignored issues
show
Bug introduced by
Accessing allocationSize on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
568
                        ];
569
570 9
                        break;
571
572 277
                    case isset($propertyAnnotations[Annotation\CustomIdGenerator::class]):
573 3
                        $customGeneratorAnnot = $propertyAnnotations[Annotation\CustomIdGenerator::class];
574
575
                        $idGeneratorDefinition = [
576 3
                            'class' => $customGeneratorAnnot->class,
577 3
                            'arguments' => $customGeneratorAnnot->arguments,
578
                        ];
579
580 3
                        break;
581
582
                    /* @todo If it is not supported, why does this exist? */
583 274
                    case isset($propertyAnnotations['Doctrine\ORM\Mapping\TableGenerator']):
584
                        throw Mapping\MappingException::tableIdGeneratorNotImplemented($className);
585
                }
586
587 286
                $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata($idGeneratorType, $idGeneratorDefinition));
588
            }
589
        }
590
591 360
        return $fieldMetadata;
592
    }
593
594
    /**
595
     * @param Annotation\Annotation[] $propertyAnnotations
596
     *
597
     * @return Mapping\OneToOneAssociationMetadata
598
     */
599 111
    private function convertReflectionPropertyToOneToOneAssociationMetadata(
600
        \ReflectionProperty $reflectionProperty,
601
        array $propertyAnnotations,
602
        Mapping\ClassMetadata $metadata
603
    ) {
604 111
        $className     = $metadata->getClassName();
605 111
        $fieldName     = $reflectionProperty->getName();
606 111
        $oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class];
607 111
        $assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName);
608 111
        $targetEntity  = $oneToOneAnnot->targetEntity;
0 ignored issues
show
Bug introduced by
Accessing targetEntity on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
609
610 111
        $assocMetadata->setTargetEntity($targetEntity);
611 111
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade));
0 ignored issues
show
Bug introduced by
Accessing cascade on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
612 111
        $assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval);
0 ignored issues
show
Bug introduced by
Accessing orphanRemoval on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
613 111
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch));
0 ignored issues
show
Bug introduced by
Accessing fetch on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
614
615 111
        if (! empty($oneToOneAnnot->mappedBy)) {
0 ignored issues
show
Bug introduced by
Accessing mappedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
616 39
            $assocMetadata->setMappedBy($oneToOneAnnot->mappedBy);
617 39
            $assocMetadata->setOwningSide(false);
618
        }
619
620 111
        if (! empty($oneToOneAnnot->inversedBy)) {
0 ignored issues
show
Bug introduced by
Accessing inversedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
621 51
            $assocMetadata->setInversedBy($oneToOneAnnot->inversedBy);
622
        }
623
624
        // Check for Id
625 111
        if (isset($propertyAnnotations[Annotation\Id::class])) {
626 11
            $assocMetadata->setPrimaryKey(true);
627
        }
628
629 111
        $this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata);
630
631
        // Check for JoinColumn/JoinColumns annotations
632
        switch (true) {
633 111
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
634 79
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
635
636 79
                $assocMetadata->addJoinColumn(
637 79
                    $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
638
                );
639
640 79
                break;
641
642 52
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
643 3
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
644
645 3
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
646 3
                    $assocMetadata->addJoinColumn(
647 3
                        $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
648
                    );
649
                }
650
651 3
                break;
652
        }
653
654 111
        return $assocMetadata;
655
    }
656
657
    /**
658
     * @param Annotation\Annotation[] $propertyAnnotations
659
     *
660
     * @return Mapping\ManyToOneAssociationMetadata
661
     */
662 138
    private function convertReflectionPropertyToManyToOneAssociationMetadata(
663
        \ReflectionProperty $reflectionProperty,
664
        array $propertyAnnotations,
665
        Mapping\ClassMetadata $metadata
666
    ) {
667 138
        $className      = $metadata->getClassName();
668 138
        $fieldName      = $reflectionProperty->getName();
669 138
        $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class];
670 138
        $assocMetadata  = new Mapping\ManyToOneAssociationMetadata($fieldName);
671 138
        $targetEntity   = $manyToOneAnnot->targetEntity;
0 ignored issues
show
Bug introduced by
Accessing targetEntity on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
672
673 138
        $assocMetadata->setTargetEntity($targetEntity);
674 138
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade));
0 ignored issues
show
Bug introduced by
Accessing cascade on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
675 138
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch));
0 ignored issues
show
Bug introduced by
Accessing fetch on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
676
677 138
        if (! empty($manyToOneAnnot->inversedBy)) {
0 ignored issues
show
Bug introduced by
Accessing inversedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
678 92
            $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy);
679
        }
680
681
        // Check for Id
682 138
        if (isset($propertyAnnotations[Annotation\Id::class])) {
683 33
            $assocMetadata->setPrimaryKey(true);
684
        }
685
686 138
        $this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata);
687
688
        // Check for JoinColumn/JoinColumns annotations
689
        switch (true) {
690 138
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
691 78
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
692
693 78
                $assocMetadata->addJoinColumn(
694 78
                    $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
695
                );
696
697 78
                break;
698
699 69
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
700 16
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
701
702 16
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
703 16
                    $assocMetadata->addJoinColumn(
704 16
                        $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
705
                    );
706
                }
707
708 16
                break;
709
        }
710
711 138
        return $assocMetadata;
712
    }
713
714
    /**
715
     * @param Annotation\Annotation[] $propertyAnnotations
716
     *
717
     * @throws Mapping\MappingException
718
     */
719 107
    private function convertReflectionPropertyToOneToManyAssociationMetadata(
720
        \ReflectionProperty $reflectionProperty,
721
        array $propertyAnnotations,
722
        Mapping\ClassMetadata $metadata
723
    ) : Mapping\OneToManyAssociationMetadata {
724 107
        $className      = $metadata->getClassName();
725 107
        $fieldName      = $reflectionProperty->getName();
726 107
        $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class];
727 107
        $assocMetadata  = new Mapping\OneToManyAssociationMetadata($fieldName);
728 107
        $targetEntity   = $oneToManyAnnot->targetEntity;
0 ignored issues
show
Bug introduced by
Accessing targetEntity on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
729
730 107
        $assocMetadata->setTargetEntity($targetEntity);
731 107
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade));
0 ignored issues
show
Bug introduced by
Accessing cascade on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
732 107
        $assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval);
0 ignored issues
show
Bug introduced by
Accessing orphanRemoval on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
733 107
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch));
0 ignored issues
show
Bug introduced by
Accessing fetch on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
734 107
        $assocMetadata->setOwningSide(false);
735 107
        $assocMetadata->setMappedBy($oneToManyAnnot->mappedBy);
0 ignored issues
show
Bug introduced by
Accessing mappedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
736
737 107
        if (! empty($oneToManyAnnot->indexBy)) {
0 ignored issues
show
Bug introduced by
Accessing indexBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
738 8
            $assocMetadata->setIndexedBy($oneToManyAnnot->indexBy);
739
        }
740
741
        // Check for OrderBy
742 107
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
743 14
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
744
745 14
            $assocMetadata->setOrderBy($orderByAnnot->value);
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...
746
        }
747
748
        // Check for Id
749 107
        if (isset($propertyAnnotations[Annotation\Id::class])) {
750
            throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName);
751
        }
752
753 107
        $this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata);
754
755 107
        return $assocMetadata;
756
    }
757
758
    /**
759
     * @param Annotation\Annotation[] $propertyAnnotations
760
     *
761
     * @throws Mapping\MappingException
762
     */
763 89
    private function convertReflectionPropertyToManyToManyAssociationMetadata(
764
        \ReflectionProperty $reflectionProperty,
765
        array $propertyAnnotations,
766
        Mapping\ClassMetadata $metadata
767
    ) : Mapping\ManyToManyAssociationMetadata {
768 89
        $className       = $metadata->getClassName();
769 89
        $fieldName       = $reflectionProperty->getName();
770 89
        $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class];
771 89
        $assocMetadata   = new Mapping\ManyToManyAssociationMetadata($fieldName);
772 89
        $targetEntity    = $manyToManyAnnot->targetEntity;
0 ignored issues
show
Bug introduced by
Accessing targetEntity on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
773
774 89
        $assocMetadata->setTargetEntity($targetEntity);
775 89
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade));
0 ignored issues
show
Bug introduced by
Accessing cascade on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
776 89
        $assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval);
0 ignored issues
show
Bug introduced by
Accessing orphanRemoval on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
777 89
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch));
0 ignored issues
show
Bug introduced by
Accessing fetch on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
778
779 89
        if (! empty($manyToManyAnnot->mappedBy)) {
0 ignored issues
show
Bug introduced by
Accessing mappedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
780 36
            $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy);
781 36
            $assocMetadata->setOwningSide(false);
782
        }
783
784 89
        if (! empty($manyToManyAnnot->inversedBy)) {
0 ignored issues
show
Bug introduced by
Accessing inversedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
785 45
            $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy);
786
        }
787
788 89
        if (! empty($manyToManyAnnot->indexBy)) {
0 ignored issues
show
Bug introduced by
Accessing indexBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
789 3
            $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy);
790
        }
791
792
        // Check for JoinTable
793 89
        if (isset($propertyAnnotations[Annotation\JoinTable::class])) {
794 71
            $joinTableAnnot    = $propertyAnnotations[Annotation\JoinTable::class];
795 71
            $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata($joinTableAnnot);
796
797 71
            $assocMetadata->setJoinTable($joinTableMetadata);
798
        }
799
800
        // Check for OrderBy
801 89
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
802 3
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
803
804 3
            $assocMetadata->setOrderBy($orderByAnnot->value);
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...
805
        }
806
807
        // Check for Id
808 89
        if (isset($propertyAnnotations[Annotation\Id::class])) {
809
            throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName);
810
        }
811
812 89
        $this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata);
813
814 89
        return $assocMetadata;
815
    }
816
817
    /**
818
     * Parse the given Column as FieldMetadata
819
     */
820 360
    private function convertColumnAnnotationToFieldMetadata(
821
        Annotation\Column $columnAnnot,
822
        string $fieldName,
823
        bool $isVersioned
824
    ) : Mapping\FieldMetadata {
825 360
        $fieldMetadata = $isVersioned
826 16
            ? new Mapping\VersionFieldMetadata($fieldName)
827 360
            : new Mapping\FieldMetadata($fieldName)
828
        ;
829
830 360
        $fieldMetadata->setType(Type::getType($columnAnnot->type));
831
832 360
        if (! empty($columnAnnot->name)) {
833 78
            $fieldMetadata->setColumnName($columnAnnot->name);
834
        }
835
836 360
        if (! empty($columnAnnot->columnDefinition)) {
837 4
            $fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition);
838
        }
839
840 360
        if (! empty($columnAnnot->length)) {
841 360
            $fieldMetadata->setLength($columnAnnot->length);
842
        }
843
844 360
        if ($columnAnnot->options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $columnAnnot->options of type array 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...
845 6
            $fieldMetadata->setOptions($columnAnnot->options);
846
        }
847
848 360
        $fieldMetadata->setScale($columnAnnot->scale);
849 360
        $fieldMetadata->setPrecision($columnAnnot->precision);
850 360
        $fieldMetadata->setNullable($columnAnnot->nullable);
851 360
        $fieldMetadata->setUnique($columnAnnot->unique);
852
853 360
        return $fieldMetadata;
854
    }
855
856
    /**
857
     * Parse the given Table as TableMetadata
858
     */
859 191
    private function convertTableAnnotationToTableMetadata(
860
        Annotation\Table $tableAnnot,
861
        Mapping\TableMetadata $tableMetadata
862
    ) : void {
863 191
        if (! empty($tableAnnot->name)) {
864 186
            $tableMetadata->setName($tableAnnot->name);
865
        }
866
867 191
        if (! empty($tableAnnot->schema)) {
868 5
            $tableMetadata->setSchema($tableAnnot->schema);
869
        }
870
871 191
        foreach ($tableAnnot->options as $optionName => $optionValue) {
872 4
            $tableMetadata->addOption($optionName, $optionValue);
873
        }
874
875 191
        foreach ($tableAnnot->indexes as $indexAnnot) {
876 13
            $tableMetadata->addIndex([
877 13
                'name'    => $indexAnnot->name,
878 13
                'columns' => $indexAnnot->columns,
879 13
                'unique'  => $indexAnnot->unique,
880 13
                'options' => $indexAnnot->options,
881 13
                'flags'   => $indexAnnot->flags,
882
            ]);
883
        }
884
885 191
        foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
886 6
            $tableMetadata->addUniqueConstraint([
887 6
                'name'    => $uniqueConstraintAnnot->name,
888 6
                'columns' => $uniqueConstraintAnnot->columns,
889 6
                'options' => $uniqueConstraintAnnot->options,
890 6
                'flags'   => $uniqueConstraintAnnot->flags,
891
            ]);
892
        }
893 191
    }
894
895
    /**
896
     * Parse the given JoinTable as JoinTableMetadata
897
     */
898 71
    private function convertJoinTableAnnotationToJoinTableMetadata(
899
        Annotation\JoinTable $joinTableAnnot
900
    ) : Mapping\JoinTableMetadata {
901 71
        $joinTable = new Mapping\JoinTableMetadata();
902
903 71
        if (! empty($joinTableAnnot->name)) {
904 69
            $joinTable->setName($joinTableAnnot->name);
905
        }
906
907 71
        if (! empty($joinTableAnnot->schema)) {
908
            $joinTable->setSchema($joinTableAnnot->schema);
909
        }
910
911 71
        foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) {
912 70
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot);
913
914 70
            $joinTable->addJoinColumn($joinColumn);
915
        }
916
917 71
        foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) {
918 70
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot);
919
920 70
            $joinTable->addInverseJoinColumn($joinColumn);
921
        }
922
923 71
        return $joinTable;
924
    }
925
926
    /**
927
     * Parse the given JoinColumn as JoinColumnMetadata
928
     */
929 179
    private function convertJoinColumnAnnotationToJoinColumnMetadata(
930
        Annotation\JoinColumn $joinColumnAnnot
931
    ) : Mapping\JoinColumnMetadata {
932 179
        $joinColumn = new Mapping\JoinColumnMetadata();
933
934
        // @todo Remove conditionals for name and referencedColumnName once naming strategy is brought into drivers
935 179
        if (! empty($joinColumnAnnot->name)) {
936 173
            $joinColumn->setColumnName($joinColumnAnnot->name);
937
        }
938
939 179
        if (! empty($joinColumnAnnot->referencedColumnName)) {
940 179
            $joinColumn->setReferencedColumnName($joinColumnAnnot->referencedColumnName);
941
        }
942
943 179
        $joinColumn->setNullable($joinColumnAnnot->nullable);
944 179
        $joinColumn->setUnique($joinColumnAnnot->unique);
945
946 179
        if (! empty($joinColumnAnnot->fieldName)) {
947
            $joinColumn->setAliasedName($joinColumnAnnot->fieldName);
948
        }
949
950 179
        if (! empty($joinColumnAnnot->columnDefinition)) {
951 3
            $joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition);
952
        }
953
954 179
        if ($joinColumnAnnot->onDelete) {
955 16
            $joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete));
956
        }
957
958 179
        return $joinColumn;
959
    }
960
961
    /**
962
     * Parse the given Cache as CacheMetadata
963
     *
964
     * @param string|null $fieldName
965
     */
966 18
    private function convertCacheAnnotationToCacheMetadata(
967
        Annotation\Cache $cacheAnnot,
968
        Mapping\ClassMetadata $metadata,
969
        $fieldName = null
970
    ) : Mapping\CacheMetadata {
971 18
        $baseRegion    = strtolower(str_replace('\\', '_', $metadata->getRootClassName()));
972 18
        $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : '');
973
974 18
        $usage  = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage));
975 18
        $region = $cacheAnnot->region ?: $defaultRegion;
976
977 18
        return new Mapping\CacheMetadata($usage, $region);
978
    }
979
980
    /**
981
     * @return mixed[]
982
     */
983
    private function convertSqlResultSetMapping(Annotation\SqlResultSetMapping $resultSetMapping)
0 ignored issues
show
Unused Code introduced by
The method convertSqlResultSetMapping() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
Bug introduced by
The type Doctrine\ORM\Annotation\SqlResultSetMapping 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...
984
    {
985
        $entities = [];
986
987
        foreach ($resultSetMapping->entities as $entityResultAnnot) {
988
            $entityResult = [
989
                'fields'                => [],
990
                'entityClass'           => $entityResultAnnot->entityClass,
991
                'discriminatorColumn'   => $entityResultAnnot->discriminatorColumn,
992
            ];
993
994
            foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
995
                $entityResult['fields'][] = [
996
                    'name'      => $fieldResultAnnot->name,
997
                    'column'    => $fieldResultAnnot->column,
998
                ];
999
            }
1000
1001
            $entities[] = $entityResult;
1002
        }
1003
1004
        $columns = [];
1005
1006
        foreach ($resultSetMapping->columns as $columnResultAnnot) {
1007
            $columns[] = [
1008
                'name' => $columnResultAnnot->name,
1009
            ];
1010
        }
1011
1012
        return [
1013
            'name'     => $resultSetMapping->name,
1014
            'entities' => $entities,
1015
            'columns'  => $columns,
1016
        ];
1017
    }
1018
1019
    /**
1020
     * @param Annotation\Annotation[] $classAnnotations
1021
     */
1022 365
    private function attachTable(
1023
        array $classAnnotations,
1024
        \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

1024
        /** @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...
1025
        Mapping\ClassMetadata $metadata,
1026
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
1027
    ) : void {
1028 365
        $parent = $metadata->getParent();
1029
1030 365
        if ($parent && $parent->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) {
1031
            // Handle the case where a middle mapped super class inherits from a single table inheritance tree.
1032
            do {
1033 29
                if (! $parent->isMappedSuperclass) {
1034 29
                    $metadata->setTable($parent->table);
1035
1036 29
                    break;
1037
                }
1038
1039 4
                $parent = $parent->getParent();
1040 4
            } while ($parent !== null);
1041
1042 29
            return;
1043
        }
1044
1045 365
        $namingStrategy = $metadataBuildingContext->getNamingStrategy();
1046 365
        $tableMetadata  = new Mapping\TableMetadata();
1047
1048 365
        $tableMetadata->setName($namingStrategy->classToTableName($metadata->getClassName()));
1049
1050
        // Evaluate @Table annotation
1051 365
        if (isset($classAnnotations[Annotation\Table::class])) {
1052 191
            $tableAnnot = $classAnnotations[Annotation\Table::class];
1053
1054 191
            $this->convertTableAnnotationToTableMetadata($tableAnnot, $tableMetadata);
1055
        }
1056
1057 365
        $metadata->setTable($tableMetadata);
1058 365
    }
1059
1060
    /**
1061
     * @param Annotation\Annotation[] $classAnnotations
1062
     *
1063
     * @throws Mapping\MappingException
1064
     */
1065 79
    private function attachDiscriminatorColumn(
1066
        array $classAnnotations,
1067
        \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

1067
        /** @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...
1068
        Mapping\ClassMetadata $metadata
1069
    ) : void {
1070 79
        $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata();
1071
1072 79
        $discriminatorColumn->setTableName($metadata->getTableName());
1073 79
        $discriminatorColumn->setColumnName('dtype');
1074 79
        $discriminatorColumn->setType(Type::getType('string'));
1075 79
        $discriminatorColumn->setLength(255);
1076
1077
        // Evaluate DiscriminatorColumn annotation
1078 79
        if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) {
1079
            /** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnotation */
1080 63
            $discriminatorColumnAnnotation = $classAnnotations[Annotation\DiscriminatorColumn::class];
1081 63
            $typeName                      = ! empty($discriminatorColumnAnnotation->type)
1082 59
                ? $discriminatorColumnAnnotation->type
1083 63
                : 'string';
1084
1085 63
            $discriminatorColumn->setType(Type::getType($typeName));
1086 63
            $discriminatorColumn->setColumnName($discriminatorColumnAnnotation->name);
1087
1088 63
            if (! empty($discriminatorColumnAnnotation->columnDefinition)) {
1089 1
                $discriminatorColumn->setColumnDefinition($discriminatorColumnAnnotation->columnDefinition);
1090
            }
1091
1092 63
            if (! empty($discriminatorColumnAnnotation->length)) {
1093 5
                $discriminatorColumn->setLength($discriminatorColumnAnnotation->length);
1094
            }
1095
        }
1096
1097 79
        $metadata->setDiscriminatorColumn($discriminatorColumn);
1098
1099
        // Evaluate DiscriminatorMap annotation
1100 79
        if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) {
1101 78
            $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class];
1102 78
            $discriminatorMap           = $discriminatorMapAnnotation->value;
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...
1103
1104 78
            $metadata->setDiscriminatorMap($discriminatorMap);
1105
        }
1106 79
    }
1107
1108
    /**
1109
     * @param Annotation\Annotation[] $classAnnotations
1110
     */
1111 366
    private function attachLifecycleCallbacks(
1112
        array $classAnnotations,
1113
        \ReflectionClass $reflectionClass,
1114
        Mapping\ClassMetadata $metadata
1115
    ) : void {
1116
        // Evaluate @HasLifecycleCallbacks annotation
1117 366
        if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) {
1118
            /* @var $method \ReflectionMethod */
1119 15
            foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
1120 14
                foreach ($this->getMethodCallbacks($method) as $callback) {
1121 14
                    $metadata->addLifecycleCallback($method->getName(), $callback);
1122
                }
1123
            }
1124
        }
1125 366
    }
1126
1127
    /**
1128
     * @param Annotation\Annotation[] $classAnnotations
1129
     *
1130
     * @throws \ReflectionException
1131
     * @throws Mapping\MappingException
1132
     */
1133 366
    private function attachEntityListeners(
1134
        array $classAnnotations,
1135
        \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

1135
        /** @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...
1136
        Mapping\ClassMetadata $metadata
1137
    ) : void {
1138
        // Evaluate @EntityListeners annotation
1139 366
        if (isset($classAnnotations[Annotation\EntityListeners::class])) {
1140
            /** @var Annotation\EntityListeners $entityListenersAnnot */
1141 9
            $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class];
1142
1143 9
            foreach ($entityListenersAnnot->value as $listenerClassName) {
1144 9
                if (! class_exists($listenerClassName)) {
1145
                    throw Mapping\MappingException::entityListenerClassNotFound(
1146
                        $listenerClassName,
1147
                        $metadata->getClassName()
1148
                    );
1149
                }
1150
1151 9
                $listenerClass = new \ReflectionClass($listenerClassName);
1152
1153
                /* @var $method \ReflectionMethod */
1154 9
                foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
1155 9
                    foreach ($this->getMethodCallbacks($method) as $callback) {
1156 9
                        $metadata->addEntityListener($callback, $listenerClassName, $method->getName());
1157
                    }
1158
                }
1159
            }
1160
        }
1161 366
    }
1162
1163
    /**
1164
     * @param Annotation\Annotation[] $classAnnotations
1165
     *
1166
     * @throws Mapping\MappingException
1167
     */
1168 363
    private function attachPropertyOverrides(
1169
        array $classAnnotations,
1170
        \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

1170
        /** @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...
1171
        Mapping\ClassMetadata $metadata
1172
    ) : void {
1173
        // Evaluate AssociationOverrides annotation
1174 363
        if (isset($classAnnotations[Annotation\AssociationOverrides::class])) {
1175 5
            $associationOverridesAnnot = $classAnnotations[Annotation\AssociationOverrides::class];
1176
1177 5
            foreach ($associationOverridesAnnot->value as $associationOverride) {
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...
1178 5
                $fieldName = $associationOverride->name;
1179 5
                $property  = $metadata->getProperty($fieldName);
1180
1181 5
                if (! $property) {
1182
                    throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName);
1183
                }
1184
1185 5
                $existingClass = get_class($property);
1186 5
                $override      = new $existingClass($fieldName);
1187
1188
                // Check for JoinColumn/JoinColumns annotations
1189 5
                if ($associationOverride->joinColumns) {
1190 3
                    $joinColumns = [];
1191
1192 3
                    foreach ($associationOverride->joinColumns as $joinColumnAnnot) {
1193 3
                        $joinColumns[] = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot);
1194
                    }
1195
1196 3
                    $override->setJoinColumns($joinColumns);
1197
                }
1198
1199
                // Check for JoinTable annotations
1200 5
                if ($associationOverride->joinTable) {
1201 2
                    $joinTableAnnot    = $associationOverride->joinTable;
1202 2
                    $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata($joinTableAnnot);
1203
1204 2
                    $override->setJoinTable($joinTableMetadata);
1205
                }
1206
1207
                // Check for inversedBy
1208 5
                if ($associationOverride->inversedBy) {
1209 1
                    $override->setInversedBy($associationOverride->inversedBy);
1210
                }
1211
1212
                // Check for fetch
1213 5
                if ($associationOverride->fetch) {
1214 1
                    $override->setFetchMode(
1215 1
                        constant(Mapping\FetchMode::class . '::' . $associationOverride->fetch)
1216
                    );
1217
                }
1218
1219 5
                $metadata->setPropertyOverride($override);
1220
            }
1221
        }
1222
1223
        // Evaluate AttributeOverrides annotation
1224 363
        if (isset($classAnnotations[Annotation\AttributeOverrides::class])) {
1225 3
            $attributeOverridesAnnot = $classAnnotations[Annotation\AttributeOverrides::class];
1226
1227 3
            foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnot) {
1228 3
                $fieldMetadata = $this->convertColumnAnnotationToFieldMetadata(
1229 3
                    $attributeOverrideAnnot->column,
1230 3
                    $attributeOverrideAnnot->name,
1231 3
                    false
1232
                );
1233
1234 3
                $metadata->setPropertyOverride($fieldMetadata);
1235
            }
1236
        }
1237 363
    }
1238
1239
    /**
1240
     * @param Annotation\Annotation[] $propertyAnnotations
1241
     */
1242 249
    private function attachAssociationPropertyCache(
1243
        array $propertyAnnotations,
1244
        \ReflectionProperty $reflectionProperty,
1245
        Mapping\AssociationMetadata $assocMetadata,
1246
        Mapping\ClassMetadata $metadata
1247
    ) : void {
1248
        // Check for Cache
1249 249
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
1250 14
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
1251 14
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata(
1252 14
                $cacheAnnot,
1253 14
                $metadata,
1254 14
                $reflectionProperty->getName()
1255
            );
1256
1257 14
            $assocMetadata->setCache($cacheMetadata);
1258
        }
1259 249
    }
1260
1261
    /**
1262
     * Attempts to resolve the cascade modes.
1263
     *
1264
     * @param string   $className        The class name.
1265
     * @param string   $fieldName        The field name.
1266
     * @param string[] $originalCascades The original unprocessed field cascades.
1267
     *
1268
     * @return string[] The processed field cascades.
1269
     *
1270
     * @throws Mapping\MappingException If a cascade option is not valid.
1271
     */
1272 249
    private function getCascade(string $className, string $fieldName, array $originalCascades)
1273
    {
1274 249
        $cascadeTypes = ['remove', 'persist', 'refresh'];
1275 249
        $cascades     = array_map('strtolower', $originalCascades);
1276
1277 249
        if (in_array('all', $cascades, true)) {
1278 22
            $cascades = $cascadeTypes;
1279
        }
1280
1281 249
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
1282
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
1283
1284
            throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName);
1285
        }
1286
1287 249
        return $cascades;
1288
    }
1289
1290
    /**
1291
     * Attempts to resolve the fetch mode.
1292
     *
1293
     * @param string $className The class name.
1294
     * @param string $fetchMode The fetch mode.
1295
     *
1296
     * @return string The fetch mode as defined in ClassMetadata.
1297
     *
1298
     * @throws Mapping\MappingException If the fetch mode is not valid.
1299
     */
1300 249
    private function getFetchMode($className, $fetchMode) : string
1301
    {
1302 249
        $fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode);
1303
1304 249
        if (! defined($fetchModeConstant)) {
1305
            throw Mapping\MappingException::invalidFetchMode($className, $fetchMode);
1306
        }
1307
1308 249
        return constant($fetchModeConstant);
1309
    }
1310
1311
    /**
1312
     * Parses the given method.
1313
     *
1314
     * @return string[]
1315
     */
1316 23
    private function getMethodCallbacks(\ReflectionMethod $method) : array
1317
    {
1318 23
        $annotations = $this->getMethodAnnotations($method);
1319
        $events      = [
1320 23
            Events::prePersist  => Annotation\PrePersist::class,
1321 23
            Events::postPersist => Annotation\PostPersist::class,
1322 23
            Events::preUpdate   => Annotation\PreUpdate::class,
1323 23
            Events::postUpdate  => Annotation\PostUpdate::class,
1324 23
            Events::preRemove   => Annotation\PreRemove::class,
1325 23
            Events::postRemove  => Annotation\PostRemove::class,
1326 23
            Events::postLoad    => Annotation\PostLoad::class,
1327 23
            Events::preFlush    => Annotation\PreFlush::class,
1328
        ];
1329
1330
        // Check for callbacks
1331 23
        $callbacks = [];
1332
1333 23
        foreach ($events as $eventName => $annotationClassName) {
1334 23
            if (isset($annotations[$annotationClassName]) || $method->getName() === $eventName) {
1335 23
                $callbacks[] = $eventName;
1336
            }
1337
        }
1338
1339 23
        return $callbacks;
1340
    }
1341
1342
    /**
1343
     * @return Annotation\Annotation[]
1344
     */
1345 369
    private function getClassAnnotations(\ReflectionClass $reflectionClass) : array
1346
    {
1347 369
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
1348
1349 369
        foreach ($classAnnotations as $key => $annot) {
1350 366
            if (! is_numeric($key)) {
1351
                continue;
1352
            }
1353
1354 366
            $classAnnotations[get_class($annot)] = $annot;
1355
        }
1356
1357 369
        return $classAnnotations;
1358
    }
1359
1360
    /**
1361
     * @return Annotation\Annotation[]
1362
     */
1363 366
    private function getPropertyAnnotations(\ReflectionProperty $reflectionProperty) : array
1364
    {
1365 366
        $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty);
1366
1367 365
        foreach ($propertyAnnotations as $key => $annot) {
1368 365
            if (! is_numeric($key)) {
1369
                continue;
1370
            }
1371
1372 365
            $propertyAnnotations[get_class($annot)] = $annot;
1373
        }
1374
1375 365
        return $propertyAnnotations;
1376
    }
1377
1378
    /**
1379
     * @return Annotation\Annotation[]
1380
     */
1381 23
    private function getMethodAnnotations(\ReflectionMethod $reflectionMethod) : array
1382
    {
1383 23
        $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod);
1384
1385 23
        foreach ($methodAnnotations as $key => $annot) {
1386 18
            if (! is_numeric($key)) {
1387
                continue;
1388
            }
1389
1390 18
            $methodAnnotations[get_class($annot)] = $annot;
1391
        }
1392
1393 23
        return $methodAnnotations;
1394
    }
1395
1396
    /**
1397
     * Factory method for the Annotation Driver.
1398
     *
1399
     * @param string|string[] $paths
1400
     *
1401
     * @return AnnotationDriver
1402
     */
1403
    public static function create($paths = [], ?AnnotationReader $reader = null)
1404
    {
1405
        if ($reader === null) {
1406
            $reader = new AnnotationReader();
1407
        }
1408
1409
        return new self($reader, $paths);
1410
    }
1411
}
1412