Passed
Pull Request — master (#7113)
by Michael
14:58 queued 03:10
created

convertReflectionPropertyToManyToOneAssociationMetadata()   C

Complexity

Conditions 8
Paths 25

Size

Total Lines 69
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

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

113
            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...
114
                continue;
115
            }
116
117
            $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty);
118
            $property            = $this->convertReflectionPropertyAnnotationsToProperty(
119
                $reflectionProperty,
120
                $propertyAnnotations,
121
                $classMetadata
122
            );
123
124
            $classMetadata->addDeclaredProperty($property);
125
        }
126
127
        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...
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function getAllClassNames()
134
    {
135
        if ($this->classNames !== null) {
136
            return $this->classNames;
137
        }
138
139
        $classNames = array_filter(
140
            $this->locator->getAllClassNames(null),
141
            function ($className) {
142
                return ! $this->isTransient($className);
143
            }
144
        );
145
146
        $this->classNames = $classNames;
147
148
        return $classNames;
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function isTransient($className)
155
    {
156
        $reflectionClass  = new \ReflectionClass($className);
157
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
158
159
        foreach ($classAnnotations as $annotation) {
160
            if (isset(self::$entityAnnotationClasses[get_class($annotation)])) {
161
                return false;
162
            }
163
        }
164
165
        return true;
166
    }
167
168
    /**
169
     * @param Annotation\Annotation[] $classAnnotations
170
     *
171
     * @return Mapping\ClassMetadata|Mapping\ComponentMetadata
172
     *
173
     * @throws Mapping\MappingException
174
     */
175
    private function convertClassAnnotationsToClassMetadata(
176
        array $classAnnotations,
177
        \ReflectionClass $reflectionClass,
178
        Mapping\ClassMetadata $parent
179
    ) {
180
        switch (true) {
181
            case isset($classAnnotations[Annotation\Entity::class]):
182
                return $this->convertClassAnnotationsToEntityClassMetadata(
183
                    $classAnnotations,
184
                    $reflectionClass,
185
                    $parent
186
                );
187
188
                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...
189
190
            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
191
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
192
                    $classAnnotations,
193
                    $reflectionClass,
194
                    $parent
195
                );
196
197
            case isset($classAnnotations[Annotation\Embeddable::class]):
198
                return $this->convertClassAnnotationsToEntityClassMetadata(
199
                    $classAnnotations,
200
                    $reflectionClass,
201
                    $parent
202
                );
203
204
            default:
205
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
206
        }
207
    }
208
209
    /**
210
     * @param Annotation\Annotation[] $classAnnotations
211
     *
212
     * @return Mapping\ClassMetadata
213
     *
214
     * @throws Mapping\MappingException
215
     * @throws \UnexpectedValueException
216
     */
217
    private function convertClassAnnotationsToEntityClassMetadata(
218
        array $classAnnotations,
219
        \ReflectionClass $reflectionClass,
220
        Mapping\ClassMetadata $parent
221
    ) {
222
        /** @var Annotation\Entity $entityAnnot */
223
        $entityAnnot   = $classAnnotations[Annotation\Entity::class];
224
        $classMetadata = new Mapping\ClassMetadata($reflectionClass->getName(), $parent);
0 ignored issues
show
Bug introduced by
$parent of type Doctrine\ORM\Mapping\ClassMetadata is incompatible with the type Doctrine\ORM\Mapping\ClassMetadataBuildingContext expected by parameter $metadataBuildingContext of Doctrine\ORM\Mapping\ClassMetadata::__construct(). ( Ignorable by Annotation )

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

224
        $classMetadata = new Mapping\ClassMetadata($reflectionClass->getName(), /** @scrutinizer ignore-type */ $parent);
Loading history...
225
226
        if ($entityAnnot->repositoryClass !== null) {
227
            $classMetadata->setCustomRepositoryClassName($entityAnnot->repositoryClass);
228
        }
229
230
        if ($entityAnnot->readOnly) {
231
            $classMetadata->asReadOnly();
232
        }
233
234
        // Evaluate @Table annotation
235
        if (isset($classAnnotations[Annotation\Table::class])) {
236
            /** @var Annotation\Table $tableAnnot */
237
            $tableAnnot = $classAnnotations[Annotation\Table::class];
238
            $table      = $this->convertTableAnnotationToTableMetadata($tableAnnot);
239
240
            $classMetadata->setTable($table);
241
        }
242
243
        // Evaluate @ChangeTrackingPolicy annotation
244
        if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) {
245
            /** @var Annotation\ChangeTrackingPolicy $changeTrackingAnnot */
246
            $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class];
247
248
            $classMetadata->setChangeTrackingPolicy(
249
                constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value))
250
            );
251
        }
252
253
        // Evaluate @EntityListeners annotation
254
        if (isset($classAnnotations[Annotation\EntityListeners::class])) {
255
            /** @var Annotation\EntityListeners $entityListenersAnnot */
256
            $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class];
257
258
            foreach ($entityListenersAnnot->value as $listenerClassName) {
259
                if (! class_exists($listenerClassName)) {
260
                    throw Mapping\MappingException::entityListenerClassNotFound(
261
                        $listenerClassName,
262
                        $reflectionClass->getName()
263
                    );
264
                }
265
266
                $listenerClass = new \ReflectionClass($listenerClassName);
267
268
                /* @var $method \ReflectionMethod */
269
                foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
270
                    foreach ($this->getMethodCallbacks($method) as $callback) {
271
                        $classMetadata->addEntityListener($callback, $listenerClassName, $method->getName());
272
                    }
273
                }
274
            }
275
        }
276
277
        // Evaluate @HasLifecycleCallbacks annotation
278
        if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) {
279
            /* @var $method \ReflectionMethod */
280
            foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
281
                foreach ($this->getMethodCallbacks($method) as $callback) {
282
                    $classMetadata->addLifecycleCallback($method->getName(), $callback);
283
                }
284
            }
285
        }
286
287
        // Evaluate @InheritanceType annotation
288
        if (isset($classAnnotations[Annotation\InheritanceType::class])) {
289
            /** @var Annotation\InheritanceType $inheritanceTypeAnnot */
290
            $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class];
291
292
            $classMetadata->setInheritanceType(
293
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value))
294
            );
295
296
            if ($classMetadata->inheritanceType !== Mapping\InheritanceType::NONE) {
297
                $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata();
298
299
                // Evaluate @DiscriminatorColumn annotation
300
                if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) {
301
                    /** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnot */
302
                    $discriminatorColumnAnnot = $classAnnotations[Annotation\DiscriminatorColumn::class];
303
304
                    $discriminatorColumn->setColumnName($discriminatorColumnAnnot->name);
305
306
                    if (! empty($discriminatorColumnAnnot->columnDefinition)) {
307
                        $discriminatorColumn->setColumnDefinition($discriminatorColumnAnnot->columnDefinition);
308
                    }
309
310
                    if (! empty($discriminatorColumnAnnot->type)) {
311
                        $discriminatorColumn->setType(Type::getType($discriminatorColumnAnnot->type));
312
                    }
313
314
                    if (! empty($discriminatorColumnAnnot->length)) {
315
                        $discriminatorColumn->setLength($discriminatorColumnAnnot->length);
316
                    }
317
                }
318
319
                if (empty($discriminatorColumn->getColumnName())) {
320
                    throw Mapping\MappingException::nameIsMandatoryForDiscriminatorColumns($reflectionClass->getName());
321
                }
322
323
                $classMetadata->setDiscriminatorColumn($discriminatorColumn);
324
325
                // Evaluate @DiscriminatorMap annotation
326
                if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) {
327
                    /** @var Annotation\DiscriminatorMap $discriminatorMapAnnotation */
328
                    $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class];
329
                    $discriminatorMap           = $discriminatorMapAnnotation->value;
330
331
                    $classMetadata->setDiscriminatorMap($discriminatorMap);
332
                }
333
            }
334
        }
335
336
        return $classMetadata;
337
    }
338
339
    /**
340
     * @param Annotation\Annotation[] $classAnnotations
341
     *
342
     * @return Mapping\MappedSuperClassMetadata
343
     */
344
    private function convertClassAnnotationsToMappedSuperClassMetadata(
345
        array $classAnnotations,
346
        \ReflectionClass $reflectionClass,
347
        Mapping\ClassMetadata $parent
348
    ) {
349
        /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */
350
        $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class];
351
        $classMetadata         = new Mapping\MappedSuperClassMetadata($reflectionClass->getName(), $parent);
0 ignored issues
show
Bug introduced by
$parent of type Doctrine\ORM\Mapping\ClassMetadata is incompatible with the type Doctrine\ORM\Mapping\ClassMetadataBuildingContext expected by parameter $metadataBuildingContext of Doctrine\ORM\Mapping\Map...Metadata::__construct(). ( Ignorable by Annotation )

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

351
        $classMetadata         = new Mapping\MappedSuperClassMetadata($reflectionClass->getName(), /** @scrutinizer ignore-type */ $parent);
Loading history...
352
353
        if ($mappedSuperclassAnnot->repositoryClass !== null) {
354
            $classMetadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass);
355
        }
356
357
        return $classMetadata;
358
    }
359
360
    /**
361
     * Parse the given Table as TableMetadata
362
     *
363
     * @return Mapping\TableMetadata
364
     */
365
    private function convertTableAnnotationToTableMetadata(Annotation\Table $tableAnnot)
366
    {
367
        $table = new Mapping\TableMetadata();
368
369
        if (! empty($tableAnnot->name)) {
370
            $table->setName($tableAnnot->name);
371
        }
372
373
        if (! empty($tableAnnot->schema)) {
374
            $table->setSchema($tableAnnot->schema);
375
        }
376
377
        foreach ($tableAnnot->options as $optionName => $optionValue) {
378
            $table->addOption($optionName, $optionValue);
379
        }
380
381
        foreach ($tableAnnot->indexes as $indexAnnot) {
382
            $table->addIndex([
383
                'name'    => $indexAnnot->name,
384
                'columns' => $indexAnnot->columns,
385
                'unique'  => $indexAnnot->unique,
386
                'options' => $indexAnnot->options,
387
                'flags'   => $indexAnnot->flags,
388
            ]);
389
        }
390
391
        foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
392
            $table->addUniqueConstraint([
393
                'name'    => $uniqueConstraintAnnot->name,
394
                'columns' => $uniqueConstraintAnnot->columns,
395
                'options' => $uniqueConstraintAnnot->options,
396
                'flags'   => $uniqueConstraintAnnot->flags,
397
            ]);
398
        }
399
400
        return $table;
401
    }
402
403
    /**
404
     * Parse the given Cache as CacheMetadata
405
     *
406
     * @param string|null $fieldName
407
     *
408
     * @return Mapping\CacheMetadata
409
     */
410
    private function convertCacheAnnotationToCacheMetadata(
411
        Annotation\Cache $cacheAnnot,
412
        Mapping\ClassMetadata $metadata,
413
        $fieldName = null
414
    ) {
415
        $usage         = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage));
416
        $baseRegion    = strtolower(str_replace('\\', '_', $metadata->getRootClassName()));
417
        $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : '');
418
419
        return new Mapping\CacheMetadata($usage, $cacheAnnot->region ?: $defaultRegion);
420
    }
421
422
    /**
423
     * @param Annotation\Annotation[] $propertyAnnotations
424
     *
425
     * @return Mapping\Property
426
     *
427
     * @throws Mapping\MappingException
428
     */
429
    private function convertReflectionPropertyAnnotationsToProperty(
430
        \ReflectionProperty $reflectionProperty,
431
        array $propertyAnnotations,
432
        Mapping\ClassMetadata $classMetadata
433
    ) {
434
        // Field can only be annotated with one of:
435
        // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

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