vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php line 46

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\ORM\Mapping\Driver;
  4. use Doctrine\Common\Annotations\AnnotationReader;
  5. use Doctrine\ORM\Events;
  6. use Doctrine\ORM\Mapping;
  7. use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
  8. use Doctrine\ORM\Mapping\ClassMetadataInfo;
  9. use Doctrine\ORM\Mapping\MappingException;
  10. use Doctrine\Persistence\Mapping\ClassMetadata;
  11. use Doctrine\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver;
  12. use ReflectionClass;
  13. use ReflectionMethod;
  14. use ReflectionProperty;
  15. use UnexpectedValueException;
  16. use function assert;
  17. use function class_exists;
  18. use function constant;
  19. use function count;
  20. use function defined;
  21. use function get_class;
  22. use function is_array;
  23. use function is_numeric;
  24. /**
  25.  * The AnnotationDriver reads the mapping metadata from docblock annotations.
  26.  */
  27. class AnnotationDriver extends AbstractAnnotationDriver
  28. {
  29.     /**
  30.      * @var int[]
  31.      * @psalm-var array<class-string, int>
  32.      */
  33.     protected $entityAnnotationClasses = [
  34.         Mapping\Entity::class => 1,
  35.         Mapping\MappedSuperclass::class => 2,
  36.     ];
  37.     /**
  38.      * {@inheritDoc}
  39.      */
  40.     public function loadMetadataForClass($classNameClassMetadata $metadata)
  41.     {
  42.         assert($metadata instanceof Mapping\ClassMetadata);
  43.         $class $metadata->getReflectionClass()
  44.             // this happens when running annotation driver in combination with
  45.             // static reflection services. This is not the nicest fix
  46.             ?? new ReflectionClass($metadata->name);
  47.         $classAnnotations $this->reader->getClassAnnotations($class);
  48.         foreach ($classAnnotations as $key => $annot) {
  49.             if (! is_numeric($key)) {
  50.                 continue;
  51.             }
  52.             $classAnnotations[get_class($annot)] = $annot;
  53.         }
  54.         // Evaluate Entity annotation
  55.         if (isset($classAnnotations[Mapping\Entity::class])) {
  56.             $entityAnnot $classAnnotations[Mapping\Entity::class];
  57.             assert($entityAnnot instanceof Mapping\Entity);
  58.             if ($entityAnnot->repositoryClass !== null) {
  59.                 $metadata->setCustomRepositoryClass($entityAnnot->repositoryClass);
  60.             }
  61.             if ($entityAnnot->readOnly) {
  62.                 $metadata->markReadOnly();
  63.             }
  64.         } elseif (isset($classAnnotations[Mapping\MappedSuperclass::class])) {
  65.             $mappedSuperclassAnnot $classAnnotations[Mapping\MappedSuperclass::class];
  66.             assert($mappedSuperclassAnnot instanceof Mapping\MappedSuperclass);
  67.             $metadata->setCustomRepositoryClass($mappedSuperclassAnnot->repositoryClass);
  68.             $metadata->isMappedSuperclass true;
  69.         } elseif (isset($classAnnotations[Mapping\Embeddable::class])) {
  70.             $metadata->isEmbeddedClass true;
  71.         } else {
  72.             throw MappingException::classIsNotAValidEntityOrMappedSuperClass($className);
  73.         }
  74.         // Evaluate Table annotation
  75.         if (isset($classAnnotations[Mapping\Table::class])) {
  76.             $tableAnnot $classAnnotations[Mapping\Table::class];
  77.             assert($tableAnnot instanceof Mapping\Table);
  78.             $primaryTable = [
  79.                 'name'   => $tableAnnot->name,
  80.                 'schema' => $tableAnnot->schema,
  81.             ];
  82.             foreach ($tableAnnot->indexes ?? [] as $indexAnnot) {
  83.                 $index = [];
  84.                 if (! empty($indexAnnot->columns)) {
  85.                     $index['columns'] = $indexAnnot->columns;
  86.                 }
  87.                 if (! empty($indexAnnot->fields)) {
  88.                     $index['fields'] = $indexAnnot->fields;
  89.                 }
  90.                 if (
  91.                     isset($index['columns'], $index['fields'])
  92.                     || (
  93.                         ! isset($index['columns'])
  94.                         && ! isset($index['fields'])
  95.                     )
  96.                 ) {
  97.                     throw MappingException::invalidIndexConfiguration(
  98.                         $className,
  99.                         (string) ($indexAnnot->name ?? count($primaryTable['indexes']))
  100.                     );
  101.                 }
  102.                 if (! empty($indexAnnot->flags)) {
  103.                     $index['flags'] = $indexAnnot->flags;
  104.                 }
  105.                 if (! empty($indexAnnot->options)) {
  106.                     $index['options'] = $indexAnnot->options;
  107.                 }
  108.                 if (! empty($indexAnnot->name)) {
  109.                     $primaryTable['indexes'][$indexAnnot->name] = $index;
  110.                 } else {
  111.                     $primaryTable['indexes'][] = $index;
  112.                 }
  113.             }
  114.             foreach ($tableAnnot->uniqueConstraints ?? [] as $uniqueConstraintAnnot) {
  115.                 $uniqueConstraint = [];
  116.                 if (! empty($uniqueConstraintAnnot->columns)) {
  117.                     $uniqueConstraint['columns'] = $uniqueConstraintAnnot->columns;
  118.                 }
  119.                 if (! empty($uniqueConstraintAnnot->fields)) {
  120.                     $uniqueConstraint['fields'] = $uniqueConstraintAnnot->fields;
  121.                 }
  122.                 if (
  123.                     isset($uniqueConstraint['columns'], $uniqueConstraint['fields'])
  124.                     || (
  125.                         ! isset($uniqueConstraint['columns'])
  126.                         && ! isset($uniqueConstraint['fields'])
  127.                     )
  128.                 ) {
  129.                     throw MappingException::invalidUniqueConstraintConfiguration(
  130.                         $className,
  131.                         (string) ($uniqueConstraintAnnot->name ?? count($primaryTable['uniqueConstraints']))
  132.                     );
  133.                 }
  134.                 if (! empty($uniqueConstraintAnnot->options)) {
  135.                     $uniqueConstraint['options'] = $uniqueConstraintAnnot->options;
  136.                 }
  137.                 if (! empty($uniqueConstraintAnnot->name)) {
  138.                     $primaryTable['uniqueConstraints'][$uniqueConstraintAnnot->name] = $uniqueConstraint;
  139.                 } else {
  140.                     $primaryTable['uniqueConstraints'][] = $uniqueConstraint;
  141.                 }
  142.             }
  143.             if ($tableAnnot->options) {
  144.                 $primaryTable['options'] = $tableAnnot->options;
  145.             }
  146.             $metadata->setPrimaryTable($primaryTable);
  147.         }
  148.         // Evaluate @Cache annotation
  149.         if (isset($classAnnotations[Mapping\Cache::class])) {
  150.             $cacheAnnot $classAnnotations[Mapping\Cache::class];
  151.             $cacheMap   = [
  152.                 'region' => $cacheAnnot->region,
  153.                 'usage'  => (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' $cacheAnnot->usage),
  154.             ];
  155.             $metadata->enableCache($cacheMap);
  156.         }
  157.         // Evaluate NamedNativeQueries annotation
  158.         if (isset($classAnnotations[Mapping\NamedNativeQueries::class])) {
  159.             $namedNativeQueriesAnnot $classAnnotations[Mapping\NamedNativeQueries::class];
  160.             foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) {
  161.                 $metadata->addNamedNativeQuery(
  162.                     [
  163.                         'name'              => $namedNativeQuery->name,
  164.                         'query'             => $namedNativeQuery->query,
  165.                         'resultClass'       => $namedNativeQuery->resultClass,
  166.                         'resultSetMapping'  => $namedNativeQuery->resultSetMapping,
  167.                     ]
  168.                 );
  169.             }
  170.         }
  171.         // Evaluate SqlResultSetMappings annotation
  172.         if (isset($classAnnotations[Mapping\SqlResultSetMappings::class])) {
  173.             $sqlResultSetMappingsAnnot $classAnnotations[Mapping\SqlResultSetMappings::class];
  174.             foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) {
  175.                 $entities = [];
  176.                 $columns  = [];
  177.                 foreach ($resultSetMapping->entities as $entityResultAnnot) {
  178.                     $entityResult = [
  179.                         'fields'                => [],
  180.                         'entityClass'           => $entityResultAnnot->entityClass,
  181.                         'discriminatorColumn'   => $entityResultAnnot->discriminatorColumn,
  182.                     ];
  183.                     foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
  184.                         $entityResult['fields'][] = [
  185.                             'name'      => $fieldResultAnnot->name,
  186.                             'column'    => $fieldResultAnnot->column,
  187.                         ];
  188.                     }
  189.                     $entities[] = $entityResult;
  190.                 }
  191.                 foreach ($resultSetMapping->columns as $columnResultAnnot) {
  192.                     $columns[] = [
  193.                         'name' => $columnResultAnnot->name,
  194.                     ];
  195.                 }
  196.                 $metadata->addSqlResultSetMapping(
  197.                     [
  198.                         'name'          => $resultSetMapping->name,
  199.                         'entities'      => $entities,
  200.                         'columns'       => $columns,
  201.                     ]
  202.                 );
  203.             }
  204.         }
  205.         // Evaluate NamedQueries annotation
  206.         if (isset($classAnnotations[Mapping\NamedQueries::class])) {
  207.             $namedQueriesAnnot $classAnnotations[Mapping\NamedQueries::class];
  208.             if (! is_array($namedQueriesAnnot->value)) {
  209.                 throw new UnexpectedValueException('@NamedQueries should contain an array of @NamedQuery annotations.');
  210.             }
  211.             foreach ($namedQueriesAnnot->value as $namedQuery) {
  212.                 if (! ($namedQuery instanceof Mapping\NamedQuery)) {
  213.                     throw new UnexpectedValueException('@NamedQueries should contain an array of @NamedQuery annotations.');
  214.                 }
  215.                 $metadata->addNamedQuery(
  216.                     [
  217.                         'name'  => $namedQuery->name,
  218.                         'query' => $namedQuery->query,
  219.                     ]
  220.                 );
  221.             }
  222.         }
  223.         // Evaluate InheritanceType annotation
  224.         if (isset($classAnnotations[Mapping\InheritanceType::class])) {
  225.             $inheritanceTypeAnnot $classAnnotations[Mapping\InheritanceType::class];
  226.             assert($inheritanceTypeAnnot instanceof Mapping\InheritanceType);
  227.             $metadata->setInheritanceType(
  228.                 constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' $inheritanceTypeAnnot->value)
  229.             );
  230.             if ($metadata->inheritanceType !== ClassMetadataInfo::INHERITANCE_TYPE_NONE) {
  231.                 // Evaluate DiscriminatorColumn annotation
  232.                 if (isset($classAnnotations[Mapping\DiscriminatorColumn::class])) {
  233.                     $discrColumnAnnot $classAnnotations[Mapping\DiscriminatorColumn::class];
  234.                     assert($discrColumnAnnot instanceof Mapping\DiscriminatorColumn);
  235.                     $metadata->setDiscriminatorColumn(
  236.                         [
  237.                             'name'             => $discrColumnAnnot->name,
  238.                             'type'             => $discrColumnAnnot->type ?: 'string',
  239.                             'length'           => $discrColumnAnnot->length ?? 255,
  240.                             'columnDefinition' => $discrColumnAnnot->columnDefinition,
  241.                         ]
  242.                     );
  243.                 } else {
  244.                     $metadata->setDiscriminatorColumn(['name' => 'dtype''type' => 'string''length' => 255]);
  245.                 }
  246.                 // Evaluate DiscriminatorMap annotation
  247.                 if (isset($classAnnotations[Mapping\DiscriminatorMap::class])) {
  248.                     $discrMapAnnot $classAnnotations[Mapping\DiscriminatorMap::class];
  249.                     assert($discrMapAnnot instanceof Mapping\DiscriminatorMap);
  250.                     $metadata->setDiscriminatorMap($discrMapAnnot->value);
  251.                 }
  252.             }
  253.         }
  254.         // Evaluate DoctrineChangeTrackingPolicy annotation
  255.         if (isset($classAnnotations[Mapping\ChangeTrackingPolicy::class])) {
  256.             $changeTrackingAnnot $classAnnotations[Mapping\ChangeTrackingPolicy::class];
  257.             assert($changeTrackingAnnot instanceof Mapping\ChangeTrackingPolicy);
  258.             $metadata->setChangeTrackingPolicy(constant('Doctrine\ORM\Mapping\ClassMetadata::CHANGETRACKING_' $changeTrackingAnnot->value));
  259.         }
  260.         // Evaluate annotations on properties/fields
  261.         foreach ($class->getProperties() as $property) {
  262.             if (
  263.                 $metadata->isMappedSuperclass && ! $property->isPrivate()
  264.                 ||
  265.                 $metadata->isInheritedField($property->name)
  266.                 ||
  267.                 $metadata->isInheritedAssociation($property->name)
  268.                 ||
  269.                 $metadata->isInheritedEmbeddedClass($property->name)
  270.             ) {
  271.                 continue;
  272.             }
  273.             $mapping              = [];
  274.             $mapping['fieldName'] = $property->getName();
  275.             // Evaluate @Cache annotation
  276.             $cacheAnnot $this->reader->getPropertyAnnotation($propertyMapping\Cache::class);
  277.             if ($cacheAnnot !== null) {
  278.                 $mapping['cache'] = $metadata->getAssociationCacheDefaults(
  279.                     $mapping['fieldName'],
  280.                     [
  281.                         'usage'  => (int) constant('Doctrine\ORM\Mapping\ClassMetadata::CACHE_USAGE_' $cacheAnnot->usage),
  282.                         'region' => $cacheAnnot->region,
  283.                     ]
  284.                 );
  285.             }
  286.             // Check for JoinColumn/JoinColumns annotations
  287.             $joinColumns = [];
  288.             $joinColumnAnnot $this->reader->getPropertyAnnotation($propertyMapping\JoinColumn::class);
  289.             if ($joinColumnAnnot) {
  290.                 $joinColumns[] = $this->joinColumnToArray($joinColumnAnnot);
  291.             } else {
  292.                 $joinColumnsAnnot $this->reader->getPropertyAnnotation($propertyMapping\JoinColumns::class);
  293.                 if ($joinColumnsAnnot) {
  294.                     foreach ($joinColumnsAnnot->value as $joinColumn) {
  295.                         $joinColumns[] = $this->joinColumnToArray($joinColumn);
  296.                     }
  297.                 }
  298.             }
  299.             // Field can only be annotated with one of:
  300.             // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany
  301.             $columnAnnot $this->reader->getPropertyAnnotation($propertyMapping\Column::class);
  302.             if ($columnAnnot) {
  303.                 $mapping $this->columnToArray($property->getName(), $columnAnnot);
  304.                 $idAnnot $this->reader->getPropertyAnnotation($propertyMapping\Id::class);
  305.                 if ($idAnnot) {
  306.                     $mapping['id'] = true;
  307.                 }
  308.                 $generatedValueAnnot $this->reader->getPropertyAnnotation($propertyMapping\GeneratedValue::class);
  309.                 if ($generatedValueAnnot) {
  310.                     $metadata->setIdGeneratorType(constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_' $generatedValueAnnot->strategy));
  311.                 }
  312.                 if ($this->reader->getPropertyAnnotation($propertyMapping\Version::class)) {
  313.                     $metadata->setVersionMapping($mapping);
  314.                 }
  315.                 $metadata->mapField($mapping);
  316.                 // Check for SequenceGenerator/TableGenerator definition
  317.                 $seqGeneratorAnnot $this->reader->getPropertyAnnotation($propertyMapping\SequenceGenerator::class);
  318.                 if ($seqGeneratorAnnot) {
  319.                     $metadata->setSequenceGeneratorDefinition(
  320.                         [
  321.                             'sequenceName' => $seqGeneratorAnnot->sequenceName,
  322.                             'allocationSize' => $seqGeneratorAnnot->allocationSize,
  323.                             'initialValue' => $seqGeneratorAnnot->initialValue,
  324.                         ]
  325.                     );
  326.                 } else {
  327.                     $customGeneratorAnnot $this->reader->getPropertyAnnotation($propertyMapping\CustomIdGenerator::class);
  328.                     if ($customGeneratorAnnot) {
  329.                         $metadata->setCustomGeneratorDefinition(
  330.                             [
  331.                                 'class' => $customGeneratorAnnot->class,
  332.                             ]
  333.                         );
  334.                     }
  335.                 }
  336.             } else {
  337.                 $this->loadRelationShipMapping(
  338.                     $property,
  339.                     $mapping,
  340.                     $metadata,
  341.                     $joinColumns,
  342.                     $className
  343.                 );
  344.             }
  345.         }
  346.         // Evaluate AssociationOverrides annotation
  347.         if (isset($classAnnotations[Mapping\AssociationOverrides::class])) {
  348.             $associationOverridesAnnot $classAnnotations[Mapping\AssociationOverrides::class];
  349.             assert($associationOverridesAnnot instanceof Mapping\AssociationOverrides);
  350.             foreach ($associationOverridesAnnot->overrides as $associationOverride) {
  351.                 $override  = [];
  352.                 $fieldName $associationOverride->name;
  353.                 // Check for JoinColumn/JoinColumns annotations
  354.                 if ($associationOverride->joinColumns) {
  355.                     $joinColumns = [];
  356.                     foreach ($associationOverride->joinColumns as $joinColumn) {
  357.                         $joinColumns[] = $this->joinColumnToArray($joinColumn);
  358.                     }
  359.                     $override['joinColumns'] = $joinColumns;
  360.                 }
  361.                 // Check for JoinTable annotations
  362.                 if ($associationOverride->joinTable) {
  363.                     $joinTableAnnot $associationOverride->joinTable;
  364.                     $joinTable      = [
  365.                         'name'      => $joinTableAnnot->name,
  366.                         'schema'    => $joinTableAnnot->schema,
  367.                     ];
  368.                     foreach ($joinTableAnnot->joinColumns as $joinColumn) {
  369.                         $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
  370.                     }
  371.                     foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
  372.                         $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
  373.                     }
  374.                     $override['joinTable'] = $joinTable;
  375.                 }
  376.                 // Check for inversedBy
  377.                 if ($associationOverride->inversedBy) {
  378.                     $override['inversedBy'] = $associationOverride->inversedBy;
  379.                 }
  380.                 // Check for `fetch`
  381.                 if ($associationOverride->fetch) {
  382.                     $override['fetch'] = constant(Mapping\ClassMetadata::class . '::FETCH_' $associationOverride->fetch);
  383.                 }
  384.                 $metadata->setAssociationOverride($fieldName$override);
  385.             }
  386.         }
  387.         // Evaluate AttributeOverrides annotation
  388.         if (isset($classAnnotations[Mapping\AttributeOverrides::class])) {
  389.             $attributeOverridesAnnot $classAnnotations[Mapping\AttributeOverrides::class];
  390.             assert($attributeOverridesAnnot instanceof Mapping\AttributeOverrides);
  391.             foreach ($attributeOverridesAnnot->overrides as $attributeOverrideAnnot) {
  392.                 $attributeOverride $this->columnToArray($attributeOverrideAnnot->name$attributeOverrideAnnot->column);
  393.                 $metadata->setAttributeOverride($attributeOverrideAnnot->name$attributeOverride);
  394.             }
  395.         }
  396.         // Evaluate EntityListeners annotation
  397.         if (isset($classAnnotations[Mapping\EntityListeners::class])) {
  398.             $entityListenersAnnot $classAnnotations[Mapping\EntityListeners::class];
  399.             assert($entityListenersAnnot instanceof Mapping\EntityListeners);
  400.             foreach ($entityListenersAnnot->value as $item) {
  401.                 $listenerClassName $metadata->fullyQualifiedClassName($item);
  402.                 if (! class_exists($listenerClassName)) {
  403.                     throw MappingException::entityListenerClassNotFound($listenerClassName$className);
  404.                 }
  405.                 $hasMapping    false;
  406.                 $listenerClass = new ReflectionClass($listenerClassName);
  407.                 foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
  408.                     // find method callbacks.
  409.                     $callbacks  $this->getMethodCallbacks($method);
  410.                     $hasMapping $hasMapping ?: ! empty($callbacks);
  411.                     foreach ($callbacks as $value) {
  412.                         $metadata->addEntityListener($value[1], $listenerClassName$value[0]);
  413.                     }
  414.                 }
  415.                 // Evaluate the listener using naming convention.
  416.                 if (! $hasMapping) {
  417.                     EntityListenerBuilder::bindEntityListener($metadata$listenerClassName);
  418.                 }
  419.             }
  420.         }
  421.         // Evaluate @HasLifecycleCallbacks annotation
  422.         if (isset($classAnnotations[Mapping\HasLifecycleCallbacks::class])) {
  423.             foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
  424.                 foreach ($this->getMethodCallbacks($method) as $value) {
  425.                     $metadata->addLifecycleCallback($value[0], $value[1]);
  426.                 }
  427.             }
  428.         }
  429.     }
  430.     /**
  431.      * @param mixed[] $joinColumns
  432.      * @psalm-param array<string, mixed> $mapping
  433.      */
  434.     private function loadRelationShipMapping(
  435.         ReflectionProperty $property,
  436.         array &$mapping,
  437.         ClassMetadata $metadata,
  438.         array $joinColumns,
  439.         string $className
  440.     ): void {
  441.         $oneToOneAnnot $this->reader->getPropertyAnnotation($propertyMapping\OneToOne::class);
  442.         if ($oneToOneAnnot) {
  443.             $idAnnot $this->reader->getPropertyAnnotation($propertyMapping\Id::class);
  444.             if ($idAnnot) {
  445.                 $mapping['id'] = true;
  446.             }
  447.             $mapping['targetEntity']  = $oneToOneAnnot->targetEntity;
  448.             $mapping['joinColumns']   = $joinColumns;
  449.             $mapping['mappedBy']      = $oneToOneAnnot->mappedBy;
  450.             $mapping['inversedBy']    = $oneToOneAnnot->inversedBy;
  451.             $mapping['cascade']       = $oneToOneAnnot->cascade;
  452.             $mapping['orphanRemoval'] = $oneToOneAnnot->orphanRemoval;
  453.             $mapping['fetch']         = $this->getFetchMode($className$oneToOneAnnot->fetch);
  454.             $metadata->mapOneToOne($mapping);
  455.             return;
  456.         }
  457.         $oneToManyAnnot $this->reader->getPropertyAnnotation($propertyMapping\OneToMany::class);
  458.         if ($oneToManyAnnot) {
  459.             $mapping['mappedBy']      = $oneToManyAnnot->mappedBy;
  460.             $mapping['targetEntity']  = $oneToManyAnnot->targetEntity;
  461.             $mapping['cascade']       = $oneToManyAnnot->cascade;
  462.             $mapping['indexBy']       = $oneToManyAnnot->indexBy;
  463.             $mapping['orphanRemoval'] = $oneToManyAnnot->orphanRemoval;
  464.             $mapping['fetch']         = $this->getFetchMode($className$oneToManyAnnot->fetch);
  465.             $orderByAnnot $this->reader->getPropertyAnnotation($propertyMapping\OrderBy::class);
  466.             if ($orderByAnnot) {
  467.                 $mapping['orderBy'] = $orderByAnnot->value;
  468.             }
  469.             $metadata->mapOneToMany($mapping);
  470.         }
  471.         $manyToOneAnnot $this->reader->getPropertyAnnotation($propertyMapping\ManyToOne::class);
  472.         if ($manyToOneAnnot) {
  473.             $idAnnot $this->reader->getPropertyAnnotation($propertyMapping\Id::class);
  474.             if ($idAnnot) {
  475.                 $mapping['id'] = true;
  476.             }
  477.             $mapping['joinColumns']  = $joinColumns;
  478.             $mapping['cascade']      = $manyToOneAnnot->cascade;
  479.             $mapping['inversedBy']   = $manyToOneAnnot->inversedBy;
  480.             $mapping['targetEntity'] = $manyToOneAnnot->targetEntity;
  481.             $mapping['fetch']        = $this->getFetchMode($className$manyToOneAnnot->fetch);
  482.             $metadata->mapManyToOne($mapping);
  483.         }
  484.         $manyToManyAnnot $this->reader->getPropertyAnnotation($propertyMapping\ManyToMany::class);
  485.         if ($manyToManyAnnot) {
  486.             $joinTable = [];
  487.             $joinTableAnnot $this->reader->getPropertyAnnotation($propertyMapping\JoinTable::class);
  488.             if ($joinTableAnnot) {
  489.                 $joinTable = [
  490.                     'name' => $joinTableAnnot->name,
  491.                     'schema' => $joinTableAnnot->schema,
  492.                 ];
  493.                 foreach ($joinTableAnnot->joinColumns as $joinColumn) {
  494.                     $joinTable['joinColumns'][] = $this->joinColumnToArray($joinColumn);
  495.                 }
  496.                 foreach ($joinTableAnnot->inverseJoinColumns as $joinColumn) {
  497.                     $joinTable['inverseJoinColumns'][] = $this->joinColumnToArray($joinColumn);
  498.                 }
  499.             }
  500.             $mapping['joinTable']     = $joinTable;
  501.             $mapping['targetEntity']  = $manyToManyAnnot->targetEntity;
  502.             $mapping['mappedBy']      = $manyToManyAnnot->mappedBy;
  503.             $mapping['inversedBy']    = $manyToManyAnnot->inversedBy;
  504.             $mapping['cascade']       = $manyToManyAnnot->cascade;
  505.             $mapping['indexBy']       = $manyToManyAnnot->indexBy;
  506.             $mapping['orphanRemoval'] = $manyToManyAnnot->orphanRemoval;
  507.             $mapping['fetch']         = $this->getFetchMode($className$manyToManyAnnot->fetch);
  508.             $orderByAnnot $this->reader->getPropertyAnnotation($propertyMapping\OrderBy::class);
  509.             if ($orderByAnnot) {
  510.                 $mapping['orderBy'] = $orderByAnnot->value;
  511.             }
  512.             $metadata->mapManyToMany($mapping);
  513.         }
  514.         $embeddedAnnot $this->reader->getPropertyAnnotation($propertyMapping\Embedded::class);
  515.         if ($embeddedAnnot) {
  516.             $mapping['class']        = $embeddedAnnot->class;
  517.             $mapping['columnPrefix'] = $embeddedAnnot->columnPrefix;
  518.             $metadata->mapEmbedded($mapping);
  519.         }
  520.     }
  521.     /**
  522.      * Attempts to resolve the fetch mode.
  523.      *
  524.      * @psalm-return \Doctrine\ORM\Mapping\ClassMetadata::FETCH_* The fetch mode as defined in ClassMetadata.
  525.      *
  526.      * @throws MappingException If the fetch mode is not valid.
  527.      */
  528.     private function getFetchMode(string $classNamestring $fetchMode): int
  529.     {
  530.         if (! defined('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' $fetchMode)) {
  531.             throw MappingException::invalidFetchMode($className$fetchMode);
  532.         }
  533.         return constant('Doctrine\ORM\Mapping\ClassMetadata::FETCH_' $fetchMode);
  534.     }
  535.     /**
  536.      * Attempts to resolve the generated mode.
  537.      *
  538.      * @psalm-return ClassMetadataInfo::GENERATED_*
  539.      *
  540.      * @throws MappingException If the fetch mode is not valid.
  541.      */
  542.     private function getGeneratedMode(string $generatedMode): int
  543.     {
  544.         if (! defined('Doctrine\ORM\Mapping\ClassMetadata::GENERATED_' $generatedMode)) {
  545.             throw MappingException::invalidGeneratedMode($generatedMode);
  546.         }
  547.         return constant('Doctrine\ORM\Mapping\ClassMetadata::GENERATED_' $generatedMode);
  548.     }
  549.     /**
  550.      * Parses the given method.
  551.      *
  552.      * @return callable[]
  553.      * @psalm-return list<callable-array>
  554.      */
  555.     private function getMethodCallbacks(ReflectionMethod $method): array
  556.     {
  557.         $callbacks   = [];
  558.         $annotations $this->reader->getMethodAnnotations($method);
  559.         foreach ($annotations as $annot) {
  560.             if ($annot instanceof Mapping\PrePersist) {
  561.                 $callbacks[] = [$method->nameEvents::prePersist];
  562.             }
  563.             if ($annot instanceof Mapping\PostPersist) {
  564.                 $callbacks[] = [$method->nameEvents::postPersist];
  565.             }
  566.             if ($annot instanceof Mapping\PreUpdate) {
  567.                 $callbacks[] = [$method->nameEvents::preUpdate];
  568.             }
  569.             if ($annot instanceof Mapping\PostUpdate) {
  570.                 $callbacks[] = [$method->nameEvents::postUpdate];
  571.             }
  572.             if ($annot instanceof Mapping\PreRemove) {
  573.                 $callbacks[] = [$method->nameEvents::preRemove];
  574.             }
  575.             if ($annot instanceof Mapping\PostRemove) {
  576.                 $callbacks[] = [$method->nameEvents::postRemove];
  577.             }
  578.             if ($annot instanceof Mapping\PostLoad) {
  579.                 $callbacks[] = [$method->nameEvents::postLoad];
  580.             }
  581.             if ($annot instanceof Mapping\PreFlush) {
  582.                 $callbacks[] = [$method->nameEvents::preFlush];
  583.             }
  584.         }
  585.         return $callbacks;
  586.     }
  587.     /**
  588.      * Parse the given JoinColumn as array
  589.      *
  590.      * @return mixed[]
  591.      * @psalm-return array{
  592.      *                   name: string|null,
  593.      *                   unique: bool,
  594.      *                   nullable: bool,
  595.      *                   onDelete: mixed,
  596.      *                   columnDefinition: string|null,
  597.      *                   referencedColumnName: string
  598.      *               }
  599.      */
  600.     private function joinColumnToArray(Mapping\JoinColumn $joinColumn): array
  601.     {
  602.         return [
  603.             'name' => $joinColumn->name,
  604.             'unique' => $joinColumn->unique,
  605.             'nullable' => $joinColumn->nullable,
  606.             'onDelete' => $joinColumn->onDelete,
  607.             'columnDefinition' => $joinColumn->columnDefinition,
  608.             'referencedColumnName' => $joinColumn->referencedColumnName,
  609.         ];
  610.     }
  611.     /**
  612.      * Parse the given Column as array
  613.      *
  614.      * @return mixed[]
  615.      * @psalm-return array{
  616.      *                   fieldName: string,
  617.      *                   type: mixed,
  618.      *                   scale: int,
  619.      *                   length: int,
  620.      *                   unique: bool,
  621.      *                   nullable: bool,
  622.      *                   precision: int,
  623.      *                   notInsertable?: bool,
  624.      *                   notUpdateble?: bool,
  625.      *                   generated?: ClassMetadataInfo::GENERATED_*,
  626.      *                   enumType?: class-string,
  627.      *                   options?: mixed[],
  628.      *                   columnName?: string,
  629.      *                   columnDefinition?: string
  630.      *               }
  631.      */
  632.     private function columnToArray(string $fieldNameMapping\Column $column): array
  633.     {
  634.         $mapping = [
  635.             'fieldName'     => $fieldName,
  636.             'type'          => $column->type,
  637.             'scale'         => $column->scale,
  638.             'length'        => $column->length,
  639.             'unique'        => $column->unique,
  640.             'nullable'      => $column->nullable,
  641.             'precision'     => $column->precision,
  642.         ];
  643.         if (! $column->insertable) {
  644.             $mapping['notInsertable'] = true;
  645.         }
  646.         if (! $column->updatable) {
  647.             $mapping['notUpdatable'] = true;
  648.         }
  649.         if ($column->generated) {
  650.             $mapping['generated'] = $this->getGeneratedMode($column->generated);
  651.         }
  652.         if ($column->options) {
  653.             $mapping['options'] = $column->options;
  654.         }
  655.         if (isset($column->name)) {
  656.             $mapping['columnName'] = $column->name;
  657.         }
  658.         if (isset($column->columnDefinition)) {
  659.             $mapping['columnDefinition'] = $column->columnDefinition;
  660.         }
  661.         if ($column->enumType !== null) {
  662.             $mapping['enumType'] = $column->enumType;
  663.         }
  664.         return $mapping;
  665.     }
  666.     /**
  667.      * Factory method for the Annotation Driver.
  668.      *
  669.      * @param mixed[]|string $paths
  670.      *
  671.      * @return AnnotationDriver
  672.      */
  673.     public static function create($paths = [], ?AnnotationReader $reader null)
  674.     {
  675.         if ($reader === null) {
  676.             $reader = new AnnotationReader();
  677.         }
  678.         return new self($reader$paths);
  679.     }
  680. }