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

ORM/Mapping/Driver/NewAnnotationDriver.php (42 issues)

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\Persistence\Mapping\Driver\FileLocator;
9
use Doctrine\DBAL\DBALException;
10
use Doctrine\DBAL\Types\Type;
11
use Doctrine\ORM\Annotation;
12
use Doctrine\ORM\Events;
13
use Doctrine\ORM\Mapping;
14
use Doctrine\ORM\Mapping\Factory;
15
use ReflectionClass;
16
use ReflectionException;
17
use ReflectionMethod;
18
use ReflectionProperty;
19
use function array_diff;
20
use function array_filter;
21
use function array_intersect;
22
use function array_map;
23
use function array_merge;
24
use function class_exists;
25
use function constant;
26
use function count;
27
use function defined;
28
use function get_class;
29
use function in_array;
30
use function is_numeric;
31
use function sprintf;
32
use function str_replace;
33
use function strtolower;
34
use function strtoupper;
35
36
class NewAnnotationDriver implements MappingDriver
37
{
38
    /** @var int[] */
39
    protected static $entityAnnotationClasses = [
40
        Annotation\Entity::class           => 1,
41
        Annotation\MappedSuperclass::class => 2,
42
    ];
43
44
    /**
45
     * The Annotation reader.
46
     *
47
     * @var AnnotationReader
48
     */
49
    protected $reader;
50
51
    /**
52
     * The file locator.
53
     *
54
     * @var FileLocator
55
     */
56
    protected $locator;
57
58
    /** @var Factory\NamingStrategy */
59
    protected $namingStrategy;
60
61
    /**
62
     * Cache for AnnotationDriver#getAllClassNames().
63
     *
64
     * @var string[]|null
65
     */
66
    private $classNames;
67
68
    /**
69
     * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading docblock annotations.
70
     *
71
     * @param AnnotationReader       $reader         The AnnotationReader to use, duck-typed.
72
     * @param FileLocator            $locator        A FileLocator or one/multiple paths where mapping documents can be found.
73
     * @param Factory\NamingStrategy $namingStrategy The NamingStrategy to use.
74
     */
75
    public function __construct(AnnotationReader $reader, FileLocator $locator, Factory\NamingStrategy $namingStrategy)
76
    {
77
        $this->reader         = $reader;
78
        $this->locator        = $locator;
79
        $this->namingStrategy = $namingStrategy;
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     *
85
     * @return Mapping\ComponentMetadata
86
     *
87
     * @throws Mapping\MappingException
88
     * @throws ReflectionException
89
     */
90
    public function loadMetadataForClass(
91
        string $className,
92
        ?Mapping\ComponentMetadata $parent,
93
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
94
    ) : Mapping\ComponentMetadata {
95
        // IMPORTANT: We're handling $metadata as "parent" metadata here, while building the $className ClassMetadata.
96
        $reflectionClass = new ReflectionClass($className);
97
98
        // Evaluate annotations on class metadata
99
        $classAnnotations = $this->getClassAnnotations($reflectionClass);
100
        $classMetadata    = $this->convertClassAnnotationsToClassMetadata(
101
            $classAnnotations,
102
            $reflectionClass,
103
            $parent,
104
            $metadataBuildingContext
105
        );
106
107
        // Evaluate @Cache annotation
108
        if (isset($classAnnotations[Annotation\Cache::class])) {
109
            $cacheAnnot = $classAnnotations[Annotation\Cache::class];
110
            $cache      = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata);
0 ignored issues
show
It seems like $classMetadata can also be of type Doctrine\ORM\Mapping\MappedSuperClassMetadata; however, parameter $metadata of Doctrine\ORM\Mapping\Dri...tationToCacheMetadata() does only seem to accept Doctrine\ORM\Mapping\ClassMetadata, maybe add an additional type check? ( Ignorable by Annotation )

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

110
            $cache      = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, /** @scrutinizer ignore-type */ $classMetadata);
Loading history...
111
112
            $classMetadata->setCache($cache);
113
        }
114
115
        // Evaluate annotations on properties/fields
116
        /** @var ReflectionProperty $reflectionProperty */
117
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
118
            if ($reflectionProperty->getDeclaringClass()->getClassName() !== $reflectionClass->getName()) {
0 ignored issues
show
The method getClassName() does not exist on ReflectionClass. ( Ignorable by Annotation )

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

118
            if ($reflectionProperty->getDeclaringClass()->/** @scrutinizer ignore-call */ getClassName() !== $reflectionClass->getName()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
119
                continue;
120
            }
121
122
            $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty);
123
            $property            = $this->convertReflectionPropertyAnnotationsToProperty(
124
                $reflectionProperty,
125
                $propertyAnnotations,
126
                $classMetadata
0 ignored issues
show
It seems like $classMetadata can also be of type Doctrine\ORM\Mapping\MappedSuperClassMetadata; however, parameter $classMetadata of Doctrine\ORM\Mapping\Dri...AnnotationsToProperty() does only seem to accept Doctrine\ORM\Mapping\ClassMetadata, maybe add an additional type check? ( Ignorable by Annotation )

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

126
                /** @scrutinizer ignore-type */ $classMetadata
Loading history...
127
            );
128
129
            $classMetadata->addProperty($property);
130
        }
131
132
        return $classMetadata;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function getAllClassNames() : array
139
    {
140
        if ($this->classNames !== null) {
141
            return $this->classNames;
142
        }
143
144
        $classNames = array_filter(
145
            $this->locator->getAllClassNames(null),
146
            function ($className) {
147
                return ! $this->isTransient($className);
148
            }
149
        );
150
151
        $this->classNames = $classNames;
152
153
        return $classNames;
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function isTransient($className) : bool
160
    {
161
        $reflectionClass  = new ReflectionClass($className);
162
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
163
164
        foreach ($classAnnotations as $annotation) {
165
            if (isset(self::$entityAnnotationClasses[get_class($annotation)])) {
166
                return false;
167
            }
168
        }
169
170
        return true;
171
    }
172
173
    /**
174
     * @param Annotation\Annotation[] $classAnnotations
175
     *
176
     * @return Mapping\ClassMetadata|Mapping\ComponentMetadata
177
     *
178
     * @throws DBALException
179
     * @throws ReflectionException
180
     */
181
    private function convertClassAnnotationsToClassMetadata(
182
        array $classAnnotations,
183
        ReflectionClass $reflectionClass,
184
        ?Mapping\ComponentMetadata $parent,
185
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
186
    ) : Mapping\ComponentMetadata {
187
        switch (true) {
188
            case isset($classAnnotations[Annotation\Entity::class]):
189
                return $this->convertClassAnnotationsToEntityClassMetadata(
190
                    $classAnnotations,
191
                    $reflectionClass,
192
                    $parent,
193
                    $metadataBuildingContext
194
                );
195
196
                break;
0 ignored issues
show
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...
197
198
            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
199
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
200
                    $classAnnotations,
201
                    $reflectionClass,
202
                    $parent,
203
                    $metadataBuildingContext
204
                );
205
            case isset($classAnnotations[Annotation\Embeddable::class]):
206
                return $this->convertClassAnnotationsToEntityClassMetadata(
207
                    $classAnnotations,
208
                    $reflectionClass,
209
                    $parent,
210
                    $metadataBuildingContext
211
                );
212
            default:
213
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
214
        }
215
    }
216
217
    /**
218
     * @param Annotation\Annotation[] $classAnnotations
219
     *
220
     * @return Mapping\ClassMetadata
221
     *
222
     * @throws DBALException
223
     * @throws ReflectionException
224
     */
225
    private function convertClassAnnotationsToEntityClassMetadata(
226
        array $classAnnotations,
227
        ReflectionClass $reflectionClass,
228
        ?Mapping\ComponentMetadata $parent,
229
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
0 ignored issues
show
The parameter $metadataBuildingContext 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

229
        /** @scrutinizer ignore-unused */ Mapping\ClassMetadataBuildingContext $metadataBuildingContext

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...
230
    ) {
231
        /** @var Annotation\Entity $entityAnnot */
232
        $entityAnnot   = $classAnnotations[Annotation\Entity::class];
233
        $classMetadata = new Mapping\ClassMetadata($reflectionClass->getName(), $parent);
234
235
        if ($entityAnnot->repositoryClass !== null) {
236
            $classMetadata->setCustomRepositoryClassName($entityAnnot->repositoryClass);
237
        }
238
239
        if ($entityAnnot->readOnly) {
240
            $classMetadata->asReadOnly();
241
        }
242
243
        // Evaluate @Table annotation
244
        if (isset($classAnnotations[Annotation\Table::class])) {
245
            /** @var Annotation\Table $tableAnnot */
246
            $tableAnnot = $classAnnotations[Annotation\Table::class];
247
            $table      = $this->convertTableAnnotationToTableMetadata($tableAnnot);
248
249
            $classMetadata->setTable($table);
250
        }
251
252
        // Evaluate @ChangeTrackingPolicy annotation
253
        if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) {
254
            /** @var Annotation\ChangeTrackingPolicy $changeTrackingAnnot */
255
            $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class];
256
257
            $classMetadata->setChangeTrackingPolicy(
258
                constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value))
259
            );
260
        }
261
262
        // Evaluate @EntityListeners annotation
263
        if (isset($classAnnotations[Annotation\EntityListeners::class])) {
264
            /** @var Annotation\EntityListeners $entityListenersAnnot */
265
            $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class];
266
            $eventMap             = [
267
                Events::prePersist  => Annotation\PrePersist::class,
268
                Events::postPersist => Annotation\PostPersist::class,
269
                Events::preUpdate   => Annotation\PreUpdate::class,
270
                Events::postUpdate  => Annotation\PostUpdate::class,
271
                Events::preRemove   => Annotation\PreRemove::class,
272
                Events::postRemove  => Annotation\PostRemove::class,
273
                Events::postLoad    => Annotation\PostLoad::class,
274
                Events::preFlush    => Annotation\PreFlush::class,
275
            ];
276
277
            foreach ($entityListenersAnnot->value as $listenerClassName) {
278
                if (! class_exists($listenerClassName)) {
279
                    throw Mapping\MappingException::entityListenerClassNotFound(
280
                        $listenerClassName,
281
                        $classMetadata->getClassName()
282
                    );
283
                }
284
285
                $listenerClass = new ReflectionClass($listenerClassName);
286
287
                /** @var ReflectionMethod $reflectionMethod */
288
                foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
289
                    $annotations = $this->getMethodAnnotations($reflectionMethod);
290
291
                    foreach ($eventMap as $eventName => $annotationClassName) {
292
                        if (isset($annotations[$annotationClassName])) {
293
                            $classMetadata->addEntityListener($eventName, $listenerClassName, $reflectionMethod->getName());
294
                        }
295
                    }
296
                }
297
            }
298
        }
299
300
        // Evaluate @HasLifecycleCallbacks annotation
301
        if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) {
302
            $eventMap = [
303
                Events::prePersist  => Annotation\PrePersist::class,
304
                Events::postPersist => Annotation\PostPersist::class,
305
                Events::preUpdate   => Annotation\PreUpdate::class,
306
                Events::postUpdate  => Annotation\PostUpdate::class,
307
                Events::preRemove   => Annotation\PreRemove::class,
308
                Events::postRemove  => Annotation\PostRemove::class,
309
                Events::postLoad    => Annotation\PostLoad::class,
310
                Events::preFlush    => Annotation\PreFlush::class,
311
            ];
312
313
            /** @var ReflectionMethod $reflectionMethod */
314
            foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
315
                $annotations = $this->getMethodAnnotations($reflectionMethod);
316
317
                foreach ($eventMap as $eventName => $annotationClassName) {
318
                    if (isset($annotations[$annotationClassName])) {
319
                        $classMetadata->addLifecycleCallback($eventName, $reflectionMethod->getName());
320
                    }
321
                }
322
            }
323
        }
324
325
        // Evaluate @InheritanceType annotation
326
        if (isset($classAnnotations[Annotation\InheritanceType::class])) {
327
            /** @var Annotation\InheritanceType $inheritanceTypeAnnot */
328
            $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class];
329
330
            $classMetadata->setInheritanceType(
331
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value))
332
            );
333
334
            if ($classMetadata->inheritanceType !== Mapping\InheritanceType::NONE) {
335
                $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata();
336
337
                // Evaluate @DiscriminatorColumn annotation
338
                if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) {
339
                    /** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnot */
340
                    $discriminatorColumnAnnot = $classAnnotations[Annotation\DiscriminatorColumn::class];
341
342
                    $discriminatorColumn->setColumnName($discriminatorColumnAnnot->name);
343
344
                    if (! empty($discriminatorColumnAnnot->columnDefinition)) {
345
                        $discriminatorColumn->setColumnDefinition($discriminatorColumnAnnot->columnDefinition);
346
                    }
347
348
                    if (! empty($discriminatorColumnAnnot->type)) {
349
                        $discriminatorColumn->setType(Type::getType($discriminatorColumnAnnot->type));
350
                    }
351
352
                    if (! empty($discriminatorColumnAnnot->length)) {
353
                        $discriminatorColumn->setLength($discriminatorColumnAnnot->length);
354
                    }
355
                }
356
357
                if (empty($discriminatorColumn->getColumnName())) {
358
                    throw Mapping\MappingException::nameIsMandatoryForDiscriminatorColumns($reflectionClass->getName());
359
                }
360
361
                $classMetadata->setDiscriminatorColumn($discriminatorColumn);
362
363
                // Evaluate @DiscriminatorMap annotation
364
                if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) {
365
                    /** @var Annotation\DiscriminatorMap $discriminatorMapAnnotation */
366
                    $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class];
367
                    $discriminatorMap           = $discriminatorMapAnnotation->value;
368
369
                    $classMetadata->setDiscriminatorMap($discriminatorMap);
370
                }
371
            }
372
        }
373
374
        return $classMetadata;
375
    }
376
377
    /**
378
     * @param Annotation\Annotation[] $classAnnotations
379
     *
380
     * @return Mapping\MappedSuperClassMetadata
381
     */
382
    private function convertClassAnnotationsToMappedSuperClassMetadata(
383
        array $classAnnotations,
384
        ReflectionClass $reflectionClass,
385
        ?Mapping\ComponentMetadata $parent,
0 ignored issues
show
The parameter $parent 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

385
        /** @scrutinizer ignore-unused */ ?Mapping\ComponentMetadata $parent,

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...
386
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
0 ignored issues
show
The parameter $metadataBuildingContext 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

386
        /** @scrutinizer ignore-unused */ Mapping\ClassMetadataBuildingContext $metadataBuildingContext

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...
387
    ) {
388
        /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */
389
        $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class];
390
        $classMetadata         = new Mapping\MappedSuperClassMetadata($reflectionClass->getName());
391
392
        if ($mappedSuperclassAnnot->repositoryClass !== null) {
393
            $classMetadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass);
394
        }
395
396
        return $classMetadata;
397
    }
398
399
    /**
400
     * Parse the given Table as TableMetadata
401
     *
402
     * @return Mapping\TableMetadata
403
     */
404
    private function convertTableAnnotationToTableMetadata(Annotation\Table $tableAnnot)
405
    {
406
        $table = new Mapping\TableMetadata();
407
408
        if (! empty($tableAnnot->name)) {
409
            $table->setName($tableAnnot->name);
410
        }
411
412
        if (! empty($tableAnnot->schema)) {
413
            $table->setSchema($tableAnnot->schema);
414
        }
415
416
        foreach ($tableAnnot->options as $optionName => $optionValue) {
417
            $table->addOption($optionName, $optionValue);
418
        }
419
420
        foreach ($tableAnnot->indexes as $indexAnnot) {
421
            $table->addIndex([
422
                'name'    => $indexAnnot->name,
423
                'columns' => $indexAnnot->columns,
424
                'unique'  => $indexAnnot->unique,
425
                'options' => $indexAnnot->options,
426
                'flags'   => $indexAnnot->flags,
427
            ]);
428
        }
429
430
        foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
431
            $table->addUniqueConstraint([
432
                'name'    => $uniqueConstraintAnnot->name,
433
                'columns' => $uniqueConstraintAnnot->columns,
434
                'options' => $uniqueConstraintAnnot->options,
435
                'flags'   => $uniqueConstraintAnnot->flags,
436
            ]);
437
        }
438
439
        return $table;
440
    }
441
442
    /**
443
     * Parse the given Cache as CacheMetadata
444
     *
445
     * @param string|null $fieldName
446
     *
447
     * @return Mapping\CacheMetadata
448
     */
449
    private function convertCacheAnnotationToCacheMetadata(
450
        Annotation\Cache $cacheAnnot,
451
        Mapping\ClassMetadata $metadata,
452
        $fieldName = null
453
    ) {
454
        $usage         = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage));
455
        $baseRegion    = strtolower(str_replace('\\', '_', $metadata->getRootClassName()));
456
        $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : '');
457
458
        return new Mapping\CacheMetadata($usage, $cacheAnnot->region ?: $defaultRegion);
459
    }
460
461
    /**
462
     * @param Annotation\Annotation[] $propertyAnnotations
463
     *
464
     * @return Mapping\Property
465
     *
466
     * @throws Mapping\MappingException
467
     */
468
    private function convertReflectionPropertyAnnotationsToProperty(
469
        ReflectionProperty $reflectionProperty,
470
        array $propertyAnnotations,
471
        Mapping\ClassMetadata $classMetadata
472
    ) {
473
        // Field can only be annotated with one of:
474
        // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded
475
        switch (true) {
476
            case isset($propertyAnnotations[Annotation\Column::class]):
477
                return $this->convertReflectionPropertyToFieldMetadata(
478
                    $reflectionProperty,
479
                    $propertyAnnotations,
480
                    $classMetadata
481
                );
482
            case isset($propertyAnnotations[Annotation\OneToOne::class]):
483
                return $this->convertReflectionPropertyToOneToOneAssociationMetadata(
484
                    $reflectionProperty,
485
                    $propertyAnnotations,
486
                    $classMetadata
487
                );
488
            case isset($propertyAnnotations[Annotation\ManyToOne::class]):
489
                return $this->convertReflectionPropertyToManyToOneAssociationMetadata(
490
                    $reflectionProperty,
491
                    $propertyAnnotations,
492
                    $classMetadata
493
                );
494
            case isset($propertyAnnotations[Annotation\OneToMany::class]):
495
                return $this->convertReflectionPropertyToOneToManyAssociationMetadata(
496
                    $reflectionProperty,
497
                    $propertyAnnotations,
498
                    $classMetadata
499
                );
500
            case isset($propertyAnnotations[Annotation\ManyToMany::class]):
501
                return $this->convertReflectionPropertyToManyToManyAssociationMetadata(
502
                    $reflectionProperty,
503
                    $propertyAnnotations,
504
                    $classMetadata
505
                );
506
            case isset($propertyAnnotations[Annotation\Embedded::class]):
507
                // @todo guilhermeblanco Implement later... =)
508
                break;
509
510
            default:
511
                return new Mapping\TransientMetadata($reflectionProperty->getName());
512
        }
513
    }
514
515
    /**
516
     * @param Annotation\Annotation[] $propertyAnnotations
517
     *
518
     * @return Mapping\FieldMetadata
519
     *
520
     * @throws Mapping\MappingException
521
     * @throws DBALException
522
     */
523
    private function convertReflectionPropertyToFieldMetadata(
524
        ReflectionProperty $reflectionProperty,
525
        array $propertyAnnotations,
526
        Mapping\ClassMetadata $classMetadata
527
    ) {
528
        $className    = $classMetadata->getClassName();
529
        $fieldName    = $reflectionProperty->getName();
530
        $columnAnnot  = $propertyAnnotations[Annotation\Column::class];
531
        $isVersioned  = isset($propertyAnnotations[Annotation\Version::class]);
532
        $isPrimaryKey = isset($propertyAnnotations[Annotation\Id::class]);
533
534
        if ($columnAnnot->type === null) {
0 ignored issues
show
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...
535
            throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName);
536
        }
537
538
        if ($isVersioned && $isPrimaryKey) {
539
            throw Mapping\MappingException::cannotVersionIdField($className, $fieldName);
540
        }
541
542
        $fieldType  = Type::getType($columnAnnot->type);
543
        $columnName = empty($columnAnnot->name)
0 ignored issues
show
Accessing name on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
544
            ? $this->namingStrategy->propertyToColumnName($fieldName, $className)
545
            : $columnAnnot->name;
546
547
        $fieldMetadata = new Mapping\FieldMetadata($fieldName);
548
549
        $fieldMetadata->setType($fieldType);
550
        $fieldMetadata->setVersioned($isVersioned);
551
        $fieldMetadata->setColumnName($columnName);
552
        $fieldMetadata->setScale($columnAnnot->scale);
0 ignored issues
show
Accessing scale on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
553
        $fieldMetadata->setPrecision($columnAnnot->precision);
0 ignored issues
show
Accessing precision on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
554
        $fieldMetadata->setNullable($columnAnnot->nullable);
0 ignored issues
show
Accessing nullable on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
555
        $fieldMetadata->setUnique($columnAnnot->unique);
0 ignored issues
show
Accessing unique on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
556
557
        // Check for Id
558
        if ($isPrimaryKey) {
559
            if ($fieldType->canRequireSQLConversion()) {
560
                throw Mapping\MappingException::sqlConversionNotAllowedForPrimaryKeyProperties(
561
                    $className,
562
                    $fieldName,
563
                    $fieldType->getName()
564
                );
565
            }
566
567
            $fieldMetadata->setPrimaryKey(true);
568
        }
569
570
        if (! empty($columnAnnot->columnDefinition)) {
0 ignored issues
show
Accessing columnDefinition on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
571
            $fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition);
572
        }
573
574
        if (! empty($columnAnnot->length)) {
0 ignored issues
show
Accessing length on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
575
            $fieldMetadata->setLength($columnAnnot->length);
576
        }
577
578
        // Assign default options
579
        $customOptions  = $columnAnnot->options ?? [];
0 ignored issues
show
Accessing options on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
580
        $defaultOptions = [];
581
582
        if ($isVersioned) {
583
            switch ($fieldMetadata->getTypeName()) {
584
                case 'integer':
585
                case 'bigint':
586
                case 'smallint':
587
                    $defaultOptions['default'] = 1;
588
                    break;
589
590
                case 'datetime':
591
                    $defaultOptions['default'] = 'CURRENT_TIMESTAMP';
592
                    break;
593
594
                default:
595
                    if (! isset($customOptions['default'])) {
596
                        throw Mapping\MappingException::unsupportedOptimisticLockingType($fieldMetadata->getType());
597
                    }
598
            }
599
        }
600
601
        $fieldMetadata->setOptions(array_merge($defaultOptions, $customOptions));
602
603
        return $fieldMetadata;
604
    }
605
606
    /**
607
     * @param Annotation\Annotation[] $propertyAnnotations
608
     *
609
     * @return Mapping\OneToOneAssociationMetadata
610
     */
611
    private function convertReflectionPropertyToOneToOneAssociationMetadata(
612
        ReflectionProperty $reflectionProperty,
613
        array $propertyAnnotations,
614
        Mapping\ClassMetadata $classMetadata
615
    ) {
616
        $className     = $classMetadata->getClassName();
617
        $fieldName     = $reflectionProperty->getName();
618
        $oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class];
619
620
        if ($oneToOneAnnot->targetEntity === null) {
0 ignored issues
show
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...
621
            throw Mapping\MappingException::missingTargetEntity($fieldName);
622
        }
623
624
        $assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName);
625
        $targetEntity  = $oneToOneAnnot->targetEntity;
626
627
        $assocMetadata->setSourceEntity($className);
628
        $assocMetadata->setTargetEntity($targetEntity);
629
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade));
0 ignored issues
show
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...
630
        $assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval);
0 ignored issues
show
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...
631
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch));
0 ignored issues
show
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...
632
633
        if (! empty($oneToOneAnnot->mappedBy)) {
0 ignored issues
show
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...
634
            $assocMetadata->setMappedBy($oneToOneAnnot->mappedBy);
635
        }
636
637
        if (! empty($oneToOneAnnot->inversedBy)) {
0 ignored issues
show
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...
638
            $assocMetadata->setInversedBy($oneToOneAnnot->inversedBy);
639
        }
640
641
        // Check for Id
642
        if (isset($propertyAnnotations[Annotation\Id::class])) {
643
            $assocMetadata->setPrimaryKey(true);
644
        }
645
646
        // Check for Cache
647
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
648
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
649
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
650
651
            $assocMetadata->setCache($cacheMetadata);
652
        }
653
654
        // Check for JoinColumn/JoinColumns annotations
655
        switch (true) {
656
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
657
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
658
                $joinColumn      = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
659
                    $reflectionProperty,
660
                    $joinColumnAnnot,
661
                    $classMetadata
662
                );
663
664
                $assocMetadata->addJoinColumn($joinColumn);
665
666
                break;
667
668
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
669
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
670
671
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
672
                    $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
673
                        $reflectionProperty,
674
                        $joinColumnAnnot,
675
                        $classMetadata
676
                    );
677
678
                    $assocMetadata->addJoinColumn($joinColumn);
679
                }
680
681
                break;
682
        }
683
684
        return $assocMetadata;
685
    }
686
687
    /**
688
     * @param Annotation\Annotation[] $propertyAnnotations
689
     *
690
     * @return Mapping\ManyToOneAssociationMetadata
691
     */
692
    private function convertReflectionPropertyToManyToOneAssociationMetadata(
693
        ReflectionProperty $reflectionProperty,
694
        array $propertyAnnotations,
695
        Mapping\ClassMetadata $classMetadata
696
    ) {
697
        $className      = $classMetadata->getClassName();
698
        $fieldName      = $reflectionProperty->getName();
699
        $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class];
700
701
        if ($manyToOneAnnot->targetEntity === null) {
0 ignored issues
show
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...
702
            throw Mapping\MappingException::missingTargetEntity($fieldName);
703
        }
704
705
        $assocMetadata = new Mapping\ManyToOneAssociationMetadata($fieldName);
706
        $targetEntity  = $manyToOneAnnot->targetEntity;
707
708
        $assocMetadata->setSourceEntity($className);
709
        $assocMetadata->setTargetEntity($targetEntity);
710
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade));
0 ignored issues
show
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...
711
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch));
0 ignored issues
show
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...
712
713
        if (! empty($manyToOneAnnot->inversedBy)) {
0 ignored issues
show
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...
714
            $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy);
715
        }
716
717
        // Check for Id
718
        if (isset($propertyAnnotations[Annotation\Id::class])) {
719
            $assocMetadata->setPrimaryKey(true);
720
        }
721
722
        // Check for Cache
723
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
724
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
725
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
726
727
            $assocMetadata->setCache($cacheMetadata);
728
        }
729
730
        // Check for JoinColumn/JoinColumns annotations
731
        switch (true) {
732
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
733
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
734
                $joinColumn      = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
735
                    $reflectionProperty,
736
                    $joinColumnAnnot,
737
                    $classMetadata
738
                );
739
740
                $assocMetadata->addJoinColumn($joinColumn);
741
742
                break;
743
744
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
745
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
746
747
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
748
                    $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
749
                        $reflectionProperty,
750
                        $joinColumnAnnot,
751
                        $classMetadata
752
                    );
753
754
                    $assocMetadata->addJoinColumn($joinColumn);
755
                }
756
757
                break;
758
        }
759
760
        return $assocMetadata;
761
    }
762
763
    /**
764
     * @param Annotation\Annotation[] $propertyAnnotations
765
     *
766
     * @return Mapping\OneToManyAssociationMetadata
767
     */
768
    private function convertReflectionPropertyToOneToManyAssociationMetadata(
769
        ReflectionProperty $reflectionProperty,
770
        array $propertyAnnotations,
771
        Mapping\ClassMetadata $classMetadata
772
    ) {
773
        $className      = $classMetadata->getClassName();
774
        $fieldName      = $reflectionProperty->getName();
775
        $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class];
776
777
        if ($oneToManyAnnot->targetEntity === null) {
0 ignored issues
show
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...
778
            throw Mapping\MappingException::missingTargetEntity($fieldName);
779
        }
780
781
        $assocMetadata = new Mapping\OneToManyAssociationMetadata($fieldName);
782
        $targetEntity  = $oneToManyAnnot->targetEntity;
783
784
        $assocMetadata->setSourceEntity($className);
785
        $assocMetadata->setTargetEntity($targetEntity);
786
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade));
0 ignored issues
show
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...
787
        $assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval);
0 ignored issues
show
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...
788
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch));
0 ignored issues
show
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...
789
790
        if (! empty($oneToManyAnnot->mappedBy)) {
0 ignored issues
show
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...
791
            $assocMetadata->setMappedBy($oneToManyAnnot->mappedBy);
792
        }
793
794
        if (! empty($oneToManyAnnot->indexBy)) {
0 ignored issues
show
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...
795
            $assocMetadata->setIndexedBy($oneToManyAnnot->indexBy);
796
        }
797
798
        // Check for OrderBy
799
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
800
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
801
802
            $assocMetadata->setOrderBy($orderByAnnot->value);
0 ignored issues
show
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...
803
        }
804
805
        // Check for Id
806
        if (isset($propertyAnnotations[Annotation\Id::class])) {
807
            $assocMetadata->setPrimaryKey(true);
808
        }
809
810
        // Check for Cache
811
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
812
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
813
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
814
815
            $assocMetadata->setCache($cacheMetadata);
816
        }
817
818
        return $assocMetadata;
819
    }
820
821
    /**
822
     * @param Annotation\Annotation[] $propertyAnnotations
823
     *
824
     * @return Mapping\ManyToManyAssociationMetadata
825
     */
826
    private function convertReflectionPropertyToManyToManyAssociationMetadata(
827
        ReflectionProperty $reflectionProperty,
828
        array $propertyAnnotations,
829
        Mapping\ClassMetadata $classMetadata
830
    ) {
831
        $className       = $classMetadata->getClassName();
832
        $fieldName       = $reflectionProperty->getName();
833
        $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class];
834
835
        if ($manyToManyAnnot->targetEntity === null) {
0 ignored issues
show
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...
836
            throw Mapping\MappingException::missingTargetEntity($fieldName);
837
        }
838
839
        $assocMetadata = new Mapping\ManyToManyAssociationMetadata($fieldName);
840
        $targetEntity  = $manyToManyAnnot->targetEntity;
841
842
        $assocMetadata->setSourceEntity($className);
843
        $assocMetadata->setTargetEntity($targetEntity);
844
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade));
0 ignored issues
show
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...
845
        $assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval);
0 ignored issues
show
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...
846
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch));
0 ignored issues
show
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...
847
848
        if (! empty($manyToManyAnnot->mappedBy)) {
0 ignored issues
show
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...
849
            $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy);
850
        }
851
852
        if (! empty($manyToManyAnnot->inversedBy)) {
0 ignored issues
show
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...
853
            $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy);
854
        }
855
856
        if (! empty($manyToManyAnnot->indexBy)) {
0 ignored issues
show
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...
857
            $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy);
858
        }
859
860
        // Check for JoinTable
861
        if (isset($propertyAnnotations[Annotation\JoinTable::class])) {
862
            $joinTableAnnot    = $propertyAnnotations[Annotation\JoinTable::class];
863
            $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata(
864
                $reflectionProperty,
865
                $joinTableAnnot,
866
                $classMetadata
867
            );
868
869
            $assocMetadata->setJoinTable($joinTableMetadata);
870
        }
871
872
        // Check for OrderBy
873
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
874
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
875
876
            $assocMetadata->setOrderBy($orderByAnnot->value);
0 ignored issues
show
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...
877
        }
878
879
        // Check for Id
880
        if (isset($propertyAnnotations[Annotation\Id::class])) {
881
            $assocMetadata->setPrimaryKey(true);
882
        }
883
884
        // Check for Cache
885
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
886
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
887
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
888
889
            $assocMetadata->setCache($cacheMetadata);
890
        }
891
892
        return $assocMetadata;
893
    }
894
895
    /**
896
     * Parse the given JoinTable as JoinTableMetadata
897
     *
898
     * @return Mapping\JoinTableMetadata
899
     */
900
    private function convertJoinTableAnnotationToJoinTableMetadata(
901
        ReflectionProperty $reflectionProperty,
902
        Annotation\JoinTable $joinTableAnnot,
903
        Mapping\ClassMetadata $classMetadata
904
    ) {
905
        $joinTable = new Mapping\JoinTableMetadata();
906
907
        if (! empty($joinTableAnnot->name)) {
908
            $joinTable->setName($joinTableAnnot->name);
909
        }
910
911
        if (! empty($joinTableAnnot->schema)) {
912
            $joinTable->setSchema($joinTableAnnot->schema);
913
        }
914
915
        foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) {
916
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
917
                $reflectionProperty,
918
                $joinColumnAnnot,
919
                $classMetadata
920
            );
921
922
            $joinTable->addJoinColumn($joinColumn);
923
        }
924
925
        foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) {
926
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
927
                $reflectionProperty,
928
                $joinColumnAnnot,
929
                $classMetadata
930
            );
931
932
            $joinTable->addInverseJoinColumn($joinColumn);
933
        }
934
935
        return $joinTable;
936
    }
937
938
    /**
939
     * Parse the given JoinColumn as JoinColumnMetadata
940
     *
941
     * @return Mapping\JoinColumnMetadata
942
     */
943
    private function convertJoinColumnAnnotationToJoinColumnMetadata(
944
        ReflectionProperty $reflectionProperty,
945
        Annotation\JoinColumn $joinColumnAnnot,
946
        Mapping\ClassMetadata $classMetadata
947
    ) {
948
        $fieldName            = $reflectionProperty->getName();
949
        $joinColumn           = new Mapping\JoinColumnMetadata();
950
        $columnName           = empty($joinColumnAnnot->name)
951
            ? $this->namingStrategy->propertyToColumnName($fieldName, $classMetadata->getClassName())
952
            : $joinColumnAnnot->name;
953
        $referencedColumnName = empty($joinColumnAnnot->referencedColumnName)
954
            ? $this->namingStrategy->referenceColumnName()
955
            : $joinColumnAnnot->referencedColumnName;
956
957
        $joinColumn->setColumnName($columnName);
958
        $joinColumn->setReferencedColumnName($referencedColumnName);
959
        $joinColumn->setNullable($joinColumnAnnot->nullable);
960
        $joinColumn->setUnique($joinColumnAnnot->unique);
961
962
        if (! empty($joinColumnAnnot->fieldName)) {
963
            $joinColumn->setAliasedName($joinColumnAnnot->fieldName);
964
        }
965
966
        if (! empty($joinColumnAnnot->columnDefinition)) {
967
            $joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition);
968
        }
969
970
        if ($joinColumnAnnot->onDelete) {
971
            $joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete));
972
        }
973
974
        return $joinColumn;
975
    }
976
977
    /**
978
     * Parses the given method.
979
     *
980
     * @return string[]
981
     */
982
    private function getMethodCallbacks(ReflectionMethod $method)
0 ignored issues
show
The method getMethodCallbacks() 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...
983
    {
984
        $annotations = $this->getMethodAnnotations($method);
985
        $events      = [
986
            Events::prePersist  => Annotation\PrePersist::class,
987
            Events::postPersist => Annotation\PostPersist::class,
988
            Events::preUpdate   => Annotation\PreUpdate::class,
989
            Events::postUpdate  => Annotation\PostUpdate::class,
990
            Events::preRemove   => Annotation\PreRemove::class,
991
            Events::postRemove  => Annotation\PostRemove::class,
992
            Events::postLoad    => Annotation\PostLoad::class,
993
            Events::preFlush    => Annotation\PreFlush::class,
994
        ];
995
996
        // Check for callbacks
997
        $callbacks = [];
998
999
        foreach ($events as $eventName => $annotationClassName) {
1000
            if (isset($annotations[$annotationClassName]) || $method->getName() === $eventName) {
1001
                $callbacks[] = $eventName;
1002
            }
1003
        }
1004
1005
        return $callbacks;
1006
    }
1007
1008
    /**
1009
     * Attempts to resolve the fetch mode.
1010
     *
1011
     * @param string $className The class name.
1012
     * @param string $fetchMode The fetch mode.
1013
     *
1014
     * @return int The fetch mode as defined in ClassMetadata.
1015
     *
1016
     * @throws Mapping\MappingException If the fetch mode is not valid.
1017
     */
1018
    private function getFetchMode($className, $fetchMode)
1019
    {
1020
        $fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode);
1021
1022
        if (! defined($fetchModeConstant)) {
1023
            throw Mapping\MappingException::invalidFetchMode($className, $fetchMode);
1024
        }
1025
1026
        return constant($fetchModeConstant);
1027
    }
1028
1029
    /**
1030
     * @param string   $className        The class name.
1031
     * @param string   $fieldName        The field name.
1032
     * @param string[] $originalCascades The original unprocessed field cascades.
1033
     *
1034
     * @return string[] The processed field cascades.
1035
     *
1036
     * @throws Mapping\MappingException If a cascade option is not valid.
1037
     */
1038
    private function getCascade(string $className, string $fieldName, array $originalCascades)
1039
    {
1040
        $cascadeTypes = ['remove', 'persist', 'refresh'];
1041
        $cascades     = array_map('strtolower', $originalCascades);
1042
1043
        if (in_array('all', $cascades, true)) {
1044
            $cascades = $cascadeTypes;
1045
        }
1046
1047
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
1048
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
1049
1050
            throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName);
1051
        }
1052
1053
        return $cascades;
1054
    }
1055
1056
    /**
1057
     * @return Annotation\Annotation[]
1058
     */
1059
    private function getClassAnnotations(ReflectionClass $reflectionClass)
1060
    {
1061
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
1062
1063
        foreach ($classAnnotations as $key => $annot) {
1064
            if (! is_numeric($key)) {
1065
                continue;
1066
            }
1067
1068
            $classAnnotations[get_class($annot)] = $annot;
1069
        }
1070
1071
        return $classAnnotations;
1072
    }
1073
1074
    /**
1075
     * @return Annotation\Annotation[]
1076
     */
1077
    private function getPropertyAnnotations(ReflectionProperty $reflectionProperty)
1078
    {
1079
        $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty);
1080
1081
        foreach ($propertyAnnotations as $key => $annot) {
1082
            if (! is_numeric($key)) {
1083
                continue;
1084
            }
1085
1086
            $propertyAnnotations[get_class($annot)] = $annot;
1087
        }
1088
1089
        return $propertyAnnotations;
1090
    }
1091
1092
    /**
1093
     * @return Annotation\Annotation[]
1094
     */
1095
    private function getMethodAnnotations(ReflectionMethod $reflectionMethod)
1096
    {
1097
        $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod);
1098
1099
        foreach ($methodAnnotations as $key => $annot) {
1100
            if (! is_numeric($key)) {
1101
                continue;
1102
            }
1103
1104
            $methodAnnotations[get_class($annot)] = $annot;
1105
        }
1106
1107
        return $methodAnnotations;
1108
    }
1109
}
1110