From 680e4b4d5717a87fc496a41c42a9b8b689809804 Mon Sep 17 00:00:00 2001 From: Michael Babker Date: Tue, 28 Nov 2023 14:33:12 -0500 Subject: [PATCH] Make the annotation/attribute reader optional in the extension metadata factory --- rector.php | 1 + src/Mapping/ExtensionMetadataFactory.php | 44 +++++++++++++++++------- src/Mapping/MappedEventSubscriber.php | 24 +++++++------ 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/rector.php b/rector.php index 2803e9f70..851d21cd2 100644 --- a/rector.php +++ b/rector.php @@ -26,6 +26,7 @@ $rectorConfig->skip([ TypedPropertyFromAssignsRector::class => [ + __DIR__.'/src/Mapping/MappedEventSubscriber.php', // Rector is trying to set a type on the $annotationReader property which requires a union type, not supported on PHP 7.4 __DIR__.'/tests/Gedmo/Wrapper/Fixture/Entity/CompositeRelation.php', // @todo: remove this when https://github.com/doctrine/orm/issues/8255 is solved ], ]); diff --git a/src/Mapping/ExtensionMetadataFactory.php b/src/Mapping/ExtensionMetadataFactory.php index aa66605fe..8b5ae4961 100644 --- a/src/Mapping/ExtensionMetadataFactory.php +++ b/src/Mapping/ExtensionMetadataFactory.php @@ -61,20 +61,20 @@ class ExtensionMetadataFactory protected $extensionNamespace; /** - * Custom annotation reader + * Metadata annotation reader * - * @var Reader|AttributeReader|object + * @var Reader|AttributeReader|object|null */ protected $annotationReader; private ?CacheItemPoolInterface $cacheItemPool = null; /** - * @param Reader|AttributeReader|object $annotationReader + * @param Reader|AttributeReader|object|null $annotationReader */ - public function __construct(ObjectManager $objectManager, string $extensionNamespace, object $annotationReader, ?CacheItemPoolInterface $cacheItemPool = null) + public function __construct(ObjectManager $objectManager, string $extensionNamespace, ?object $annotationReader = null, ?CacheItemPoolInterface $cacheItemPool = null) { - if (!$annotationReader instanceof Reader && !$annotationReader instanceof AttributeReader) { + if (null !== $annotationReader && !$annotationReader instanceof Reader && !$annotationReader instanceof AttributeReader) { trigger_deprecation( 'gedmo/doctrine-extensions', '3.11', @@ -98,7 +98,7 @@ public function __construct(ObjectManager $objectManager, string $extensionNames * * @param ClassMetadata&(DocumentClassMetadata|EntityClassMetadata|LegacyEntityClassMetadata) $meta * - * @return array the metatada configuration + * @return array the metadata configuration */ public function getExtensionMetadata($meta) { @@ -209,8 +209,20 @@ protected function getDriver($omDriver) // create driver instance $driverClassName = $this->extensionNamespace.'\Mapping\Driver\\'.$driverName; if (!class_exists($driverClassName)) { - $driverClassName = $this->extensionNamespace.'\Mapping\Driver\Annotation'; + $originalDriverClassName = $driverClassName; + + // try to fall back to either an annotation or attribute driver depending on the available dependencies + if (interface_exists(Reader::class)) { + $driverClassName = $this->extensionNamespace.'\Mapping\Driver\Annotation'; + } elseif (\PHP_VERSION_ID >= 80000) { + $driverClassName = $this->extensionNamespace.'\Mapping\Driver\Attribute'; + } + if (!class_exists($driverClassName)) { + if ($originalDriverClassName !== $driverClassName) { + throw new RuntimeException("Failed to create mapping driver: ({$originalDriverClassName}), the extension driver nor a fallback annotation or attribute driver could be found."); + } + throw new RuntimeException("Failed to fallback to annotation driver: ({$driverClassName}), extension driver was not found."); } } @@ -227,14 +239,20 @@ protected function getDriver($omDriver) } } - if ($driver instanceof AttributeDriverInterface) { - if ($this->annotationReader instanceof AttributeReader) { - $driver->setAnnotationReader($this->annotationReader); + if ($driver instanceof AnnotationDriverInterface) { + if (null === $this->annotationReader) { + throw new RuntimeException("Cannot use metadata driver ({$driverClassName}), an annotation or attribute reader was not provided."); + } + + if ($driver instanceof AttributeDriverInterface) { + if ($this->annotationReader instanceof AttributeReader) { + $driver->setAnnotationReader($this->annotationReader); + } else { + $driver->setAnnotationReader(new AttributeAnnotationReader(new AttributeReader(), $this->annotationReader)); + } } else { - $driver->setAnnotationReader(new AttributeAnnotationReader(new AttributeReader(), $this->annotationReader)); + $driver->setAnnotationReader($this->annotationReader); } - } elseif ($driver instanceof AnnotationDriverInterface) { - $driver->setAnnotationReader($this->annotationReader); } } diff --git a/src/Mapping/MappedEventSubscriber.php b/src/Mapping/MappedEventSubscriber.php index f14045665..5ba286e3e 100644 --- a/src/Mapping/MappedEventSubscriber.php +++ b/src/Mapping/MappedEventSubscriber.php @@ -23,7 +23,6 @@ use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\ObjectManager; use Gedmo\Exception\InvalidArgumentException; -use Gedmo\Exception\RuntimeException; use Gedmo\Mapping\Driver\AttributeReader; use Gedmo\Mapping\Event\AdapterInterface; use Gedmo\ReferenceIntegrity\Mapping\Validator as ReferenceIntegrityValidator; @@ -81,14 +80,14 @@ abstract class MappedEventSubscriber implements EventSubscriber /** * Custom annotation reader * - * @var Reader|AttributeReader|object|null + * @var Reader|AttributeReader|object|false|null */ - private $annotationReader; + private $annotationReader = false; /** - * @var Reader|AttributeReader|null + * @var Reader|AttributeReader|false|null */ - private static $defaultAnnotationReader; + private static $defaultAnnotationReader = false; /** * @var CacheItemPoolInterface|null @@ -171,8 +170,8 @@ public function getExtensionMetadataFactory(ObjectManager $objectManager) { $oid = spl_object_id($objectManager); if (!isset($this->extensionMetadataFactory[$oid])) { - if (null === $this->annotationReader) { - // create default annotation reader for extensions + if (false === $this->annotationReader) { + // create default annotation/attribute reader for extensions $this->annotationReader = $this->getDefaultAnnotationReader(); } $this->extensionMetadataFactory[$oid] = new ExtensionMetadataFactory( @@ -307,19 +306,22 @@ protected function setFieldValue(AdapterInterface $adapter, $object, $field, $ol } /** - * Create default annotation reader for extensions + * Get the default annotation or attribute reader for extensions, creating it if necessary. * - * @return Reader|AttributeReader + * If a reader cannot be created due to missing requirements, no default will be set as the reader is only required for annotation or attribute metadata, + * and the {@see ExtensionMetadataFactory} can handle raising an error if it tries to create a mapping driver that requires this reader. + * + * @return Reader|AttributeReader|null */ private function getDefaultAnnotationReader() { - if (null === self::$defaultAnnotationReader) { + if (false === self::$defaultAnnotationReader) { if (class_exists(PsrCachedReader::class)) { self::$defaultAnnotationReader = new PsrCachedReader(new AnnotationReader(), new ArrayAdapter()); } elseif (\PHP_VERSION_ID >= 80000) { self::$defaultAnnotationReader = new AttributeReader(); } else { - throw new RuntimeException(sprintf('Cannot create a default annotation reader in "%1$s". Ensure you are running PHP 8 to use attributes, have installed the "doctrine/annotations" package, or call "%1$s::setAnnotationReader()" with a configured reader.', self::class)); + self::$defaultAnnotationReader = null; } }