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

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