diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ac9c427..dae62d42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,10 @@ ### Added - Added cluster support for `XADD`, `XDEL` and `XRANGE` (#1587) - Added prefixable interface for `HEXPIRE` and `HEXPIRETIME` (#1592) +- Added new experimental CAS/CAD functionality (#1609) - Added temporary XREADGROUP_CLAIM command (#1608) - Added support for MSET command (#1610) +- Added experimental support for FT.HYBRID (#1607) ### Changed - Refactor pipeline data writing depends on connection type (#1586) @@ -20,7 +22,6 @@ ### Added - Added support for `XDELEX` and `XACKDEL` (#1580) - Added missing VSIM argument (#1582) -- Added experimental support for FT.HYBRID (#1607) ### Changed - Extended `XTRIM` and `XADD` commands with new parameters (#1580) diff --git a/src/ClientContextInterface.php b/src/ClientContextInterface.php index f7b512f9..088bef0c 100644 --- a/src/ClientContextInterface.php +++ b/src/ClientContextInterface.php @@ -53,6 +53,8 @@ use Predis\Command\Redis\VADD; * * @method $this copy(string $source, string $destination, int $db = -1, bool $replace = false) * @method $this del(array|string $keys) + * @method $this delex(string $key, string $flag, $flagValue) + * @method $this digest(string $key) * @method $this dump($key) * @method $this exists($key) * @method $this expire($key, $seconds, string $expireOption = '') @@ -153,7 +155,7 @@ use Predis\Command\Redis\VADD; * @method $this msetex(array $dictionary, ?string $existModifier = null, ?string $expireResolution = null, ?int $expireTTL = null) * @method $this msetnx(array $dictionary) * @method $this psetex($key, $milliseconds, $value) - * @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null) + * @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null, $flagValue = null) * @method $this setbit($key, $offset, $value) * @method $this setex($key, $seconds, $value) * @method $this setnx($key, $value) diff --git a/src/ClientInterface.php b/src/ClientInterface.php index c8bc7b8f..dfcb0c63 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -63,6 +63,8 @@ use Predis\Response\Status; * * @method int copy(string $source, string $destination, int $db = -1, bool $replace = false) * @method int del(string[]|string $keyOrKeys, string ...$keys = null) + * @method int delex(string $key, string $flag, $flagValue) + * @method string digest(string $key) * @method string|null dump(string $key) * @method int exists(string $key) * @method int expire(string $key, int $seconds, string $expireOption = '') @@ -163,7 +165,7 @@ use Predis\Response\Status; * @method array msetex(array $dictionary, ?string $existModifier = null, ?string $expireResolution = null, ?int $expireTTL = null) * @method int msetnx(array $dictionary) * @method Status psetex(string $key, $milliseconds, $value) - * @method Status|null set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null) + * @method Status|null set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null, $flagValue = null) * @method int setbit(string $key, $offset, $value) * @method Status setex(string $key, $seconds, $value) * @method int setnx(string $key, $value) diff --git a/src/Command/Redis/DELEX.php b/src/Command/Redis/DELEX.php new file mode 100644 index 00000000..35532696 --- /dev/null +++ b/src/Command/Redis/DELEX.php @@ -0,0 +1,34 @@ +applyPrefixForFirstArgument($prefix); + } +} diff --git a/src/Command/Redis/DIGEST.php b/src/Command/Redis/DIGEST.php new file mode 100644 index 00000000..ca018973 --- /dev/null +++ b/src/Command/Redis/DIGEST.php @@ -0,0 +1,34 @@ +applyPrefixForFirstArgument($prefix); + } +} diff --git a/src/Command/Redis/Utils/CommandUtility.php b/src/Command/Redis/Utils/CommandUtility.php index 540afea9..4dbd25af 100644 --- a/src/Command/Redis/Utils/CommandUtility.php +++ b/src/Command/Redis/Utils/CommandUtility.php @@ -12,6 +12,7 @@ namespace Predis\Command\Redis\Utils; +use RuntimeException; use UnexpectedValueException; class CommandUtility @@ -54,6 +55,21 @@ class CommandUtility return $dict; } + /** + * Converts a value into XXH3 hash. + * + * @param $value + * @return string + */ + public static function xxh3Hash($value): string + { + if (!in_array('xxh3', hash_algos(), true)) { + throw new RuntimeException('XXH3 algorithm is not supported. Please install PECL xxhash extension.'); + } + + return hash('xxh3', $value); + } + /** * Converts associative array into flatten array (key1, value1...keyN, valueN). * diff --git a/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php b/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php index c52a32c8..a3486f19 100644 --- a/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php +++ b/tests/Predis/Command/Processor/KeyPrefixProcessorTest.php @@ -241,6 +241,14 @@ class KeyPrefixProcessorTest extends PredisTestCase ['key1', 'key2', 'key3'], ['prefix:key1', 'prefix:key2', 'prefix:key3'], ], + ['DELEX', + ['key1', 'IFEQ', 'value'], + ['prefix:key1', 'IFEQ', 'value'], + ], + ['DIGEST', + ['key1'], + ['prefix:key1'], + ], ['TYPE', ['key'], ['prefix:key'], diff --git a/tests/Predis/Command/Redis/DELEX_Test.php b/tests/Predis/Command/Redis/DELEX_Test.php new file mode 100644 index 00000000..074f916c --- /dev/null +++ b/tests/Predis/Command/Redis/DELEX_Test.php @@ -0,0 +1,115 @@ +getCommand(); + $command->setArguments($arguments); + $this->assertSame($expected, $command->getArguments()); + } + + /** + * @group connected + * @requires PHP >= 8.1 + * @requiresRedisVersion >= 8.3.224 + */ + public function testDeleteValueWithDifferentModifiers(): void + { + $redis = $this->getClient(); + $this->assertEquals( + 'OK', + $redis->set('foo', 'bar') + ); + + $value = $redis->get('foo'); + $this->assertEquals( + 1, + $redis->delex('foo', 'IFEQ', $value) + ); + $this->assertEquals( + 'OK', + $redis->set('foo', 'bar') + ); + + $this->assertEquals( + 0, + $redis->delex('foo', 'IFEQ', 'wrong') + ); + $this->assertEquals( + 1, + $redis->delex('foo', 'IFNE', 'not equal') + ); + $this->assertEquals( + 'OK', + $redis->set('foo', 'bar') + ); + + $this->assertEquals( + 0, + $redis->delex('foo', 'IFNE', $value) + ); + $this->assertEquals( + 1, + $redis->delex('foo', 'IFDEQ', CommandUtility::xxh3Hash($value)) + ); + $this->assertEquals( + 'OK', + $redis->set('foo', 'bar') + ); + + $this->assertEquals( + 0, + $redis->delex('foo', 'IFDEQ', CommandUtility::xxh3Hash('wrong')) + ); + $this->assertEquals( + 1, + $redis->delex('foo', 'IFDNE', CommandUtility::xxh3Hash('not equal')) + ); + $this->assertEquals( + 'OK', + $redis->set('foo', 'bar') + ); + + $this->assertEquals( + 0, + $redis->delex('foo', 'IFDNE', CommandUtility::xxh3Hash($value)) + ); + } +} diff --git a/tests/Predis/Command/Redis/DIGEST_Test.php b/tests/Predis/Command/Redis/DIGEST_Test.php new file mode 100644 index 00000000..b1eb5ffb --- /dev/null +++ b/tests/Predis/Command/Redis/DIGEST_Test.php @@ -0,0 +1,60 @@ +getCommand(); + $command->setArguments($arguments); + $this->assertSame($expected, $command->getArguments()); + } + + /** + * @group connected + * @requires PHP >= 8.1 + * @requiresRedisVersion >= 8.3.224 + */ + public function testReturnsDigestOfGivenValue(): void + { + $redis = $this->getClient(); + + $this->assertEquals('OK', $redis->set('foo', 'bar')); + $this->assertEquals(CommandUtility::xxh3Hash('bar'), $redis->digest('foo')); + } +} diff --git a/tests/Predis/Command/Redis/SET_Test.php b/tests/Predis/Command/Redis/SET_Test.php index 41f1c0e7..f2fcb675 100644 --- a/tests/Predis/Command/Redis/SET_Test.php +++ b/tests/Predis/Command/Redis/SET_Test.php @@ -13,6 +13,7 @@ namespace Predis\Command\Redis; use Predis\Command\PrefixableCommand; +use Predis\Command\Redis\Utils\CommandUtility; /** * @group commands @@ -256,6 +257,50 @@ class SET_Test extends PredisCommandTestCase $this->testSetStringValueWithModifierXX(); } + /** + * @group connected + * @requires PHP >= 8.1 + * @requiresRedisVersion >= 8.3.224 + */ + public function testSetStringValueWithNewModifiers(): void + { + $redis = $this->getClient(); + $this->assertEquals( + 'OK', + $redis->set('foo', 'bar') + ); + + $value = $redis->get('foo'); + $this->assertEquals( + 'OK', + $redis->set('foo', $value, null, null, 'IFEQ', $value) + ); + $this->assertNull( + $redis->set('foo', $value, null, null, 'IFEQ', 'wrong') + ); + $this->assertEquals( + 'OK', + $redis->set('foo', $value, null, null, 'IFNE', 'not equal') + ); + $this->assertNull( + $redis->set('foo', $value, null, null, 'IFNE', $value) + ); + $this->assertEquals( + 'OK', + $redis->set('foo', $value, null, null, 'IFDEQ', CommandUtility::xxh3Hash($value)) + ); + $this->assertNull( + $redis->set('foo', $value, null, null, 'IFDEQ', CommandUtility::xxh3Hash('wrong')) + ); + $this->assertEquals( + 'OK', + $redis->set('foo', $value, null, null, 'IFDNE', CommandUtility::xxh3Hash('not equal')) + ); + $this->assertNull( + $redis->set('foo', $value, null, null, 'IFDNE', CommandUtility::xxh3Hash($value)) + ); + } + /** * @group connected * @group cluster diff --git a/tests/Predis/Command/Utils/CommandUtilityTest.php b/tests/Predis/Command/Utils/CommandUtilityTest.php index 11fa6688..55d7e317 100644 --- a/tests/Predis/Command/Utils/CommandUtilityTest.php +++ b/tests/Predis/Command/Utils/CommandUtilityTest.php @@ -14,11 +14,13 @@ namespace Predis\Command\Utils; use Predis\Command\Redis\Utils\CommandUtility; use PredisTestCase; +use RuntimeException; use UnexpectedValueException; class CommandUtilityTest extends PredisTestCase { /** + * @group disconnected * @dataProvider arrayProvider * @param array $actual * @param array $expected @@ -32,6 +34,7 @@ class CommandUtilityTest extends PredisTestCase } /** + * @group disconnected * @return void */ public function testArrayToDictionaryThrowsExceptionOnOddNumberOfElements() @@ -43,6 +46,32 @@ class CommandUtilityTest extends PredisTestCase } /** + * @group disconnected + * @requires PHP >= 8.1 + * @return void + */ + public function testXXH3Hash() + { + $expectedHash = '87d57e269b9df0f0'; + + $this->assertEquals($expectedHash, CommandUtility::xxh3Hash('value')); + } + + /** + * @group disconnected + * @requires PHP < 8.1 + * @return void + */ + public function testXXH3HashRaiseExceptionOnPHPLowerThan81() + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('XXH3 algorithm is not supported. Please install PECL xxhash extension.'); + + CommandUtility::xxh3Hash('value'); + } + + /** + * @group disconnected * @return void */ public function testDictionaryToArray(): void