From 50e005bfe33b94a137609ebe8bcbffd5e92886b4 Mon Sep 17 00:00:00 2001 From: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com> Date: Wed, 25 Jan 2023 18:00:23 +0200 Subject: [PATCH] Added support for new arguments for EXPIRE, EXPIREAT commands (#1046) --- src/ClientContextInterface.php | 4 +- src/ClientInterface.php | 4 +- src/Command/Redis/EXPIRE.php | 7 +++ src/Command/Redis/EXPIREAT.php | 6 ++ src/Command/Traits/Expire/ExpireOptions.php | 36 ++++++++++++ tests/Predis/Command/Redis/EXPIREAT_Test.php | 57 ++++++++++++++++++ tests/Predis/Command/Redis/EXPIRE_Test.php | 61 +++++++++++++++++++- 7 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 src/Command/Traits/Expire/ExpireOptions.php diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index 20403f65..c576334d 100644 --- a/src/ClientContextInterface.php +++ b/src/ClientContextInterface.php @@ -24,8 +24,8 @@ use Predis\Command\CommandInterface; * @method $this del(array|string $keys) * @method $this dump($key) * @method $this exists($key) - * @method $this expire($key, $seconds) - * @method $this expireat($key, $timestamp) + * @method $this expire($key, $seconds, string $expireOption = '') + * @method $this expireat($key, $timestamp, string $expireOption = '') * @method $this expiretime(string $key) * @method $this keys($pattern) * @method $this move($key, $db) diff --git a/src/ClientInterface.php b/src/ClientInterface.php index de239f3d..b2989274 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -33,8 +33,8 @@ use Predis\Response\Status; * @method int del(string[]|string $keyOrKeys, string ...$keys = null) * @method string|null dump(string $key) * @method int exists(string $key) - * @method int expire(string $key, int $seconds) - * @method int expireat(string $key, int $timestamp) + * @method int expire(string $key, int $seconds, string $expireOption = '') + * @method int expireat(string $key, int $timestamp, string $expireOption = '') * @method int expiretime(string $key) * @method array keys(string $pattern) * @method int move(string $key, int $db) diff --git a/src/Command/Redis/EXPIRE.php b/src/Command/Redis/EXPIRE.php index a66d92fd..9957acde 100644 --- a/src/Command/Redis/EXPIRE.php +++ b/src/Command/Redis/EXPIRE.php @@ -13,12 +13,19 @@ namespace Predis\Command\Redis; use Predis\Command\Command as RedisCommand; +use Predis\Command\Traits\Expire\ExpireOptions; /** * @see http://redis.io/commands/expire + * + * Set a timeout on key. + * After the timeout has expired, the key will automatically be deleted. + * A key with an associated timeout is often said to be volatile in Redis terminology. */ class EXPIRE extends RedisCommand { + use ExpireOptions; + /** * {@inheritdoc} */ diff --git a/src/Command/Redis/EXPIREAT.php b/src/Command/Redis/EXPIREAT.php index d1a50060..ad9314de 100644 --- a/src/Command/Redis/EXPIREAT.php +++ b/src/Command/Redis/EXPIREAT.php @@ -13,12 +13,18 @@ namespace Predis\Command\Redis; use Predis\Command\Command as RedisCommand; +use Predis\Command\Traits\Expire\ExpireOptions; /** * @see http://redis.io/commands/expireat + * + * EXPIREAT has the same effect and semantic as EXPIRE, but instead of specifying + * the number of seconds representing the TTL (time to live), it takes an absolute Unix timestamp */ class EXPIREAT extends RedisCommand { + use ExpireOptions; + /** * {@inheritdoc} */ diff --git a/src/Command/Traits/Expire/ExpireOptions.php b/src/Command/Traits/Expire/ExpireOptions.php new file mode 100644 index 00000000..85cff7a2 --- /dev/null +++ b/src/Command/Traits/Expire/ExpireOptions.php @@ -0,0 +1,36 @@ + 'NX', + 'xx' => 'XX', + 'gt' => 'GT', + 'lt' => 'LT', + ]; + + public function setArguments(array $arguments) + { + $value = array_pop($arguments); + + if (in_array(strtoupper($value), self::$argumentEnum, true)) { + $arguments[] = self::$argumentEnum[strtolower($value)]; + } else { + $arguments[] = $value; + } + + parent::setArguments($arguments); + } +} diff --git a/tests/Predis/Command/Redis/EXPIREAT_Test.php b/tests/Predis/Command/Redis/EXPIREAT_Test.php index bbc71769..78490200 100644 --- a/tests/Predis/Command/Redis/EXPIREAT_Test.php +++ b/tests/Predis/Command/Redis/EXPIREAT_Test.php @@ -90,6 +90,33 @@ class EXPIREAT_Test extends PredisCommandTestCase $this->assertSame(0, $redis->exists('foo')); } + /** + * @medium + * @group connected + * @dataProvider keysProvider + * @group slow + * @param array $firstKeyArguments + * @param array $secondKeyArguments + * @param array $positivePathArguments + * @param array $negativePathArguments + * @return void + * @requiresRedisVersion >= 7.0.0 + */ + public function testSetNewExpirationTimeWithExpireOptions( + array $firstKeyArguments, + array $secondKeyArguments, + array $positivePathArguments, + array $negativePathArguments + ): void { + $redis = $this->getClient(); + + $redis->set(...$firstKeyArguments); + $redis->set(...$secondKeyArguments); + + $this->assertSame(1, $redis->expireat(...$positivePathArguments)); + $this->assertSame(0, $redis->expireat(...$negativePathArguments)); + } + /** * @group connected */ @@ -103,4 +130,34 @@ class EXPIREAT_Test extends PredisCommandTestCase $this->assertSame(1, $redis->expireat('foo', $now - 100)); $this->assertSame(0, $redis->exists('foo')); } + + public function keysProvider(): array + { + return [ + 'only if key has no expiry' => [ + ['noExpiry', 'value'], + ['withExpiry', 'value', 'EX', 10], + ['noExpiry', time() + 10, 'NX'], + ['withExpiry', time() + 10, 'NX'], + ], + 'only if key has expiry' => [ + ['noExpiry', 'value'], + ['withExpiry', 'value', 'EX', 10], + ['withExpiry', time() + 10, 'XX'], + ['noExpiry', time() + 10, 'XX'], + ], + 'only if new expiry is greater then current one' => [ + ['newExpiryLower', 'value', 'EXAT', time() + 1000], + ['newExpiryGreater', 'value', 'EXAT', time() + 10], + ['newExpiryGreater', time() + 20, 'GT'], + ['newExpiryLower', time() + 20, 'GT'], + ], + 'only if new expiry is lower then current one' => [ + ['newExpiryLower', 'value', 'EXAT', time() + 1000], + ['newExpiryGreater', 'value', 'EXAT', time() + 10], + ['newExpiryLower', time() + 20, 'LT'], + ['newExpiryGreater', time() + 20, 'LT'], + ], + ]; + } } diff --git a/tests/Predis/Command/Redis/EXPIRE_Test.php b/tests/Predis/Command/Redis/EXPIRE_Test.php index eb325109..2e849767 100644 --- a/tests/Predis/Command/Redis/EXPIRE_Test.php +++ b/tests/Predis/Command/Redis/EXPIRE_Test.php @@ -39,8 +39,8 @@ class EXPIRE_Test extends PredisCommandTestCase */ public function testFilterArguments(): void { - $arguments = ['key', 'ttl']; - $expected = ['key', 'ttl']; + $arguments = ['key', 'ttl', 'xx']; + $expected = ['key', 'ttl', 'XX']; $command = $this->getCommand(); $command->setArguments($arguments); @@ -87,6 +87,33 @@ class EXPIRE_Test extends PredisCommandTestCase $this->assertSame(0, $redis->exists('foo')); } + /** + * @medium + * @group connected + * @dataProvider keysProvider + * @group slow + * @param array $firstKeyArguments + * @param array $secondKeyArguments + * @param array $positivePathArguments + * @param array $negativePathArguments + * @return void + * @requiresRedisVersion >= 7.0.0 + */ + public function testSetNewExpirationTimeWithExpireOptions( + array $firstKeyArguments, + array $secondKeyArguments, + array $positivePathArguments, + array $negativePathArguments + ): void { + $redis = $this->getClient(); + + $redis->set(...$firstKeyArguments); + $redis->set(...$secondKeyArguments); + + $this->assertSame(1, $redis->expire(...$positivePathArguments)); + $this->assertSame(0, $redis->expire(...$negativePathArguments)); + } + /** * @group connected */ @@ -99,4 +126,34 @@ class EXPIRE_Test extends PredisCommandTestCase $this->assertSame(1, $redis->expire('foo', -10)); $this->assertSame(0, $redis->exists('foo')); } + + public function keysProvider(): array + { + return [ + 'only if key has no expiry' => [ + ['noExpiry', 'value'], + ['withExpiry', 'value', 'EX', 10], + ['noExpiry', 2, 'NX'], + ['withExpiry', 2, 'NX'], + ], + 'only if key has expiry' => [ + ['noExpiry', 'value'], + ['withExpiry', 'value', 'EX', 10], + ['withExpiry', 2, 'XX'], + ['noExpiry', 2, 'XX'], + ], + 'only if new expiry is greater then current one' => [ + ['newExpiryLower', 'value', 'EXAT', time() + 1000], + ['newExpiryGreater', 'value', 'EXAT', time() + 10], + ['newExpiryGreater', 20, 'GT'], + ['newExpiryLower', 20, 'GT'], + ], + 'only if new expiry is lower then current one' => [ + ['newExpiryLower', 'value', 'EXAT', time() + 1000], + ['newExpiryGreater', 'value', 'EXAT', time() + 10], + ['newExpiryLower', 20, 'LT'], + ['newExpiryGreater', 20, 'LT'], + ], + ]; + } }