vendor/gedmo/doctrine-extensions/src/Mapping/ExtensionMetadataFactory.php line 143

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Doctrine Behavioral Extensions package.
  4.  * (c) Gediminas Morkevicius <gediminas.morkevicius@gmail.com> http://www.gediminasm.org
  5.  * For the full copyright and license information, please view the LICENSE
  6.  * file that was distributed with this source code.
  7.  */
  8. namespace Gedmo\Mapping;
  9. use Doctrine\Bundle\DoctrineBundle\Mapping\MappingDriver as DoctrineBundleMappingDriver;
  10. use Doctrine\Common\Annotations\Reader;
  11. use Doctrine\Deprecations\Deprecation;
  12. use Doctrine\ODM\MongoDB\Mapping\ClassMetadata as DocumentClassMetadata;
  13. use Doctrine\ORM\Mapping\ClassMetadata as EntityClassMetadata;
  14. use Doctrine\ORM\Mapping\ClassMetadataInfo as LegacyEntityClassMetadata;
  15. use Doctrine\Persistence\Mapping\ClassMetadata;
  16. use Doctrine\Persistence\Mapping\Driver\DefaultFileLocator;
  17. use Doctrine\Persistence\Mapping\Driver\MappingDriver;
  18. use Doctrine\Persistence\Mapping\Driver\MappingDriverChain;
  19. use Doctrine\Persistence\Mapping\Driver\SymfonyFileLocator;
  20. use Doctrine\Persistence\ObjectManager;
  21. use Gedmo\Exception\RuntimeException;
  22. use Gedmo\Mapping\Driver\AnnotationDriverInterface;
  23. use Gedmo\Mapping\Driver\AttributeAnnotationReader;
  24. use Gedmo\Mapping\Driver\AttributeDriverInterface;
  25. use Gedmo\Mapping\Driver\AttributeReader;
  26. use Gedmo\Mapping\Driver\Chain;
  27. use Gedmo\Mapping\Driver\File as FileDriver;
  28. use Psr\Cache\CacheItemPoolInterface;
  29. /**
  30.  * The extension metadata factory is responsible for extension driver
  31.  * initialization and fully reading the extension metadata
  32.  *
  33.  * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
  34.  *
  35.  * @final since gedmo/doctrine-extensions 3.11
  36.  */
  37. class ExtensionMetadataFactory
  38. {
  39.     /**
  40.      * Extension driver
  41.      *
  42.      * @var Driver
  43.      */
  44.     protected $driver;
  45.     /**
  46.      * Object manager, entity or document
  47.      *
  48.      * @var ObjectManager
  49.      */
  50.     protected $objectManager;
  51.     /**
  52.      * Extension namespace
  53.      *
  54.      * @var string
  55.      */
  56.     protected $extensionNamespace;
  57.     /**
  58.      * Metadata annotation reader
  59.      *
  60.      * @var Reader|AttributeReader|object|null
  61.      */
  62.     protected $annotationReader;
  63.     private ?CacheItemPoolInterface $cacheItemPool null;
  64.     /**
  65.      * @param Reader|AttributeReader|object|null $annotationReader
  66.      */
  67.     public function __construct(ObjectManager $objectManagerstring $extensionNamespace, ?object $annotationReader null, ?CacheItemPoolInterface $cacheItemPool null)
  68.     {
  69.         if (null !== $annotationReader && !$annotationReader instanceof Reader && !$annotationReader instanceof AttributeReader) {
  70.             Deprecation::trigger(
  71.                 'gedmo/doctrine-extensions',
  72.                 'https://github.com/doctrine-extensions/DoctrineExtensions/pull/2258',
  73.                 'Providing an annotation reader which does not implement %s or is not an instance of %s to %s is deprecated.',
  74.                 Reader::class,
  75.                 AttributeReader::class,
  76.                 static::class
  77.             );
  78.         }
  79.         $this->objectManager $objectManager;
  80.         $this->annotationReader $annotationReader;
  81.         $this->extensionNamespace $extensionNamespace;
  82.         $omDriver $objectManager->getConfiguration()->getMetadataDriverImpl();
  83.         $this->driver $this->getDriver($omDriver);
  84.         $this->cacheItemPool $cacheItemPool;
  85.     }
  86.     /**
  87.      * Reads extension metadata
  88.      *
  89.      * @param ClassMetadata&(DocumentClassMetadata|EntityClassMetadata|LegacyEntityClassMetadata) $meta
  90.      *
  91.      * @return array<string, mixed> the metadata configuration
  92.      */
  93.     public function getExtensionMetadata($meta)
  94.     {
  95.         if ($meta->isMappedSuperclass) {
  96.             return []; // ignore mappedSuperclasses for now
  97.         }
  98.         $config = [];
  99.         $cmf $this->objectManager->getMetadataFactory();
  100.         $useObjectName $meta->getName();
  101.         // collect metadata from inherited classes
  102.         if (null !== $meta->reflClass) {
  103.             foreach (array_reverse(class_parents($meta->getName())) as $parentClass) {
  104.                 // read only inherited mapped classes
  105.                 if ($cmf->hasMetadataFor($parentClass) || !$cmf->isTransient($parentClass)) {
  106.                     assert(class_exists($parentClass));
  107.                     $class $this->objectManager->getClassMetadata($parentClass);
  108.                     assert($class instanceof DocumentClassMetadata || $class instanceof EntityClassMetadata || $class instanceof LegacyEntityClassMetadata);
  109.                     $extendedMetadata $this->driver->readExtendedMetadata($class$config);
  110.                     if (\is_array($extendedMetadata)) {
  111.                         $config $extendedMetadata;
  112.                     }
  113.                     // @todo: In the next major release remove the assignment to `$extendedMetadata`, the previous conditional
  114.                     // block and uncomment the following line.
  115.                     // $config = $this->driver->readExtendedMetadata($class, $config);
  116.                     $isBaseInheritanceLevel = !$class->isInheritanceTypeNone()
  117.                         && [] === $class->parentClasses
  118.                         && [] !== $config
  119.                     ;
  120.                     if ($isBaseInheritanceLevel) {
  121.                         $useObjectName $class->getName();
  122.                     }
  123.                 }
  124.             }
  125.             $extendedMetadata $this->driver->readExtendedMetadata($meta$config);
  126.             if (\is_array($extendedMetadata)) {
  127.                 $config $extendedMetadata;
  128.             }
  129.             // @todo: In the next major release remove the assignment to `$extendedMetadata`, the previous conditional
  130.             // block and uncomment the following line.
  131.             // $config = $this->driver->readExtendedMetadata($meta, $config);
  132.         }
  133.         if ([] !== $config) {
  134.             $config['useObjectClass'] = $useObjectName;
  135.         }
  136.         $this->storeConfiguration($meta->getName(), $config);
  137.         return $config;
  138.     }
  139.     /**
  140.      * Get the cache id
  141.      *
  142.      * @param string $className
  143.      * @param string $extensionNamespace
  144.      *
  145.      * @return string
  146.      */
  147.     public static function getCacheId($className$extensionNamespace)
  148.     {
  149.         return str_replace('\\''_'$className).'_$'.strtoupper(str_replace('\\''_'$extensionNamespace)).'_CLASSMETADATA';
  150.     }
  151.     /**
  152.      * Get the extended driver instance which will
  153.      * read the metadata required by extension
  154.      *
  155.      * @param MappingDriver $omDriver
  156.      *
  157.      * @throws RuntimeException if driver was not found in extension
  158.      *
  159.      * @return Driver
  160.      */
  161.     protected function getDriver($omDriver)
  162.     {
  163.         if ($omDriver instanceof DoctrineBundleMappingDriver) {
  164.             $omDriver $omDriver->getDriver();
  165.         }
  166.         $driver null;
  167.         $className get_class($omDriver);
  168.         $driverName substr($classNamestrrpos($className'\\') + 1);
  169.         if ($omDriver instanceof MappingDriverChain || 'DriverChain' === $driverName) {
  170.             $driver = new Chain();
  171.             foreach ($omDriver->getDrivers() as $namespace => $nestedOmDriver) {
  172.                 $driver->addDriver($this->getDriver($nestedOmDriver), $namespace);
  173.             }
  174.             if (null !== $omDriver->getDefaultDriver()) {
  175.                 $driver->setDefaultDriver($this->getDriver($omDriver->getDefaultDriver()));
  176.             }
  177.         } else {
  178.             $driverName substr($driverName0strpos($driverName'Driver'));
  179.             $isSimplified false;
  180.             if ('Simplified' === substr($driverName010)) {
  181.                 // support for simplified file drivers
  182.                 $driverName substr($driverName10);
  183.                 $isSimplified true;
  184.             }
  185.             // create driver instance
  186.             $driverClassName $this->extensionNamespace.'\Mapping\Driver\\'.$driverName;
  187.             if (!class_exists($driverClassName)) {
  188.                 $originalDriverClassName $driverClassName;
  189.                 // try to fall back to either an annotation or attribute driver depending on the available dependencies
  190.                 if (interface_exists(Reader::class)) {
  191.                     $driverClassName $this->extensionNamespace.'\Mapping\Driver\Annotation';
  192.                 } elseif (\PHP_VERSION_ID >= 80000) {
  193.                     $driverClassName $this->extensionNamespace.'\Mapping\Driver\Attribute';
  194.                 }
  195.                 if (!class_exists($driverClassName)) {
  196.                     if ($originalDriverClassName !== $driverClassName) {
  197.                         throw new RuntimeException("Failed to create mapping driver: ({$originalDriverClassName}), the extension driver nor a fallback annotation or attribute driver could be found.");
  198.                     }
  199.                     throw new RuntimeException("Failed to fallback to annotation driver: ({$driverClassName}), extension driver was not found.");
  200.                 }
  201.             }
  202.             $driver = new $driverClassName();
  203.             $driver->setOriginalDriver($omDriver);
  204.             if ($driver instanceof FileDriver) {
  205.                 if ($omDriver instanceof MappingDriver) {
  206.                     $driver->setLocator($omDriver->getLocator());
  207.                 // BC for Doctrine 2.2
  208.                 } elseif ($isSimplified) {
  209.                     $driver->setLocator(new SymfonyFileLocator($omDriver->getNamespacePrefixes(), $omDriver->getFileExtension()));
  210.                 } else {
  211.                     $driver->setLocator(new DefaultFileLocator($omDriver->getPaths(), $omDriver->getFileExtension()));
  212.                 }
  213.             }
  214.             if ($driver instanceof AnnotationDriverInterface) {
  215.                 if (null === $this->annotationReader) {
  216.                     throw new RuntimeException("Cannot use metadata driver ({$driverClassName}), an annotation or attribute reader was not provided.");
  217.                 }
  218.                 if ($driver instanceof AttributeDriverInterface) {
  219.                     if ($this->annotationReader instanceof AttributeReader) {
  220.                         $driver->setAnnotationReader($this->annotationReader);
  221.                     } else {
  222.                         $driver->setAnnotationReader(new AttributeAnnotationReader(new AttributeReader(), $this->annotationReader));
  223.                     }
  224.                 } else {
  225.                     $driver->setAnnotationReader($this->annotationReader);
  226.                 }
  227.             }
  228.         }
  229.         return $driver;
  230.     }
  231.     /**
  232.      * @param array<string, mixed> $config
  233.      */
  234.     private function storeConfiguration(string $className, array $config): void
  235.     {
  236.         if (null === $this->cacheItemPool) {
  237.             return;
  238.         }
  239.         // Cache the result, even if it's empty, to prevent re-parsing non-existent annotations.
  240.         $cacheId self::getCacheId($className$this->extensionNamespace);
  241.         $item $this->cacheItemPool->getItem($cacheId);
  242.         $this->cacheItemPool->save($item->set($config));
  243.     }
  244. }