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

  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.      * @note Providing any object as the third argument is deprecated, as of 4.0 an {@see AttributeReader} will be required
  68.      */
  69.     public function __construct(ObjectManager $objectManagerstring $extensionNamespace, ?object $annotationReader null, ?CacheItemPoolInterface $cacheItemPool null)
  70.     {
  71.         if (null !== $annotationReader) {
  72.             if ($annotationReader instanceof Reader) {
  73.                 Deprecation::trigger(
  74.                     'gedmo/doctrine-extensions',
  75.                     'https://github.com/doctrine-extensions/DoctrineExtensions/pull/2772',
  76.                     'Annotations support is deprecated, migrate your application to use attributes and pass an instance of %s to the %s constructor instead.',
  77.                     AttributeReader::class,
  78.                     static::class
  79.                 );
  80.             } elseif (!$annotationReader instanceof AttributeReader) {
  81.                 Deprecation::trigger(
  82.                     'gedmo/doctrine-extensions',
  83.                     'https://github.com/doctrine-extensions/DoctrineExtensions/pull/2258',
  84.                     'Providing an annotation reader which does not implement %s or is not an instance of %s to %s is deprecated.',
  85.                     Reader::class,
  86.                     AttributeReader::class,
  87.                     static::class
  88.                 );
  89.             }
  90.         }
  91.         $this->objectManager $objectManager;
  92.         $this->annotationReader $annotationReader;
  93.         $this->extensionNamespace $extensionNamespace;
  94.         $omDriver $objectManager->getConfiguration()->getMetadataDriverImpl();
  95.         $this->driver $this->getDriver($omDriver);
  96.         $this->cacheItemPool $cacheItemPool;
  97.     }
  98.     /**
  99.      * Reads extension metadata
  100.      *
  101.      * @param ClassMetadata<object>&(DocumentClassMetadata<object>|EntityClassMetadata<object>|LegacyEntityClassMetadata<object>) $meta
  102.      *
  103.      * @return array<string, mixed> the metadata configuration
  104.      */
  105.     public function getExtensionMetadata($meta)
  106.     {
  107.         if ($meta->isMappedSuperclass) {
  108.             return []; // ignore mappedSuperclasses for now
  109.         }
  110.         $config = [];
  111.         $cmf $this->objectManager->getMetadataFactory();
  112.         $useObjectName $meta->getName();
  113.         // collect metadata from inherited classes
  114.         if (null !== $meta->reflClass) {
  115.             foreach (array_reverse(class_parents($meta->getName())) as $parentClass) {
  116.                 // read only inherited mapped classes
  117.                 if ($cmf->hasMetadataFor($parentClass) || !$cmf->isTransient($parentClass)) {
  118.                     assert(class_exists($parentClass));
  119.                     $class $this->objectManager->getClassMetadata($parentClass);
  120.                     assert($class instanceof DocumentClassMetadata || $class instanceof EntityClassMetadata || $class instanceof LegacyEntityClassMetadata);
  121.                     $extendedMetadata $this->driver->readExtendedMetadata($class$config);
  122.                     if (\is_array($extendedMetadata)) {
  123.                         $config $extendedMetadata;
  124.                     }
  125.                     // @todo: In the next major release remove the assignment to `$extendedMetadata`, the previous conditional
  126.                     // block and uncomment the following line.
  127.                     // $config = $this->driver->readExtendedMetadata($class, $config);
  128.                     $isBaseInheritanceLevel = !$class->isInheritanceTypeNone()
  129.                         && [] === $class->parentClasses
  130.                         && [] !== $config
  131.                     ;
  132.                     if ($isBaseInheritanceLevel) {
  133.                         $useObjectName $class->getName();
  134.                     }
  135.                 }
  136.             }
  137.             $extendedMetadata $this->driver->readExtendedMetadata($meta$config);
  138.             if (\is_array($extendedMetadata)) {
  139.                 $config $extendedMetadata;
  140.             }
  141.             // @todo: In the next major release remove the assignment to `$extendedMetadata`, the previous conditional
  142.             // block and uncomment the following line.
  143.             // $config = $this->driver->readExtendedMetadata($meta, $config);
  144.         }
  145.         if ([] !== $config) {
  146.             $config['useObjectClass'] = $useObjectName;
  147.         }
  148.         $this->storeConfiguration($meta->getName(), $config);
  149.         return $config;
  150.     }
  151.     /**
  152.      * Get the cache id
  153.      *
  154.      * @param string $className
  155.      * @param string $extensionNamespace
  156.      *
  157.      * @return string
  158.      */
  159.     public static function getCacheId($className$extensionNamespace)
  160.     {
  161.         return str_replace('\\''_'$className).'_$'.strtoupper(str_replace('\\''_'$extensionNamespace)).'_CLASSMETADATA';
  162.     }
  163.     /**
  164.      * Get the extended driver instance which will
  165.      * read the metadata required by extension
  166.      *
  167.      * @param MappingDriver $omDriver
  168.      *
  169.      * @throws RuntimeException if driver was not found in extension
  170.      *
  171.      * @return Driver
  172.      */
  173.     protected function getDriver($omDriver)
  174.     {
  175.         if ($omDriver instanceof DoctrineBundleMappingDriver) {
  176.             $omDriver $omDriver->getDriver();
  177.         }
  178.         $driver null;
  179.         $className get_class($omDriver);
  180.         $driverName substr($classNamestrrpos($className'\\') + 1);
  181.         if ($omDriver instanceof MappingDriverChain || 'DriverChain' === $driverName) {
  182.             $driver = new Chain();
  183.             foreach ($omDriver->getDrivers() as $namespace => $nestedOmDriver) {
  184.                 $driver->addDriver($this->getDriver($nestedOmDriver), $namespace);
  185.             }
  186.             if (null !== $omDriver->getDefaultDriver()) {
  187.                 $driver->setDefaultDriver($this->getDriver($omDriver->getDefaultDriver()));
  188.             }
  189.         } else {
  190.             $driverName substr($driverName0strpos($driverName'Driver'));
  191.             $isSimplified false;
  192.             if ('Simplified' === substr($driverName010)) {
  193.                 // support for simplified file drivers
  194.                 $driverName substr($driverName10);
  195.                 $isSimplified true;
  196.             }
  197.             // create driver instance
  198.             $driverClassName $this->extensionNamespace.'\Mapping\Driver\\'.$driverName;
  199.             if (!class_exists($driverClassName)) {
  200.                 $originalDriverClassName $driverClassName;
  201.                 // try to fall back to either an annotation or attribute driver depending on the available dependencies
  202.                 if (interface_exists(Reader::class)) {
  203.                     $driverClassName $this->extensionNamespace.'\Mapping\Driver\Annotation';
  204.                 } elseif (\PHP_VERSION_ID >= 80000) {
  205.                     $driverClassName $this->extensionNamespace.'\Mapping\Driver\Attribute';
  206.                 }
  207.                 if (!class_exists($driverClassName)) {
  208.                     if ($originalDriverClassName !== $driverClassName) {
  209.                         throw new RuntimeException("Failed to create mapping driver: ({$originalDriverClassName}), the extension driver nor a fallback annotation or attribute driver could be found.");
  210.                     }
  211.                     throw new RuntimeException("Failed to fallback to annotation driver: ({$driverClassName}), extension driver was not found.");
  212.                 }
  213.             }
  214.             $driver = new $driverClassName();
  215.             $driver->setOriginalDriver($omDriver);
  216.             if ($driver instanceof FileDriver) {
  217.                 if ($omDriver instanceof MappingDriver) {
  218.                     $driver->setLocator($omDriver->getLocator());
  219.                 // BC for Doctrine 2.2
  220.                 } elseif ($isSimplified) {
  221.                     $driver->setLocator(new SymfonyFileLocator($omDriver->getNamespacePrefixes(), $omDriver->getFileExtension()));
  222.                 } else {
  223.                     $driver->setLocator(new DefaultFileLocator($omDriver->getPaths(), $omDriver->getFileExtension()));
  224.                 }
  225.             }
  226.             if ($driver instanceof AttributeDriverInterface) {
  227.                 if (null === $this->annotationReader) {
  228.                     throw new RuntimeException("Cannot use metadata driver ({$driverClassName}), an annotation or attribute reader was not provided.");
  229.                 }
  230.                 if ($driver instanceof AnnotationDriverInterface) {
  231.                     $driver->setAnnotationReader($this->annotationReader);
  232.                 } else {
  233.                     if ($this->annotationReader instanceof AttributeReader) {
  234.                         $driver->setAnnotationReader($this->annotationReader);
  235.                     } else {
  236.                         $driver->setAnnotationReader(new AttributeAnnotationReader(new AttributeReader(), $this->annotationReader));
  237.                     }
  238.                 }
  239.             }
  240.         }
  241.         return $driver;
  242.     }
  243.     /**
  244.      * @param array<string, mixed> $config
  245.      */
  246.     private function storeConfiguration(string $className, array $config): void
  247.     {
  248.         if (null === $this->cacheItemPool) {
  249.             return;
  250.         }
  251.         // Cache the result, even if it's empty, to prevent re-parsing non-existent annotations.
  252.         $cacheId self::getCacheId($className$this->extensionNamespace);
  253.         $item $this->cacheItemPool->getItem($cacheId);
  254.         $this->cacheItemPool->save($item->set($config));
  255.     }
  256. }