feat: bless commands family (#1733)

* feat: bless commands family

* fix: bless scan args processing
This commit is contained in:
Vladislav Kotsev
2026-10-06 14:36:19 +03:00
committed by GitHub
parent a33f9f85c2
commit c270c2d24e
10 changed files with 470 additions and 1 deletions
+2
View File
@@ -42,6 +42,7 @@ use Predis\Command\Argument\TimeSeries\RangeArguments;
use Predis\Command\Argument\TimeSeries\ReadArguments;
use Predis\Command\CommandInterface;
use Predis\Command\Container\ACL;
use Predis\Command\Container\BLESS;
use Predis\Command\Container\CLIENT;
use Predis\Command\Container\FUNCTIONS;
use Predis\Command\Container\HIMPORT;
@@ -431,6 +432,7 @@ use Predis\Command\Redis\VADD;
* @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* Container commands
* @property BLESS $bless
* @property CLIENT $client
* @property HIMPORT $himport
* @property HOTKEYS $hotkeys
+2
View File
@@ -42,6 +42,7 @@ use Predis\Command\Argument\TimeSeries\RangeArguments;
use Predis\Command\Argument\TimeSeries\ReadArguments;
use Predis\Command\CommandInterface;
use Predis\Command\Container\ACL;
use Predis\Command\Container\BLESS;
use Predis\Command\Container\CLIENT;
use Predis\Command\Container\FUNCTIONS;
use Predis\Command\Container\HIMPORT;
@@ -446,6 +447,7 @@ use Predis\Response\Status;
* @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* Container commands
* @property BLESS $bless
* @property CLIENT $client
* @property HIMPORT $himport
* @property HOTKEYS $hotkeys
+25
View File
@@ -214,6 +214,9 @@ abstract class ClusterStrategy implements StrategyInterface
'XSETID' => $getKeyFromFirstArgument,
'XTRIM' => $getKeyFromFirstArgument,
/* commands operating on blessings */
'BLESS' => [$this, 'getKeyFromBlessCommands'],
/* commands operating on vector sets */
'VADD' => $getKeyFromFirstArgument,
'VCARD' => $getKeyFromFirstArgument,
@@ -482,6 +485,28 @@ abstract class ClusterStrategy implements StrategyInterface
return $arguments[1];
}
/**
* Extracts the key from BLESS commands.
*
* BLESS GET/SET/CLEAR operate on a key at position 1, after the subcommand.
* BLESS SCAN takes a cursor and a flag instead of a key, so it has no slot
* and this returns null.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromBlessCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
if (isset($arguments[0], $arguments[1]) && in_array(strtoupper($arguments[0]), ['GET', 'SET', 'CLEAR'], true)) {
return $arguments[1];
}
return null;
}
/**
* Extracts the key from HIMPORT commands.
*
+42
View File
@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2026 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\Container;
/**
* @method array get(string $key) Returns the flags currently set on the key.
* @method int set(string $key, string $flag) Returns 1 if the flag was added, 0 if it was already set.
* @method int clear(string $key, string $flag) Returns 1 if the flag was removed, 0 if it was not set.
*/
class BLESS extends AbstractContainer
{
public function getContainerCommandId(): string
{
return 'BLESS';
}
/**
* Iterates over the keys carrying the given flag.
*
* Mirrors the plain SCAN command signature: options are passed as an
* array, e.g. ['COUNT' => 10].
*
* @param string|int $cursor Cursor returned by the previous call, 0 to start a new iteration.
* @param string $flag Flag to scan for.
* @param array|null $options Scan options, supports COUNT.
* @return array|mixed [next cursor, list of keys]; an error response when the client runs with exceptions disabled
*/
public function scan($cursor, string $flag, ?array $options = null)
{
return $this->__call('SCAN', func_get_args());
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2026 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 as RedisCommand;
/**
* @see https://redis.io/commands/?name=bless
*
* Container command corresponds to any BLESS *.
* Represents any BLESS command with subcommand as first argument.
*/
class BLESS extends RedisCommand
{
private const KEYED_SUBCOMMANDS = ['GET', 'SET', 'CLEAR'];
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BLESS';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
if (isset($arguments[0]) && strtoupper($arguments[0]) === 'SCAN') {
$this->setScanArguments($arguments);
} else {
parent::setArguments($arguments);
}
}
/**
* BLESS SCAN cursor flag [COUNT count].
*
* Mirrors the plain SCAN command: options are passed as an array
* (e.g. ['COUNT' => 10]) and expanded into Redis modifiers.
*
* @param array $arguments
* @return void
*/
private function setScanArguments(array $arguments): void
{
if (count($arguments) === 4) {
$options = array_pop($arguments);
if (is_array($options)) {
$options = $this->prepareScanOptions($options);
$arguments = array_merge($arguments, $options);
}
}
parent::setArguments($arguments);
}
/**
* Returns a list of SCAN options and modifiers compatible with Redis.
*
* @param array $options List of options.
* @return array
*/
private function prepareScanOptions(array $options): array
{
$options = array_change_key_case($options, CASE_UPPER);
$normalized = [];
if (!empty($options['COUNT'])) {
$normalized[] = 'COUNT';
$normalized[] = $options['COUNT'];
}
return $normalized;
}
public function prefixKeys($prefix)
{
$arguments = $this->getArguments();
// BLESS GET/SET/CLEAR operate on a key at index 1, right after the subcommand.
// BLESS SCAN takes a cursor and a flag, never a key.
if (isset($arguments[0], $arguments[1]) && in_array(strtoupper($arguments[0]), self::KEYED_SUBCOMMANDS, true)) {
$arguments[1] = $prefix . $arguments[1];
$this->setRawArguments($arguments);
}
}
}