diff --git a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php index 5973b3d562602..4c086c3d55c74 100644 --- a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php @@ -73,7 +73,9 @@ static function ($sourceItem, $item, $sourceMetadata = null) use ($defaultLifeti $item->isHit = $sourceItem->isHit; $item->metadata = $item->newMetadata = $sourceItem->metadata = $sourceMetadata; - if (0 < $defaultLifetime) { + if (isset($item->metadata[CacheItem::METADATA_EXPIRY])) { + $item->expiresAt(\DateTime::createFromFormat('U.u', $item->metadata[CacheItem::METADATA_EXPIRY])); + } elseif (0 < $defaultLifetime) { $item->expiresAfter($defaultLifetime); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php index 53295a1eef0e5..b2905297ebe7d 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php @@ -16,8 +16,10 @@ use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\ChainAdapter; use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter; use Symfony\Component\Cache\Tests\Fixtures\PrunableAdapter; +use Symfony\Contracts\Cache\ItemInterface; /** * @author Kévin Dunglas @@ -34,6 +36,11 @@ public function createCachePool(int $defaultLifetime = 0, string $testMethod = n return new ChainAdapter([new ArrayAdapter($defaultLifetime), new ExternalAdapter($defaultLifetime), new FilesystemAdapter('', $defaultLifetime)], $defaultLifetime); } + public static function tearDownAfterClass(): void + { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + public function testEmptyAdaptersException() { $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException'); @@ -187,6 +194,48 @@ public function testMultipleCachesExpirationWhenCommonTtlIsSet() $this->assertFalse($item->isHit()); } + public function testExpirationOnAllAdapters() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $itemValidator = function (CacheItem $item) { + $refl = new \ReflectionObject($item); + $propExpiry = $refl->getProperty('expiry'); + $propExpiry->setAccessible(true); + $expiry = $propExpiry->getValue($item); + $this->assertGreaterThan(10, $expiry - time(), 'Item should be saved with the given ttl, not the default for the adapter.'); + + return true; + }; + + $adapter1 = $this->getMockBuilder(FilesystemAdapter::class) + ->setConstructorArgs(['', 2]) + ->setMethods(['save']) + ->getMock(); + $adapter1->expects($this->once()) + ->method('save') + ->with($this->callback($itemValidator)) + ->willReturn(true); + + $adapter2 = $this->getMockBuilder(FilesystemAdapter::class) + ->setConstructorArgs(['', 4]) + ->setMethods(['save']) + ->getMock(); + $adapter2->expects($this->once()) + ->method('save') + ->with($this->callback($itemValidator)) + ->willReturn(true); + + $cache = new ChainAdapter([$adapter1, $adapter2], 6); + $cache->get('test_key', function (ItemInterface $item) { + $item->expiresAfter(15); + + return 'chain'; + }); + } + private function getPruneableMock(): AdapterInterface { $pruneable = $this->createMock(PrunableAdapter::class);