From af490ed1e1fad5d0ad20af5794dfd14dad573e67 Mon Sep 17 00:00:00 2001 From: dron Date: Mon, 18 Feb 2019 23:38:46 +0200 Subject: [PATCH 1/5] Fix Metadata Caching when it changes in EventListeners --- .../Mapping/AbstractClassMetadataFactory.php | 82 ++++++++++--------- .../Mapping/ClassMetadataFactoryTest.php | 35 +++++++- 2 files changed, 75 insertions(+), 42 deletions(-) diff --git a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php index 9b37bbc9..4f10ea5d 100644 --- a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php +++ b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -35,6 +35,9 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory /** @var ClassMetadata[] */ private $loadedMetadata = []; + /** @var string[] */ + protected $aliasesMap = []; + /** @var bool */ protected $initialized = false; @@ -152,58 +155,35 @@ abstract protected function isEntity(ClassMetadata $class); */ public function getMetadataFor($className) { + $className = $this->getRealClassName($className); + if (isset($this->loadedMetadata[$className])) { return $this->loadedMetadata[$className]; } - // Check for namespace alias - if (strpos($className, ':') !== false) { - [$namespaceAlias, $simpleClassName] = explode(':', $className, 2); - - $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); - } else { - $realClassName = $this->getRealClass($className); - } - - if (isset($this->loadedMetadata[$realClassName])) { - // We do not have the alias name in the map, include it - return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; - } - $loadingException = null; try { if ($this->cacheDriver) { - $cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt); + $cached = $this->cacheDriver->fetch($className . $this->cacheSalt); if ($cached instanceof ClassMetadata) { - $this->loadedMetadata[$realClassName] = $cached; + $this->loadedMetadata[$className] = $cached; $this->wakeupReflection($cached, $this->getReflectionService()); } else { - foreach ($this->loadMetadata($realClassName) as $loadedClassName) { - $this->cacheDriver->save( - $loadedClassName . $this->cacheSalt, - $this->loadedMetadata[$loadedClassName], - null - ); - } + $this->loadMetadata($className); } } else { - $this->loadMetadata($realClassName); + $this->loadMetadata($className); } } catch (MappingException $loadingException) { - $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName); + $fallbackMetadataResponse = $this->onNotFoundMetadata($className); if (! $fallbackMetadataResponse) { throw $loadingException; } - $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse; - } - - if ($className !== $realClassName) { - // We do not have the alias name in the map, include it - $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + $this->setMetadataFor($className, $fallbackMetadataResponse); } return $this->loadedMetadata[$className]; @@ -218,6 +198,8 @@ public function getMetadataFor($className) */ public function hasMetadataFor($className) { + $className = $this->getRealClassName($className); + return isset($this->loadedMetadata[$className]); } @@ -233,7 +215,18 @@ public function hasMetadataFor($className) */ public function setMetadataFor($className, $class) { + $className = $this->getRealClassName($className); $this->loadedMetadata[$className] = $class; + + if ($this->cacheDriver === null) { + return; + } + + $this->cacheDriver->save( + $className . $this->cacheSalt, + $this->loadedMetadata[$className], + null + ); } /** @@ -303,8 +296,7 @@ protected function loadMetadata($name) $this->initializeReflection($class, $reflService); $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited); - - $this->loadedMetadata[$className] = $class; + $this->setMetadataFor($className, $class); $parent = $class; @@ -399,16 +391,28 @@ public function getReflectionService() } /** - * Gets the real class name of a class name that could be a proxy. + * Gets the real class name of a class name that could be a proxy or alias. */ - private function getRealClass(string $class) : string + protected function getRealClassName(string $className) : string { - $pos = strrpos($class, '\\' . Proxy::MARKER . '\\'); + if (isset($this->aliasesMap[$className])) { + return $this->aliasesMap[$className]; + } - if ($pos === false) { - return $class; + switch (true) { + case strpos($className, ':') !== false: // Check for namespace alias + [$namespaceAlias, $simpleClassName] = explode(':', $className, 2); + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + break; + case $pos = strrpos($className, '\\' . Proxy::MARKER . '\\') !== false: // Check for namespace proxy + $realClassName = substr($className, $pos + Proxy::MARKER_LENGTH + 2); + break; + default: + $realClassName = $className; } - return substr($class, $pos + Proxy::MARKER_LENGTH + 2); + $this->aliasesMap[$className] = $realClassName; + + return $realClassName; } } diff --git a/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php b/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php index baca8e1f..01b71658 100644 --- a/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php +++ b/tests/Doctrine/Tests/Common/Persistence/Mapping/ClassMetadataFactoryTest.php @@ -10,7 +10,7 @@ use Doctrine\Common\Persistence\Mapping\MappingException; use Doctrine\Common\Persistence\Mapping\ReflectionService; use Doctrine\Tests\DoctrineTestCase; -use PHPUnit_Framework_MockObject_MockObject; +use PHPUnit\Framework\MockObject\MockObject; use stdClass; /** @@ -140,7 +140,7 @@ public function testWillFailOnFallbackFailureWithNotLoadedMetadata() */ public function testWillIgnoreCacheEntriesThatAreNotMetadataInstances() { - /** @var Cache|PHPUnit_Framework_MockObject_MockObject $cacheDriver */ + /** @var Cache|MockObject $cacheDriver */ $cacheDriver = $this->createMock(Cache::class); $this->cmf->setCacheDriver($cacheDriver); @@ -150,7 +150,7 @@ public function testWillIgnoreCacheEntriesThatAreNotMetadataInstances() /** @var ClassMetadata $metadata */ $metadata = $this->createMock(ClassMetadata::class); - /** @var PHPUnit_Framework_MockObject_MockObject|stdClass|callable $fallbackCallback */ + /** @var MockObject|stdClass|callable $fallbackCallback */ $fallbackCallback = $this->getMockBuilder(stdClass::class)->setMethods(['__invoke'])->getMock(); $fallbackCallback->expects(self::any())->method('__invoke')->willReturn($metadata); @@ -159,6 +159,35 @@ public function testWillIgnoreCacheEntriesThatAreNotMetadataInstances() self::assertSame($metadata, $this->cmf->getMetadataFor('Foo')); } + + public function testFallbackMetadataShouldBeCached() : void + { + /** @var Cache|MockObject $cacheDriver */ + $cacheDriver = $this->createMock(Cache::class); + $cacheDriver->expects(self::once())->method('save'); + + $this->cmf->setCacheDriver($cacheDriver); + + $classMetadata = $this->createMock(ClassMetadata::class); + + $this->cmf->fallbackCallback = static function () use ($classMetadata) { + return $classMetadata; + }; + + $this->cmf->getMetadataFor('Foo'); + } + + public function testSetMetadataForShouldUpdateCache() : void + { + /** @var Cache|MockObject $cacheDriver */ + $cacheDriver = $this->createMock(Cache::class); + $cacheDriver->expects(self::once())->method('save'); + + $this->cmf->setCacheDriver($cacheDriver); + + $classMetadata = $this->createMock(ClassMetadata::class); + $this->cmf->setMetadataFor('Foo', $classMetadata); + } } class TestClassMetadataFactory extends AbstractClassMetadataFactory From 80567800aa638989351ebb628f4f5c6bc6c0afa5 Mon Sep 17 00:00:00 2001 From: Dron Date: Fri, 22 Feb 2019 08:34:44 +0200 Subject: [PATCH 2/5] Fix Metadata Caching when it changes in EventListeners --- .../Mapping/AbstractClassMetadataFactory.php | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php index 8d923990..974c52d2 100644 --- a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php +++ b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -36,7 +36,7 @@ abstract class AbstractClassMetadataFactory implements ClassMetadataFactory private $loadedMetadata = []; /** @var string[] */ - protected $aliasesMap = []; + private $aliasesMap = []; /** @var bool */ protected $initialized = false; @@ -398,16 +398,15 @@ protected function getRealClassName(string $className) : string return $this->aliasesMap[$className]; } - switch (true) { - case strpos($className, ':') !== false: // Check for namespace alias - [$namespaceAlias, $simpleClassName] = explode(':', $className, 2); - $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); - break; - case $pos = strrpos($className, '\\' . Proxy::MARKER . '\\') !== false: // Check for namespace proxy - $realClassName = substr($className, $pos + Proxy::MARKER_LENGTH + 2); - break; - default: - $realClassName = $className; + $realClassName = $className; + + if (strpos($className, ':') !== false) { + [$namespaceAlias, $simpleClassName] = explode(':', $className, 2); + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } + + if ($pos = strrpos($className, '\\' . Proxy::MARKER . '\\') !== false) { + $realClassName = substr($className, $pos + Proxy::MARKER_LENGTH + 2); } $this->aliasesMap[$className] = $realClassName; From 0f042fab201d31f635d9b097143bd9e6b3bfe938 Mon Sep 17 00:00:00 2001 From: Dron Date: Fri, 22 Feb 2019 08:41:54 +0200 Subject: [PATCH 3/5] Fix Metadata Caching when it changes in EventListeners --- .../Persistence/Mapping/AbstractClassMetadataFactory.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php index 974c52d2..d8cf38d4 100644 --- a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php +++ b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -402,10 +402,11 @@ protected function getRealClassName(string $className) : string if (strpos($className, ':') !== false) { [$namespaceAlias, $simpleClassName] = explode(':', $className, 2); - $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); } - if ($pos = strrpos($className, '\\' . Proxy::MARKER . '\\') !== false) { + $pos = strrpos($className, '\\' . Proxy::MARKER . '\\'); + if ($pos !== false) { $realClassName = substr($className, $pos + Proxy::MARKER_LENGTH + 2); } From 1012742b3a76580d7b9cc1e831379852917abb64 Mon Sep 17 00:00:00 2001 From: Dron Date: Sun, 24 Feb 2019 12:44:10 +0200 Subject: [PATCH 4/5] Fix Metadata Caching when it changes in EventListeners --- .../Mapping/AbstractClassMetadataFactory.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php index d8cf38d4..e3b9ace5 100644 --- a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php +++ b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -155,20 +155,24 @@ abstract protected function isEntity(ClassMetadata $class); */ public function getMetadataFor($className) { - $className = $this->getRealClassName($className); - if (isset($this->loadedMetadata[$className])) { return $this->loadedMetadata[$className]; } + $realClassName = $this->getRealClassName($className); + + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + $loadingException = null; try { if ($this->cacheDriver) { - $cached = $this->cacheDriver->fetch($className . $this->cacheSalt); + $cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt); if ($cached instanceof ClassMetadata) { - $this->loadedMetadata[$className] = $cached; - + $this->loadedMetadata[$realClassName] = $cached; $this->wakeupReflection($cached, $this->getReflectionService()); } else { $this->loadMetadata($className); @@ -198,8 +202,6 @@ public function getMetadataFor($className) */ public function hasMetadataFor($className) { - $className = $this->getRealClassName($className); - return isset($this->loadedMetadata[$className]); } @@ -215,7 +217,6 @@ public function hasMetadataFor($className) */ public function setMetadataFor($className, $class) { - $className = $this->getRealClassName($className); $this->loadedMetadata[$className] = $class; if ($this->cacheDriver === null) { From 128c4a1424721aeea2a6075cb6b394524720c78b Mon Sep 17 00:00:00 2001 From: Dron Date: Sun, 24 Feb 2019 16:51:19 +0200 Subject: [PATCH 5/5] Fix Metadata Caching when it changes in EventListeners --- .../Persistence/Mapping/AbstractClassMetadataFactory.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php index e3b9ace5..94774256 100644 --- a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php +++ b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php @@ -175,10 +175,10 @@ public function getMetadataFor($className) $this->loadedMetadata[$realClassName] = $cached; $this->wakeupReflection($cached, $this->getReflectionService()); } else { - $this->loadMetadata($className); + $this->loadMetadata($realClassName); } } else { - $this->loadMetadata($className); + $this->loadMetadata($realClassName); } } catch (MappingException $loadingException) { $fallbackMetadataResponse = $this->onNotFoundMetadata($className); @@ -190,6 +190,10 @@ public function getMetadataFor($className) $this->setMetadataFor($className, $fallbackMetadataResponse); } + if ($className !== $realClassName) { + $this->setMetadataFor($className, $this->loadedMetadata[$realClassName]); + } + return $this->loadedMetadata[$className]; }