From 32458c4d0031d375d495ff0320fd3d86d3943169 Mon Sep 17 00:00:00 2001 From: Adrien Brault Date: Thu, 17 Oct 2013 17:07:19 -0700 Subject: [PATCH] Cache when there is no metadata for a class --- src/Metadata/MetadataFactory.php | 46 +++++++++--- src/Metadata/NullMetadata.php | 26 +++++++ tests/Metadata/Tests/MetadataFactoryTest.php | 78 ++++++++++++++++++++ 3 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 src/Metadata/NullMetadata.php diff --git a/src/Metadata/MetadataFactory.php b/src/Metadata/MetadataFactory.php index 4118d37..25ee063 100644 --- a/src/Metadata/MetadataFactory.php +++ b/src/Metadata/MetadataFactory.php @@ -65,29 +65,38 @@ public function setCache(CacheInterface $cache) public function getMetadataForClass($className) { if (isset($this->loadedMetadata[$className])) { - return $this->loadedMetadata[$className]; + return $this->filterNullMetadata($this->loadedMetadata[$className]); } $metadata = null; foreach ($this->getClassHierarchy($className) as $class) { if (isset($this->loadedClassMetadata[$name = $class->getName()])) { - $this->addClassMetadata($metadata, $this->loadedClassMetadata[$name]); + if (null !== $classMetadata = $this->filterNullMetadata($this->loadedClassMetadata[$name])) { + $this->addClassMetadata($metadata, $classMetadata); + } continue; } // check the cache - if (null !== $this->cache && (null !== $classMetadata = $this->cache->loadClassMetadataFromCache($class))) { - if ( ! $classMetadata instanceof ClassMetadata) { - throw new \LogicException(sprintf('The cache must return instances of ClassMetadata, but got %s.', var_export($classMetadata, true))); - } - - if ($this->debug && !$classMetadata->isFresh()) { - $this->cache->evictClassMetadataFromCache($classMetadata->reflection); - } else { + if (null !== $this->cache) { + if (($classMetadata = $this->cache->loadClassMetadataFromCache($class)) instanceof NullMetadata) { $this->loadedClassMetadata[$name] = $classMetadata; - $this->addClassMetadata($metadata, $classMetadata); continue; } + + if (null !== $classMetadata) { + if ( ! $classMetadata instanceof ClassMetadata) { + throw new \LogicException(sprintf('The cache must return instances of ClassMetadata, but got %s.', var_export($classMetadata, true))); + } + + if ($this->debug && !$classMetadata->isFresh()) { + $this->cache->evictClassMetadataFromCache($classMetadata->reflection); + } else { + $this->loadedClassMetadata[$name] = $classMetadata; + $this->addClassMetadata($metadata, $classMetadata); + continue; + } + } } // load from source @@ -101,9 +110,17 @@ public function getMetadataForClass($className) continue; } + + if (null !== $this->cache && !$this->debug) { + $this->cache->putClassMetadataInCache(new NullMetadata()); + } + } + + if (null === $metadata) { + $metadata = new NullMetadata(); } - return $this->loadedMetadata[$className] = $metadata; + return $this->filterNullMetadata($this->loadedMetadata[$className] = $metadata); } /** @@ -175,4 +192,9 @@ private function getClassHierarchy($class) return $newHierarchy; } + + private function filterNullMetadata($metadata = null) + { + return !$metadata instanceof NullMetadata ? $metadata : null; + } } diff --git a/src/Metadata/NullMetadata.php b/src/Metadata/NullMetadata.php new file mode 100644 index 0000000..eb1b025 --- /dev/null +++ b/src/Metadata/NullMetadata.php @@ -0,0 +1,26 @@ + + */ +class NullMetadata extends ClassMetadata +{ + public function __construct() + { + + } + + public function serialize() + { + return ''; + } + + public function unserialize($str) + { + + } +} diff --git a/tests/Metadata/Tests/MetadataFactoryTest.php b/tests/Metadata/Tests/MetadataFactoryTest.php index 011f039..7fb5df4 100644 --- a/tests/Metadata/Tests/MetadataFactoryTest.php +++ b/tests/Metadata/Tests/MetadataFactoryTest.php @@ -132,6 +132,9 @@ public function testGetMetadataWithCache() ; $factory->setCache($cache); + + $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); + $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); $this->assertSame($metadata, reset($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')->classMetadata)); } @@ -198,4 +201,79 @@ public function testGetAllClassNamesThrowsException() $factory = new MetadataFactory($this->getMock('Metadata\Driver\DriverInterface')); $factory->getAllClassNames(); } + + public function testNotFoundMetadataIsCached() + { + $driver = $this->getMock('Metadata\Driver\DriverInterface'); + $driver + ->expects($this->once()) // This is the important part of this test + ->method('loadMetadataForClass') + ->will($this->returnValue(null)) + ; + + $cachedMetadata = null; + $cache = $this->getMock('Metadata\Cache\CacheInterface'); + $cache + ->expects($this->any()) + ->method('loadClassMetadataFromCache') + ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestObject'))) + ->will($this->returnCallback(function () use (&$cachedMetadata) { + return $cachedMetadata; + })) + ; + $cache + ->expects($this->once()) + ->method('putClassMetadataInCache') + ->will($this->returnCallback(function ($metadata) use (&$cachedMetadata) { + $cachedMetadata = $metadata; + })) + ; + + $factory = new MetadataFactory($driver); + $factory->setCache($cache); + $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); + $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); + $this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')); + + // We use another factory with the same cache, to simulate another request and skip the in memory + $factory = new MetadataFactory($driver); + $factory->setCache($cache); + $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); + $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); + $this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')); + } + + public function testNotFoundMetadataIsNotCachedInDebug() + { + $driver = $this->getMock('Metadata\Driver\DriverInterface'); + $driver + ->expects($this->exactly(2)) + ->method('loadMetadataForClass') + ->will($this->returnValue(null)) + ; + + $cachedMetadata = null; + $cache = $this->getMock('Metadata\Cache\CacheInterface'); + $cache + ->expects($this->any()) + ->method('loadClassMetadataFromCache') + ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestObject'))) + ->will($this->returnValue(null)) + ; + $cache + ->expects($this->never()) + ->method('putClassMetadataInCache') + ; + + $factory = new MetadataFactory($driver, 'Metadata\ClassHierarchyMetadata', true); + $factory->setCache($cache); + $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); + $this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')); + + // We use another factory with the same cache, to simulate another request and skip the in memory + $factory = new MetadataFactory($driver, 'Metadata\ClassHierarchyMetadata', true); + $factory->setCache($cache); + $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); + $this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')); + } }