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

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