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

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