Passed
Push — master ( 89e39b...ec508a )
by Marco
11:02
created

NewAnnotationDriver::convertSqlResultSetMapping()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 19
nc 6
nop 1
dl 0
loc 33
ccs 0
cts 27
cp 0
crap 20
rs 8.5806
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A NewAnnotationDriver::convertCacheAnnotationToCacheMetadata() 0 10 3
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
    /** @var Factory\NamingStrategy */
54
    protected $namingStrategy;
55
56
    /**
57
     * Cache for AnnotationDriver#getAllClassNames().
58
     *
59
     * @var string[]|null
60
     */
61
    private $classNames;
62
63
    /**
64
     * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading docblock annotations.
65
     *
66
     * @param AnnotationReader       $reader         The AnnotationReader to use, duck-typed.
67
     * @param FileLocator            $locator        A FileLocator or one/multiple paths where mapping documents can be found.
68
     * @param Factory\NamingStrategy $namingStrategy The NamingStrategy to use.
69
     */
70
    public function __construct(AnnotationReader $reader, FileLocator $locator, Factory\NamingStrategy $namingStrategy)
71
    {
72
        $this->reader         = $reader;
73
        $this->locator        = $locator;
74
        $this->namingStrategy = $namingStrategy;
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     *
80
     * @return Mapping\ClassMetadata
81
     *
82
     * @throws Mapping\MappingException
83
     */
84
    public function loadMetadataForClass(
85
        string $className,
86
        Mapping\ClassMetadata $metadata,
87
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
88
    ) {
89
        // IMPORTANT: We're handling $metadata as "parent" metadata here, while building the $className ClassMetadata.
90
        $reflectionClass = new \ReflectionClass($className);
91
92
        // Evaluate annotations on class metadata
93
        $classAnnotations = $this->getClassAnnotations($reflectionClass);
94
        $classMetadata    = $this->convertClassAnnotationsToClassMetadata(
95
            $classAnnotations,
96
            $reflectionClass,
97
            $metadata
98
        );
99
100
        // Evaluate @Cache annotation
101
        if (isset($classAnnotations[Annotation\Cache::class])) {
102
            $cacheAnnot = $classAnnotations[Annotation\Cache::class];
103
            $cache      = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata);
104
105
            $classMetadata->setCache($cache);
106
        }
107
108
        // Evaluate annotations on properties/fields
109
        /* @var \ReflectionProperty $reflectionProperty */
110
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
111
            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

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

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

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