Failed Conditions
Push — master ( becf73...b9880b )
by Guilherme
09:53
created

convertReflectionPropertyToFieldMetadata()   C

Complexity

Conditions 15
Paths 116

Size

Total Lines 76
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 0
Metric Value
cc 15
eloc 45
nc 116
nop 3
dl 0
loc 76
ccs 0
cts 59
cp 0
crap 240
rs 5.7833
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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

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

110
            $cache      = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, /** @scrutinizer ignore-type */ $classMetadata);
Loading history...
111
112
            $classMetadata->setCache($cache);
113
        }
114
115
        // Evaluate annotations on properties/fields
116
        /** @var ReflectionProperty $reflectionProperty */
117
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
118
            if ($reflectionProperty->getDeclaringClass()->getClassName() !== $reflectionClass->getName()) {
0 ignored issues
show
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

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

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

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

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

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

126
                /** @scrutinizer ignore-type */ $classMetadata
Loading history...
127
            );
128
129
            $classMetadata->addDeclaredProperty($property);
130
        }
131
132
        return $classMetadata;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function getAllClassNames() : array
139
    {
140
        if ($this->classNames !== null) {
141
            return $this->classNames;
142
        }
143
144
        $classNames = array_filter(
145
            $this->locator->getAllClassNames(null),
146
            function ($className) {
147
                return ! $this->isTransient($className);
148
            }
149
        );
150
151
        $this->classNames = $classNames;
152
153
        return $classNames;
154
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159
    public function isTransient($className) : bool
160
    {
161
        $reflectionClass  = new ReflectionClass($className);
162
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
163
164
        foreach ($classAnnotations as $annotation) {
165
            if (isset(self::$entityAnnotationClasses[get_class($annotation)])) {
166
                return false;
167
            }
168
        }
169
170
        return true;
171
    }
172
173
    /**
174
     * @param Annotation\Annotation[] $classAnnotations
175
     *
176
     * @return Mapping\ClassMetadata|Mapping\ComponentMetadata
177
     *
178
     * @throws DBALException
179
     * @throws ReflectionException
180
     */
181
    private function convertClassAnnotationsToClassMetadata(
182
        array $classAnnotations,
183
        ReflectionClass $reflectionClass,
184
        ?Mapping\ComponentMetadata $parent,
185
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
186
    ) : Mapping\ComponentMetadata {
187
        switch (true) {
188
            case isset($classAnnotations[Annotation\Entity::class]):
189
                return $this->convertClassAnnotationsToEntityClassMetadata(
190
                    $classAnnotations,
191
                    $reflectionClass,
192
                    $parent,
193
                    $metadataBuildingContext
194
                );
195
196
                break;
0 ignored issues
show
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...
197
198
            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
199
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
200
                    $classAnnotations,
201
                    $reflectionClass,
202
                    $parent,
203
                    $metadataBuildingContext
204
                );
205
            case isset($classAnnotations[Annotation\Embeddable::class]):
206
                return $this->convertClassAnnotationsToEntityClassMetadata(
207
                    $classAnnotations,
208
                    $reflectionClass,
209
                    $parent,
210
                    $metadataBuildingContext
211
                );
212
            default:
213
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
214
        }
215
    }
216
217
    /**
218
     * @param Annotation\Annotation[] $classAnnotations
219
     *
220
     * @return Mapping\ClassMetadata
221
     *
222
     * @throws DBALException
223
     * @throws ReflectionException
224
     */
225
    private function convertClassAnnotationsToEntityClassMetadata(
226
        array $classAnnotations,
227
        ReflectionClass $reflectionClass,
228
        ?Mapping\ComponentMetadata $parent,
229
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
230
    ) {
231
        /** @var Annotation\Entity $entityAnnot */
232
        $entityAnnot   = $classAnnotations[Annotation\Entity::class];
233
        $classMetadata = new Mapping\ClassMetadata($reflectionClass->getName(), $parent, $metadataBuildingContext);
234
235
        if ($entityAnnot->repositoryClass !== null) {
236
            $classMetadata->setCustomRepositoryClassName($entityAnnot->repositoryClass);
237
        }
238
239
        if ($entityAnnot->readOnly) {
240
            $classMetadata->asReadOnly();
241
        }
242
243
        // Evaluate @Table annotation
244
        if (isset($classAnnotations[Annotation\Table::class])) {
245
            /** @var Annotation\Table $tableAnnot */
246
            $tableAnnot = $classAnnotations[Annotation\Table::class];
247
            $table      = $this->convertTableAnnotationToTableMetadata($tableAnnot);
248
249
            $classMetadata->setTable($table);
250
        }
251
252
        // Evaluate @ChangeTrackingPolicy annotation
253
        if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) {
254
            /** @var Annotation\ChangeTrackingPolicy $changeTrackingAnnot */
255
            $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class];
256
257
            $classMetadata->setChangeTrackingPolicy(
258
                constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value))
259
            );
260
        }
261
262
        // Evaluate @EntityListeners annotation
263
        if (isset($classAnnotations[Annotation\EntityListeners::class])) {
264
            /** @var Annotation\EntityListeners $entityListenersAnnot */
265
            $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class];
266
267
            foreach ($entityListenersAnnot->value as $listenerClassName) {
268
                if (! class_exists($listenerClassName)) {
269
                    throw Mapping\MappingException::entityListenerClassNotFound(
270
                        $listenerClassName,
271
                        $reflectionClass->getName()
272
                    );
273
                }
274
275
                $listenerClass = new ReflectionClass($listenerClassName);
276
277
                /** @var ReflectionMethod $method */
278
                foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
279
                    foreach ($this->getMethodCallbacks($method) as $callback) {
280
                        $classMetadata->addEntityListener($callback, $listenerClassName, $method->getName());
281
                    }
282
                }
283
            }
284
        }
285
286
        // Evaluate @HasLifecycleCallbacks annotation
287
        if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) {
288
            /** @var ReflectionMethod $method */
289
            foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
290
                foreach ($this->getMethodCallbacks($method) as $callback) {
291
                    $classMetadata->addLifecycleCallback($method->getName(), $callback);
292
                }
293
            }
294
        }
295
296
        // Evaluate @InheritanceType annotation
297
        if (isset($classAnnotations[Annotation\InheritanceType::class])) {
298
            /** @var Annotation\InheritanceType $inheritanceTypeAnnot */
299
            $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class];
300
301
            $classMetadata->setInheritanceType(
302
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value))
303
            );
304
305
            if ($classMetadata->inheritanceType !== Mapping\InheritanceType::NONE) {
306
                $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata();
307
308
                // Evaluate @DiscriminatorColumn annotation
309
                if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) {
310
                    /** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnot */
311
                    $discriminatorColumnAnnot = $classAnnotations[Annotation\DiscriminatorColumn::class];
312
313
                    $discriminatorColumn->setColumnName($discriminatorColumnAnnot->name);
314
315
                    if (! empty($discriminatorColumnAnnot->columnDefinition)) {
316
                        $discriminatorColumn->setColumnDefinition($discriminatorColumnAnnot->columnDefinition);
317
                    }
318
319
                    if (! empty($discriminatorColumnAnnot->type)) {
320
                        $discriminatorColumn->setType(Type::getType($discriminatorColumnAnnot->type));
321
                    }
322
323
                    if (! empty($discriminatorColumnAnnot->length)) {
324
                        $discriminatorColumn->setLength($discriminatorColumnAnnot->length);
325
                    }
326
                }
327
328
                if (empty($discriminatorColumn->getColumnName())) {
329
                    throw Mapping\MappingException::nameIsMandatoryForDiscriminatorColumns($reflectionClass->getName());
330
                }
331
332
                $classMetadata->setDiscriminatorColumn($discriminatorColumn);
333
334
                // Evaluate @DiscriminatorMap annotation
335
                if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) {
336
                    /** @var Annotation\DiscriminatorMap $discriminatorMapAnnotation */
337
                    $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class];
338
                    $discriminatorMap           = $discriminatorMapAnnotation->value;
339
340
                    $classMetadata->setDiscriminatorMap($discriminatorMap);
341
                }
342
            }
343
        }
344
345
        return $classMetadata;
346
    }
347
348
    /**
349
     * @param Annotation\Annotation[] $classAnnotations
350
     *
351
     * @return Mapping\MappedSuperClassMetadata
352
     */
353
    private function convertClassAnnotationsToMappedSuperClassMetadata(
354
        array $classAnnotations,
355
        ReflectionClass $reflectionClass,
356
        ?Mapping\ComponentMetadata $parent,
357
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
0 ignored issues
show
Unused Code introduced by
The parameter $metadataBuildingContext is not used and could be removed. ( Ignorable by Annotation )

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

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

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
358
    ) {
359
        /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */
360
        $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class];
361
        $classMetadata         = new Mapping\MappedSuperClassMetadata($reflectionClass->getName(), $parent);
0 ignored issues
show
Bug introduced by
$parent of type Doctrine\ORM\Mapping\ComponentMetadata|null 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

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