Failed Conditions
Push — master ( 8be1e3...e3936d )
by Marco
14s
created

convertReflectionPropertyToManyToManyAssociationMetadata()   C

Complexity

Conditions 7
Paths 64

Size

Total Lines 51
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 7.0024

Importance

Changes 0
Metric Value
cc 7
eloc 26
nc 64
nop 3
dl 0
loc 51
ccs 26
cts 27
cp 0.963
crap 7.0024
rs 6.9743
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace 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\Events;
12
use Doctrine\ORM\Mapping;
13
14
/**
15
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
16
 *
17
 */
18
class AnnotationDriver implements MappingDriver
19
{
20
    /**
21
     * @var int[]
22
     */
23
    protected $entityAnnotationClasses = [
24
        Annotation\Entity::class           => 1,
25
        Annotation\MappedSuperclass::class => 2,
26
    ];
27
28
    /**
29
     * The AnnotationReader.
30
     *
31
     * @var AnnotationReader
32
     */
33
    protected $reader;
34
35
    /**
36
     * The paths where to look for mapping files.
37
     *
38
     * @var string[]
39
     */
40
    protected $paths = [];
41
42
    /**
43
     * The paths excluded from path where to look for mapping files.
44
     *
45
     * @var string[]
46
     */
47
    protected $excludePaths = [];
48
49
    /**
50
     * The file extension of mapping documents.
51
     *
52
     * @var string
53
     */
54
    protected $fileExtension = '.php';
55
56
    /**
57
     * Cache for AnnotationDriver#getAllClassNames().
58
     *
59
     * @var string[]|null
60
     */
61
    protected $classNames;
62
63
    /**
64
     * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
65
     * docblock annotations.
66
     *
67
     * @param Reader               $reader The AnnotationReader to use, duck-typed.
68
     * @param string|string[]|null $paths  One or multiple paths where mapping classes can be found.
69
     */
70 2291
    public function __construct(Reader $reader, $paths = null)
71
    {
72 2291
        $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...
73 2291
        if ($paths) {
74 2206
            $this->addPaths((array) $paths);
75
        }
76 2291
    }
77
78
    /**
79
     * Appends lookup paths to metadata driver.
80
     *
81
     * @param string[] $paths
82
     */
83 2210
    public function addPaths(array $paths)
84
    {
85 2210
        $this->paths = array_unique(array_merge($this->paths, $paths));
86 2210
    }
87
88
    /**
89
     * Retrieves the defined metadata lookup paths.
90
     *
91
     * @return string[]
92
     */
93
    public function getPaths()
94
    {
95
        return $this->paths;
96
    }
97
98
    /**
99
     * Append exclude lookup paths to metadata driver.
100
     *
101
     * @param string[] $paths
102
     */
103
    public function addExcludePaths(array $paths)
104
    {
105
        $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths));
106
    }
107
108
    /**
109
     * Retrieve the defined metadata lookup exclude paths.
110
     *
111
     * @return string[]
112
     */
113
    public function getExcludePaths()
114
    {
115
        return $this->excludePaths;
116
    }
117
118
    /**
119
     * Retrieve the current annotation reader
120
     *
121
     * @return AnnotationReader
122
     */
123 1
    public function getReader()
124
    {
125 1
        return $this->reader;
126
    }
127
128
    /**
129
     * Gets the file extension used to look for mapping files under.
130
     *
131
     * @return string
132
     */
133
    public function getFileExtension()
134
    {
135
        return $this->fileExtension;
136
    }
137
138
    /**
139
     * Sets the file extension used to look for mapping files under.
140
     *
141
     * @param string $fileExtension The file extension to set.
142
     *
143
     */
144
    public function setFileExtension($fileExtension)
145
    {
146
        $this->fileExtension = $fileExtension;
147
    }
148
149
    /**
150
     * Returns whether the class with the specified name is transient. Only non-transient
151
     * classes, that is entities and mapped superclasses, should have their metadata loaded.
152
     *
153
     * A class is non-transient if it is annotated with an annotation
154
     * from the {@see AnnotationDriver::entityAnnotationClasses}.
155
     *
156
     * @param string $className
157
     *
158
     * @return bool
159
     */
160 193
    public function isTransient($className)
161
    {
162 193
        $classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className));
163
164 193
        foreach ($classAnnotations as $annot) {
165 188
            if (isset($this->entityAnnotationClasses[get_class($annot)])) {
166 188
                return false;
167
            }
168
        }
169 12
        return true;
170
    }
171
172
    /**
173
     * {@inheritDoc}
174
     */
175 61
    public function getAllClassNames()
176
    {
177 61
        if ($this->classNames !== null) {
178 46
            return $this->classNames;
179
        }
180
181 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...
182
            throw Mapping\MappingException::pathRequired();
183
        }
184
185 61
        $classes       = [];
186 61
        $includedFiles = [];
187
188 61
        foreach ($this->paths as $path) {
189 61
            if (! is_dir($path)) {
190
                throw Mapping\MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
191
            }
192
193 61
            $iterator = new \RegexIterator(
194 61
                new \RecursiveIteratorIterator(
195 61
                    new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
196 61
                    \RecursiveIteratorIterator::LEAVES_ONLY
197
                ),
198 61
                '/^.+' . preg_quote($this->fileExtension) . '$/i',
199 61
                \RecursiveRegexIterator::GET_MATCH
200
            );
201
202 61
            foreach ($iterator as $file) {
203 61
                $sourceFile = $file[0];
204
205 61
                if (! preg_match('(^phar:)i', $sourceFile)) {
206 61
                    $sourceFile = realpath($sourceFile);
207
                }
208
209 61
                foreach ($this->excludePaths as $excludePath) {
210
                    $exclude = str_replace('\\', '/', realpath($excludePath));
211
                    $current = str_replace('\\', '/', $sourceFile);
212
213
                    if (strpos($current, $exclude) !== false) {
214
                        continue 2;
215
                    }
216
                }
217
218 61
                require_once $sourceFile;
219
220 61
                $includedFiles[] = $sourceFile;
221
            }
222
        }
223
224 61
        $declared = get_declared_classes();
225
226 61
        foreach ($declared as $className) {
227 61
            $rc         = new \ReflectionClass($className);
228 61
            $sourceFile = $rc->getFileName();
229 61
            if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) {
230 61
                $classes[] = $className;
231
            }
232
        }
233
234 61
        $this->classNames = $classes;
235
236 61
        return $classes;
237
    }
238
239
    /**
240
     * {@inheritDoc}
241
     */
242 367
    public function loadMetadataForClass(
243
        string $className,
244
        Mapping\ClassMetadata $metadata,
245
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
246
    ) : Mapping\ClassMetadata {
247 367
        $reflectionClass = $metadata->getReflectionClass();
248
249 367
        if (! $reflectionClass) {
250
            // this happens when running annotation driver in combination with
251
            // static reflection services. This is not the nicest fix
252
            $reflectionClass = new \ReflectionClass($metadata->getClassName());
253
        }
254
255 367
        $classAnnotations = $this->getClassAnnotations($reflectionClass);
256 367
        $classMetadata    = $this->convertClassAnnotationsToClassMetadata(
257 367
            $classAnnotations,
258 367
            $reflectionClass,
259 367
            $metadata,
260 367
            $metadataBuildingContext
261
        );
262
263
        // Evaluate @Cache annotation
264 364
        if (isset($classAnnotations[Annotation\Cache::class])) {
265 17
            $cacheAnnot = $classAnnotations[Annotation\Cache::class];
266 17
            $cache      = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $metadata);
267
268 17
            $classMetadata->setCache($cache);
269
        }
270
271
        // Evaluate annotations on properties/fields
272
        /* @var $reflProperty \ReflectionProperty */
273 364
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
274 364
            if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) {
275 75
                continue;
276
            }
277
278 364
            $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty);
279 363
            $property            = $this->convertPropertyAnnotationsToProperty(
280 363
                $propertyAnnotations,
281 363
                $reflectionProperty,
282 363
                $classMetadata
283
            );
284
285 363
            if (! $property) {
286 4
                continue;
287
            }
288
289 363
            $metadata->addProperty($property);
290
        }
291
292 362
        $this->attachPropertyOverrides($classAnnotations, $reflectionClass, $metadata);
293
294 362
        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...
295
    }
296
297
    /**
298
     * @param Annotation\Annotation[] $classAnnotations
299
     *
300
     * @throws Mapping\MappingException
301
     */
302 367
    private function convertClassAnnotationsToClassMetadata(
303
        array $classAnnotations,
304
        \ReflectionClass $reflectionClass,
305
        Mapping\ClassMetadata $metadata,
306
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
307
    ) : Mapping\ClassMetadata {
308
        switch (true) {
309 367
            case isset($classAnnotations[Annotation\Entity::class]):
310 363
                return $this->convertClassAnnotationsToEntityClassMetadata(
311 363
                    $classAnnotations,
312 363
                    $reflectionClass,
313 363
                    $metadata,
314 363
                    $metadataBuildingContext
315
                );
316
317
                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...
318
319 28
            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
320 25
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
321 25
                    $classAnnotations,
322 25
                    $reflectionClass,
323 25
                    $metadata
324
                );
325
326 3
            case isset($classAnnotations[Annotation\Embeddable::class]):
327
                return $this->convertClassAnnotationsToEmbeddableClassMetadata(
328
                    $classAnnotations,
329
                    $reflectionClass,
330
                    $metadata
331
                );
332
333
            default:
334 3
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
335
        }
336
    }
337
338
    /**
339
     * @param Annotation\Annotation[] $classAnnotations
340
     *
341
     * @return Mapping\ClassMetadata
342
     *
343
     * @throws Mapping\MappingException
344
     * @throws \UnexpectedValueException
345
     */
346 363
    private function convertClassAnnotationsToEntityClassMetadata(
347
        array $classAnnotations,
348
        \ReflectionClass $reflectionClass,
349
        Mapping\ClassMetadata $metadata,
350
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
351
    ) {
352
        /** @var Annotation\Entity $entityAnnot */
353 363
        $entityAnnot = $classAnnotations[Annotation\Entity::class];
354
355 363
        if ($entityAnnot->repositoryClass !== null) {
356 3
            $metadata->setCustomRepositoryClassName($entityAnnot->repositoryClass);
357
        }
358
359 363
        if ($entityAnnot->readOnly) {
360 1
            $metadata->asReadOnly();
361
        }
362
363 363
        $metadata->isMappedSuperclass = false;
364 363
        $metadata->isEmbeddedClass    = false;
365
366 363
        $this->attachTable($classAnnotations, $reflectionClass, $metadata, $metadataBuildingContext);
367
368
        // Evaluate @ChangeTrackingPolicy annotation
369 363
        if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) {
370 5
            $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class];
371
372 5
            $metadata->setChangeTrackingPolicy(
373 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...
374
            );
375
        }
376
377
        // Evaluate @InheritanceType annotation
378 363
        if (isset($classAnnotations[Annotation\InheritanceType::class])) {
379 78
            $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class];
380
381 78
            $metadata->setInheritanceType(
382 78
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value))
383
            );
384
385 78
            if ($metadata->inheritanceType !== Mapping\InheritanceType::NONE) {
386 78
                $this->attachDiscriminatorColumn($classAnnotations, $reflectionClass, $metadata);
387
            }
388
        }
389
390 363
        $this->attachNamedQueries($classAnnotations, $reflectionClass, $metadata);
391 363
        $this->attachNamedNativeQueries($classAnnotations, $reflectionClass, $metadata);
392 363
        $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata);
393 363
        $this->attachEntityListeners($classAnnotations, $reflectionClass, $metadata);
394
395 363
        return $metadata;
396
    }
397
398
    /**
399
     * @param Annotation\Annotation[] $classAnnotations
400
     */
401 25
    private function convertClassAnnotationsToMappedSuperClassMetadata(
402
        array $classAnnotations,
403
        \ReflectionClass $reflectionClass,
404
        Mapping\ClassMetadata $metadata
405
    ) : Mapping\ClassMetadata {
406
        /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */
407 25
        $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class];
408
409 25
        if ($mappedSuperclassAnnot->repositoryClass !== null) {
410 2
            $metadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass);
411
        }
412
413 25
        $metadata->isMappedSuperclass = true;
414 25
        $metadata->isEmbeddedClass    = false;
415
416 25
        $this->attachNamedQueries($classAnnotations, $reflectionClass, $metadata);
417 25
        $this->attachNamedNativeQueries($classAnnotations, $reflectionClass, $metadata);
418 25
        $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata);
419 25
        $this->attachEntityListeners($classAnnotations, $reflectionClass, $metadata);
420
421 25
        return $metadata;
422
    }
423
424
    /**
425
     * @param Annotation\Annotation[] $classAnnotations
426
     */
427
    private function convertClassAnnotationsToEmbeddableClassMetadata(
428
        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

428
        /** @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...
429
        \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

429
        /** @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...
430
        Mapping\ClassMetadata $metadata
431
    ) : Mapping\ClassMetadata {
432
        $metadata->isMappedSuperclass = false;
433
        $metadata->isEmbeddedClass    = true;
434
435
        return $metadata;
436
    }
437
438
    /**
439
     * @param Annotation\Annotation[] $propertyAnnotations
440
     *
441
     * @todo guilhermeblanco Remove nullable typehint once embeddables are back
442
     */
443 363
    private function convertPropertyAnnotationsToProperty(
444
        array $propertyAnnotations,
445
        \ReflectionProperty $reflectionProperty,
446
        Mapping\ClassMetadata $metadata
447
    ) : ?Mapping\Property {
448
        switch (true) {
449 363
            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...
450 359
                return $this->convertReflectionPropertyToFieldMetadata(
451 359
                    $reflectionProperty,
452 359
                    $propertyAnnotations,
453 359
                    $metadata
454
                );
455
456 252
            case isset($propertyAnnotations[Annotation\OneToOne::class]):
457 108
                return $this->convertReflectionPropertyToOneToOneAssociationMetadata(
458 108
                    $reflectionProperty,
459 108
                    $propertyAnnotations,
460 108
                    $metadata
461
                );
462
463 202
            case isset($propertyAnnotations[Annotation\ManyToOne::class]):
464 136
                return $this->convertReflectionPropertyToManyToOneAssociationMetadata(
465 136
                    $reflectionProperty,
466 136
                    $propertyAnnotations,
467 136
                    $metadata
468
                );
469
470 164
            case isset($propertyAnnotations[Annotation\OneToMany::class]):
471 107
                return $this->convertReflectionPropertyToOneToManyAssociationMetadata(
472 107
                    $reflectionProperty,
473 107
                    $propertyAnnotations,
474 107
                    $metadata
475
                );
476
477 107
            case isset($propertyAnnotations[Annotation\ManyToMany::class]):
478 86
                return $this->convertReflectionPropertyToManyToManyAssociationMetadata(
479 86
                    $reflectionProperty,
480 86
                    $propertyAnnotations,
481 86
                    $metadata
482
                );
483
484 36
            case isset($propertyAnnotations[Annotation\Embedded::class]):
485 4
                return null;
486
487
            default:
488 32
                return new Mapping\TransientMetadata($reflectionProperty->getName());
489
        }
490
    }
491
492
    /**
493
     * @param Annotation\Annotation[] $propertyAnnotations
494
     *
495
     * @throws Mapping\MappingException
496
     */
497 359
    private function convertReflectionPropertyToFieldMetadata(
498
        \ReflectionProperty $reflProperty,
499
        array $propertyAnnotations,
500
        Mapping\ClassMetadata $metadata
501
    ) : Mapping\FieldMetadata {
502 359
        $className   = $metadata->getClassName();
503 359
        $fieldName   = $reflProperty->getName();
504 359
        $isVersioned = isset($propertyAnnotations[Annotation\Version::class]);
505 359
        $columnAnnot = $propertyAnnotations[Annotation\Column::class];
506
507 359
        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...
508
            throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName);
509
        }
510
511 359
        $fieldMetadata = $this->convertColumnAnnotationToFieldMetadata($columnAnnot, $fieldName, $isVersioned);
512
513
        // Check for Id
514 359
        if (isset($propertyAnnotations[Annotation\Id::class])) {
515 355
            $fieldMetadata->setPrimaryKey(true);
516
        }
517
518
        // Check for GeneratedValue strategy
519 359
        if (isset($propertyAnnotations[Annotation\GeneratedValue::class])) {
520 305
            $generatedValueAnnot = $propertyAnnotations[Annotation\GeneratedValue::class];
521 305
            $strategy            = strtoupper($generatedValueAnnot->strategy);
522 305
            $idGeneratorType     = constant(sprintf('%s::%s', Mapping\GeneratorType::class, $strategy));
523
524 305
            if ($idGeneratorType !== Mapping\GeneratorType::NONE) {
525 283
                $idGeneratorDefinition = [];
526
527
                // Check for CustomGenerator/SequenceGenerator/TableGenerator definition
528
                switch (true) {
529 283
                    case isset($propertyAnnotations[Annotation\SequenceGenerator::class]):
530 10
                        $seqGeneratorAnnot = $propertyAnnotations[Annotation\SequenceGenerator::class];
531
532
                        $idGeneratorDefinition = [
533 10
                            'sequenceName' => $seqGeneratorAnnot->sequenceName,
534 10
                            'allocationSize' => $seqGeneratorAnnot->allocationSize,
535
                        ];
536
537 10
                        break;
538
539 273
                    case isset($propertyAnnotations[Annotation\CustomIdGenerator::class]):
540 3
                        $customGeneratorAnnot = $propertyAnnotations[Annotation\CustomIdGenerator::class];
541
542
                        $idGeneratorDefinition = [
543 3
                            'class' => $customGeneratorAnnot->class,
544 3
                            'arguments' => $customGeneratorAnnot->arguments,
545
                        ];
546
547 3
                        break;
548
549
                    /* @todo If it is not supported, why does this exist? */
550 270
                    case isset($propertyAnnotations['Doctrine\ORM\Mapping\TableGenerator']):
551
                        throw Mapping\MappingException::tableIdGeneratorNotImplemented($className);
552
                }
553
554 283
                $fieldMetadata->setValueGenerator(new Mapping\ValueGeneratorMetadata($idGeneratorType, $idGeneratorDefinition));
555
            }
556
        }
557
558 359
        return $fieldMetadata;
559
    }
560
561
    /**
562
     * @param Annotation\Annotation[] $propertyAnnotations
563
     *
564
     * @return Mapping\OneToOneAssociationMetadata
565
     */
566 108
    private function convertReflectionPropertyToOneToOneAssociationMetadata(
567
        \ReflectionProperty $reflectionProperty,
568
        array $propertyAnnotations,
569
        Mapping\ClassMetadata $metadata
570
    ) {
571 108
        $className     = $metadata->getClassName();
572 108
        $fieldName     = $reflectionProperty->getName();
573 108
        $oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class];
574 108
        $assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName);
575 108
        $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...
576
577 108
        $assocMetadata->setTargetEntity($targetEntity);
578 108
        $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...
579 108
        $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...
580 108
        $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...
581
582 108
        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...
583 37
            $assocMetadata->setMappedBy($oneToOneAnnot->mappedBy);
584
        }
585
586 108
        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...
587 53
            $assocMetadata->setInversedBy($oneToOneAnnot->inversedBy);
588
        }
589
590
        // Check for Id
591 108
        if (isset($propertyAnnotations[Annotation\Id::class])) {
592 9
            $assocMetadata->setPrimaryKey(true);
593
        }
594
595 108
        $this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata);
596
597
        // Check for JoinColumn/JoinColumns annotations
598
        switch (true) {
599 108
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
600 80
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
601
602 80
                $assocMetadata->addJoinColumn(
603 80
                    $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
604
                );
605
606 80
                break;
607
608 46
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
609 3
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
610
611 3
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
612 3
                    $assocMetadata->addJoinColumn(
613 3
                        $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
614
                    );
615
                }
616
617 3
                break;
618
        }
619
620 108
        return $assocMetadata;
621
    }
622
623
    /**
624
     * @param Annotation\Annotation[] $propertyAnnotations
625
     *
626
     * @return Mapping\ManyToOneAssociationMetadata
627
     */
628 136
    private function convertReflectionPropertyToManyToOneAssociationMetadata(
629
        \ReflectionProperty $reflectionProperty,
630
        array $propertyAnnotations,
631
        Mapping\ClassMetadata $metadata
632
    ) {
633 136
        $className      = $metadata->getClassName();
634 136
        $fieldName      = $reflectionProperty->getName();
635 136
        $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class];
636 136
        $assocMetadata  = new Mapping\ManyToOneAssociationMetadata($fieldName);
637 136
        $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...
638
639 136
        $assocMetadata->setTargetEntity($targetEntity);
640 136
        $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...
641 136
        $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...
642
643 136
        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...
644 91
            $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy);
645
        }
646
647
        // Check for Id
648 136
        if (isset($propertyAnnotations[Annotation\Id::class])) {
649 32
            $assocMetadata->setPrimaryKey(true);
650
        }
651
652 136
        $this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata);
653
654
        // Check for JoinColumn/JoinColumns annotations
655
        switch (true) {
656 136
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
657 76
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
658
659 76
                $assocMetadata->addJoinColumn(
660 76
                    $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
661
                );
662
663 76
                break;
664
665 69
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
666 16
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
667
668 16
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
669 16
                    $assocMetadata->addJoinColumn(
670 16
                        $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot)
671
                    );
672
                }
673
674 16
                break;
675
        }
676
677 136
        return $assocMetadata;
678
    }
679
680
    /**
681
     * @param Annotation\Annotation[] $propertyAnnotations
682
     *
683
     * @throws Mapping\MappingException
684
     */
685 107
    private function convertReflectionPropertyToOneToManyAssociationMetadata(
686
        \ReflectionProperty $reflectionProperty,
687
        array $propertyAnnotations,
688
        Mapping\ClassMetadata $metadata
689
    ) : Mapping\OneToManyAssociationMetadata {
690 107
        $className      = $metadata->getClassName();
691 107
        $fieldName      = $reflectionProperty->getName();
692 107
        $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class];
693 107
        $assocMetadata  = new Mapping\OneToManyAssociationMetadata($fieldName);
694 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...
695
696 107
        $assocMetadata->setTargetEntity($targetEntity);
697 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...
698 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...
699 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...
700
701 107
        if (! empty($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...
702 107
            $assocMetadata->setMappedBy($oneToManyAnnot->mappedBy);
703
        }
704
705 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...
706 7
            $assocMetadata->setIndexedBy($oneToManyAnnot->indexBy);
707
        }
708
709
        // Check for OrderBy
710 107
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
711 15
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
712
713 15
            $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...
714
        }
715
716
        // Check for Id
717 107
        if (isset($propertyAnnotations[Annotation\Id::class])) {
718
            throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName);
719
        }
720
721 107
        $this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata);
722
723 107
        return $assocMetadata;
724
    }
725
726
    /**
727
     * @param Annotation\Annotation[] $propertyAnnotations
728
     *
729
     * @throws Mapping\MappingException
730
     */
731 86
    private function convertReflectionPropertyToManyToManyAssociationMetadata(
732
        \ReflectionProperty $reflectionProperty,
733
        array $propertyAnnotations,
734
        Mapping\ClassMetadata $metadata
735
    ) : Mapping\ManyToManyAssociationMetadata {
736 86
        $className       = $metadata->getClassName();
737 86
        $fieldName       = $reflectionProperty->getName();
738 86
        $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class];
739 86
        $assocMetadata   = new Mapping\ManyToManyAssociationMetadata($fieldName);
740 86
        $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...
741
742 86
        $assocMetadata->setTargetEntity($targetEntity);
743 86
        $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...
744 86
        $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...
745 86
        $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...
746
747 86
        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...
748 34
            $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy);
749
        }
750
751 86
        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...
752 43
            $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy);
753
        }
754
755 86
        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...
756 3
            $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy);
757
        }
758
759
        // Check for JoinTable
760 86
        if (isset($propertyAnnotations[Annotation\JoinTable::class])) {
761 68
            $joinTableAnnot    = $propertyAnnotations[Annotation\JoinTable::class];
762 68
            $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata($joinTableAnnot);
763
764 68
            $assocMetadata->setJoinTable($joinTableMetadata);
765
        }
766
767
        // Check for OrderBy
768 86
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
769 3
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
770
771 3
            $assocMetadata->setOrderBy($orderByAnnot->value);
772
        }
773
774
        // Check for Id
775 86
        if (isset($propertyAnnotations[Annotation\Id::class])) {
776
            throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName);
777
        }
778
779 86
        $this->attachAssociationPropertyCache($propertyAnnotations, $reflectionProperty, $assocMetadata, $metadata);
780
781 86
        return $assocMetadata;
782
    }
783
784
    /**
785
     * Parse the given Column as FieldMetadata
786
     */
787 359
    private function convertColumnAnnotationToFieldMetadata(
788
        Annotation\Column $columnAnnot,
789
        string $fieldName,
790
        bool $isVersioned
791
    ) : Mapping\FieldMetadata {
792 359
        $fieldMetadata = $isVersioned
793 16
            ? new Mapping\VersionFieldMetadata($fieldName)
794 359
            : new Mapping\FieldMetadata($fieldName)
795
        ;
796
797 359
        $fieldMetadata->setType(Type::getType($columnAnnot->type));
798
799 359
        if (! empty($columnAnnot->name)) {
800 74
            $fieldMetadata->setColumnName($columnAnnot->name);
801
        }
802
803 359
        if (! empty($columnAnnot->columnDefinition)) {
804 5
            $fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition);
805
        }
806
807 359
        if (! empty($columnAnnot->length)) {
808 359
            $fieldMetadata->setLength($columnAnnot->length);
809
        }
810
811 359
        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...
812 7
            $fieldMetadata->setOptions($columnAnnot->options);
813
        }
814
815 359
        $fieldMetadata->setScale($columnAnnot->scale);
816 359
        $fieldMetadata->setPrecision($columnAnnot->precision);
817 359
        $fieldMetadata->setNullable($columnAnnot->nullable);
818 359
        $fieldMetadata->setUnique($columnAnnot->unique);
819
820 359
        return $fieldMetadata;
821
    }
822
823
    /**
824
     * Parse the given Table as TableMetadata
825
     */
826 191
    private function convertTableAnnotationToTableMetadata(
827
        Annotation\Table $tableAnnot,
828
        Mapping\TableMetadata $tableMetadata
829
    ) : void {
830 191
        if (! empty($tableAnnot->name)) {
831 186
            $tableMetadata->setName($tableAnnot->name);
832
        }
833
834 191
        if (! empty($tableAnnot->schema)) {
835 5
            $tableMetadata->setSchema($tableAnnot->schema);
836
        }
837
838 191
        foreach ($tableAnnot->options as $optionName => $optionValue) {
839 5
            $tableMetadata->addOption($optionName, $optionValue);
840
        }
841
842 191
        foreach ($tableAnnot->indexes as $indexAnnot) {
843 14
            $tableMetadata->addIndex([
844 14
                'name'    => $indexAnnot->name,
845 14
                'columns' => $indexAnnot->columns,
846 14
                'unique'  => $indexAnnot->unique,
847 14
                'options' => $indexAnnot->options,
848 14
                'flags'   => $indexAnnot->flags,
849
            ]);
850
        }
851
852 191
        foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
853 7
            $tableMetadata->addUniqueConstraint([
854 7
                'name'    => $uniqueConstraintAnnot->name,
855 7
                'columns' => $uniqueConstraintAnnot->columns,
856 7
                'options' => $uniqueConstraintAnnot->options,
857 7
                'flags'   => $uniqueConstraintAnnot->flags,
858
            ]);
859
        }
860 191
    }
861
862
    /**
863
     * Parse the given JoinTable as JoinTableMetadata
864
     */
865 68
    private function convertJoinTableAnnotationToJoinTableMetadata(
866
        Annotation\JoinTable $joinTableAnnot
867
    ) : Mapping\JoinTableMetadata {
868 68
        $joinTable = new Mapping\JoinTableMetadata();
869
870 68
        if (! empty($joinTableAnnot->name)) {
871 66
            $joinTable->setName($joinTableAnnot->name);
872
        }
873
874 68
        if (! empty($joinTableAnnot->schema)) {
875
            $joinTable->setSchema($joinTableAnnot->schema);
876
        }
877
878 68
        foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) {
879 67
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot);
880
881 67
            $joinTable->addJoinColumn($joinColumn);
882
        }
883
884 68
        foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) {
885 67
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata($joinColumnAnnot);
886
887 67
            $joinTable->addInverseJoinColumn($joinColumn);
888
        }
889
890 68
        return $joinTable;
891
    }
892
893
    /**
894
     * Parse the given JoinColumn as JoinColumnMetadata
895
     */
896 176
    private function convertJoinColumnAnnotationToJoinColumnMetadata(
897
        Annotation\JoinColumn $joinColumnAnnot
898
    ) : Mapping\JoinColumnMetadata {
899 176
        $joinColumn = new Mapping\JoinColumnMetadata();
900
901
        // @todo Remove conditionals for name and referencedColumnName once naming strategy is brought into drivers
902 176
        if (! empty($joinColumnAnnot->name)) {
903 169
            $joinColumn->setColumnName($joinColumnAnnot->name);
904
        }
905
906 176
        if (! empty($joinColumnAnnot->referencedColumnName)) {
907 176
            $joinColumn->setReferencedColumnName($joinColumnAnnot->referencedColumnName);
908
        }
909
910 176
        $joinColumn->setNullable($joinColumnAnnot->nullable);
911 176
        $joinColumn->setUnique($joinColumnAnnot->unique);
912
913 176
        if (! empty($joinColumnAnnot->fieldName)) {
914
            $joinColumn->setAliasedName($joinColumnAnnot->fieldName);
915
        }
916
917 176
        if (! empty($joinColumnAnnot->columnDefinition)) {
918 4
            $joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition);
919
        }
920
921 176
        if ($joinColumnAnnot->onDelete) {
922 16
            $joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete));
923
        }
924
925 176
        return $joinColumn;
926
    }
927
928
    /**
929
     * Parse the given Cache as CacheMetadata
930
     *
931
     * @param string|null $fieldName
932
     */
933 17
    private function convertCacheAnnotationToCacheMetadata(
934
        Annotation\Cache $cacheAnnot,
935
        Mapping\ClassMetadata $metadata,
936
        $fieldName = null
937
    ) : Mapping\CacheMetadata {
938 17
        $baseRegion    = strtolower(str_replace('\\', '_', $metadata->getRootClassName()));
939 17
        $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : '');
940
941 17
        $usage  = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage));
942 17
        $region = $cacheAnnot->region ?: $defaultRegion;
943
944 17
        return new Mapping\CacheMetadata($usage, $region);
945
    }
946
947
    /**
948
     * @return mixed[]
949
     */
950 14
    private function convertSqlResultSetMapping(Annotation\SqlResultSetMapping $resultSetMapping)
951
    {
952 14
        $entities = [];
953
954 14
        foreach ($resultSetMapping->entities as $entityResultAnnot) {
955
            $entityResult = [
956 14
                'fields'                => [],
957 14
                'entityClass'           => $entityResultAnnot->entityClass,
958 14
                'discriminatorColumn'   => $entityResultAnnot->discriminatorColumn,
959
            ];
960
961 14
            foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
962 14
                $entityResult['fields'][] = [
963 14
                    'name'      => $fieldResultAnnot->name,
964 14
                    'column'    => $fieldResultAnnot->column,
965
                ];
966
            }
967
968 14
            $entities[] = $entityResult;
969
        }
970
971 14
        $columns = [];
972
973 14
        foreach ($resultSetMapping->columns as $columnResultAnnot) {
974 8
            $columns[] = [
975 8
                'name' => $columnResultAnnot->name,
976
            ];
977
        }
978
979
        return [
980 14
            'name'     => $resultSetMapping->name,
981 14
            'entities' => $entities,
982 14
            'columns'  => $columns,
983
        ];
984
    }
985
986
    /**
987
     * @param Annotation\Annotation[] $classAnnotations
988
     */
989 363
    private function attachTable(
990
        array $classAnnotations,
991
        \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

991
        /** @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...
992
        Mapping\ClassMetadata $metadata,
993
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
994
    ) : void {
995 363
        $parent = $metadata->getParent();
996
997 363
        if ($parent && $parent->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) {
998
            // Handle the case where a middle mapped super class inherits from a single table inheritance tree.
999
            do {
1000 29
                if (! $parent->isMappedSuperclass) {
1001 29
                    $metadata->setTable($parent->table);
1002
1003 29
                    break;
1004
                }
1005
1006 4
                $parent = $parent->getParent();
1007 4
            } while ($parent !== null);
1008
1009 29
            return;
1010
        }
1011
1012 363
        $namingStrategy = $metadataBuildingContext->getNamingStrategy();
1013 363
        $tableMetadata  = new Mapping\TableMetadata();
1014
1015 363
        $tableMetadata->setName($namingStrategy->classToTableName($metadata->getClassName()));
1016
1017
        // Evaluate @Table annotation
1018 363
        if (isset($classAnnotations[Annotation\Table::class])) {
1019 191
            $tableAnnot = $classAnnotations[Annotation\Table::class];
1020
1021 191
            $this->convertTableAnnotationToTableMetadata($tableAnnot, $tableMetadata);
1022
        }
1023
1024 363
        $metadata->setTable($tableMetadata);
1025 363
    }
1026
1027
    /**
1028
     * @param Annotation\Annotation[] $classAnnotations
1029
     *
1030
     * @throws Mapping\MappingException
1031
     */
1032 78
    private function attachDiscriminatorColumn(
1033
        array $classAnnotations,
1034
        \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

1034
        /** @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...
1035
        Mapping\ClassMetadata $metadata
1036
    ) : void {
1037 78
        $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata();
1038
1039 78
        $discriminatorColumn->setTableName($metadata->getTableName());
1040 78
        $discriminatorColumn->setColumnName('dtype');
1041 78
        $discriminatorColumn->setType(Type::getType('string'));
1042 78
        $discriminatorColumn->setLength(255);
1043
1044
        // Evaluate DiscriminatorColumn annotation
1045 78
        if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) {
1046
            /** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnotation */
1047 61
            $discriminatorColumnAnnotation = $classAnnotations[Annotation\DiscriminatorColumn::class];
1048 61
            $typeName                      = ! empty($discriminatorColumnAnnotation->type)
1049 57
                ? $discriminatorColumnAnnotation->type
1050 61
                : 'string';
1051
1052 61
            $discriminatorColumn->setType(Type::getType($typeName));
1053 61
            $discriminatorColumn->setColumnName($discriminatorColumnAnnotation->name);
1054
1055 61
            if (! empty($discriminatorColumnAnnotation->columnDefinition)) {
1056 1
                $discriminatorColumn->setColumnDefinition($discriminatorColumnAnnotation->columnDefinition);
1057
            }
1058
1059 61
            if (! empty($discriminatorColumnAnnotation->length)) {
1060 5
                $discriminatorColumn->setLength($discriminatorColumnAnnotation->length);
1061
            }
1062
        }
1063
1064 78
        $metadata->setDiscriminatorColumn($discriminatorColumn);
1065
1066
        // Evaluate DiscriminatorMap annotation
1067 78
        if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) {
1068 77
            $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class];
1069 77
            $discriminatorMap           = $discriminatorMapAnnotation->value;
1070
1071 77
            $metadata->setDiscriminatorMap($discriminatorMap);
1072
        }
1073 78
    }
1074
1075
    /**
1076
     * @param Annotation\Annotation[] $classAnnotations
1077
     *
1078
     * @throws \UnexpectedValueException
1079
     * @throws Mapping\MappingException
1080
     */
1081 364
    private function attachNamedQueries(
1082
        array $classAnnotations,
1083
        \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

1083
        /** @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...
1084
        Mapping\ClassMetadata $metadata
1085
    ) : void {
1086
        // Evaluate @NamedQueries annotation
1087 364
        if (isset($classAnnotations[Annotation\NamedQueries::class])) {
1088 10
            $namedQueriesAnnot = $classAnnotations[Annotation\NamedQueries::class];
1089
1090 10
            if (! is_array($namedQueriesAnnot->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...
1091
                throw new \UnexpectedValueException('@NamedQueries should contain an array of @NamedQuery annotations.');
1092
            }
1093
1094 10
            foreach ($namedQueriesAnnot->value as $namedQuery) {
1095 10
                if (! ($namedQuery instanceof Annotation\NamedQuery)) {
1096
                    throw new \UnexpectedValueException('@NamedQueries should contain an array of @NamedQuery annotations.');
1097
                }
1098
1099 10
                $metadata->addNamedQuery($namedQuery->name, $namedQuery->query);
1100
            }
1101
        }
1102 364
    }
1103
1104
    /**
1105
     * @param Annotation\Annotation[] $classAnnotations
1106
     */
1107 364
    private function attachNamedNativeQueries(
1108
        array $classAnnotations,
1109
        \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

1109
        /** @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...
1110
        Mapping\ClassMetadata $metadata
1111
    ) : void {
1112
        // Evaluate @NamedNativeQueries annotation
1113 364
        if (isset($classAnnotations[Annotation\NamedNativeQueries::class])) {
1114 14
            $namedNativeQueriesAnnot = $classAnnotations[Annotation\NamedNativeQueries::class];
1115
1116 14
            foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) {
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...
1117 14
                $metadata->addNamedNativeQuery(
1118 14
                    $namedNativeQuery->name,
1119 14
                    $namedNativeQuery->query,
1120
                    [
1121 14
                        'resultClass'      => $namedNativeQuery->resultClass,
1122 14
                        'resultSetMapping' => $namedNativeQuery->resultSetMapping,
1123
                    ]
1124
                );
1125
            }
1126
        }
1127
1128
        // Evaluate @SqlResultSetMappings annotation
1129 364
        if (isset($classAnnotations[Annotation\SqlResultSetMappings::class])) {
1130 14
            $sqlResultSetMappingsAnnot = $classAnnotations[Annotation\SqlResultSetMappings::class];
1131
1132 14
            foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) {
1133 14
                $sqlResultSetMapping = $this->convertSqlResultSetMapping($resultSetMapping);
1134
1135 14
                $metadata->addSqlResultSetMapping($sqlResultSetMapping);
1136
            }
1137
        }
1138 364
    }
1139
1140
    /**
1141
     * @param Annotation\Annotation[] $classAnnotations
1142
     */
1143 364
    private function attachLifecycleCallbacks(
1144
        array $classAnnotations,
1145
        \ReflectionClass $reflectionClass,
1146
        Mapping\ClassMetadata $metadata
1147
    ) : void {
1148
        // Evaluate @HasLifecycleCallbacks annotation
1149 364
        if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) {
1150
            /* @var $method \ReflectionMethod */
1151 16
            foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
1152 15
                foreach ($this->getMethodCallbacks($method) as $callback) {
1153 15
                    $metadata->addLifecycleCallback($method->getName(), $callback);
1154
                }
1155
            }
1156
        }
1157 364
    }
1158
1159
    /**
1160
     * @param Annotation\Annotation[] $classAnnotations
1161
     *
1162
     * @throws \ReflectionException
1163
     * @throws Mapping\MappingException
1164
     */
1165 364
    private function attachEntityListeners(
1166
        array $classAnnotations,
1167
        \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

1167
        /** @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...
1168
        Mapping\ClassMetadata $metadata
1169
    ) : void {
1170
        // Evaluate @EntityListeners annotation
1171 364
        if (isset($classAnnotations[Annotation\EntityListeners::class])) {
1172
            /** @var Annotation\EntityListeners $entityListenersAnnot */
1173 10
            $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class];
1174
1175 10
            foreach ($entityListenersAnnot->value as $listenerClassName) {
1176 10
                if (! class_exists($listenerClassName)) {
1177
                    throw Mapping\MappingException::entityListenerClassNotFound(
1178
                        $listenerClassName,
1179
                        $metadata->getClassName()
1180
                    );
1181
                }
1182
1183 10
                $listenerClass = new \ReflectionClass($listenerClassName);
1184
1185
                /* @var $method \ReflectionMethod */
1186 10
                foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
1187 10
                    foreach ($this->getMethodCallbacks($method) as $callback) {
1188 10
                        $metadata->addEntityListener($callback, $listenerClassName, $method->getName());
1189
                    }
1190
                }
1191
            }
1192
        }
1193 364
    }
1194
1195
    /**
1196
     * @param Annotation\Annotation[] $classAnnotations
1197
     *
1198
     * @throws Mapping\MappingException
1199
     */
1200 362
    private function attachPropertyOverrides(
1201
        array $classAnnotations,
1202
        \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

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