Added new experimental CAS/CAD functionallity (#1609)

* Added new CAS/CAD functionallity

* Updated CHANGELOG.md

* Codestyle fixes

* Fixed version constraint

* Marked feature as experimental

* Updated version constraints, added missing annotations
This commit is contained in:
Vladyslav Vildanov
2025-11-12 10:14:06 +02:00
committed by GitHub
parent d0f37f0bac
commit 80430b4cde
11 changed files with 349 additions and 3 deletions
+2 -1
View File
@@ -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)
+3 -1
View File
@@ -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)
+3 -1
View File
@@ -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)
+34
View File
@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\PrefixableCommand;
class DELEX extends PrefixableCommand
{
/**
* {@inheritDoc}
*/
public function getId()
{
return 'DELEX';
}
/**
* {@inheritDoc}
*/
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\PrefixableCommand;
class DIGEST extends PrefixableCommand
{
/**
* {@inheritDoc}
*/
public function getId()
{
return 'DIGEST';
}
/**
* {@inheritDoc}
*/
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
@@ -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).
*
@@ -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'],
+115
View File
@@ -0,0 +1,115 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Redis\Utils\CommandUtility;
class DELEX_Test extends PredisCommandTestCase
{
/**
* {@inheritDoc}
*/
protected function getExpectedCommand(): string
{
return DELEX::class;
}
/**
* {@inheritDoc}
*/
protected function getExpectedId(): string
{
return 'DELEX';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = ['key1'];
$expected = ['key1'];
$command = $this->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))
);
}
}
@@ -0,0 +1,60 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Redis\Utils\CommandUtility;
class DIGEST_Test extends PredisCommandTestCase
{
/**
* {@inheritDoc}
*/
protected function getExpectedCommand(): string
{
return DIGEST::class;
}
/**
* {@inheritDoc}
*/
protected function getExpectedId(): string
{
return 'DIGEST';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = ['key1'];
$expected = ['key1'];
$command = $this->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'));
}
}
+45
View File
@@ -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
@@ -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