From 8afab5105f24f6bbfc9eda855fce769f97a69795 Mon Sep 17 00:00:00 2001 From: Rostislav Wolny Date: Wed, 30 Mar 2022 15:10:49 +0200 Subject: [PATCH] Add cache only MappingDriver implementation and use in prod. mode We used to rely on Doctrine never calling MappingDriver when metadata are cached. But we learned that there are some cases where Doctrine uses the driver. In this commit we add custom MappingDriver that works on top of metadata cache. [MAILPOET-4218] --- .../lib/Doctrine/CacheOnlyMappingDriver.php | 55 +++++++++++++++++++ .../lib/Doctrine/ConfigurationFactory.php | 15 ++--- 2 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 mailpoet/lib/Doctrine/CacheOnlyMappingDriver.php diff --git a/mailpoet/lib/Doctrine/CacheOnlyMappingDriver.php b/mailpoet/lib/Doctrine/CacheOnlyMappingDriver.php new file mode 100644 index 0000000000..3ef0ba7462 --- /dev/null +++ b/mailpoet/lib/Doctrine/CacheOnlyMappingDriver.php @@ -0,0 +1,55 @@ +metaDataCache = $metaDataCache; + } + + /** + * @inerhitDoc + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) { + // We don't need to load anything it is all cached. + } + + /** + * @inerhitDoc + */ + public function getAllClassNames() { + throw new RuntimeException('CacheOnlyMappingDriver::getAllClassNames should not be called'); + } + + /** + * @inerhitDoc + */ + public function isTransient($className) { + // Everything in cache are metadata and class with metadata is non-transient + // See https://github.com/doctrine/persistence/blob/b07e347a24e7a19a2b6462e00a6dff899e4c2dd2/src/Persistence/Mapping/Driver/MappingDriver.php#L34 + return !$this->metaDataCache->hasItem($this->getCacheKey($className)); + } + + /** + * Copy pasted from MailPoetVendor\Doctrine\Persistence\Mapping\AbstractClassMetadataFactory + */ + protected function getCacheKey(string $className) : string { + return str_replace('\\', '__', $className) . $this->cacheSalt; + } +} diff --git a/mailpoet/lib/Doctrine/ConfigurationFactory.php b/mailpoet/lib/Doctrine/ConfigurationFactory.php index 18f6e8d16a..db8cd0982d 100644 --- a/mailpoet/lib/Doctrine/ConfigurationFactory.php +++ b/mailpoet/lib/Doctrine/ConfigurationFactory.php @@ -6,7 +6,6 @@ use MailPoet\Doctrine\Annotations\AnnotationReaderProvider; use MailPoetVendor\Doctrine\Common\Proxy\AbstractProxyFactory; use MailPoetVendor\Doctrine\ORM\Configuration; use MailPoetVendor\Doctrine\ORM\Mapping\Driver\AnnotationDriver; -use MailPoetVendor\Doctrine\Persistence\Mapping\Driver\PHPDriver; use MailPoetVendor\Doctrine\ORM\Mapping\UnderscoreNamingStrategy; class ConfigurationFactory { @@ -41,17 +40,15 @@ class ConfigurationFactory { // annotation reader exists only in dev environment, on production cache is pre-generated $annotationReader = $this->annotationReaderProvider->getAnnotationReader(); - if ($annotationReader) { - $configuration->setMetadataDriverImpl(new AnnotationDriver($annotationReader, [self::ENTITY_DIR])); - } else { - // Should never be called but Doctrine requires having driver set - $configuration->setMetadataDriverImpl(new PHPDriver([])); - } - - // metadata cache (for production cache is pre-generated at build time) $isReadOnly = !$annotationReader; $metadataStorage = new PSRMetadataCache(self::METADATA_DIR, $isReadOnly); $configuration->setMetadataCache($metadataStorage); + + if ($isReadOnly) { + $configuration->setMetadataDriverImpl(new CacheOnlyMappingDriver($metadataStorage)); + } else { + $configuration->setMetadataDriverImpl(new AnnotationDriver($annotationReader, [self::ENTITY_DIR])); + } } private function configureProxies(Configuration $configuration) {