Failed Conditions
Push — master ( d60694...8601d9 )
by Guilherme
09:39
created

Doctrine/ORM/Mapping/Driver/AnnotationDriver.php (3 issues)

1
<?php /** @noinspection ALL */
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping\Driver;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Annotations\Reader;
9
use Doctrine\DBAL\Types\Type;
0 ignored issues
show
Type Doctrine\DBAL\Types\Type is not used in this file.
Loading history...
10
use Doctrine\ORM\Annotation;
11
use Doctrine\ORM\Cache\Exception\CacheException;
12
use Doctrine\ORM\Events;
13
use Doctrine\ORM\Mapping;
14
use Doctrine\ORM\Mapping\Builder;
15
use FilesystemIterator;
16
use RecursiveDirectoryIterator;
17
use RecursiveIteratorIterator;
18
use RecursiveRegexIterator;
19
use ReflectionClass;
20
use ReflectionException;
21
use ReflectionMethod;
22
use ReflectionProperty;
23
use RegexIterator;
24
use RuntimeException;
25
use UnexpectedValueException;
26
use function array_diff;
27
use function array_intersect;
28
use function array_map;
29
use function array_merge;
30
use function array_unique;
31
use function class_exists;
32
use function constant;
33
use function count;
34
use function defined;
35
use function get_class;
36
use function get_declared_classes;
37
use function in_array;
38
use function is_dir;
39
use function is_numeric;
40
use function preg_match;
41
use function preg_quote;
42
use function realpath;
43
use function sprintf;
44
use function str_replace;
45
use function strpos;
46
use function strtoupper;
0 ignored issues
show
Type strtoupper is not used in this file.
Loading history...
47
use function var_export;
0 ignored issues
show
Type var_export is not used in this file.
Loading history...
48
49
/**
50
 * The AnnotationDriver reads the mapping metadata from docblock annotations.
51
 */
52
class AnnotationDriver implements MappingDriver
53
{
54
    /** @var int[] */
55
    protected $entityAnnotationClasses = [
56
        Annotation\Entity::class           => 1,
57
        Annotation\MappedSuperclass::class => 2,
58
    ];
59
60
    /**
61
     * The AnnotationReader.
62
     *
63
     * @var AnnotationReader
64
     */
65
    protected $reader;
66
67
    /**
68
     * The paths where to look for mapping files.
69
     *
70
     * @var string[]
71
     */
72
    protected $paths = [];
73
74
    /**
75
     * The paths excluded from path where to look for mapping files.
76
     *
77
     * @var string[]
78
     */
79
    protected $excludePaths = [];
80
81
    /**
82
     * The file extension of mapping documents.
83
     *
84
     * @var string
85
     */
86
    protected $fileExtension = '.php';
87
88
    /**
89
     * Cache for AnnotationDriver#getAllClassNames().
90
     *
91
     * @var string[]|null
92
     */
93
    protected $classNames;
94
95
    /**
96
     * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading
97
     * docblock annotations.
98
     *
99
     * @param Reader               $reader The AnnotationReader to use, duck-typed.
100
     * @param string|string[]|null $paths  One or multiple paths where mapping classes can be found.
101
     */
102 2297
    public function __construct(Reader $reader, $paths = null)
103
    {
104 2297
        $this->reader = $reader;
105
106 2297
        if ($paths) {
107 2211
            $this->addPaths((array) $paths);
108
        }
109 2297
    }
110
111
    /**
112
     * Appends lookup paths to metadata driver.
113
     *
114
     * @param string[] $paths
115
     */
116 2215
    public function addPaths(array $paths)
117
    {
118 2215
        $this->paths = array_unique(array_merge($this->paths, $paths));
119 2215
    }
120
121
    /**
122
     * Retrieves the defined metadata lookup paths.
123
     *
124
     * @return string[]
125
     */
126
    public function getPaths()
127
    {
128
        return $this->paths;
129
    }
130
131
    /**
132
     * Append exclude lookup paths to metadata driver.
133
     *
134
     * @param string[] $paths
135
     */
136
    public function addExcludePaths(array $paths)
137
    {
138
        $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths));
139
    }
140
141
    /**
142
     * Retrieve the defined metadata lookup exclude paths.
143
     *
144
     * @return string[]
145
     */
146
    public function getExcludePaths()
147
    {
148
        return $this->excludePaths;
149
    }
150
151
    /**
152
     * Retrieve the current annotation reader
153
     *
154
     * @return Reader
155
     */
156 1
    public function getReader()
157
    {
158 1
        return $this->reader;
159
    }
160
161
    /**
162
     * Gets the file extension used to look for mapping files under.
163
     *
164
     * @return string
165
     */
166
    public function getFileExtension()
167
    {
168
        return $this->fileExtension;
169
    }
170
171
    /**
172
     * Sets the file extension used to look for mapping files under.
173
     *
174
     * @param string $fileExtension The file extension to set.
175
     */
176
    public function setFileExtension($fileExtension)
177
    {
178
        $this->fileExtension = $fileExtension;
179
    }
180
181
    /**
182
     * Returns whether the class with the specified name is transient. Only non-transient
183
     * classes, that is entities and mapped superclasses, should have their metadata loaded.
184
     *
185
     * A class is non-transient if it is annotated with an annotation
186
     * from the {@see AnnotationDriver::entityAnnotationClasses}.
187
     *
188
     * @param string $className
189
     *
190
     * @throws ReflectionException
191
     */
192 193
    public function isTransient($className) : bool
193
    {
194 193
        $classAnnotations = $this->reader->getClassAnnotations(new ReflectionClass($className));
195
196 193
        foreach ($classAnnotations as $annotation) {
197 188
            if (isset($this->entityAnnotationClasses[get_class($annotation)])) {
198 188
                return false;
199
            }
200
        }
201
202 12
        return true;
203
    }
204
205
    /**
206
     * {@inheritdoc}
207
     *
208
     * @throws ReflectionException
209
     */
210 60
    public function getAllClassNames() : array
211
    {
212 60
        if ($this->classNames !== null) {
213 45
            return $this->classNames;
214
        }
215
216 60
        if (! $this->paths) {
217
            throw Mapping\MappingException::pathRequired();
218
        }
219
220 60
        $classes       = [];
221 60
        $includedFiles = [];
222
223 60
        foreach ($this->paths as $path) {
224 60
            if (! is_dir($path)) {
225
                throw Mapping\MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path);
226
            }
227
228 60
            $iterator = new RegexIterator(
229 60
                new RecursiveIteratorIterator(
230 60
                    new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
231 60
                    RecursiveIteratorIterator::LEAVES_ONLY
232
                ),
233 60
                '/^.+' . preg_quote($this->fileExtension) . '$/i',
234 60
                RecursiveRegexIterator::GET_MATCH
235
            );
236
237 60
            foreach ($iterator as $file) {
238 60
                $sourceFile = $file[0];
239
240 60
                if (! preg_match('(^phar:)i', $sourceFile)) {
241 60
                    $sourceFile = realpath($sourceFile);
242
                }
243
244 60
                foreach ($this->excludePaths as $excludePath) {
245
                    $exclude = str_replace('\\', '/', realpath($excludePath));
246
                    $current = str_replace('\\', '/', $sourceFile);
247
248
                    if (strpos($current, $exclude) !== false) {
249
                        continue 2;
250
                    }
251
                }
252
253 60
                require_once $sourceFile;
254
255 60
                $includedFiles[] = $sourceFile;
256
            }
257
        }
258
259 60
        $declared = get_declared_classes();
260
261 60
        foreach ($declared as $className) {
262 60
            $reflectionClass = new ReflectionClass($className);
263 60
            $sourceFile      = $reflectionClass->getFileName();
264
265 60
            if (in_array($sourceFile, $includedFiles, true) && ! $this->isTransient($className)) {
266 60
                $classes[] = $className;
267
            }
268
        }
269
270 60
        $this->classNames = $classes;
271
272 60
        return $classes;
273
    }
274
275
    /**
276
     * {@inheritDoc}
277
     *
278
     * @throws CacheException
279
     * @throws Mapping\MappingException
280
     * @throws ReflectionException
281
     * @throws RuntimeException
282
     * @throws UnexpectedValueException
283
     */
284 379
    public function loadMetadataForClass(
285
        string $className,
286
        ?Mapping\ComponentMetadata $parent,
287
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
288
    ) : Mapping\ComponentMetadata {
289 379
        $reflectionClass  = new ReflectionClass($className);
290 379
        $metadata         = new Mapping\ClassMetadata($className, $parent, $metadataBuildingContext);
291 379
        $classAnnotations = $this->getClassAnnotations($reflectionClass);
292 379
        $classMetadata    = $this->convertClassAnnotationsToClassMetadata(
293 379
            $classAnnotations,
294 379
            $reflectionClass,
295 379
            $metadata,
296 379
            $metadataBuildingContext
297
        );
298
299
        // Evaluate @Cache annotation
300 373
        if (isset($classAnnotations[Annotation\Cache::class])) {
301 18
            $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
302
303
            $cacheBuilder
304 18
                ->withComponentMetadata($metadata)
305 18
                ->withCacheAnnotation($classAnnotations[Annotation\Cache::class]);
306
307 18
            $metadata->setCache($cacheBuilder->build());
308
        }
309
310
        // Evaluate annotations on properties/fields
311
        /** @var ReflectionProperty $reflProperty */
312 373
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
313 373
            if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) {
314 74
                continue;
315
            }
316
317 373
            $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty);
318 372
            $property            = $this->convertPropertyAnnotationsToProperty(
319 372
                $propertyAnnotations,
320 372
                $reflectionProperty,
321 372
                $classMetadata,
322 372
                $metadataBuildingContext
323
            );
324
325 372
            if ($classMetadata->isMappedSuperclass &&
326 372
                $property instanceof Mapping\ToManyAssociationMetadata &&
327 372
                ! $property->isOwningSide()) {
328 1
                throw Mapping\MappingException::illegalToManyAssociationOnMappedSuperclass(
329 1
                    $classMetadata->getClassName(),
330 1
                    $property->getName()
331
                );
332
            }
333
334 371
            $metadata->addProperty($property);
335
        }
336
337 370
        $this->attachPropertyOverrides($classAnnotations, $reflectionClass, $metadata, $metadataBuildingContext);
338
339 370
        return $classMetadata;
340
    }
341
342
    /**
343
     * @param Annotation\Annotation[] $classAnnotations
344
     *
345
     * @throws Mapping\MappingException
346
     * @throws UnexpectedValueException
347
     * @throws ReflectionException
348
     */
349 379
    private function convertClassAnnotationsToClassMetadata(
350
        array $classAnnotations,
351
        ReflectionClass $reflectionClass,
352
        Mapping\ClassMetadata $metadata,
353
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
354
    ) : Mapping\ClassMetadata {
355
        switch (true) {
356 379
            case isset($classAnnotations[Annotation\Entity::class]):
357 372
                return $this->convertClassAnnotationsToEntityClassMetadata(
358 372
                    $classAnnotations,
359 372
                    $reflectionClass,
360 372
                    $metadata,
361 372
                    $metadataBuildingContext
362
                );
363
364
                break;
365
366 29
            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
367 23
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
368 23
                    $classAnnotations,
369 23
                    $reflectionClass,
370 23
                    $metadata
371
                );
372 6
            case isset($classAnnotations[Annotation\Embeddable::class]):
373
                return $this->convertClassAnnotationsToEmbeddableClassMetadata(
374
                    $classAnnotations,
375
                    $reflectionClass,
376
                    $metadata
377
                );
378
            default:
379 6
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
380
        }
381
    }
382
383
    /**
384
     * @param Annotation\Annotation[] $classAnnotations
385
     *
386
     * @return Mapping\ClassMetadata
387
     *
388
     * @throws Mapping\MappingException
389
     * @throws ReflectionException
390
     * @throws UnexpectedValueException
391
     */
392 372
    private function convertClassAnnotationsToEntityClassMetadata(
393
        array $classAnnotations,
394
        ReflectionClass $reflectionClass,
395
        Mapping\ClassMetadata $metadata,
396
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
397
    ) {
398
        /** @var Annotation\Entity $entityAnnot */
399 372
        $entityAnnot = $classAnnotations[Annotation\Entity::class];
400
401 372
        if ($entityAnnot->repositoryClass !== null) {
402 3
            $metadata->setCustomRepositoryClassName($entityAnnot->repositoryClass);
403
        }
404
405 372
        if ($entityAnnot->readOnly) {
406 1
            $metadata->asReadOnly();
407
        }
408
409 372
        $metadata->isMappedSuperclass = false;
410 372
        $metadata->isEmbeddedClass    = false;
411
412
        // Process table information
413 372
        $parent = $metadata->getParent();
414
415 372
        if ($parent && $parent->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) {
416
            // Handle the case where a middle mapped super class inherits from a single table inheritance tree.
417
            do {
418 29
                if (! $parent->isMappedSuperclass) {
419 29
                    $metadata->setTable($parent->table);
420
421 29
                    break;
422
                }
423
424 4
                $parent = $parent->getParent();
425 29
            } while ($parent !== null);
426
        } else {
427 372
            $tableBuilder = new Builder\TableMetadataBuilder($metadataBuildingContext);
428
429
            $tableBuilder
430 372
                ->withEntityClassMetadata($metadata)
431 372
                ->withTableAnnotation($classAnnotations[Annotation\Table::class] ?? null);
432
433 372
            $metadata->setTable($tableBuilder->build());
434
        }
435
436
        // Evaluate @ChangeTrackingPolicy annotation
437 372
        if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) {
438 6
            $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class];
439
440 6
            $metadata->setChangeTrackingPolicy(
441 6
                constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value))
442
            );
443
        }
444
445
        // Evaluate @InheritanceType annotation
446 372
        if (isset($classAnnotations[Annotation\InheritanceType::class])) {
447 80
            $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class];
448
449 80
            $metadata->setInheritanceType(
450 80
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value))
451
            );
452
453 80
            if ($metadata->inheritanceType !== Mapping\InheritanceType::NONE) {
454 80
                $discriminatorColumnBuilder = new Builder\DiscriminatorColumnMetadataBuilder($metadataBuildingContext);
455
456
                $discriminatorColumnBuilder
457 80
                    ->withComponentMetadata($metadata)
458 80
                    ->withDiscriminatorColumnAnnotation($classAnnotations[Annotation\DiscriminatorColumn::class] ?? null);
459
460 80
                $metadata->setDiscriminatorColumn($discriminatorColumnBuilder->build());
461
462
                // Evaluate DiscriminatorMap annotation
463 80
                if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) {
464 77
                    $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class];
465 77
                    $discriminatorMap           = $discriminatorMapAnnotation->value;
466
467 77
                    $metadata->setDiscriminatorMap($discriminatorMap);
468
                }
469
            }
470
        }
471
472 372
        $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata);
473 372
        $this->attachEntityListeners($classAnnotations, $metadata);
474
475 372
        return $metadata;
476
    }
477
478
    /**
479
     * @param Annotation\Annotation[] $classAnnotations
480
     *
481
     * @throws Mapping\MappingException
482
     * @throws ReflectionException
483
     */
484 23
    private function convertClassAnnotationsToMappedSuperClassMetadata(
485
        array $classAnnotations,
486
        ReflectionClass $reflectionClass,
487
        Mapping\ClassMetadata $metadata
488
    ) : Mapping\ClassMetadata {
489
        /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */
490 23
        $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class];
491
492 23
        if ($mappedSuperclassAnnot->repositoryClass !== null) {
493 2
            $metadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass);
494
        }
495
496 23
        $metadata->isMappedSuperclass = true;
497 23
        $metadata->isEmbeddedClass    = false;
498
499 23
        $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata);
500 23
        $this->attachEntityListeners($classAnnotations, $metadata);
501
502 23
        return $metadata;
503
    }
504
505
    /**
506
     * @param Annotation\Annotation[] $classAnnotations
507
     */
508
    private function convertClassAnnotationsToEmbeddableClassMetadata(
509
        array $classAnnotations,
510
        ReflectionClass $reflectionClass,
511
        Mapping\ClassMetadata $metadata
512
    ) : Mapping\ClassMetadata {
513
        $metadata->isMappedSuperclass = false;
514
        $metadata->isEmbeddedClass    = true;
515
516
        return $metadata;
517
    }
518
519
    /**
520
     * @param Annotation\Annotation[] $propertyAnnotations
521
     *
522
     * @todo guilhermeblanco Remove nullable typehint once embeddables are back
523
     */
524 372
    private function convertPropertyAnnotationsToProperty(
525
        array $propertyAnnotations,
526
        ReflectionProperty $reflectionProperty,
527
        Mapping\ClassMetadata $metadata,
528
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
529
    ) : ?Mapping\Property {
530
        switch (true) {
531 372
            case isset($propertyAnnotations[Annotation\Column::class]):
532 367
                return $this->convertReflectionPropertyToFieldMetadata(
533 367
                    $reflectionProperty,
534 367
                    $propertyAnnotations,
535 367
                    $metadata,
536 367
                    $metadataBuildingContext
537
                );
538 254
            case isset($propertyAnnotations[Annotation\OneToOne::class]):
539 111
                return $this->convertReflectionPropertyToOneToOneAssociationMetadata(
540 111
                    $reflectionProperty,
541 111
                    $propertyAnnotations,
542 111
                    $metadata,
543 111
                    $metadataBuildingContext
544
                );
545 202
            case isset($propertyAnnotations[Annotation\ManyToOne::class]):
546 141
                return $this->convertReflectionPropertyToManyToOneAssociationMetadata(
547 141
                    $reflectionProperty,
548 141
                    $propertyAnnotations,
549 141
                    $metadata,
550 141
                    $metadataBuildingContext
551
                );
552 163
            case isset($propertyAnnotations[Annotation\OneToMany::class]):
553 109
                return $this->convertReflectionPropertyToOneToManyAssociationMetadata(
554 109
                    $reflectionProperty,
555 109
                    $propertyAnnotations,
556 109
                    $metadata,
557 109
                    $metadataBuildingContext
558
                );
559 104
            case isset($propertyAnnotations[Annotation\ManyToMany::class]):
560 89
                return $this->convertReflectionPropertyToManyToManyAssociationMetadata(
561 89
                    $reflectionProperty,
562 89
                    $propertyAnnotations,
563 89
                    $metadata,
564 89
                    $metadataBuildingContext
565
                );
566 29
            case isset($propertyAnnotations[Annotation\Embedded::class]):
567
                return null;
568
            default:
569 29
                $transientBuilder = new Builder\TransientMetadataBuilder($metadataBuildingContext);
570
571
                $transientBuilder
572 29
                    ->withComponentMetadata($metadata)
573 29
                    ->withFieldName($reflectionProperty->getName());
574
575 29
                return $transientBuilder->build();
576
        }
577
    }
578
579
    /**
580
     * @param Annotation\Annotation[] $propertyAnnotations
581
     *
582
     * @throws Mapping\MappingException
583
     */
584 367
    private function convertReflectionPropertyToFieldMetadata(
585
        ReflectionProperty $reflectionProperty,
586
        array $propertyAnnotations,
587
        Mapping\ClassMetadata $metadata,
588
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
589
    ) : Mapping\FieldMetadata {
590 367
        $fieldBuilder  = new Builder\FieldMetadataBuilder($metadataBuildingContext);
591
        $fieldMetadata = $fieldBuilder
592 367
            ->withComponentMetadata($metadata)
593 367
            ->withFieldName($reflectionProperty->getName())
594 367
            ->withColumnAnnotation($propertyAnnotations[Annotation\Column::class])
595 367
            ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null)
596 367
            ->withVersionAnnotation($propertyAnnotations[Annotation\Version::class] ?? null)
597 367
            ->withGeneratedValueAnnotation($propertyAnnotations[Annotation\GeneratedValue::class] ?? null)
598 367
            ->withSequenceGeneratorAnnotation($propertyAnnotations[Annotation\SequenceGenerator::class] ?? null)
599 367
            ->withCustomIdGeneratorAnnotation($propertyAnnotations[Annotation\CustomIdGenerator::class] ?? null)
600 367
            ->build();
601
602
        // Prevent column duplication
603 367
        if ($metadata->checkPropertyDuplication($fieldMetadata->getColumnName())) {
604
            throw Mapping\MappingException::duplicateColumnName(
605
                $metadata->getClassName(),
606
                $fieldMetadata->getColumnName()
607
            );
608
        }
609
610
//        // Check for GeneratedValue strategy
611
//        if (isset($propertyAnnotations[Annotation\GeneratedValue::class])) {
612
//            $generatedValueAnnot = $propertyAnnotations[Annotation\GeneratedValue::class];
613
//            $strategy            = strtoupper($generatedValueAnnot->strategy);
614
//            $idGeneratorType     = constant(sprintf('%s::%s', Mapping\GeneratorType::class, $strategy));
615
//
616
//            if ($idGeneratorType !== Mapping\GeneratorType::NONE) {
617
//                $idGeneratorDefinition = [];
618
//
619
//                // Check for CustomGenerator/SequenceGenerator/TableGenerator definition
620
//                switch (true) {
621
//                    case isset($propertyAnnotations[Annotation\SequenceGenerator::class]):
622
//                        $seqGeneratorAnnot = $propertyAnnotations[Annotation\SequenceGenerator::class];
623
//
624
//                        $idGeneratorDefinition = [
625
//                            'sequenceName' => $seqGeneratorAnnot->sequenceName,
626
//                            'allocationSize' => $seqGeneratorAnnot->allocationSize,
627
//                        ];
628
//
629
//                        break;
630
//
631
//                    case isset($propertyAnnotations[Annotation\CustomIdGenerator::class]):
632
//                        $customGeneratorAnnot = $propertyAnnotations[Annotation\CustomIdGenerator::class];
633
//
634
//                        $idGeneratorDefinition = [
635
//                            'class' => $customGeneratorAnnot->class,
636
//                            'arguments' => $customGeneratorAnnot->arguments,
637
//                        ];
638
//
639
//                        if (! isset($idGeneratorDefinition['class'])) {
640
//                            throw new Mapping\MappingException(
641
//                                sprintf('Cannot instantiate custom generator, no class has been defined')
642
//                            );
643
//                        }
644
//
645
//                        if (! class_exists($idGeneratorDefinition['class'])) {
646
//                            throw new Mapping\MappingException(
647
//                                sprintf('Cannot instantiate custom generator : %s', var_export($idGeneratorDefinition, true))
648
//                            );
649
//                        }
650
//
651
//                        break;
652
//
653
//                    /** @todo If it is not supported, why does this exist? */
654
//                    case isset($propertyAnnotations['Doctrine\ORM\Mapping\TableGenerator']):
655
//                        throw Mapping\MappingException::tableIdGeneratorNotImplemented($metadata->getClassName());
656
//                }
657
//
658
//                $fieldMetadata->setValueGenerator(
659
//                    new Mapping\ValueGeneratorMetadata($idGeneratorType, $idGeneratorDefinition)
660
//                );
661
//            }
662
//        }
663
664 367
        return $fieldMetadata;
665
    }
666
667
    /**
668
     * @param Annotation\Annotation[] $propertyAnnotations
669
     */
670 111
    private function convertReflectionPropertyToOneToOneAssociationMetadata(
671
        ReflectionProperty $reflectionProperty,
672
        array $propertyAnnotations,
673
        Mapping\ClassMetadata $metadata,
674
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
675
    ) : Mapping\OneToOneAssociationMetadata {
676 111
        $className     = $metadata->getClassName();
677 111
        $fieldName     = $reflectionProperty->getName();
678 111
        $oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class];
679 111
        $assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName);
680 111
        $targetEntity  = $oneToOneAnnot->targetEntity;
681
682 111
        $assocMetadata->setTargetEntity($targetEntity);
683 111
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade));
684 111
        $assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval);
685 111
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch));
686
687 111
        if (! empty($oneToOneAnnot->mappedBy)) {
688 40
            $assocMetadata->setMappedBy($oneToOneAnnot->mappedBy);
689 40
            $assocMetadata->setOwningSide(false);
690
        }
691
692 111
        if (! empty($oneToOneAnnot->inversedBy)) {
693 51
            $assocMetadata->setInversedBy($oneToOneAnnot->inversedBy);
694 51
            $assocMetadata->setOwningSide(true);
695
        }
696
697
        // Check for Id
698 111
        if (isset($propertyAnnotations[Annotation\Id::class])) {
699 12
            $assocMetadata->setPrimaryKey(true);
700
        }
701
702
        // Check for Cache
703 111
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
704 4
            $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
705
706
            $cacheBuilder
707 4
                ->withComponentMetadata($metadata)
708 4
                ->withFieldName($fieldName)
709 4
                ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class]);
710
711 4
            $assocMetadata->setCache($cacheBuilder->build());
712
        }
713
714
        // Check for owning side to consider join column
715 111
        if (! $assocMetadata->isOwningSide()) {
716 40
            return $assocMetadata;
717
        }
718
719
        // Check for JoinColumn/JoinColumns annotations
720 105
        $joinColumnBuilder = new Builder\JoinColumnMetadataBuilder($metadataBuildingContext);
721
722
        $joinColumnBuilder
723 105
            ->withComponentMetadata($metadata)
724 105
            ->withFieldName($fieldName);
725
726
        switch (true) {
727 105
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
728 79
                $joinColumnBuilder->withJoinColumnAnnotation($propertyAnnotations[Annotation\JoinColumn::class]);
729
730 79
                $assocMetadata->addJoinColumn($joinColumnBuilder->build());
731 79
                break;
732
733 29
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
734 3
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
735
736 3
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
737 3
                    $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnot);
738
739 3
                    $assocMetadata->addJoinColumn($joinColumnBuilder->build());
740
                }
741
742 3
                break;
743
744
            default:
745 26
                $assocMetadata->addJoinColumn($joinColumnBuilder->build());
746 26
                break;
747
        }
748
749 105
        return $assocMetadata;
750
    }
751
752
    /**
753
     * @param Annotation\Annotation[] $propertyAnnotations
754
     */
755 141
    private function convertReflectionPropertyToManyToOneAssociationMetadata(
756
        ReflectionProperty $reflectionProperty,
757
        array $propertyAnnotations,
758
        Mapping\ClassMetadata $metadata,
759
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
760
    ) : Mapping\ManyToOneAssociationMetadata {
761
        // ManyToOne must be owning side by design
762 141
        $className      = $metadata->getClassName();
763 141
        $fieldName      = $reflectionProperty->getName();
764 141
        $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class];
765 141
        $assocMetadata  = new Mapping\ManyToOneAssociationMetadata($fieldName);
766 141
        $targetEntity   = $manyToOneAnnot->targetEntity;
767
768 141
        $assocMetadata->setTargetEntity($targetEntity);
769 141
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade));
770 141
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch));
771
772 141
        if (! empty($manyToOneAnnot->inversedBy)) {
773 94
            $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy);
774
        }
775
776
        // Check for Id
777 141
        if (isset($propertyAnnotations[Annotation\Id::class])) {
778 34
            $assocMetadata->setPrimaryKey(true);
779
        }
780
781
        // Check for Cache
782 141
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
783 12
            $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
784
785
            $cacheBuilder
786 12
                ->withComponentMetadata($metadata)
787 12
                ->withFieldName($fieldName)
788 12
                ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class]);
789
790 12
            $assocMetadata->setCache($cacheBuilder->build());
791
        }
792
793
        // Check for JoinColumn/JoinColumns annotations
794 141
        $joinColumnBuilder = new Builder\JoinColumnMetadataBuilder($metadataBuildingContext);
795
796
        $joinColumnBuilder
797 141
            ->withComponentMetadata($metadata)
798 141
            ->withFieldName($fieldName);
799
800
        switch (true) {
801 141
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
802 81
                $joinColumnBuilder->withJoinColumnAnnotation($propertyAnnotations[Annotation\JoinColumn::class]);
803
804 81
                $assocMetadata->addJoinColumn($joinColumnBuilder->build());
805 81
                break;
806
807 69
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
808 16
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
809
810 16
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
811 16
                    $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnot);
812
813 16
                    $assocMetadata->addJoinColumn($joinColumnBuilder->build());
814
                }
815
816 16
                break;
817
818
            default:
819 56
                $assocMetadata->addJoinColumn($joinColumnBuilder->build());
820 56
                break;
821
        }
822
823 141
        return $assocMetadata;
824
    }
825
826
    /**
827
     * @param Annotation\Annotation[] $propertyAnnotations
828
     *
829
     * @throws Mapping\MappingException
830
     */
831 109
    private function convertReflectionPropertyToOneToManyAssociationMetadata(
832
        ReflectionProperty $reflectionProperty,
833
        array $propertyAnnotations,
834
        Mapping\ClassMetadata $metadata,
835
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
836
    ) : Mapping\OneToManyAssociationMetadata {
837 109
        $className      = $metadata->getClassName();
838 109
        $fieldName      = $reflectionProperty->getName();
839 109
        $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class];
840 109
        $assocMetadata  = new Mapping\OneToManyAssociationMetadata($fieldName);
841 109
        $targetEntity   = $oneToManyAnnot->targetEntity;
842
843 109
        $assocMetadata->setTargetEntity($targetEntity);
844 109
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade));
845 109
        $assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval);
846 109
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch));
847 109
        $assocMetadata->setOwningSide(false);
848 109
        $assocMetadata->setMappedBy($oneToManyAnnot->mappedBy);
849
850 109
        if (! empty($oneToManyAnnot->indexBy)) {
851 8
            $assocMetadata->setIndexedBy($oneToManyAnnot->indexBy);
852
        }
853
854
        // Check for OrderBy
855 109
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
856 14
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
857
858 14
            $assocMetadata->setOrderBy($orderByAnnot->value);
859
        }
860
861
        // Check for Id
862 109
        if (isset($propertyAnnotations[Annotation\Id::class])) {
863
            throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName);
864
        }
865
866
        // Check for Cache
867 109
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
868 9
            $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
869
870
            $cacheBuilder
871 9
                ->withComponentMetadata($metadata)
872 9
                ->withFieldName($fieldName)
873 9
                ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class]);
874
875 9
            $assocMetadata->setCache($cacheBuilder->build());
876
        }
877
878 109
        return $assocMetadata;
879
    }
880
881
    /**
882
     * @param Annotation\Annotation[] $propertyAnnotations
883
     *
884
     * @throws Mapping\MappingException
885
     */
886 89
    private function convertReflectionPropertyToManyToManyAssociationMetadata(
887
        ReflectionProperty $reflectionProperty,
888
        array $propertyAnnotations,
889
        Mapping\ClassMetadata $metadata,
890
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
891
    ) : Mapping\ManyToManyAssociationMetadata {
892 89
        $className       = $metadata->getClassName();
893 89
        $fieldName       = $reflectionProperty->getName();
894 89
        $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class];
895 89
        $assocMetadata   = new Mapping\ManyToManyAssociationMetadata($fieldName);
896 89
        $targetEntity    = $manyToManyAnnot->targetEntity;
897
898 89
        $assocMetadata->setTargetEntity($targetEntity);
899 89
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade));
900 89
        $assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval);
901 89
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch));
902
903 89
        if (! empty($manyToManyAnnot->mappedBy)) {
904 36
            $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy);
905 36
            $assocMetadata->setOwningSide(false);
906
        }
907
908 89
        if (! empty($manyToManyAnnot->inversedBy)) {
909 45
            $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy);
910
        }
911
912 89
        if (! empty($manyToManyAnnot->indexBy)) {
913 3
            $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy);
914
        }
915
916
        // Check for OrderBy
917 89
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
918 3
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
919
920 3
            $assocMetadata->setOrderBy($orderByAnnot->value);
921
        }
922
923
        // Check for Id
924 89
        if (isset($propertyAnnotations[Annotation\Id::class])) {
925
            throw Mapping\MappingException::illegalToManyIdentifierAssociation($className, $fieldName);
926
        }
927
928
        // Check for Cache
929 89
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
930 2
            $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext);
931
932
            $cacheBuilder
933 2
                ->withComponentMetadata($metadata)
934 2
                ->withFieldName($fieldName)
935 2
                ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class]);
936
937 2
            $assocMetadata->setCache($cacheBuilder->build());
938
        }
939
940
        // Check for owning side to consider join column
941 89
        if (! $assocMetadata->isOwningSide()) {
942 36
            return $assocMetadata;
943
        }
944
945 79
        $joinTableBuilder = new Builder\JoinTableMetadataBuilder($metadataBuildingContext);
946
947
        $joinTableBuilder
948 79
            ->withComponentMetadata($metadata)
949 79
            ->withTargetEntity($targetEntity)
950 79
            ->withFieldName($fieldName);
951
952
        // Check for JoinTable
953 79
        if (isset($propertyAnnotations[Annotation\JoinTable::class])) {
954 71
            $joinTableBuilder->withJoinTableAnnotation($propertyAnnotations[Annotation\JoinTable::class]);
955
        }
956
957 79
        $assocMetadata->setJoinTable($joinTableBuilder->build());
958
959 79
        return $assocMetadata;
960
    }
961
962
    /**
963
     * @param Annotation\Annotation[] $classAnnotations
964
     */
965 373
    private function attachLifecycleCallbacks(
966
        array $classAnnotations,
967
        ReflectionClass $reflectionClass,
968
        Mapping\ClassMetadata $metadata
969
    ) : void {
970
        // Evaluate @HasLifecycleCallbacks annotation
971 373
        if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) {
972
            $eventMap = [
973 14
                Events::prePersist  => Annotation\PrePersist::class,
974 14
                Events::postPersist => Annotation\PostPersist::class,
975 14
                Events::preUpdate   => Annotation\PreUpdate::class,
976 14
                Events::postUpdate  => Annotation\PostUpdate::class,
977 14
                Events::preRemove   => Annotation\PreRemove::class,
978 14
                Events::postRemove  => Annotation\PostRemove::class,
979 14
                Events::postLoad    => Annotation\PostLoad::class,
980 14
                Events::preFlush    => Annotation\PreFlush::class,
981
            ];
982
983
            /** @var ReflectionMethod $reflectionMethod */
984 14
            foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
985 13
                $annotations = $this->getMethodAnnotations($reflectionMethod);
986
987 13
                foreach ($eventMap as $eventName => $annotationClassName) {
988 13
                    if (isset($annotations[$annotationClassName])) {
989 12
                        $metadata->addLifecycleCallback($eventName, $reflectionMethod->getName());
990
                    }
991
                }
992
            }
993
        }
994 373
    }
995
996
    /**
997
     * @param Annotation\Annotation[] $classAnnotations
998
     *
999
     * @throws ReflectionException
1000
     * @throws Mapping\MappingException
1001
     */
1002 373
    private function attachEntityListeners(
1003
        array $classAnnotations,
1004
        Mapping\ClassMetadata $metadata
1005
    ) : void {
1006
        // Evaluate @EntityListeners annotation
1007 373
        if (isset($classAnnotations[Annotation\EntityListeners::class])) {
1008
            /** @var Annotation\EntityListeners $entityListenersAnnot */
1009 8
            $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class];
1010
            $eventMap             = [
1011 8
                Events::prePersist  => Annotation\PrePersist::class,
1012 8
                Events::postPersist => Annotation\PostPersist::class,
1013 8
                Events::preUpdate   => Annotation\PreUpdate::class,
1014 8
                Events::postUpdate  => Annotation\PostUpdate::class,
1015 8
                Events::preRemove   => Annotation\PreRemove::class,
1016 8
                Events::postRemove  => Annotation\PostRemove::class,
1017 8
                Events::postLoad    => Annotation\PostLoad::class,
1018 8
                Events::preFlush    => Annotation\PreFlush::class,
1019
            ];
1020
1021 8
            foreach ($entityListenersAnnot->value as $listenerClassName) {
1022 8
                if (! class_exists($listenerClassName)) {
1023
                    throw Mapping\MappingException::entityListenerClassNotFound(
1024
                        $listenerClassName,
1025
                        $metadata->getClassName()
1026
                    );
1027
                }
1028
1029 8
                $listenerClass = new ReflectionClass($listenerClassName);
1030
1031
                /** @var ReflectionMethod $reflectionMethod */
1032 8
                foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
1033 8
                    $annotations = $this->getMethodAnnotations($reflectionMethod);
1034
1035 8
                    foreach ($eventMap as $eventName => $annotationClassName) {
1036 8
                        if (isset($annotations[$annotationClassName])) {
1037 6
                            $metadata->addEntityListener($eventName, $listenerClassName, $reflectionMethod->getName());
1038
                        }
1039
                    }
1040
                }
1041
            }
1042
        }
1043 373
    }
1044
1045
    /**
1046
     * @param Annotation\Annotation[] $classAnnotations
1047
     *
1048
     * @throws Mapping\MappingException
1049
     */
1050 370
    private function attachPropertyOverrides(
1051
        array $classAnnotations,
1052
        ReflectionClass $reflectionClass,
1053
        Mapping\ClassMetadata $metadata,
1054
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
1055
    ) : void {
1056
        // Evaluate AssociationOverrides annotation
1057 370
        if (isset($classAnnotations[Annotation\AssociationOverrides::class])) {
1058 5
            $associationOverridesAnnot = $classAnnotations[Annotation\AssociationOverrides::class];
1059
1060 5
            foreach ($associationOverridesAnnot->value as $associationOverride) {
1061 5
                $fieldName = $associationOverride->name;
1062 5
                $property  = $metadata->getProperty($fieldName);
1063
1064 5
                if (! $property) {
1065
                    throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName);
1066
                }
1067
1068 5
                $existingClass = get_class($property);
1069 5
                $override      = new $existingClass($fieldName);
1070
1071 5
                $override->setTargetEntity($property->getTargetEntity());
1072
1073
                // Check for JoinColumn/JoinColumns annotations
1074 5
                if ($associationOverride->joinColumns) {
1075 3
                    $joinColumnBuilder = new Builder\JoinColumnMetadataBuilder($metadataBuildingContext);
1076
1077
                    $joinColumnBuilder
1078 3
                        ->withComponentMetadata($metadata)
1079 3
                        ->withFieldName($fieldName);
1080
1081 3
                    $joinColumns = [];
1082
1083 3
                    foreach ($associationOverride->joinColumns as $joinColumnAnnotation) {
1084 3
                        $joinColumnBuilder->withJoinColumnAnnotation($joinColumnAnnotation);
1085
1086 3
                        $override->addJoinColumn($joinColumnBuilder->build());
1087
                    }
1088
                }
1089
1090
                // Check for JoinTable annotations
1091 5
                if ($associationOverride->joinTable) {
1092 2
                    $joinTableBuilder = new Builder\JoinTableMetadataBuilder($metadataBuildingContext);
1093
1094
                    $joinTableBuilder
1095 2
                        ->withComponentMetadata($metadata)
1096 2
                        ->withFieldName($fieldName)
1097 2
                        ->withTargetEntity($property->getTargetEntity())
1098 2
                        ->withJoinTableAnnotation($associationOverride->joinTable);
1099
1100 2
                    $override->setJoinTable($joinTableBuilder->build());
1101
                }
1102
1103
                // Check for inversedBy
1104 5
                if ($associationOverride->inversedBy) {
1105 1
                    $override->setInversedBy($associationOverride->inversedBy);
1106
                }
1107
1108
                // Check for fetch
1109 5
                if ($associationOverride->fetch) {
1110 1
                    $override->setFetchMode(
1111 1
                        constant(Mapping\FetchMode::class . '::' . $associationOverride->fetch)
1112
                    );
1113
                }
1114
1115 5
                $metadata->setPropertyOverride($override);
1116
            }
1117
        }
1118
1119
        // Evaluate AttributeOverrides annotation
1120 370
        if (isset($classAnnotations[Annotation\AttributeOverrides::class])) {
1121 3
            $attributeOverridesAnnot = $classAnnotations[Annotation\AttributeOverrides::class];
1122 3
            $fieldBuilder            = new Builder\FieldMetadataBuilder($metadataBuildingContext);
1123
1124
            $fieldBuilder
1125 3
                ->withComponentMetadata($metadata)
1126 3
                ->withIdAnnotation(null)
1127 3
                ->withVersionAnnotation(null);
1128
1129 3
            foreach ($attributeOverridesAnnot->value as $attributeOverrideAnnotation) {
1130
                $fieldBuilder
1131 3
                    ->withFieldName($attributeOverrideAnnotation->name)
1132 3
                    ->withColumnAnnotation($attributeOverrideAnnotation->column);
1133
1134 3
                $metadata->setPropertyOverride($fieldBuilder->build());
1135
            }
1136
        }
1137 370
    }
1138
1139
    /**
1140
     * Attempts to resolve the cascade modes.
1141
     *
1142
     * @param string   $className        The class name.
1143
     * @param string   $fieldName        The field name.
1144
     * @param string[] $originalCascades The original unprocessed field cascades.
1145
     *
1146
     * @return string[] The processed field cascades.
1147
     *
1148
     * @throws Mapping\MappingException If a cascade option is not valid.
1149
     */
1150 251
    private function getCascade(string $className, string $fieldName, array $originalCascades) : array
1151
    {
1152 251
        $cascadeTypes = ['remove', 'persist', 'refresh'];
1153 251
        $cascades     = array_map('strtolower', $originalCascades);
1154
1155 251
        if (in_array('all', $cascades, true)) {
1156 23
            $cascades = $cascadeTypes;
1157
        }
1158
1159 251
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
1160
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
1161
1162
            throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName);
1163
        }
1164
1165 251
        return $cascades;
1166
    }
1167
1168
    /**
1169
     * Attempts to resolve the fetch mode.
1170
     *
1171
     * @param string $className The class name.
1172
     * @param string $fetchMode The fetch mode.
1173
     *
1174
     * @return string The fetch mode as defined in ClassMetadata.
1175
     *
1176
     * @throws Mapping\MappingException If the fetch mode is not valid.
1177
     */
1178 251
    private function getFetchMode($className, $fetchMode) : string
1179
    {
1180 251
        $fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode);
1181
1182 251
        if (! defined($fetchModeConstant)) {
1183
            throw Mapping\MappingException::invalidFetchMode($className, $fetchMode);
1184
        }
1185
1186 251
        return constant($fetchModeConstant);
1187
    }
1188
1189
    /**
1190
     * @return Annotation\Annotation[]
1191
     */
1192 379
    private function getClassAnnotations(ReflectionClass $reflectionClass) : array
1193
    {
1194 379
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
1195
1196 379
        foreach ($classAnnotations as $key => $annot) {
1197 373
            if (! is_numeric($key)) {
1198
                continue;
1199
            }
1200
1201 373
            $classAnnotations[get_class($annot)] = $annot;
1202
        }
1203
1204 379
        return $classAnnotations;
1205
    }
1206
1207
    /**
1208
     * @return Annotation\Annotation[]
1209
     */
1210 373
    private function getPropertyAnnotations(ReflectionProperty $reflectionProperty) : array
1211
    {
1212 373
        $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty);
1213
1214 372
        foreach ($propertyAnnotations as $key => $annot) {
1215 372
            if (! is_numeric($key)) {
1216
                continue;
1217
            }
1218
1219 372
            $propertyAnnotations[get_class($annot)] = $annot;
1220
        }
1221
1222 372
        return $propertyAnnotations;
1223
    }
1224
1225
    /**
1226
     * @return Annotation\Annotation[]
1227
     */
1228 21
    private function getMethodAnnotations(ReflectionMethod $reflectionMethod) : array
1229
    {
1230 21
        $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod);
1231
1232 21
        foreach ($methodAnnotations as $key => $annot) {
1233 18
            if (! is_numeric($key)) {
1234
                continue;
1235
            }
1236
1237 18
            $methodAnnotations[get_class($annot)] = $annot;
1238
        }
1239
1240 21
        return $methodAnnotations;
1241
    }
1242
}
1243