diff --git a/CHANGELOG.md b/CHANGELOG.md index 358ce9dc8..9d31ee7d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ All notable changes to this project will be documented in this file, in reverse ### Added -- Nothing. +- [#10](https://github.com/zendframework/zend-cache/pull/10) adds TTL support + for the Redis adapter. ### Deprecated diff --git a/src/Storage/Adapter/Redis.php b/src/Storage/Adapter/Redis.php index 1470d5503..213141057 100644 --- a/src/Storage/Adapter/Redis.php +++ b/src/Storage/Adapter/Redis.php @@ -300,6 +300,25 @@ protected function internalAddItem(& $normalizedKey, & $value) } } + /** + * Internal method to touch an item. + * + * @param string &$normalizedKey Key which will be touched + * + * @return bool + * @throws Exception\RuntimeException + */ + protected function internalTouchItem(& $normalizedKey) + { + $redis = $this->getRedisResource(); + try { + $ttl = $this->getOptions()->getTtl(); + return (bool) $redis->expire($this->namespacePrefix . $normalizedKey, $ttl); + } catch (RedisResourceException $e) { + throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e); + } + } + /** * Internal method to remove an item. * @@ -426,7 +445,11 @@ protected function internalGetCapabilities() { if ($this->capabilities === null) { $this->capabilityMarker = new stdClass(); - $minTtl = $this->resourceManager->getMajorVersion($this->resourceId) < 2 ? 0 : 1; + + $redisVersion = $this->resourceManager->getMajorVersion($this->resourceId); + $minTtl = version_compare($redisVersion, '2', '<') ? 0 : 1; + $supportedMetadata = version_compare($redisVersion, '2', '>=') ? ['ttl'] : []; + //without serialization redis supports only strings for simple //get/set methods $this->capabilities = new Capabilities( @@ -443,7 +466,7 @@ protected function internalGetCapabilities() 'object' => false, 'resource' => false, ], - 'supportedMetadata' => [], + 'supportedMetadata' => $supportedMetadata, 'minTtl' => $minTtl, 'maxTtl' => 0, 'staticTtl' => true, @@ -458,4 +481,69 @@ protected function internalGetCapabilities() return $this->capabilities; } + + /** + * {@inheritDoc} + * + * @throws Exception\ExceptionInterface + */ + protected function internalGetMetadata(& $normalizedKey) + { + $redis = $this->getRedisResource(); + $metadata = []; + + try { + $redisVersion = $this->resourceManager->getMajorVersion($this->resourceId); + + // redis >= 2.8 + // The command 'pttl' returns -2 if the item does not exist + // and -1 if the item has no associated expire + if (version_compare($redisVersion, '2.8', '>=')) { + $pttl = $redis->pttl($this->namespacePrefix . $normalizedKey); + if ($pttl <= -2) { + return false; + } + $metadata['ttl'] = ($pttl == -1) ? null : $pttl / 1000; + + // redis >= 2.6 + // The command 'pttl' returns -1 if the item does not exist or the item as no associated expire + } elseif (version_compare($redisVersion, '2.6', '>=')) { + $pttl = $redis->pttl($this->namespacePrefix . $normalizedKey); + if ($pttl <= -1) { + if (!$this->internalHasItem($normalizedKey)) { + return false; + } + $metadata['ttl'] = null; + } else { + $metadata['ttl'] = $pttl / 1000; + } + + // redis >= 2 + // The command 'pttl' is not supported but 'ttl' + // The command 'ttl' returns 0 if the item does not exist same as if the item is going to be expired + // NOTE: In case of ttl=0 we return false because the item is going to be expired in a very near future + // and then doesn't exist any more + } elseif (version_compare($redisVersion, '2', '>=')) { + $ttl = $redis->ttl($this->namespacePrefix . $normalizedKey); + if ($ttl <= -1) { + if (!$this->internalHasItem($normalizedKey)) { + return false; + } + $metadata['ttl'] = null; + } else { + $metadata['ttl'] = $ttl; + } + + // redis < 2 + // The commands 'pttl' and 'ttl' are not supported + // but item existence have to be checked + } elseif (!$this->internalHasItem($normalizedKey)) { + return false; + } + } catch (RedisResourceException $e) { + throw new Exception\RuntimeException($redis->getLastError(), $e->getCode(), $e); + } + + return $metadata; + } } diff --git a/test/Storage/Adapter/RedisTest.php b/test/Storage/Adapter/RedisTest.php index 5e2a6f6fd..a8d966e98 100644 --- a/test/Storage/Adapter/RedisTest.php +++ b/test/Storage/Adapter/RedisTest.php @@ -312,4 +312,20 @@ public function testOptionsGetSetPassword() $this->_options->setPassword($password); $this->assertEquals($password, $this->_options->getPassword(), 'Password was set incorrectly using RedisOptions'); } + + public function testTouchItem() + { + $key = 'key'; + + // no TTL + $this->_storage->getOptions()->setTtl(0); + $this->_storage->setItem($key, 'val'); + $this->assertEquals(0, $this->_storage->getMetadata($key)['ttl']); + + // touch with a specific TTL will add this TTL + $ttl = 1000; + $this->_storage->getOptions()->setTtl($ttl); + $this->assertTrue($this->_storage->touchItem($key)); + $this->assertEquals($ttl, ceil($this->_storage->getMetadata($key)['ttl'])); + } }