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

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