vendor/doctrine/orm/src/Mapping/Driver/AnnotationDriver.php line 97

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