diff --git a/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php b/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php index a4f527e6..94774256 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[] */ + private $aliasesMap = []; + /** @var bool */ protected $initialized = false; @@ -156,14 +159,7 @@ public function getMetadataFor($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); - } + $realClassName = $this->getRealClassName($className); if (isset($this->loadedMetadata[$realClassName])) { // We do not have the alias name in the map, include it @@ -177,32 +173,25 @@ public function getMetadataFor($className) $cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt); if ($cached instanceof ClassMetadata) { $this->loadedMetadata[$realClassName] = $cached; - $this->wakeupReflection($cached, $this->getReflectionService()); } else { - foreach ($this->loadMetadata($realClassName) as $loadedClassName) { - $this->cacheDriver->save( - $loadedClassName . $this->cacheSalt, - $this->loadedMetadata[$loadedClassName] - ); - } + $this->loadMetadata($realClassName); } } else { $this->loadMetadata($realClassName); } } catch (MappingException $loadingException) { - $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName); + $fallbackMetadataResponse = $this->onNotFoundMetadata($className); if (! $fallbackMetadataResponse) { throw $loadingException; } - $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse; + $this->setMetadataFor($className, $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, $this->loadedMetadata[$realClassName]); } return $this->loadedMetadata[$className]; @@ -233,6 +222,15 @@ public function hasMetadataFor($className) public function setMetadataFor($className, $class) { $this->loadedMetadata[$className] = $class; + + if ($this->cacheDriver === null) { + return; + } + + $this->cacheDriver->save( + $className . $this->cacheSalt, + $this->loadedMetadata[$className] + ); } /** @@ -302,8 +300,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; @@ -398,16 +395,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; + $realClassName = $className; + + if (strpos($className, ':') !== false) { + [$namespaceAlias, $simpleClassName] = explode(':', $className, 2); + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } + + $pos = strrpos($className, '\\' . Proxy::MARKER . '\\'); + if ($pos !== false) { + $realClassName = substr($className, $pos + Proxy::MARKER_LENGTH + 2); } - 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