Predis to v2.2.2

This commit is contained in:
the-djmaze 2024-04-02 20:15:23 +02:00
parent f6b440adef
commit 84ffe1e552
259 changed files with 2407 additions and 9937 deletions

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -11,21 +12,35 @@
namespace Predis; namespace Predis;
use ArrayIterator;
use InvalidArgumentException;
use IteratorAggregate;
use Predis\Command\CommandInterface; use Predis\Command\CommandInterface;
use Predis\Command\RawCommand; use Predis\Command\RawCommand;
use Predis\Command\Redis\Container\ContainerFactory;
use Predis\Command\Redis\Container\ContainerInterface;
use Predis\Command\ScriptCommand; use Predis\Command\ScriptCommand;
use Predis\Configuration\Options; use Predis\Configuration\Options;
use Predis\Configuration\OptionsInterface; use Predis\Configuration\OptionsInterface;
use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\ConnectionInterface; use Predis\Connection\ConnectionInterface;
use Predis\Connection\Parameters;
use Predis\Connection\ParametersInterface; use Predis\Connection\ParametersInterface;
use Predis\Connection\RelayConnection;
use Predis\Monitor\Consumer as MonitorConsumer; use Predis\Monitor\Consumer as MonitorConsumer;
use Predis\Pipeline\Atomic;
use Predis\Pipeline\FireAndForget;
use Predis\Pipeline\Pipeline; use Predis\Pipeline\Pipeline;
use Predis\Pipeline\RelayAtomic;
use Predis\Pipeline\RelayPipeline;
use Predis\PubSub\Consumer as PubSubConsumer; use Predis\PubSub\Consumer as PubSubConsumer;
use Predis\PubSub\RelayConsumer as RelayPubSubConsumer;
use Predis\Response\ErrorInterface as ErrorResponseInterface; use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ResponseInterface; use Predis\Response\ResponseInterface;
use Predis\Response\ServerException; use Predis\Response\ServerException;
use Predis\Transaction\MultiExec as MultiExecTransaction; use Predis\Transaction\MultiExec as MultiExecTransaction;
use ReturnTypeWillChange;
use RuntimeException;
use Traversable;
/** /**
* Client class used for connecting and executing commands on Redis. * Client class used for connecting and executing commands on Redis.
@ -34,17 +49,20 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
* abstractions are built. Internally it aggregates various other classes each * abstractions are built. Internally it aggregates various other classes each
* one with its own responsibility and scope. * one with its own responsibility and scope.
* *
* {@inheritdoc} * @template-implements \IteratorAggregate<string, static>
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
class Client implements ClientInterface class Client implements ClientInterface, IteratorAggregate
{ {
const VERSION = '1.0.3'; public const VERSION = '2.2.2';
protected $connection; /** @var OptionsInterface */
protected $options; private $options;
private $profile;
/** @var ConnectionInterface */
private $connection;
/** @var Command\FactoryInterface */
private $commands;
/** /**
* @param mixed $parameters Connection parameters for one or more servers. * @param mixed $parameters Connection parameters for one or more servers.
@ -52,126 +70,99 @@ class Client implements ClientInterface
*/ */
public function __construct($parameters = null, $options = null) public function __construct($parameters = null, $options = null)
{ {
$this->options = $this->createOptions($options ?: array()); $this->options = static::createOptions($options ?? new Options());
$this->connection = $this->createConnection($parameters ?: array()); $this->connection = static::createConnection($this->options, $parameters ?? new Parameters());
$this->profile = $this->options->profile; $this->commands = $this->options->commands;
} }
/** /**
* Creates a new instance of Predis\Configuration\Options from different * Creates a new set of client options for the client.
* types of arguments or simply returns the passed argument if it is an
* instance of Predis\Configuration\OptionsInterface.
* *
* @param mixed $options Client options. * @param array|OptionsInterface $options Set of client options
*
* @throws \InvalidArgumentException
* *
* @return OptionsInterface * @return OptionsInterface
* @throws InvalidArgumentException
*/ */
protected function createOptions($options) protected static function createOptions($options)
{ {
if (is_array($options)) { if (is_array($options)) {
return new Options($options); return new Options($options);
} } elseif ($options instanceof OptionsInterface) {
if ($options instanceof OptionsInterface) {
return $options; return $options;
} else {
throw new InvalidArgumentException('Invalid type for client options');
} }
throw new \InvalidArgumentException('Invalid type for client options.');
} }
/** /**
* Creates single or aggregate connections from different types of arguments * Creates single or aggregate connections from supplied arguments.
* (string, array) or returns the passed argument if it is an instance of a
* class implementing Predis\Connection\ConnectionInterface.
* *
* Accepted types for connection parameters are: * This method accepts the following types to create a connection instance:
* *
* - Instance of Predis\Connection\ConnectionInterface. * - Array (dictionary: single connection, indexed: aggregate connections)
* - Instance of Predis\Connection\ParametersInterface. * - String (URI for a single connection)
* - Array * - Callable (connection initializer callback)
* - String * - Instance of Predis\Connection\ParametersInterface (used as-is)
* - Callable * - Instance of Predis\Connection\ConnectionInterface (returned as-is)
* *
* @param mixed $parameters Connection parameters or connection instance. * When a callable is passed, it receives the original set of client options
* and must return an instance of Predis\Connection\ConnectionInterface.
* *
* @throws \InvalidArgumentException * Connections are created using the connection factory (in case of single
* connections) or a specialized aggregate connection initializer (in case
* of cluster and replication) retrieved from the supplied client options.
*
* @param OptionsInterface $options Client options container
* @param mixed $parameters Connection parameters
* *
* @return ConnectionInterface * @return ConnectionInterface
* @throws InvalidArgumentException
*/ */
protected function createConnection($parameters) protected static function createConnection(OptionsInterface $options, $parameters)
{ {
if ($parameters instanceof ConnectionInterface) { if ($parameters instanceof ConnectionInterface) {
return $parameters; return $parameters;
} }
if ($parameters instanceof ParametersInterface || is_string($parameters)) { if ($parameters instanceof ParametersInterface || is_string($parameters)) {
return $this->options->connections->create($parameters); return $options->connections->create($parameters);
} }
if (is_array($parameters)) { if (is_array($parameters)) {
if (!isset($parameters[0])) { if (!isset($parameters[0])) {
return $this->options->connections->create($parameters); return $options->connections->create($parameters);
} } elseif ($options->defined('cluster') && $initializer = $options->cluster) {
return $initializer($parameters, true);
$options = $this->options; } elseif ($options->defined('replication') && $initializer = $options->replication) {
return $initializer($parameters, true);
if ($options->defined('aggregate')) { } elseif ($options->defined('aggregate') && $initializer = $options->aggregate) {
$initializer = $this->getConnectionInitializerWrapper($options->aggregate); return $initializer($parameters, false);
$connection = $initializer($parameters, $options);
} else { } else {
if ($options->defined('replication') && $replication = $options->replication) { throw new InvalidArgumentException(
$connection = $replication; 'Array of connection parameters requires `cluster`, `replication` or `aggregate` client option'
} else { );
$connection = $options->cluster;
}
$options->connections->aggregate($connection, $parameters);
} }
return $connection;
} }
if (is_callable($parameters)) { if (is_callable($parameters)) {
$initializer = $this->getConnectionInitializerWrapper($parameters); $connection = call_user_func($parameters, $options);
$connection = $initializer($this->options);
if (!$connection instanceof ConnectionInterface) {
throw new InvalidArgumentException('Callable parameters must return a valid connection');
}
return $connection; return $connection;
} }
throw new \InvalidArgumentException('Invalid type for connection parameters.'); throw new InvalidArgumentException('Invalid type for connection parameters');
}
/**
* Wraps a callable to make sure that its returned value represents a valid
* connection type.
*
* @param mixed $callable
*
* @return \Closure
*/
protected function getConnectionInitializerWrapper($callable)
{
return function () use ($callable) {
$connection = call_user_func_array($callable, func_get_args());
if (!$connection instanceof ConnectionInterface) {
throw new \UnexpectedValueException(
'The callable connection initializer returned an invalid type.'
);
}
return $connection;
};
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getProfile() public function getCommandFactory()
{ {
return $this->profile; return $this->commands;
} }
/** /**
@ -183,23 +174,53 @@ class Client implements ClientInterface
} }
/** /**
* Creates a new client instance for the specified connection ID or alias, * Creates a new client using a specific underlying connection.
* only when working with an aggregate connection (cluster, replication).
* The new client instances uses the same options of the original one.
* *
* @param string $connectionID Identifier of a connection. * This method allows to create a new client instance by picking a specific
* connection out of an aggregate one, with the same options of the original
* client instance.
* *
* @throws \InvalidArgumentException * The specified selector defines which logic to use to look for a suitable
* connection by the specified value. Supported selectors are:
* *
* @return Client * - `id`
* - `key`
* - `slot`
* - `command`
* - `alias`
* - `role`
*
* Internally the client relies on duck-typing and follows this convention:
*
* $selector string => getConnectionBy$selector($value) method
*
* This means that support for specific selectors may vary depending on the
* actual logic implemented by connection classes and there is no interface
* binding a connection class to implement any of these.
*
* @param string $selector Type of selector.
* @param mixed $value Value to be used by the selector.
*
* @return ClientInterface
*/ */
public function getClientFor($connectionID) public function getClientBy($selector, $value)
{ {
if (!$connection = $this->getConnectionById($connectionID)) { $selector = strtolower($selector);
throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
if (!in_array($selector, ['id', 'key', 'slot', 'role', 'alias', 'command'])) {
throw new InvalidArgumentException("Invalid selector type: `$selector`");
} }
return new static($connection, $this->options); if (!method_exists($this->connection, $method = "getConnectionBy$selector")) {
$class = get_class($this->connection);
throw new InvalidArgumentException("Selecting connection by $selector is not supported by $class");
}
if (!$connection = $this->connection->$method($value)) {
throw new InvalidArgumentException("Cannot find a connection by $selector matching `$value`");
}
return new static($connection, $this->getOptions());
} }
/** /**
@ -248,24 +269,29 @@ class Client implements ClientInterface
} }
/** /**
* Retrieves the specified connection from the aggregate connection when the * Applies the configured serializer and compression to given value.
* client is in cluster or replication mode.
* *
* @param string $connectionID Index or alias of the single connection. * @param mixed $value
* * @return string
* @throws NotSupportedException
*
* @return Connection\NodeConnectionInterface
*/ */
public function getConnectionById($connectionID) public function pack($value)
{ {
if (!$this->connection instanceof AggregateConnectionInterface) { return $this->connection instanceof RelayConnection
throw new NotSupportedException( ? $this->connection->pack($value)
'Retrieving connections by ID is supported only by aggregate connections.' : $value;
); }
}
return $this->connection->getConnectionById($connectionID); /**
* Deserializes and decompresses to given value.
*
* @param mixed $value
* @return string
*/
public function unpack($value)
{
return $this->connection instanceof RelayConnection
? $this->connection->unpack($value)
: $value;
} }
/** /**
@ -273,7 +299,7 @@ class Client implements ClientInterface
* applying any prefix to keys or throwing exceptions on Redis errors even * applying any prefix to keys or throwing exceptions on Redis errors even
* regardless of client options. * regardless of client options.
* *
* It is possibile to indentify Redis error responses from normal responses * It is possible to identify Redis error responses from normal responses
* using the second optional argument which is populated by reference. * using the second optional argument which is populated by reference.
* *
* @param array $arguments Command arguments as defined by the command signature. * @param array $arguments Command arguments as defined by the command signature.
@ -284,9 +310,10 @@ class Client implements ClientInterface
public function executeRaw(array $arguments, &$error = null) public function executeRaw(array $arguments, &$error = null)
{ {
$error = false; $error = false;
$commandID = array_shift($arguments);
$response = $this->connection->executeCommand( $response = $this->connection->executeCommand(
new RawCommand($arguments) new RawCommand($commandID, $arguments)
); );
if ($response instanceof ResponseInterface) { if ($response instanceof ResponseInterface) {
@ -313,9 +340,37 @@ class Client implements ClientInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function createCommand($commandID, $arguments = array()) public function createCommand($commandID, $arguments = [])
{ {
return $this->profile->createCommand($commandID, $arguments); return $this->commands->create($commandID, $arguments);
}
/**
* @param string $name
* @return ContainerInterface
*/
public function __get(string $name)
{
return ContainerFactory::create($this, $name);
}
/**
* @param string $name
* @param mixed $value
* @return mixed
*/
public function __set(string $name, $value)
{
throw new RuntimeException('Not allowed');
}
/**
* @param string $name
* @return mixed
*/
public function __isset(string $name)
{
throw new RuntimeException('Not allowed');
} }
/** /**
@ -342,17 +397,13 @@ class Client implements ClientInterface
* @param CommandInterface $command Redis command that generated the error. * @param CommandInterface $command Redis command that generated the error.
* @param ErrorResponseInterface $response Instance of the error response. * @param ErrorResponseInterface $response Instance of the error response.
* *
* @throws ServerException
*
* @return mixed * @return mixed
* @throws ServerException
*/ */
protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response) protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
{ {
if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') { if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
$eval = $this->createCommand('EVAL'); $response = $this->executeCommand($command->getEvalCommand());
$eval->setRawArguments($command->getEvalArguments());
$response = $this->executeCommand($eval);
if (!$response instanceof ResponseInterface) { if (!$response instanceof ResponseInterface) {
$response = $command->parseResponse($response); $response = $command->parseResponse($response);
@ -370,7 +421,7 @@ class Client implements ClientInterface
/** /**
* Executes the specified initializer method on `$this` by adjusting the * Executes the specified initializer method on `$this` by adjusting the
* actual invokation depending on the arity (0, 1 or 2 arguments). This is * actual invocation depending on the arity (0, 1 or 2 arguments). This is
* simply an utility method to create Redis contexts instances since they * simply an utility method to create Redis contexts instances since they
* follow a common initialization path. * follow a common initialization path.
* *
@ -391,7 +442,7 @@ class Client implements ClientInterface
: $this->$initializer(null, $argv[0]); : $this->$initializer(null, $argv[0]);
case 2: case 2:
list($arg0, $arg1) = $argv; [$arg0, $arg1] = $argv;
return $this->$initializer($arg0, $arg1); return $this->$initializer($arg0, $arg1);
@ -404,11 +455,11 @@ class Client implements ClientInterface
* Creates a new pipeline context and returns it, or returns the results of * Creates a new pipeline context and returns it, or returns the results of
* a pipeline executed inside the optionally provided callable object. * a pipeline executed inside the optionally provided callable object.
* *
* @param mixed ... Array of options, a callable for execution, or both. * @param mixed ...$arguments Array of options, a callable for execution, or both.
* *
* @return Pipeline|array * @return Pipeline|array
*/ */
public function pipeline(/* arguments */) public function pipeline(...$arguments)
{ {
return $this->sharedContextFactory('createPipeline', func_get_args()); return $this->sharedContextFactory('createPipeline', func_get_args());
} }
@ -416,19 +467,29 @@ class Client implements ClientInterface
/** /**
* Actual pipeline context initializer method. * Actual pipeline context initializer method.
* *
* @param array $options Options for the context. * @param array|null $options Options for the context.
* @param mixed $callable Optional callable used to execute the context. * @param mixed $callable Optional callable used to execute the context.
* *
* @return Pipeline|array * @return Pipeline|array
*/ */
protected function createPipeline(array $options = null, $callable = null) protected function createPipeline(array $options = null, $callable = null)
{ {
if (isset($options['atomic']) && $options['atomic']) { if (isset($options['atomic']) && $options['atomic']) {
$class = 'Predis\Pipeline\Atomic'; $class = Atomic::class;
} elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) { } elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
$class = 'Predis\Pipeline\FireAndForget'; $class = FireAndForget::class;
} else { } else {
$class = 'Predis\Pipeline\Pipeline'; $class = Pipeline::class;
}
if ($this->connection instanceof RelayConnection) {
if (isset($options['atomic']) && $options['atomic']) {
$class = RelayAtomic::class;
} elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
throw new NotSupportedException('The "relay" extension does not support fire-and-forget pipelines.');
} else {
$class = RelayPipeline::class;
}
} }
/* /*
@ -447,11 +508,11 @@ class Client implements ClientInterface
* Creates a new transaction context and returns it, or returns the results * Creates a new transaction context and returns it, or returns the results
* of a transaction executed inside the optionally provided callable object. * of a transaction executed inside the optionally provided callable object.
* *
* @param mixed ... Array of options, a callable for execution, or both. * @param mixed ...$arguments Array of options, a callable for execution, or both.
* *
* @return MultiExecTransaction|array * @return MultiExecTransaction|array
*/ */
public function transaction(/* arguments */) public function transaction(...$arguments)
{ {
return $this->sharedContextFactory('createTransaction', func_get_args()); return $this->sharedContextFactory('createTransaction', func_get_args());
} }
@ -476,14 +537,14 @@ class Client implements ClientInterface
} }
/** /**
* Creates a new publis/subscribe context and returns it, or starts its loop * Creates a new publish/subscribe context and returns it, or starts its loop
* inside the optionally provided callable object. * inside the optionally provided callable object.
* *
* @param mixed ... Array of options, a callable for execution, or both. * @param mixed ...$arguments Array of options, a callable for execution, or both.
* *
* @return PubSubConsumer|null * @return PubSubConsumer|null
*/ */
public function pubSubLoop(/* arguments */) public function pubSubLoop(...$arguments)
{ {
return $this->sharedContextFactory('createPubSub', func_get_args()); return $this->sharedContextFactory('createPubSub', func_get_args());
} }
@ -498,7 +559,11 @@ class Client implements ClientInterface
*/ */
protected function createPubSub(array $options = null, $callable = null) protected function createPubSub(array $options = null, $callable = null)
{ {
$pubsub = new PubSubConsumer($this, $options); if ($this->connection instanceof RelayConnection) {
$pubsub = new RelayPubSubConsumer($this, $options);
} else {
$pubsub = new PubSubConsumer($this, $options);
}
if (!isset($callable)) { if (!isset($callable)) {
return $pubsub; return $pubsub;
@ -509,6 +574,8 @@ class Client implements ClientInterface
$pubsub->stop(); $pubsub->stop();
} }
} }
return null;
} }
/** /**
@ -520,4 +587,26 @@ class Client implements ClientInterface
{ {
return new MonitorConsumer($this); return new MonitorConsumer($this);
} }
/**
* @return Traversable<string, static>
*/
#[ReturnTypeWillChange]
public function getIterator()
{
$clients = [];
$connection = $this->getConnection();
if (!$connection instanceof Traversable) {
return new ArrayIterator([
(string) $connection => new static($connection, $this->getOptions()),
]);
}
foreach ($connection as $node) {
$clients[(string) $node] = new static($node, $this->getOptions());
}
return new ArrayIterator($clients);
}
} }

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -11,16 +12,49 @@
namespace Predis; namespace Predis;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Search\AggregateArguments;
use Predis\Command\Argument\Search\AlterArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\DropArguments;
use Predis\Command\Argument\Search\ExplainArguments;
use Predis\Command\Argument\Search\ProfileArguments;
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Argument\Search\SearchArguments;
use Predis\Command\Argument\Search\SugAddArguments;
use Predis\Command\Argument\Search\SugGetArguments;
use Predis\Command\Argument\Search\SynUpdateArguments;
use Predis\Command\Argument\Server\LimitOffsetCount;
use Predis\Command\Argument\Server\To;
use Predis\Command\Argument\TimeSeries\AddArguments;
use Predis\Command\Argument\TimeSeries\AlterArguments as TSAlterArguments;
use Predis\Command\Argument\TimeSeries\CreateArguments as TSCreateArguments;
use Predis\Command\Argument\TimeSeries\DecrByArguments;
use Predis\Command\Argument\TimeSeries\GetArguments;
use Predis\Command\Argument\TimeSeries\IncrByArguments;
use Predis\Command\Argument\TimeSeries\InfoArguments;
use Predis\Command\Argument\TimeSeries\MGetArguments;
use Predis\Command\Argument\TimeSeries\MRangeArguments;
use Predis\Command\Argument\TimeSeries\RangeArguments;
use Predis\Command\CommandInterface; use Predis\Command\CommandInterface;
use Predis\Command\Redis\Container\ACL;
use Predis\Command\Redis\Container\CLUSTER;
use Predis\Command\Redis\Container\FunctionContainer;
use Predis\Command\Redis\Container\Json\JSONDEBUG;
use Predis\Command\Redis\Container\Search\FTCONFIG;
use Predis\Command\Redis\Container\Search\FTCURSOR;
/** /**
* Interface defining a client-side context such as a pipeline or transaction. * Interface defining a client-side context such as a pipeline or transaction.
* *
* @method $this del(array $keys) * @method $this copy(string $source, string $destination, int $db = -1, bool $replace = false)
* @method $this del(array|string $keys)
* @method $this dump($key) * @method $this dump($key)
* @method $this exists($key) * @method $this exists($key)
* @method $this expire($key, $seconds) * @method $this expire($key, $seconds, string $expireOption = '')
* @method $this expireat($key, $timestamp) * @method $this expireat($key, $timestamp, string $expireOption = '')
* @method $this expiretime(string $key)
* @method $this keys($pattern) * @method $this keys($pattern)
* @method $this move($key, $db) * @method $this move($key, $db)
* @method $this object($subcommand, $key) * @method $this object($subcommand, $key)
@ -33,16 +67,77 @@ use Predis\Command\CommandInterface;
* @method $this renamenx($key, $target) * @method $this renamenx($key, $target)
* @method $this scan($cursor, array $options = null) * @method $this scan($cursor, array $options = null)
* @method $this sort($key, array $options = null) * @method $this sort($key, array $options = null)
* @method $this sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false)
* @method $this ttl($key) * @method $this ttl($key)
* @method $this type($key) * @method $this type($key)
* @method $this append($key, $value) * @method $this append($key, $value)
* @method $this bitcount($key, $start = null, $end = null) * @method $this bfadd(string $key, $item)
* @method $this bfexists(string $key, $item)
* @method $this bfinfo(string $key, string $modifier = '')
* @method $this bfinsert(string $key, int $capacity = -1, float $error = -1, int $expansion = -1, bool $noCreate = false, bool $nonScaling = false, string ...$item)
* @method $this bfloadchunk(string $key, int $iterator, $data)
* @method $this bfmadd(string $key, ...$item)
* @method $this bfmexists(string $key, ...$item)
* @method $this bfreserve(string $key, float $errorRate, int $capacity, int $expansion = -1, bool $nonScaling = false)
* @method $this bfscandump(string $key, int $iterator)
* @method $this bitcount(string $key, $start = null, $end = null, string $index = 'byte')
* @method $this bitop($operation, $destkey, $key) * @method $this bitop($operation, $destkey, $key)
* @method $this bitfield($key, $subcommand, ...$subcommandArg)
* @method $this bitpos($key, $bit, $start = null, $end = null, string $index = 'byte')
* @method $this blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
* @method $this bzpopmax(array $keys, int $timeout)
* @method $this bzpopmin(array $keys, int $timeout)
* @method $this bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
* @method $this cfadd(string $key, $item)
* @method $this cfaddnx(string $key, $item)
* @method $this cfcount(string $key, $item)
* @method $this cfdel(string $key, $item)
* @method $this cfexists(string $key, $item)
* @method $this cfloadchunk(string $key, int $iterator, $data)
* @method $this cfmexists(string $key, ...$item)
* @method $this cfinfo(string $key)
* @method $this cfinsert(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
* @method $this cfinsertnx(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
* @method $this cfreserve(string $key, int $capacity, int $bucketSize = -1, int $maxIterations = -1, int $expansion = -1)
* @method $this cfscandump(string $key, int $iterator)
* @method $this cmsincrby(string $key, string|int...$itemIncrementDictionary)
* @method $this cmsinfo(string $key)
* @method $this cmsinitbydim(string $key, int $width, int $depth)
* @method $this cmsinitbyprob(string $key, float $errorRate, float $probability)
* @method $this cmsmerge(string $destination, array $sources, array $weights = [])
* @method $this cmsquery(string $key, string ...$item)
* @method $this decr($key) * @method $this decr($key)
* @method $this decrby($key, $decrement) * @method $this decrby($key, $decrement)
* @method $this failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method $this fcall(string $function, array $keys, ...$args)
* @method $this fcall_ro(string $function, array $keys, ...$args)
* @method $this ftaggregate(string $index, string $query, ?AggregateArguments $arguments = null)
* @method $this ftaliasadd(string $alias, string $index)
* @method $this ftaliasdel(string $alias)
* @method $this ftaliasupdate(string $alias, string $index)
* @method $this ftalter(string $index, FieldInterface[] $schema, ?AlterArguments $arguments = null)
* @method $this ftcreate(string $index, FieldInterface[] $schema, ?CreateArguments $arguments = null)
* @method $this ftdictadd(string $dict, ...$term)
* @method $this ftdictdel(string $dict, ...$term)
* @method $this ftdictdump(string $dict)
* @method $this ftdropindex(string $index, ?DropArguments $arguments = null)
* @method $this ftexplain(string $index, string $query, ?ExplainArguments $arguments = null)
* @method $this ftinfo(string $index)
* @method $this ftprofile(string $index, ProfileArguments $arguments)
* @method $this ftsearch(string $index, string $query, ?SearchArguments $arguments = null)
* @method $this ftspellcheck(string $index, string $query, ?SearchArguments $arguments = null)
* @method $this ftsugadd(string $key, string $string, float $score, ?SugAddArguments $arguments = null)
* @method $this ftsugdel(string $key, string $string)
* @method $this ftsugget(string $key, string $prefix, ?SugGetArguments $arguments = null)
* @method $this ftsuglen(string $key)
* @method $this ftsyndump(string $index)
* @method $this ftsynupdate(string $index, string $synonymGroupId, ?SynUpdateArguments $arguments = null, string ...$terms)
* @method $this fttagvals(string $index, string $fieldName)
* @method $this get($key) * @method $this get($key)
* @method $this getbit($key, $offset) * @method $this getbit($key, $offset)
* @method $this getex(string $key, $modifier = '', $value = false)
* @method $this getrange($key, $start, $end) * @method $this getrange($key, $start, $end)
* @method $this getdel(string $key)
* @method $this getset($key, $value) * @method $this getset($key, $value)
* @method $this incr($key) * @method $this incr($key)
* @method $this incrby($key, $increment) * @method $this incrby($key, $increment)
@ -67,19 +162,47 @@ use Predis\Command\CommandInterface;
* @method $this hlen($key) * @method $this hlen($key)
* @method $this hmget($key, array $fields) * @method $this hmget($key, array $fields)
* @method $this hmset($key, array $dictionary) * @method $this hmset($key, array $dictionary)
* @method $this hrandfield(string $key, int $count = 1, bool $withValues = false)
* @method $this hscan($key, $cursor, array $options = null) * @method $this hscan($key, $cursor, array $options = null)
* @method $this hset($key, $field, $value) * @method $this hset($key, $field, $value)
* @method $this hsetnx($key, $field, $value) * @method $this hsetnx($key, $field, $value)
* @method $this hvals($key) * @method $this hvals($key)
* @method $this blpop(array $keys, $timeout) * @method $this hstrlen($key, $field)
* @method $this brpop(array $keys, $timeout) * @method $this jsonarrappend(string $key, string $path = '$', ...$value)
* @method $this jsonarrindex(string $key, string $path, string $value, int $start = 0, int $stop = 0)
* @method $this jsonarrinsert(string $key, string $path, int $index, string ...$value)
* @method $this jsonarrlen(string $key, string $path = '$')
* @method $this jsonarrpop(string $key, string $path = '$', int $index = -1)
* @method $this jsonarrtrim(string $key, string $path, int $start, int $stop)
* @method $this jsonclear(string $key, string $path = '$')
* @method $this jsondel(string $key, string $path = '$')
* @method $this jsonforget(string $key, string $path = '$')
* @method $this jsonget(string $key, string $indent = '', string $newline = '', string $space = '', string ...$paths)
* @method $this jsonnumincrby(string $key, string $path, int $value)
* @method $this jsonmerge(string $key, string $path, string $value)
* @method $this jsonmget(array $keys, string $path)
* @method $this jsonmset(string ...$keyPathValue)
* @method $this jsonobjkeys(string $key, string $path = '$')
* @method $this jsonobjlen(string $key, string $path = '$')
* @method $this jsonresp(string $key, string $path = '$')
* @method $this jsonset(string $key, string $path, string $value, ?string $subcommand = null)
* @method $this jsonstrappend(string $key, string $path, string $value)
* @method $this jsonstrlen(string $key, string $path = '$')
* @method $this jsontoggle(string $key, string $path)
* @method $this jsontype(string $key, string $path = '$')
* @method $this blmove(string $source, string $destination, string $where, string $to, int $timeout)
* @method $this blpop(array|string $keys, $timeout)
* @method $this brpop(array|string $keys, $timeout)
* @method $this brpoplpush($source, $destination, $timeout) * @method $this brpoplpush($source, $destination, $timeout)
* @method $this lcs(string $key1, string $key2, bool $len = false, bool $idx = false, int $minMatchLen = 0, bool $withMatchLen = false)
* @method $this lindex($key, $index) * @method $this lindex($key, $index)
* @method $this linsert($key, $whence, $pivot, $value) * @method $this linsert($key, $whence, $pivot, $value)
* @method $this llen($key) * @method $this llen($key)
* @method $this lmove(string $source, string $destination, string $where, string $to)
* @method $this lmpop(array $keys, string $modifier = 'left', int $count = 1)
* @method $this lpop($key) * @method $this lpop($key)
* @method $this lpush($key, array $values) * @method $this lpush($key, array $values)
* @method $this lpushx($key, $value) * @method $this lpushx($key, array $values)
* @method $this lrange($key, $start, $stop) * @method $this lrange($key, $start, $stop)
* @method $this lrem($key, $count, $value) * @method $this lrem($key, $count, $value)
* @method $this lset($key, $index, $value) * @method $this lset($key, $index, $value)
@ -87,55 +210,109 @@ use Predis\Command\CommandInterface;
* @method $this rpop($key) * @method $this rpop($key)
* @method $this rpoplpush($source, $destination) * @method $this rpoplpush($source, $destination)
* @method $this rpush($key, array $values) * @method $this rpush($key, array $values)
* @method $this rpushx($key, $value) * @method $this rpushx($key, array $values)
* @method $this sadd($key, array $members) * @method $this sadd($key, array $members)
* @method $this scard($key) * @method $this scard($key)
* @method $this sdiff(array $keys) * @method $this sdiff(array|string $keys)
* @method $this sdiffstore($destination, array $keys) * @method $this sdiffstore($destination, array|string $keys)
* @method $this sinter(array $keys) * @method $this sinter(array|string $keys)
* @method $this sinterstore($destination, array $keys) * @method $this sintercard(array $keys, int $limit = 0)
* @method $this sinterstore($destination, array|string $keys)
* @method $this sismember($key, $member) * @method $this sismember($key, $member)
* @method $this smembers($key) * @method $this smembers($key)
* @method $this smismember(string $key, string ...$members)
* @method $this smove($source, $destination, $member) * @method $this smove($source, $destination, $member)
* @method $this spop($key) * @method $this spop($key, $count = null)
* @method $this srandmember($key, $count = null) * @method $this srandmember($key, $count = null)
* @method $this srem($key, $member) * @method $this srem($key, $member)
* @method $this sscan($key, $cursor, array $options = null) * @method $this sscan($key, $cursor, array $options = null)
* @method $this sunion(array $keys) * @method $this sunion(array|string $keys)
* @method $this sunionstore($destination, array $keys) * @method $this sunionstore($destination, array|string $keys)
* @method $this tdigestadd(string $key, float ...$value)
* @method $this tdigestbyrank(string $key, int ...$rank)
* @method $this tdigestbyrevrank(string $key, int ...$reverseRank)
* @method $this tdigestcdf(string $key, int ...$value)
* @method $this tdigestcreate(string $key, int $compression = 0)
* @method $this tdigestinfo(string $key)
* @method $this tdigestmax(string $key)
* @method $this tdigestmerge(string $destinationKey, array $sourceKeys, int $compression = 0, bool $override = false)
* @method $this tdigestquantile(string $key, float ...$quantile)
* @method $this tdigestmin(string $key)
* @method $this tdigestrank(string $key, ...$value)
* @method $this tdigestreset(string $key)
* @method $this tdigestrevrank(string $key, float ...$value)
* @method $this tdigesttrimmed_mean(string $key, float $lowCutQuantile, float $highCutQuantile)
* @method $this topkadd(string $key, ...$items)
* @method $this topkincrby(string $key, ...$itemIncrement)
* @method $this topkinfo(string $key)
* @method $this topklist(string $key, bool $withCount = false)
* @method $this topkquery(string $key, ...$items)
* @method $this topkreserve(string $key, int $topK, int $width = 8, int $depth = 7, float $decay = 0.9)
* @method $this tsadd(string $key, int $timestamp, float $value, ?AddArguments $arguments = null)
* @method $this tsalter(string $key, ?TSAlterArguments $arguments = null)
* @method $this tscreate(string $key, ?TSCreateArguments $arguments = null)
* @method $this tscreaterule(string $sourceKey, string $destKey, string $aggregator, int $bucketDuration, int $alignTimestamp = 0)
* @method $this tsdecrby(string $key, float $value, ?DecrByArguments $arguments = null)
* @method $this tsdel(string $key, int $fromTimestamp, int $toTimestamp)
* @method $this tsdeleterule(string $sourceKey, string $destKey)
* @method $this tsget(string $key, GetArguments $arguments = null)
* @method $this tsincrby(string $key, float $value, ?IncrByArguments $arguments = null)
* @method $this tsinfo(string $key, ?InfoArguments $arguments = null)
* @method $this tsmadd(mixed ...$keyTimestampValue)
* @method $this tsmget(MGetArguments $arguments, string ...$filterExpression)
* @method $this tsmrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
* @method $this tsmrevrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
* @method $this tsqueryindex(string ...$filterExpression)
* @method $this tsrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
* @method $this tsrevrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
* @method $this zadd($key, array $membersAndScoresDictionary) * @method $this zadd($key, array $membersAndScoresDictionary)
* @method $this zcard($key) * @method $this zcard($key)
* @method $this zcount($key, $min, $max) * @method $this zcount($key, $min, $max)
* @method $this zdiff(array $keys, bool $withScores = false)
* @method $this zdiffstore(string $destination, array $keys)
* @method $this zincrby($key, $increment, $member) * @method $this zincrby($key, $increment, $member)
* @method $this zinterstore($destination, array $keys, array $options = null) * @method $this zintercard(array $keys, int $limit = 0)
* @method $this zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method $this zinter(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method $this zmpop(array $keys, string $modifier = 'min', int $count = 1)
* @method $this zmscore(string $key, string ...$member)
* @method $this zrandmember(string $key, int $count = 1, bool $withScores = false)
* @method $this zrange($key, $start, $stop, array $options = null) * @method $this zrange($key, $start, $stop, array $options = null)
* @method $this zrangebyscore($key, $min, $max, array $options = null) * @method $this zrangebyscore($key, $min, $max, array $options = null)
* @method $this zrangestore(string $destination, string $source, int|string $min, string|int $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0)
* @method $this zrank($key, $member) * @method $this zrank($key, $member)
* @method $this zrem($key, $member) * @method $this zrem($key, $member)
* @method $this zremrangebyrank($key, $start, $stop) * @method $this zremrangebyrank($key, $start, $stop)
* @method $this zremrangebyscore($key, $min, $max) * @method $this zremrangebyscore($key, $min, $max)
* @method $this zrevrange($key, $start, $stop, array $options = null) * @method $this zrevrange($key, $start, $stop, array $options = null)
* @method $this zrevrangebyscore($key, $min, $max, array $options = null) * @method $this zrevrangebyscore($key, $max, $min, array $options = null)
* @method $this zrevrank($key, $member) * @method $this zrevrank($key, $member)
* @method $this zunionstore($destination, array $keys, array $options = null) * @method $this zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method $this zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method $this zscore($key, $member) * @method $this zscore($key, $member)
* @method $this zscan($key, $cursor, array $options = null) * @method $this zscan($key, $cursor, array $options = null)
* @method $this zrangebylex($key, $start, $stop, array $options = null) * @method $this zrangebylex($key, $start, $stop, array $options = null)
* @method $this zrevrangebylex($key, $start, $stop, array $options = null)
* @method $this zremrangebylex($key, $min, $max) * @method $this zremrangebylex($key, $min, $max)
* @method $this zlexcount($key, $min, $max) * @method $this zlexcount($key, $min, $max)
* @method $this pexpiretime(string $key)
* @method $this pfadd($key, array $elements) * @method $this pfadd($key, array $elements)
* @method $this pfmerge($destinationKey, array $sourceKeys) * @method $this pfmerge($destinationKey, array|string $sourceKeys)
* @method $this pfcount(array $keys) * @method $this pfcount(array|string $keys)
* @method $this pubsub($subcommand, $argument) * @method $this pubsub($subcommand, $argument)
* @method $this publish($channel, $message) * @method $this publish($channel, $message)
* @method $this discard() * @method $this discard()
* @method $this exec() * @method $this exec()
* @method $this multi() * @method $this multi()
* @method $this unwatch() * @method $this unwatch()
* @method $this waitaof(int $numLocal, int $numReplicas, int $timeout)
* @method $this watch($key) * @method $this watch($key)
* @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) * @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method $this eval_ro(string $script, array $keys, ...$argument)
* @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) * @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method $this evalsha_ro(string $sha1, array $keys, ...$argument)
* @method $this script($subcommand, $argument = null) * @method $this script($subcommand, $argument = null)
* @method $this shutdown(bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false)
* @method $this auth($password) * @method $this auth($password)
* @method $this echo($message) * @method $this echo($message)
* @method $this ping($message = null) * @method $this ping($message = null)
@ -154,8 +331,22 @@ use Predis\Command\CommandInterface;
* @method $this slowlog($subcommand, $argument = null) * @method $this slowlog($subcommand, $argument = null)
* @method $this time() * @method $this time()
* @method $this command() * @method $this command()
* @method $this geoadd($key, $longitude, $latitude, $member)
* @method $this geohash($key, array $members)
* @method $this geopos($key, array $members)
* @method $this geodist($key, $member1, $member2, $unit = null)
* @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
* @method $this geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
* @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
* *
* @author Daniele Alessandri <suppakilla@gmail.com> * Container commands
* @property CLUSTER $cluster
* @property FunctionContainer $function
* @property FTCONFIG $ftconfig
* @property FTCURSOR $ftcursor
* @property JSONDEBUG $jsondebug
* @property ACL $acl
*/ */
interface ClientContextInterface interface ClientContextInterface
{ {

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -13,8 +14,6 @@ namespace Predis;
/** /**
* Exception class that identifies client-side errors. * Exception class that identifies client-side errors.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
class ClientException extends PredisException class ClientException extends PredisException
{ {

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -11,10 +12,42 @@
namespace Predis; namespace Predis;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Search\AggregateArguments;
use Predis\Command\Argument\Search\AlterArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\DropArguments;
use Predis\Command\Argument\Search\ExplainArguments;
use Predis\Command\Argument\Search\ProfileArguments;
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Argument\Search\SearchArguments;
use Predis\Command\Argument\Search\SugAddArguments;
use Predis\Command\Argument\Search\SugGetArguments;
use Predis\Command\Argument\Search\SynUpdateArguments;
use Predis\Command\Argument\Server\LimitOffsetCount;
use Predis\Command\Argument\Server\To;
use Predis\Command\Argument\TimeSeries\AddArguments;
use Predis\Command\Argument\TimeSeries\AlterArguments as TSAlterArguments;
use Predis\Command\Argument\TimeSeries\CreateArguments as TSCreateArguments;
use Predis\Command\Argument\TimeSeries\DecrByArguments;
use Predis\Command\Argument\TimeSeries\GetArguments;
use Predis\Command\Argument\TimeSeries\IncrByArguments;
use Predis\Command\Argument\TimeSeries\InfoArguments;
use Predis\Command\Argument\TimeSeries\MGetArguments;
use Predis\Command\Argument\TimeSeries\MRangeArguments;
use Predis\Command\Argument\TimeSeries\RangeArguments;
use Predis\Command\CommandInterface; use Predis\Command\CommandInterface;
use Predis\Command\FactoryInterface;
use Predis\Command\Redis\Container\ACL;
use Predis\Command\Redis\Container\CLUSTER;
use Predis\Command\Redis\Container\FunctionContainer;
use Predis\Command\Redis\Container\Json\JSONDEBUG;
use Predis\Command\Redis\Container\Search\FTCONFIG;
use Predis\Command\Redis\Container\Search\FTCURSOR;
use Predis\Configuration\OptionsInterface; use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface; use Predis\Connection\ConnectionInterface;
use Predis\Profile\ProfileInterface; use Predis\Response\Status;
/** /**
* Interface defining a client able to execute commands against Redis. * Interface defining a client able to execute commands against Redis.
@ -24,155 +57,323 @@ use Predis\Profile\ProfileInterface;
* and more friendly interface to ease programming which is described in the * and more friendly interface to ease programming which is described in the
* following list of methods: * following list of methods:
* *
* @method int del(array $keys) * @method int copy(string $source, string $destination, int $db = -1, bool $replace = false)
* @method string dump($key) * @method int del(string[]|string $keyOrKeys, string ...$keys = null)
* @method int exists($key) * @method string|null dump(string $key)
* @method int expire($key, $seconds) * @method int exists(string $key)
* @method int expireat($key, $timestamp) * @method int expire(string $key, int $seconds, string $expireOption = '')
* @method array keys($pattern) * @method int expireat(string $key, int $timestamp, string $expireOption = '')
* @method int move($key, $db) * @method int expiretime(string $key)
* @method mixed object($subcommand, $key) * @method array keys(string $pattern)
* @method int persist($key) * @method int move(string $key, int $db)
* @method int pexpire($key, $milliseconds) * @method mixed object($subcommand, string $key)
* @method int pexpireat($key, $timestamp) * @method int persist(string $key)
* @method int pttl($key) * @method int pexpire(string $key, int $milliseconds)
* @method string randomkey() * @method int pexpireat(string $key, int $timestamp)
* @method mixed rename($key, $target) * @method int pttl(string $key)
* @method int renamenx($key, $target) * @method string|null randomkey()
* @method array scan($cursor, array $options = null) * @method mixed rename(string $key, string $target)
* @method array sort($key, array $options = null) * @method int renamenx(string $key, string $target)
* @method int ttl($key) * @method array scan($cursor, array $options = null)
* @method mixed type($key) * @method array sort(string $key, array $options = null)
* @method int append($key, $value) * @method array sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false)
* @method int bitcount($key, $start = null, $end = null) * @method int ttl(string $key)
* @method int bitop($operation, $destkey, $key) * @method mixed type(string $key)
* @method int decr($key) * @method int append(string $key, $value)
* @method int decrby($key, $decrement) * @method int bfadd(string $key, $item)
* @method string get($key) * @method int bfexists(string $key, $item)
* @method int getbit($key, $offset) * @method array bfinfo(string $key, string $modifier = '')
* @method string getrange($key, $start, $end) * @method array bfinsert(string $key, int $capacity = -1, float $error = -1, int $expansion = -1, bool $noCreate = false, bool $nonScaling = false, string ...$item)
* @method string getset($key, $value) * @method Status bfloadchunk(string $key, int $iterator, $data)
* @method int incr($key) * @method array bfmadd(string $key, ...$item)
* @method int incrby($key, $increment) * @method array bfmexists(string $key, ...$item)
* @method string incrbyfloat($key, $increment) * @method Status bfreserve(string $key, float $errorRate, int $capacity, int $expansion = -1, bool $nonScaling = false)
* @method array mget(array $keys) * @method array bfscandump(string $key, int $iterator)
* @method mixed mset(array $dictionary) * @method int bitcount(string $key, $start = null, $end = null, string $index = 'byte')
* @method int msetnx(array $dictionary) * @method int bitop($operation, $destkey, $key)
* @method mixed psetex($key, $milliseconds, $value) * @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
* @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null) * @method int bitpos(string $key, $bit, $start = null, $end = null, string $index = 'byte')
* @method int setbit($key, $offset, $value) * @method array blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
* @method int setex($key, $seconds, $value) * @method array bzpopmax(array $keys, int $timeout)
* @method int setnx($key, $value) * @method array bzpopmin(array $keys, int $timeout)
* @method int setrange($key, $offset, $value) * @method array bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
* @method int strlen($key) * @method int cfadd(string $key, $item)
* @method int hdel($key, array $fields) * @method int cfaddnx(string $key, $item)
* @method int hexists($key, $field) * @method int cfcount(string $key, $item)
* @method string hget($key, $field) * @method int cfdel(string $key, $item)
* @method array hgetall($key) * @method int cfexists(string $key, $item)
* @method int hincrby($key, $field, $increment) * @method Status cfloadchunk(string $key, int $iterator, $data)
* @method string hincrbyfloat($key, $field, $increment) * @method int cfmexists(string $key, ...$item)
* @method array hkeys($key) * @method array cfinfo(string $key)
* @method int hlen($key) * @method array cfinsert(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
* @method array hmget($key, array $fields) * @method array cfinsertnx(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
* @method mixed hmset($key, array $dictionary) * @method Status cfreserve(string $key, int $capacity, int $bucketSize = -1, int $maxIterations = -1, int $expansion = -1)
* @method array hscan($key, $cursor, array $options = null) * @method array cfscandump(string $key, int $iterator)
* @method int hset($key, $field, $value) * @method array cmsincrby(string $key, string|int...$itemIncrementDictionary)
* @method int hsetnx($key, $field, $value) * @method array cmsinfo(string $key)
* @method array hvals($key) * @method Status cmsinitbydim(string $key, int $width, int $depth)
* @method array blpop(array $keys, $timeout) * @method Status cmsinitbyprob(string $key, float $errorRate, float $probability)
* @method array brpop(array $keys, $timeout) * @method Status cmsmerge(string $destination, array $sources, array $weights = [])
* @method array brpoplpush($source, $destination, $timeout) * @method array cmsquery(string $key, string ...$item)
* @method string lindex($key, $index) * @method int decr(string $key)
* @method int linsert($key, $whence, $pivot, $value) * @method int decrby(string $key, int $decrement)
* @method int llen($key) * @method Status failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method string lpop($key) * @method mixed fcall(string $function, array $keys, ...$args)
* @method int lpush($key, array $values) * @method mixed fcall_ro(string $function, array $keys, ...$args)
* @method int lpushx($key, $value) * @method array ftaggregate(string $index, string $query, ?AggregateArguments $arguments = null)
* @method array lrange($key, $start, $stop) * @method Status ftaliasadd(string $alias, string $index)
* @method int lrem($key, $count, $value) * @method Status ftaliasdel(string $alias)
* @method mixed lset($key, $index, $value) * @method Status ftaliasupdate(string $alias, string $index)
* @method mixed ltrim($key, $start, $stop) * @method Status ftalter(string $index, FieldInterface[] $schema, ?AlterArguments $arguments = null)
* @method string rpop($key) * @method Status ftcreate(string $index, FieldInterface[] $schema, ?CreateArguments $arguments = null)
* @method string rpoplpush($source, $destination) * @method int ftdictadd(string $dict, ...$term)
* @method int rpush($key, array $values) * @method int ftdictdel(string $dict, ...$term)
* @method int rpushx($key, $value) * @method array ftdictdump(string $dict)
* @method int sadd($key, array $members) * @method Status ftdropindex(string $index, ?DropArguments $arguments = null)
* @method int scard($key) * @method string ftexplain(string $index, string $query, ?ExplainArguments $arguments = null)
* @method array sdiff(array $keys) * @method array ftinfo(string $index)
* @method int sdiffstore($destination, array $keys) * @method array ftprofile(string $index, ProfileArguments $arguments)
* @method array sinter(array $keys) * @method array ftsearch(string $index, string $query, ?SearchArguments $arguments = null)
* @method int sinterstore($destination, array $keys) * @method array ftspellcheck(string $index, string $query, ?SearchArguments $arguments = null)
* @method int sismember($key, $member) * @method int ftsugadd(string $key, string $string, float $score, ?SugAddArguments $arguments = null)
* @method array smembers($key) * @method int ftsugdel(string $key, string $string)
* @method int smove($source, $destination, $member) * @method array ftsugget(string $key, string $prefix, ?SugGetArguments $arguments = null)
* @method string spop($key) * @method int ftsuglen(string $key)
* @method string srandmember($key, $count = null) * @method array ftsyndump(string $index)
* @method int srem($key, $member) * @method Status ftsynupdate(string $index, string $synonymGroupId, ?SynUpdateArguments $arguments = null, string ...$terms)
* @method array sscan($key, $cursor, array $options = null) * @method array fttagvals(string $index, string $fieldName)
* @method array sunion(array $keys) * @method string|null get(string $key)
* @method int sunionstore($destination, array $keys) * @method int getbit(string $key, $offset)
* @method int zadd($key, array $membersAndScoresDictionary) * @method int|null getex(string $key, $modifier = '', $value = false)
* @method int zcard($key) * @method string getrange(string $key, $start, $end)
* @method string zcount($key, $min, $max) * @method string getdel(string $key)
* @method string zincrby($key, $increment, $member) * @method string|null getset(string $key, $value)
* @method int zinterstore($destination, array $keys, array $options = null) * @method int incr(string $key)
* @method array zrange($key, $start, $stop, array $options = null) * @method int incrby(string $key, int $increment)
* @method array zrangebyscore($key, $min, $max, array $options = null) * @method string incrbyfloat(string $key, int|float $increment)
* @method int zrank($key, $member) * @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
* @method int zrem($key, $member) * @method mixed mset(array $dictionary)
* @method int zremrangebyrank($key, $start, $stop) * @method int msetnx(array $dictionary)
* @method int zremrangebyscore($key, $min, $max) * @method Status psetex(string $key, $milliseconds, $value)
* @method array zrevrange($key, $start, $stop, array $options = null) * @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method array zrevrangebyscore($key, $min, $max, array $options = null) * @method int setbit(string $key, $offset, $value)
* @method int zrevrank($key, $member) * @method Status setex(string $key, $seconds, $value)
* @method int zunionstore($destination, array $keys, array $options = null) * @method int setnx(string $key, $value)
* @method string zscore($key, $member) * @method int setrange(string $key, $offset, $value)
* @method array zscan($key, $cursor, array $options = null) * @method int strlen(string $key)
* @method array zrangebylex($key, $start, $stop, array $options = null) * @method int hdel(string $key, array $fields)
* @method int zremrangebylex($key, $min, $max) * @method int hexists(string $key, string $field)
* @method int zlexcount($key, $min, $max) * @method string|null hget(string $key, string $field)
* @method int pfadd($key, array $elements) * @method array hgetall(string $key)
* @method mixed pfmerge($destinationKey, array $sourceKeys) * @method int hincrby(string $key, string $field, int $increment)
* @method int pfcount(array $keys) * @method string hincrbyfloat(string $key, string $field, int|float $increment)
* @method mixed pubsub($subcommand, $argument) * @method array hkeys(string $key)
* @method int publish($channel, $message) * @method int hlen(string $key)
* @method mixed discard() * @method array hmget(string $key, array $fields)
* @method array exec() * @method mixed hmset(string $key, array $dictionary)
* @method mixed multi() * @method array hrandfield(string $key, int $count = 1, bool $withValues = false)
* @method mixed unwatch() * @method array hscan(string $key, $cursor, array $options = null)
* @method mixed watch($key) * @method int hset(string $key, string $field, string $value)
* @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) * @method int hsetnx(string $key, string $field, string $value)
* @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) * @method array hvals(string $key)
* @method mixed script($subcommand, $argument = null) * @method int hstrlen(string $key, string $field)
* @method mixed auth($password) * @method array jsonarrappend(string $key, string $path = '$', ...$value)
* @method string echo($message) * @method array jsonarrindex(string $key, string $path, string $value, int $start = 0, int $stop = 0)
* @method mixed ping($message = null) * @method array jsonarrinsert(string $key, string $path, int $index, string ...$value)
* @method mixed select($database) * @method array jsonarrlen(string $key, string $path = '$')
* @method mixed bgrewriteaof() * @method array jsonarrpop(string $key, string $path = '$', int $index = -1)
* @method mixed bgsave() * @method int jsonclear(string $key, string $path = '$')
* @method mixed client($subcommand, $argument = null) * @method array jsonarrtrim(string $key, string $path, int $start, int $stop)
* @method mixed config($subcommand, $argument = null) * @method int jsondel(string $key, string $path = '$')
* @method int dbsize() * @method int jsonforget(string $key, string $path = '$')
* @method mixed flushall() * @method string jsonget(string $key, string $indent = '', string $newline = '', string $space = '', string ...$paths)
* @method mixed flushdb() * @method string jsonnumincrby(string $key, string $path, int $value)
* @method array info($section = null) * @method Status jsonmerge(string $key, string $path, string $value)
* @method int lastsave() * @method array jsonmget(array $keys, string $path)
* @method mixed save() * @method Status jsonmset(string ...$keyPathValue)
* @method mixed slaveof($host, $port) * @method array jsonobjkeys(string $key, string $path = '$')
* @method mixed slowlog($subcommand, $argument = null) * @method array jsonobjlen(string $key, string $path = '$')
* @method array time() * @method array jsonresp(string $key, string $path = '$')
* @method array command() * @method string jsonset(string $key, string $path, string $value, ?string $subcommand = null)
* @method array jsonstrappend(string $key, string $path, string $value)
* @method array jsonstrlen(string $key, string $path = '$')
* @method array jsontoggle(string $key, string $path)
* @method array jsontype(string $key, string $path = '$')
* @method string blmove(string $source, string $destination, string $where, string $to, int $timeout)
* @method array|null blpop(array|string $keys, int|float $timeout)
* @method array|null brpop(array|string $keys, int|float $timeout)
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
* @method mixed lcs(string $key1, string $key2, bool $len = false, bool $idx = false, int $minMatchLen = 0, bool $withMatchLen = false)
* @method string|null lindex(string $key, int $index)
* @method int linsert(string $key, $whence, $pivot, $value)
* @method int llen(string $key)
* @method string lmove(string $source, string $destination, string $where, string $to)
* @method array|null lmpop(array $keys, string $modifier = 'left', int $count = 1)
* @method string|null lpop(string $key)
* @method int lpush(string $key, array $values)
* @method int lpushx(string $key, array $values)
* @method string[] lrange(string $key, int $start, int $stop)
* @method int lrem(string $key, int $count, string $value)
* @method mixed lset(string $key, int $index, string $value)
* @method mixed ltrim(string $key, int $start, int $stop)
* @method string|null rpop(string $key)
* @method string|null rpoplpush(string $source, string $destination)
* @method int rpush(string $key, array $values)
* @method int rpushx(string $key, array $values)
* @method int sadd(string $key, array $members)
* @method int scard(string $key)
* @method string[] sdiff(array|string $keys)
* @method int sdiffstore(string $destination, array|string $keys)
* @method string[] sinter(array|string $keys)
* @method int sintercard(array $keys, int $limit = 0)
* @method int sinterstore(string $destination, array|string $keys)
* @method int sismember(string $key, string $member)
* @method string[] smembers(string $key)
* @method array smismember(string $key, string ...$members)
* @method int smove(string $source, string $destination, string $member)
* @method string|array|null spop(string $key, int $count = null)
* @method string|null srandmember(string $key, int $count = null)
* @method int srem(string $key, array|string $member)
* @method array sscan(string $key, int $cursor, array $options = null)
* @method string[] sunion(array|string $keys)
* @method int sunionstore(string $destination, array|string $keys)
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
* @method Status tdigestadd(string $key, float ...$value)
* @method array tdigestbyrank(string $key, int ...$rank)
* @method array tdigestbyrevrank(string $key, int ...$reverseRank)
* @method array tdigestcdf(string $key, int ...$value)
* @method Status tdigestcreate(string $key, int $compression = 0)
* @method array tdigestinfo(string $key)
* @method string tdigestmax(string $key)
* @method Status tdigestmerge(string $destinationKey, array $sourceKeys, int $compression = 0, bool $override = false)
* @method string[] tdigestquantile(string $key, float ...$quantile)
* @method string tdigestmin(string $key)
* @method array tdigestrank(string $key, float ...$value)
* @method Status tdigestreset(string $key)
* @method array tdigestrevrank(string $key, float ...$value)
* @method string tdigesttrimmed_mean(string $key, float $lowCutQuantile, float $highCutQuantile)
* @method array topkadd(string $key, ...$items)
* @method array topkincrby(string $key, ...$itemIncrement)
* @method array topkinfo(string $key)
* @method array topklist(string $key, bool $withCount = false)
* @method array topkquery(string $key, ...$items)
* @method Status topkreserve(string $key, int $topK, int $width = 8, int $depth = 7, float $decay = 0.9)
* @method int tsadd(string $key, int $timestamp, float $value, ?AddArguments $arguments = null)
* @method Status tsalter(string $key, ?TSAlterArguments $arguments = null)
* @method Status tscreate(string $key, ?TSCreateArguments $arguments = null)
* @method Status tscreaterule(string $sourceKey, string $destKey, string $aggregator, int $bucketDuration, int $alignTimestamp = 0)
* @method int tsdecrby(string $key, float $value, ?DecrByArguments $arguments = null)
* @method int tsdel(string $key, int $fromTimestamp, int $toTimestamp)
* @method Status tsdeleterule(string $sourceKey, string $destKey)
* @method array tsget(string $key, GetArguments $arguments = null)
* @method int tsincrby(string $key, float $value, ?IncrByArguments $arguments = null)
* @method array tsinfo(string $key, ?InfoArguments $arguments = null)
* @method array tsmadd(mixed ...$keyTimestampValue)
* @method array tsmget(MGetArguments $arguments, string ...$filterExpression)
* @method array tsmrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
* @method array tsmrevrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
* @method array tsqueryindex(string ...$filterExpression)
* @method array tsrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
* @method array tsrevrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
* @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null)
* @method int xdel(string $key, string ...$id)
* @method int xlen(string $key)
* @method array xrevrange(string $key, string $end, string $start, ?int $count = null)
* @method array xrange(string $key, string $start, string $end, ?int $count = null)
* @method string xtrim(string $key, array|string $strategy, string $threshold, array $options = null)
* @method int zadd(string $key, array $membersAndScoresDictionary)
* @method int zcard(string $key)
* @method string zcount(string $key, int|string $min, int|string $max)
* @method array zdiff(array $keys, bool $withScores = false)
* @method int zdiffstore(string $destination, array $keys)
* @method string zincrby(string $key, int $increment, string $member)
* @method int zintercard(array $keys, int $limit = 0)
* @method int zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method array zinter(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method array zmpop(array $keys, string $modifier = 'min', int $count = 1)
* @method array zmscore(string $key, string ...$member)
* @method array zpopmin(string $key, int $count = 1)
* @method array zpopmax(string $key, int $count = 1)
* @method mixed zrandmember(string $key, int $count = 1, bool $withScores = false)
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
* @method int zrangestore(string $destination, string $source, int|string $min, int|string $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0)
* @method int|null zrank(string $key, string $member)
* @method int zrem(string $key, string ...$member)
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
* @method int|null zrevrank(string $key, string $member)
* @method array zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method int zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method string|null zscore(string $key, string $member)
* @method array zscan(string $key, int $cursor, array $options = null)
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
* @method int zremrangebylex(string $key, string $min, string $max)
* @method int zlexcount(string $key, string $min, string $max)
* @method int pexpiretime(string $key)
* @method int pfadd(string $key, array $elements)
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed pubsub($subcommand, $argument)
* @method int publish($channel, $message)
* @method mixed discard()
* @method array|null exec()
* @method mixed multi()
* @method mixed unwatch()
* @method array waitaof(int $numLocal, int $numReplicas, int $timeout)
* @method mixed watch(string $key)
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed eval_ro(string $script, array $keys, ...$argument)
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed evalsha_ro(string $sha1, array $keys, ...$argument)
* @method mixed script($subcommand, $argument = null)
* @method Status shutdown(bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false)
* @method mixed auth(string $password)
* @method string echo(string $message)
* @method mixed ping(string $message = null)
* @method mixed select(int $database)
* @method mixed bgrewriteaof()
* @method mixed bgsave()
* @method mixed client($subcommand, $argument = null)
* @method mixed config($subcommand, $argument = null)
* @method int dbsize()
* @method mixed flushall()
* @method mixed flushdb()
* @method array info($section = null)
* @method int lastsave()
* @method mixed save()
* @method mixed slaveof(string $host, int $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method array time()
* @method array command()
* @method int geoadd(string $key, $longitude, $latitude, $member)
* @method array geohash(string $key, array $members)
* @method array geopos(string $key, array $members)
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
* @method array geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
* @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
* *
* @author Daniele Alessandri <suppakilla@gmail.com> * Container commands
* @property CLUSTER $cluster
* @property FunctionContainer $function
* @property FTCONFIG $ftconfig
* @property FTCURSOR $ftcursor
* @property JSONDEBUG $jsondebug
* @property ACL $acl
*/ */
interface ClientInterface interface ClientInterface
{ {
/** /**
* Returns the server profile used by the client. * Returns the command factory used by the client.
* *
* @return ProfileInterface * @return FactoryInterface
*/ */
public function getProfile(); public function getCommandFactory();
/** /**
* Returns the client options specified upon initialization. * Returns the client options specified upon initialization.
@ -206,7 +407,7 @@ interface ClientInterface
* *
* @return CommandInterface * @return CommandInterface
*/ */
public function createCommand($method, $arguments = array()); public function createCommand($method, $arguments = []);
/** /**
* Executes the specified Redis command. * Executes the specified Redis command.

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -11,21 +12,17 @@
namespace Predis\Cluster; namespace Predis\Cluster;
use InvalidArgumentException;
use Predis\Command\CommandInterface; use Predis\Command\CommandInterface;
use Predis\Command\ScriptCommand; use Predis\Command\ScriptCommand;
/** /**
* Common class implementing the logic needed to support clustering strategies. * Common class implementing the logic needed to support clustering strategies.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
abstract class ClusterStrategy implements StrategyInterface abstract class ClusterStrategy implements StrategyInterface
{ {
protected $commands; protected $commands;
/**
*
*/
public function __construct() public function __construct()
{ {
$this->commands = $this->getDefaultCommands(); $this->commands = $this->getDefaultCommands();
@ -38,12 +35,12 @@ abstract class ClusterStrategy implements StrategyInterface
*/ */
protected function getDefaultCommands() protected function getDefaultCommands()
{ {
$getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument'); $getKeyFromFirstArgument = [$this, 'getKeyFromFirstArgument'];
$getKeyFromAllArguments = array($this, 'getKeyFromAllArguments'); $getKeyFromAllArguments = [$this, 'getKeyFromAllArguments'];
return array( return [
/* commands operating on the key space */ /* commands operating on the key space */
'EXISTS' => $getKeyFromFirstArgument, 'EXISTS' => $getKeyFromAllArguments,
'DEL' => $getKeyFromAllArguments, 'DEL' => $getKeyFromAllArguments,
'TYPE' => $getKeyFromFirstArgument, 'TYPE' => $getKeyFromFirstArgument,
'EXPIRE' => $getKeyFromFirstArgument, 'EXPIRE' => $getKeyFromFirstArgument,
@ -53,9 +50,10 @@ abstract class ClusterStrategy implements StrategyInterface
'PEXPIREAT' => $getKeyFromFirstArgument, 'PEXPIREAT' => $getKeyFromFirstArgument,
'TTL' => $getKeyFromFirstArgument, 'TTL' => $getKeyFromFirstArgument,
'PTTL' => $getKeyFromFirstArgument, 'PTTL' => $getKeyFromFirstArgument,
'SORT' => $getKeyFromFirstArgument, // TODO 'SORT' => [$this, 'getKeyFromSortCommand'],
'DUMP' => $getKeyFromFirstArgument, 'DUMP' => $getKeyFromFirstArgument,
'RESTORE' => $getKeyFromFirstArgument, 'RESTORE' => $getKeyFromFirstArgument,
'FLUSHDB' => [$this, 'getFakeKey'],
/* commands operating on string values */ /* commands operating on string values */
'APPEND' => $getKeyFromFirstArgument, 'APPEND' => $getKeyFromFirstArgument,
@ -72,14 +70,15 @@ abstract class ClusterStrategy implements StrategyInterface
'INCRBYFLOAT' => $getKeyFromFirstArgument, 'INCRBYFLOAT' => $getKeyFromFirstArgument,
'SETBIT' => $getKeyFromFirstArgument, 'SETBIT' => $getKeyFromFirstArgument,
'SETEX' => $getKeyFromFirstArgument, 'SETEX' => $getKeyFromFirstArgument,
'MSET' => array($this, 'getKeyFromInterleavedArguments'), 'MSET' => [$this, 'getKeyFromInterleavedArguments'],
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'), 'MSETNX' => [$this, 'getKeyFromInterleavedArguments'],
'SETNX' => $getKeyFromFirstArgument, 'SETNX' => $getKeyFromFirstArgument,
'SETRANGE' => $getKeyFromFirstArgument, 'SETRANGE' => $getKeyFromFirstArgument,
'STRLEN' => $getKeyFromFirstArgument, 'STRLEN' => $getKeyFromFirstArgument,
'SUBSTR' => $getKeyFromFirstArgument, 'SUBSTR' => $getKeyFromFirstArgument,
'BITOP' => array($this, 'getKeyFromBitOp'), 'BITOP' => [$this, 'getKeyFromBitOp'],
'BITCOUNT' => $getKeyFromFirstArgument, 'BITCOUNT' => $getKeyFromFirstArgument,
'BITFIELD' => $getKeyFromFirstArgument,
/* commands operating on lists */ /* commands operating on lists */
'LINSERT' => $getKeyFromFirstArgument, 'LINSERT' => $getKeyFromFirstArgument,
@ -88,9 +87,9 @@ abstract class ClusterStrategy implements StrategyInterface
'LPOP' => $getKeyFromFirstArgument, 'LPOP' => $getKeyFromFirstArgument,
'RPOP' => $getKeyFromFirstArgument, 'RPOP' => $getKeyFromFirstArgument,
'RPOPLPUSH' => $getKeyFromAllArguments, 'RPOPLPUSH' => $getKeyFromAllArguments,
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'), 'BLPOP' => [$this, 'getKeyFromBlockingListCommands'],
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'), 'BRPOP' => [$this, 'getKeyFromBlockingListCommands'],
'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'), 'BRPOPLPUSH' => [$this, 'getKeyFromBlockingListCommands'],
'LPUSH' => $getKeyFromFirstArgument, 'LPUSH' => $getKeyFromFirstArgument,
'LPUSHX' => $getKeyFromFirstArgument, 'LPUSHX' => $getKeyFromFirstArgument,
'RPUSH' => $getKeyFromFirstArgument, 'RPUSH' => $getKeyFromFirstArgument,
@ -121,7 +120,7 @@ abstract class ClusterStrategy implements StrategyInterface
'ZCARD' => $getKeyFromFirstArgument, 'ZCARD' => $getKeyFromFirstArgument,
'ZCOUNT' => $getKeyFromFirstArgument, 'ZCOUNT' => $getKeyFromFirstArgument,
'ZINCRBY' => $getKeyFromFirstArgument, 'ZINCRBY' => $getKeyFromFirstArgument,
'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), 'ZINTERSTORE' => [$this, 'getKeyFromZsetAggregationCommands'],
'ZRANGE' => $getKeyFromFirstArgument, 'ZRANGE' => $getKeyFromFirstArgument,
'ZRANGEBYSCORE' => $getKeyFromFirstArgument, 'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZRANK' => $getKeyFromFirstArgument, 'ZRANK' => $getKeyFromFirstArgument,
@ -132,7 +131,7 @@ abstract class ClusterStrategy implements StrategyInterface
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument, 'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANK' => $getKeyFromFirstArgument, 'ZREVRANK' => $getKeyFromFirstArgument,
'ZSCORE' => $getKeyFromFirstArgument, 'ZSCORE' => $getKeyFromFirstArgument,
'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), 'ZUNIONSTORE' => [$this, 'getKeyFromZsetAggregationCommands'],
'ZSCAN' => $getKeyFromFirstArgument, 'ZSCAN' => $getKeyFromFirstArgument,
'ZLEXCOUNT' => $getKeyFromFirstArgument, 'ZLEXCOUNT' => $getKeyFromFirstArgument,
'ZRANGEBYLEX' => $getKeyFromFirstArgument, 'ZRANGEBYLEX' => $getKeyFromFirstArgument,
@ -162,9 +161,23 @@ abstract class ClusterStrategy implements StrategyInterface
'PFMERGE' => $getKeyFromAllArguments, 'PFMERGE' => $getKeyFromAllArguments,
/* scripting */ /* scripting */
'EVAL' => array($this, 'getKeyFromScriptingCommands'), 'EVAL' => [$this, 'getKeyFromScriptingCommands'],
'EVALSHA' => array($this, 'getKeyFromScriptingCommands'), 'EVALSHA' => [$this, 'getKeyFromScriptingCommands'],
);
/* server */
'INFO' => [$this, 'getFakeKey'],
/* commands performing geospatial operations */
'GEOADD' => $getKeyFromFirstArgument,
'GEOHASH' => $getKeyFromFirstArgument,
'GEOPOS' => $getKeyFromFirstArgument,
'GEODIST' => $getKeyFromFirstArgument,
'GEORADIUS' => [$this, 'getKeyFromGeoradiusCommands'],
'GEORADIUSBYMEMBER' => [$this, 'getKeyFromGeoradiusCommands'],
/* cluster */
'CLUSTER' => [$this, 'getFakeKey'],
];
} }
/** /**
@ -189,7 +202,7 @@ abstract class ClusterStrategy implements StrategyInterface
* @param string $commandID Command ID. * @param string $commandID Command ID.
* @param mixed $callback A valid callable object, or NULL to unset the handler. * @param mixed $callback A valid callable object, or NULL to unset the handler.
* *
* @throws \InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function setCommandHandler($commandID, $callback = null) public function setCommandHandler($commandID, $callback = null)
{ {
@ -202,7 +215,7 @@ abstract class ClusterStrategy implements StrategyInterface
} }
if (!is_callable($callback)) { if (!is_callable($callback)) {
throw new \InvalidArgumentException( throw new InvalidArgumentException(
'The argument must be a callable object or NULL.' 'The argument must be a callable object or NULL.'
); );
} }
@ -210,6 +223,16 @@ abstract class ClusterStrategy implements StrategyInterface
$this->commands[$commandID] = $callback; $this->commands[$commandID] = $callback;
} }
/**
* Get fake key for commands with no key argument.
*
* @return string
*/
protected function getFakeKey(): string
{
return 'key';
}
/** /**
* Extracts the key from the first argument of a command instance. * Extracts the key from the first argument of a command instance.
* *
@ -234,9 +257,11 @@ abstract class ClusterStrategy implements StrategyInterface
{ {
$arguments = $command->getArguments(); $arguments = $command->getArguments();
if ($this->checkSameSlotForKeys($arguments)) { if (!$this->checkSameSlotForKeys($arguments)) {
return $arguments[0]; return null;
} }
return $arguments[0];
} }
/** /**
@ -250,15 +275,48 @@ abstract class ClusterStrategy implements StrategyInterface
protected function getKeyFromInterleavedArguments(CommandInterface $command) protected function getKeyFromInterleavedArguments(CommandInterface $command)
{ {
$arguments = $command->getArguments(); $arguments = $command->getArguments();
$keys = array(); $keys = [];
for ($i = 0; $i < count($arguments); $i += 2) { for ($i = 0; $i < count($arguments); $i += 2) {
$keys[] = $arguments[$i]; $keys[] = $arguments[$i];
} }
if ($this->checkSameSlotForKeys($keys)) { if (!$this->checkSameSlotForKeys($keys)) {
return $arguments[0]; return null;
} }
return $arguments[0];
}
/**
* Extracts the key from SORT command.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromSortCommand(CommandInterface $command)
{
$arguments = $command->getArguments();
$firstKey = $arguments[0];
if (1 === $argc = count($arguments)) {
return $firstKey;
}
$keys = [$firstKey];
for ($i = 1; $i < $argc; ++$i) {
if (strtoupper($arguments[$i]) === 'STORE') {
$keys[] = $arguments[++$i];
}
}
if (!$this->checkSameSlotForKeys($keys)) {
return null;
}
return $firstKey;
} }
/** /**
@ -272,9 +330,11 @@ abstract class ClusterStrategy implements StrategyInterface
{ {
$arguments = $command->getArguments(); $arguments = $command->getArguments();
if ($this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) { if (!$this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) {
return $arguments[0]; return null;
} }
return $arguments[0];
} }
/** /**
@ -288,9 +348,42 @@ abstract class ClusterStrategy implements StrategyInterface
{ {
$arguments = $command->getArguments(); $arguments = $command->getArguments();
if ($this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) { if (!$this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) {
return $arguments[1]; return null;
} }
return $arguments[1];
}
/**
* Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromGeoradiusCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
$argc = count($arguments);
$startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
if ($argc > $startIndex) {
$keys = [$arguments[0]];
for ($i = $startIndex; $i < $argc; ++$i) {
$argument = strtoupper($arguments[$i]);
if ($argument === 'STORE' || $argument === 'STOREDIST') {
$keys[] = $arguments[++$i];
}
}
if (!$this->checkSameSlotForKeys($keys)) {
return null;
}
}
return $arguments[0];
} }
/** /**
@ -303,11 +396,13 @@ abstract class ClusterStrategy implements StrategyInterface
protected function getKeyFromZsetAggregationCommands(CommandInterface $command) protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
{ {
$arguments = $command->getArguments(); $arguments = $command->getArguments();
$keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1])); $keys = array_merge([$arguments[0]], array_slice($arguments, 2, $arguments[1]));
if ($this->checkSameSlotForKeys($keys)) { if (!$this->checkSameSlotForKeys($keys)) {
return $arguments[0]; return null;
} }
return $arguments[0];
} }
/** /**
@ -319,15 +414,15 @@ abstract class ClusterStrategy implements StrategyInterface
*/ */
protected function getKeyFromScriptingCommands(CommandInterface $command) protected function getKeyFromScriptingCommands(CommandInterface $command)
{ {
if ($command instanceof ScriptCommand) { $keys = $command instanceof ScriptCommand
$keys = $command->getKeys(); ? $command->getKeys()
} else { : array_slice($args = $command->getArguments(), 2, $args[1]);
$keys = array_slice($args = $command->getArguments(), 2, $args[1]);
if (!$keys || !$this->checkSameSlotForKeys($keys)) {
return null;
} }
if ($keys && $this->checkSameSlotForKeys($keys)) { return $keys[0];
return $keys[0];
}
} }
/** /**

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -16,8 +17,6 @@ use Predis\Cluster\Hash\HashGeneratorInterface;
/** /**
* A distributor implements the logic to automatically distribute keys among * A distributor implements the logic to automatically distribute keys among
* several nodes for client-side sharding. * several nodes for client-side sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
interface DistributorInterface interface DistributorInterface
{ {

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -11,11 +12,11 @@
namespace Predis\Cluster\Distributor; namespace Predis\Cluster\Distributor;
use Exception;
/** /**
* Exception class that identifies empty rings. * Exception class that identifies empty rings.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
class EmptyRingException extends \Exception class EmptyRingException extends Exception
{ {
} }

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -17,21 +18,19 @@ use Predis\Cluster\Hash\HashGeneratorInterface;
* This class implements an hashring-based distributor that uses the same * This class implements an hashring-based distributor that uses the same
* algorithm of memcache to distribute keys in a cluster using client-side * algorithm of memcache to distribute keys in a cluster using client-side
* sharding. * sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
* @author Lorenzo Castelli <lcastelli@gmail.com> * @author Lorenzo Castelli <lcastelli@gmail.com>
*/ */
class HashRing implements DistributorInterface, HashGeneratorInterface class HashRing implements DistributorInterface, HashGeneratorInterface
{ {
const DEFAULT_REPLICAS = 128; public const DEFAULT_REPLICAS = 128;
const DEFAULT_WEIGHT = 100; public const DEFAULT_WEIGHT = 100;
private $ring; private $ring;
private $ringKeys; private $ringKeys;
private $ringKeysCount; private $ringKeysCount;
private $replicas; private $replicas;
private $nodeHashCallback; private $nodeHashCallback;
private $nodes = array(); private $nodes = [];
/** /**
* @param int $replicas Number of replicas in the ring. * @param int $replicas Number of replicas in the ring.
@ -53,10 +52,10 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
{ {
// In case of collisions in the hashes of the nodes, the node added // In case of collisions in the hashes of the nodes, the node added
// last wins, thus the order in which nodes are added is significant. // last wins, thus the order in which nodes are added is significant.
$this->nodes[] = array( $this->nodes[] = [
'object' => $node, 'object' => $node,
'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT, 'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT,
); ];
$this->reset(); $this->reset();
} }
@ -131,7 +130,7 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
throw new EmptyRingException('Cannot initialize an empty hashring.'); throw new EmptyRingException('Cannot initialize an empty hashring.');
} }
$this->ring = array(); $this->ring = [];
$totalWeight = $this->computeTotalWeight(); $totalWeight = $this->computeTotalWeight();
$nodesCount = count($this->nodes); $nodesCount = count($this->nodes);
@ -161,7 +160,7 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
$replicas = (int) round($weightRatio * $totalNodes * $replicas); $replicas = (int) round($weightRatio * $totalNodes * $replicas);
for ($i = 0; $i < $replicas; ++$i) { for ($i = 0; $i < $replicas; ++$i) {
$key = crc32("$nodeHash:$i"); $key = $this->hash("$nodeHash:$i");
$ring[$key] = $nodeObject; $ring[$key] = $nodeObject;
} }
} }
@ -239,9 +238,8 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
public function get($value) public function get($value)
{ {
$hash = $this->hash($value); $hash = $this->hash($value);
$node = $this->getByHash($hash);
return $node; return $this->getByHash($hash);
} }
/** /**

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -15,13 +16,11 @@ namespace Predis\Cluster\Distributor;
* This class implements an hashring-based distributor that uses the same * This class implements an hashring-based distributor that uses the same
* algorithm of libketama to distribute keys in a cluster using client-side * algorithm of libketama to distribute keys in a cluster using client-side
* sharding. * sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
* @author Lorenzo Castelli <lcastelli@gmail.com> * @author Lorenzo Castelli <lcastelli@gmail.com>
*/ */
class KetamaRing extends HashRing class KetamaRing extends HashRing
{ {
const DEFAULT_REPLICAS = 160; public const DEFAULT_REPLICAS = 160;
/** /**
* @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes. * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -13,12 +14,10 @@ namespace Predis\Cluster\Hash;
/** /**
* Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster. * Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
class CRC16 implements HashGeneratorInterface class CRC16 implements HashGeneratorInterface
{ {
private static $CCITT_16 = array( private static $CCITT_16 = [
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
@ -51,7 +50,7 @@ class CRC16 implements HashGeneratorInterface
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
); ];
/** /**
* {@inheritdoc} * {@inheritdoc}
@ -61,6 +60,8 @@ class CRC16 implements HashGeneratorInterface
// CRC-CCITT-16 algorithm // CRC-CCITT-16 algorithm
$crc = 0; $crc = 0;
$CCITT_16 = self::$CCITT_16; $CCITT_16 = self::$CCITT_16;
$value = (string) $value;
$strlen = strlen($value); $strlen = strlen($value);
for ($i = 0; $i < $strlen; ++$i) { for ($i = 0; $i < $strlen; ++$i) {

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -14,8 +15,6 @@ namespace Predis\Cluster\Hash;
/** /**
* An hash generator implements the logic used to calculate the hash of a key to * An hash generator implements the logic used to calculate the hash of a key to
* distribute operations among Redis nodes. * distribute operations among Redis nodes.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
interface HashGeneratorInterface interface HashGeneratorInterface
{ {

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -16,8 +17,6 @@ use Predis\Cluster\Distributor\HashRing;
/** /**
* Default cluster strategy used by Predis to handle client-side sharding. * Default cluster strategy used by Predis to handle client-side sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
class PredisStrategy extends ClusterStrategy class PredisStrategy extends ClusterStrategy
{ {
@ -40,9 +39,8 @@ class PredisStrategy extends ClusterStrategy
{ {
$key = $this->extractKeyTag($key); $key = $this->extractKeyTag($key);
$hash = $this->distributor->hash($key); $hash = $this->distributor->hash($key);
$slot = $this->distributor->getSlot($hash);
return $slot; return $this->distributor->getSlot($hash);
} }
/** /**

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -18,8 +19,6 @@ use Predis\NotSupportedException;
/** /**
* Default class used by Predis to calculate hashes out of keys of * Default class used by Predis to calculate hashes out of keys of
* commands supported by redis-cluster. * commands supported by redis-cluster.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
class RedisStrategy extends ClusterStrategy class RedisStrategy extends ClusterStrategy
{ {
@ -41,9 +40,8 @@ class RedisStrategy extends ClusterStrategy
public function getSlotByKey($key) public function getSlotByKey($key)
{ {
$key = $this->extractKeyTag($key); $key = $this->extractKeyTag($key);
$slot = $this->hashGenerator->hash($key) & 0x3FFF;
return $slot; return $this->hashGenerator->hash($key) & 0x3FFF;
} }
/** /**
@ -51,8 +49,7 @@ class RedisStrategy extends ClusterStrategy
*/ */
public function getDistributor() public function getDistributor()
{ {
throw new NotSupportedException( $class = get_class($this);
'This cluster strategy does not provide an external distributor' throw new NotSupportedException("$class does not provide an external distributor");
);
} }
} }

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -19,8 +20,6 @@ use Predis\Command\CommandInterface;
* keys extracted from supported commands. * keys extracted from supported commands.
* *
* This is mostly useful to support clustering via client-side sharding. * This is mostly useful to support clustering via client-side sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
interface StrategyInterface interface StrategyInterface
{ {
@ -30,7 +29,7 @@ interface StrategyInterface
* *
* @param CommandInterface $command Command instance. * @param CommandInterface $command Command instance.
* *
* @return int * @return int|null
*/ */
public function getSlot(CommandInterface $command); public function getSlot(CommandInterface $command);
@ -40,7 +39,7 @@ interface StrategyInterface
* *
* @param string $key Key string. * @param string $key Key string.
* *
* @return int * @return int|null
*/ */
public function getSlotByKey($key); public function getSlotByKey($key);

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -11,8 +12,10 @@
namespace Predis\Collection\Iterator; namespace Predis\Collection\Iterator;
use Iterator;
use Predis\ClientInterface; use Predis\ClientInterface;
use Predis\NotSupportedException; use Predis\NotSupportedException;
use ReturnTypeWillChange;
/** /**
* Provides the base implementation for a fully-rewindable PHP iterator that can * Provides the base implementation for a fully-rewindable PHP iterator that can
@ -24,10 +27,8 @@ use Predis\NotSupportedException;
* can change several times during the iteration process. * can change several times during the iteration process.
* *
* @see http://redis.io/commands/scan * @see http://redis.io/commands/scan
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
abstract class CursorBasedIterator implements \Iterator abstract class CursorBasedIterator implements Iterator
{ {
protected $client; protected $client;
protected $match; protected $match;
@ -65,8 +66,8 @@ abstract class CursorBasedIterator implements \Iterator
*/ */
protected function requiredCommand(ClientInterface $client, $commandID) protected function requiredCommand(ClientInterface $client, $commandID)
{ {
if (!$client->getProfile()->supportsCommand($commandID)) { if (!$client->getCommandFactory()->supports($commandID)) {
throw new NotSupportedException("The current profile does not support '$commandID'."); throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
} }
} }
@ -77,7 +78,7 @@ abstract class CursorBasedIterator implements \Iterator
{ {
$this->valid = true; $this->valid = true;
$this->fetchmore = true; $this->fetchmore = true;
$this->elements = array(); $this->elements = [];
$this->cursor = 0; $this->cursor = 0;
$this->position = -1; $this->position = -1;
$this->current = null; $this->current = null;
@ -90,9 +91,9 @@ abstract class CursorBasedIterator implements \Iterator
*/ */
protected function getScanOptions() protected function getScanOptions()
{ {
$options = array(); $options = [];
if (strlen($this->match) > 0) { if (strlen(strval($this->match)) > 0) {
$options['MATCH'] = $this->match; $options['MATCH'] = $this->match;
} }
@ -117,7 +118,7 @@ abstract class CursorBasedIterator implements \Iterator
*/ */
protected function fetch() protected function fetch()
{ {
list($cursor, $elements) = $this->executeCommand(); [$cursor, $elements] = $this->executeCommand();
if (!$cursor) { if (!$cursor) {
$this->fetchmore = false; $this->fetchmore = false;
@ -137,8 +138,9 @@ abstract class CursorBasedIterator implements \Iterator
} }
/** /**
* {@inheritdoc} * @return void
*/ */
#[ReturnTypeWillChange]
public function rewind() public function rewind()
{ {
$this->reset(); $this->reset();
@ -146,27 +148,30 @@ abstract class CursorBasedIterator implements \Iterator
} }
/** /**
* {@inheritdoc} * @return mixed
*/ */
#[ReturnTypeWillChange]
public function current() public function current()
{ {
return $this->current; return $this->current;
} }
/** /**
* {@inheritdoc} * @return int|null
*/ */
#[ReturnTypeWillChange]
public function key() public function key()
{ {
return $this->position; return $this->position;
} }
/** /**
* {@inheritdoc} * @return void
*/ */
#[ReturnTypeWillChange]
public function next() public function next()
{ {
tryFetch: { tryFetch:
if (!$this->elements && $this->fetchmore) { if (!$this->elements && $this->fetchmore) {
$this->fetch(); $this->fetch();
} }
@ -178,12 +183,12 @@ abstract class CursorBasedIterator implements \Iterator
} else { } else {
$this->valid = false; $this->valid = false;
} }
}
} }
/** /**
* {@inheritdoc} * @return bool
*/ */
#[ReturnTypeWillChange]
public function valid() public function valid()
{ {
return $this->valid; return $this->valid;

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -17,9 +18,7 @@ use Predis\ClientInterface;
* Abstracts the iteration of fields and values of an hash by leveraging the * Abstracts the iteration of fields and values of an hash by leveraging the
* HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. * HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
* *
* @author Daniele Alessandri <suppakilla@gmail.com> * @see http://redis.io/commands/scan
*
* @link http://redis.io/commands/scan
*/ */
class HashKey extends CursorBasedIterator class HashKey extends CursorBasedIterator
{ {
@ -51,6 +50,8 @@ class HashKey extends CursorBasedIterator
protected function extractNext() protected function extractNext()
{ {
$this->position = key($this->elements); $this->position = key($this->elements);
$this->current = array_shift($this->elements); $this->current = current($this->elements);
unset($this->elements[$this->position]);
} }
} }

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -17,9 +18,7 @@ use Predis\ClientInterface;
* Abstracts the iteration of the keyspace on a Redis instance by leveraging the * Abstracts the iteration of the keyspace on a Redis instance by leveraging the
* SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. * SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
* *
* @author Daniele Alessandri <suppakilla@gmail.com> * @see http://redis.io/commands/scan
*
* @link http://redis.io/commands/scan
*/ */
class Keyspace extends CursorBasedIterator class Keyspace extends CursorBasedIterator
{ {

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -11,8 +12,11 @@
namespace Predis\Collection\Iterator; namespace Predis\Collection\Iterator;
use InvalidArgumentException;
use Iterator;
use Predis\ClientInterface; use Predis\ClientInterface;
use Predis\NotSupportedException; use Predis\NotSupportedException;
use ReturnTypeWillChange;
/** /**
* Abstracts the iteration of items stored in a list by leveraging the LRANGE * Abstracts the iteration of items stored in a list by leveraging the LRANGE
@ -24,11 +28,9 @@ use Predis\NotSupportedException;
* guarantees on the returned elements because the collection can change several * guarantees on the returned elements because the collection can change several
* times (trimmed, deleted, overwritten) during the iteration process. * times (trimmed, deleted, overwritten) during the iteration process.
* *
* @author Daniele Alessandri <suppakilla@gmail.com> * @see http://redis.io/commands/lrange
*
* @link http://redis.io/commands/lrange
*/ */
class ListKey implements \Iterator class ListKey implements Iterator
{ {
protected $client; protected $client;
protected $count; protected $count;
@ -45,14 +47,14 @@ class ListKey implements \Iterator
* @param string $key Redis list key. * @param string $key Redis list key.
* @param int $count Number of items retrieved on each fetch operation. * @param int $count Number of items retrieved on each fetch operation.
* *
* @throws \InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function __construct(ClientInterface $client, $key, $count = 10) public function __construct(ClientInterface $client, $key, $count = 10)
{ {
$this->requiredCommand($client, 'LRANGE'); $this->requiredCommand($client, 'LRANGE');
if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) { if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) {
throw new \InvalidArgumentException('The $count argument must be a positive integer.'); throw new InvalidArgumentException('The $count argument must be a positive integer.');
} }
$this->client = $client; $this->client = $client;
@ -73,8 +75,8 @@ class ListKey implements \Iterator
*/ */
protected function requiredCommand(ClientInterface $client, $commandID) protected function requiredCommand(ClientInterface $client, $commandID)
{ {
if (!$client->getProfile()->supportsCommand($commandID)) { if (!$client->getCommandFactory()->supports($commandID)) {
throw new NotSupportedException("The current profile does not support '$commandID'."); throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
} }
} }
@ -85,7 +87,7 @@ class ListKey implements \Iterator
{ {
$this->valid = true; $this->valid = true;
$this->fetchmore = true; $this->fetchmore = true;
$this->elements = array(); $this->elements = [];
$this->position = -1; $this->position = -1;
$this->current = null; $this->current = null;
} }
@ -126,8 +128,9 @@ class ListKey implements \Iterator
} }
/** /**
* {@inheritdoc} * @return void
*/ */
#[ReturnTypeWillChange]
public function rewind() public function rewind()
{ {
$this->reset(); $this->reset();
@ -135,24 +138,27 @@ class ListKey implements \Iterator
} }
/** /**
* {@inheritdoc} * @return mixed
*/ */
#[ReturnTypeWillChange]
public function current() public function current()
{ {
return $this->current; return $this->current;
} }
/** /**
* {@inheritdoc} * @return int|null
*/ */
#[ReturnTypeWillChange]
public function key() public function key()
{ {
return $this->position; return $this->position;
} }
/** /**
* {@inheritdoc} * @return void
*/ */
#[ReturnTypeWillChange]
public function next() public function next()
{ {
if (!$this->elements && $this->fetchmore) { if (!$this->elements && $this->fetchmore) {
@ -167,8 +173,9 @@ class ListKey implements \Iterator
} }
/** /**
* {@inheritdoc} * @return bool
*/ */
#[ReturnTypeWillChange]
public function valid() public function valid()
{ {
return $this->valid; return $this->valid;

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -17,9 +18,7 @@ use Predis\ClientInterface;
* Abstracts the iteration of members stored in a set by leveraging the SSCAN * Abstracts the iteration of members stored in a set by leveraging the SSCAN
* command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. * command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
* *
* @author Daniele Alessandri <suppakilla@gmail.com> * @see http://redis.io/commands/scan
*
* @link http://redis.io/commands/scan
*/ */
class SetKey extends CursorBasedIterator class SetKey extends CursorBasedIterator
{ {

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -17,9 +18,7 @@ use Predis\ClientInterface;
* Abstracts the iteration of members stored in a sorted set by leveraging the * Abstracts the iteration of members stored in a sorted set by leveraging the
* ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. * ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
* *
* @author Daniele Alessandri <suppakilla@gmail.com> * @see http://redis.io/commands/scan
*
* @link http://redis.io/commands/scan
*/ */
class SortedSetKey extends CursorBasedIterator class SortedSetKey extends CursorBasedIterator
{ {
@ -50,11 +49,9 @@ class SortedSetKey extends CursorBasedIterator
*/ */
protected function extractNext() protected function extractNext()
{ {
if ($kv = each($this->elements)) { $this->position = key($this->elements);
$this->position = $kv[0]; $this->current = current($this->elements);
$this->current = $kv[1];
unset($this->elements[$this->position]); unset($this->elements[$this->position]);
}
} }
} }

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -13,32 +14,18 @@ namespace Predis\Command;
/** /**
* Base class for Redis commands. * Base class for Redis commands.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
abstract class Command implements CommandInterface abstract class Command implements CommandInterface
{ {
private $slot; private $slot;
private $arguments = array(); private $arguments = [];
/**
* Returns a filtered array of the arguments.
*
* @param array $arguments List of arguments.
*
* @return array
*/
protected function filterArguments(array $arguments)
{
return $arguments;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function setArguments(array $arguments) public function setArguments(array $arguments)
{ {
$this->arguments = $this->filterArguments($arguments); $this->arguments = $arguments;
unset($this->slot); unset($this->slot);
} }
@ -82,9 +69,7 @@ abstract class Command implements CommandInterface
*/ */
public function getSlot() public function getSlot()
{ {
if (isset($this->slot)) { return $this->slot ?? null;
return $this->slot;
}
} }
/** /**
@ -104,7 +89,7 @@ abstract class Command implements CommandInterface
*/ */
public static function normalizeArguments(array $arguments) public static function normalizeArguments(array $arguments)
{ {
if (count($arguments) === 1 && is_array($arguments[0])) { if (count($arguments) === 1 && isset($arguments[0]) && is_array($arguments[0])) {
return $arguments[0]; return $arguments[0];
} }
@ -121,9 +106,21 @@ abstract class Command implements CommandInterface
public static function normalizeVariadic(array $arguments) public static function normalizeVariadic(array $arguments)
{ {
if (count($arguments) === 2 && is_array($arguments[1])) { if (count($arguments) === 2 && is_array($arguments[1])) {
return array_merge(array($arguments[0]), $arguments[1]); return array_merge([$arguments[0]], $arguments[1]);
} }
return $arguments; return $arguments;
} }
/**
* Remove all false values from arguments.
*
* @return void
*/
public function filterArguments(): void
{
$this->arguments = array_filter($this->arguments, static function ($argument) {
return $argument !== false && $argument !== null;
});
}
} }

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -13,8 +14,6 @@ namespace Predis\Command;
/** /**
* Defines an abstraction representing a Redis command. * Defines an abstraction representing a Redis command.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
interface CommandInterface interface CommandInterface
{ {
@ -73,7 +72,7 @@ interface CommandInterface
/** /**
* Parses a raw response and returns a PHP object. * Parses a raw response and returns a PHP object.
* *
* @param string $data Binary string containing the whole response. * @param string|array|null $data Binary string containing the whole response.
* *
* @return mixed * @return mixed
*/ */

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/auth
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionAuth extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'AUTH';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/echo
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionEcho extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'ECHO';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/ping
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionPing extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PING';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/quit
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionQuit extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'QUIT';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/select
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionSelect extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'SELECT';
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hdel
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashDelete extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HDEL';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeVariadic($arguments);
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hexists
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashExists extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HEXISTS';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hget
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGet extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HGET';
}
}

View file

@ -1,42 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hgetall
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGetAll extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HGETALL';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
$result = array();
for ($i = 0; $i < count($data); ++$i) {
$result[$data[$i]] = $data[++$i];
}
return $result;
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hmget
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGetMultiple extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HMGET';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeVariadic($arguments);
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hincrby
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashIncrementBy extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HINCRBY';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hincrbyfloat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashIncrementByFloat extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HINCRBYFLOAT';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hkeys
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashKeys extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HKEYS';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashLength extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HLEN';
}
}

View file

@ -1,85 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hscan
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashScan extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HSCAN';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (count($arguments) === 3 && is_array($arguments[2])) {
$options = $this->prepareOptions(array_pop($arguments));
$arguments = array_merge($arguments, $options);
}
return $arguments;
}
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
*
* @return array
*/
protected function prepareOptions($options)
{
$options = array_change_key_case($options, CASE_UPPER);
$normalized = array();
if (!empty($options['MATCH'])) {
$normalized[] = 'MATCH';
$normalized[] = $options['MATCH'];
}
if (!empty($options['COUNT'])) {
$normalized[] = 'COUNT';
$normalized[] = $options['COUNT'];
}
return $normalized;
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
if (is_array($data)) {
$fields = $data[1];
$result = array();
for ($i = 0; $i < count($fields); ++$i) {
$result[$fields[$i]] = $fields[++$i];
}
$data[1] = $result;
}
return $data;
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSet extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HSET';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,48 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hmset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSetMultiple extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HMSET';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$flattenedKVs = array($arguments[0]);
$args = $arguments[1];
foreach ($args as $k => $v) {
$flattenedKVs[] = $k;
$flattenedKVs[] = $v;
}
return $flattenedKVs;
}
return $arguments;
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hsetnx
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSetPreserve extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HSETNX';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hstrlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashStringLength extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HSTRLEN';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hvals
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashValues extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HVALS';
}
}

View file

@ -1,44 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pfadd
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HyperLogLogAdd extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PFADD';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeVariadic($arguments);
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pfcount
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HyperLogLogCount extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PFCOUNT';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeArguments($arguments);
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pfmerge
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HyperLogLogMerge extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PFMERGE';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeArguments($arguments);
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/del
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDelete extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DEL';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeArguments($arguments);
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/dump
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDump extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DUMP';
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/exists
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExists extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'EXISTS';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/expire
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExpire extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'EXPIRE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/expireat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExpireAt extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'EXPIREAT';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/keys
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyKeys extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'KEYS';
}
}

View file

@ -1,50 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/migrate
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyMigrate extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'MIGRATE';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (is_array(end($arguments))) {
foreach (array_pop($arguments) as $modifier => $value) {
$modifier = strtoupper($modifier);
if ($modifier === 'COPY' && $value == true) {
$arguments[] = $modifier;
}
if ($modifier === 'REPLACE' && $value == true) {
$arguments[] = $modifier;
}
}
}
return $arguments;
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/move
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyMove extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'MOVE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/persist
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPersist extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PERSIST';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pexpire
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPreciseExpire extends KeyExpire
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PEXPIRE';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pexpireat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPreciseExpireAt extends KeyExpireAt
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PEXPIREAT';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pttl
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPreciseTimeToLive extends KeyTimeToLive
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PTTL';
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/randomkey
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRandom extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RANDOMKEY';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data !== '' ? $data : null;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rename
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRename extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RENAME';
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/renamenx
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRenamePreserve extends KeyRename
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RENAMENX';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/restore
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRestore extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RESTORE';
}
}

View file

@ -1,66 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/scan
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyScan extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'SCAN';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$options = $this->prepareOptions(array_pop($arguments));
$arguments = array_merge($arguments, $options);
}
return $arguments;
}
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
*
* @return array
*/
protected function prepareOptions($options)
{
$options = array_change_key_case($options, CASE_UPPER);
$normalized = array();
if (!empty($options['MATCH'])) {
$normalized[] = 'MATCH';
$normalized[] = $options['MATCH'];
}
if (!empty($options['COUNT'])) {
$normalized[] = 'COUNT';
$normalized[] = $options['COUNT'];
}
return $normalized;
}
}

View file

@ -1,83 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/sort
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeySort extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'SORT';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (count($arguments) === 1) {
return $arguments;
}
$query = array($arguments[0]);
$sortParams = array_change_key_case($arguments[1], CASE_UPPER);
if (isset($sortParams['BY'])) {
$query[] = 'BY';
$query[] = $sortParams['BY'];
}
if (isset($sortParams['GET'])) {
$getargs = $sortParams['GET'];
if (is_array($getargs)) {
foreach ($getargs as $getarg) {
$query[] = 'GET';
$query[] = $getarg;
}
} else {
$query[] = 'GET';
$query[] = $getargs;
}
}
if (isset($sortParams['LIMIT']) &&
is_array($sortParams['LIMIT']) &&
count($sortParams['LIMIT']) == 2) {
$query[] = 'LIMIT';
$query[] = $sortParams['LIMIT'][0];
$query[] = $sortParams['LIMIT'][1];
}
if (isset($sortParams['SORT'])) {
$query[] = strtoupper($sortParams['SORT']);
}
if (isset($sortParams['ALPHA']) && $sortParams['ALPHA'] == true) {
$query[] = 'ALPHA';
}
if (isset($sortParams['STORE'])) {
$query[] = 'STORE';
$query[] = $sortParams['STORE'];
}
return $query;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/ttl
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyTimeToLive extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'TTL';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/type
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyType extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'TYPE';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lindex
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListIndex extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LINDEX';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/linsert
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListInsert extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LINSERT';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/llen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListLength extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LLEN';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lpop
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopFirst extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LPOP';
}
}

View file

@ -1,41 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/blpop
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopFirstBlocking extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BLPOP';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[0])) {
list($arguments, $timeout) = $arguments;
array_push($arguments, $timeout);
}
return $arguments;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rpop
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLast extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RPOP';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/brpop
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastBlocking extends ListPopFirstBlocking
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BRPOP';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rpoplpush
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastPushHead extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RPOPLPUSH';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/brpoplpush
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastPushHeadBlocking extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BRPOPLPUSH';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lpush
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPushHead extends ListPushTail
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LPUSH';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lpushx
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPushHeadX extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LPUSHX';
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rpush
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPushTail extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RPUSH';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeVariadic($arguments);
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/rpushx
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPushTailX extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RPUSHX';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lrange
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListRange extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LRANGE';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lrem
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListRemove extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LREM';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/lset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListSet extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LSET';
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/ltrim
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListTrim extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'LTRIM';
}
}

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -13,8 +14,6 @@ namespace Predis\Command;
/** /**
* Defines a command whose keys can be prefixed. * Defines a command whose keys can be prefixed.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
interface PrefixableCommandInterface extends CommandInterface interface PrefixableCommandInterface extends CommandInterface
{ {

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -11,14 +12,13 @@
namespace Predis\Command\Processor; namespace Predis\Command\Processor;
use InvalidArgumentException;
use Predis\Command\CommandInterface; use Predis\Command\CommandInterface;
use Predis\Command\PrefixableCommandInterface; use Predis\Command\PrefixableCommandInterface;
/** /**
* Command processor capable of prefixing keys stored in the arguments of Redis * Command processor capable of prefixing keys stored in the arguments of Redis
* commands supported. * commands supported.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
class KeyPrefixProcessor implements ProcessorInterface class KeyPrefixProcessor implements ProcessorInterface
{ {
@ -31,135 +31,270 @@ class KeyPrefixProcessor implements ProcessorInterface
public function __construct($prefix) public function __construct($prefix)
{ {
$this->prefix = $prefix; $this->prefix = $prefix;
$this->commands = array(
$prefixFirst = static::class . '::first';
$prefixAll = static::class . '::all';
$prefixInterleaved = static::class . '::interleaved';
$prefixSkipFirst = static::class . '::skipFirst';
$prefixSkipLast = static::class . '::skipLast';
$prefixSort = static::class . '::sort';
$prefixEvalKeys = static::class . '::evalKeys';
$prefixZsetStore = static::class . '::zsetStore';
$prefixMigrate = static::class . '::migrate';
$prefixGeoradius = static::class . '::georadius';
$this->commands = [
/* ---------------- Redis 1.2 ---------------- */ /* ---------------- Redis 1.2 ---------------- */
'EXISTS' => 'static::first', 'EXISTS' => $prefixAll,
'DEL' => 'static::all', 'DEL' => $prefixAll,
'TYPE' => 'static::first', 'TYPE' => $prefixFirst,
'KEYS' => 'static::first', 'KEYS' => $prefixFirst,
'RENAME' => 'static::all', 'RENAME' => $prefixAll,
'RENAMENX' => 'static::all', 'RENAMENX' => $prefixAll,
'EXPIRE' => 'static::first', 'EXPIRE' => $prefixFirst,
'EXPIREAT' => 'static::first', 'EXPIREAT' => $prefixFirst,
'TTL' => 'static::first', 'TTL' => $prefixFirst,
'MOVE' => 'static::first', 'MOVE' => $prefixFirst,
'SORT' => 'static::sort', 'SORT' => $prefixSort,
'DUMP' => 'static::first', 'DUMP' => $prefixFirst,
'RESTORE' => 'static::first', 'RESTORE' => $prefixFirst,
'SET' => 'static::first', 'SET' => $prefixFirst,
'SETNX' => 'static::first', 'SETNX' => $prefixFirst,
'MSET' => 'static::interleaved', 'MSET' => $prefixInterleaved,
'MSETNX' => 'static::interleaved', 'MSETNX' => $prefixInterleaved,
'GET' => 'static::first', 'GET' => $prefixFirst,
'MGET' => 'static::all', 'MGET' => $prefixAll,
'GETSET' => 'static::first', 'GETSET' => $prefixFirst,
'INCR' => 'static::first', 'INCR' => $prefixFirst,
'INCRBY' => 'static::first', 'INCRBY' => $prefixFirst,
'DECR' => 'static::first', 'DECR' => $prefixFirst,
'DECRBY' => 'static::first', 'DECRBY' => $prefixFirst,
'RPUSH' => 'static::first', 'RPUSH' => $prefixFirst,
'LPUSH' => 'static::first', 'LPUSH' => $prefixFirst,
'LLEN' => 'static::first', 'LLEN' => $prefixFirst,
'LRANGE' => 'static::first', 'LRANGE' => $prefixFirst,
'LTRIM' => 'static::first', 'LTRIM' => $prefixFirst,
'LINDEX' => 'static::first', 'LINDEX' => $prefixFirst,
'LSET' => 'static::first', 'LSET' => $prefixFirst,
'LREM' => 'static::first', 'LREM' => $prefixFirst,
'LPOP' => 'static::first', 'LPOP' => $prefixFirst,
'RPOP' => 'static::first', 'RPOP' => $prefixFirst,
'RPOPLPUSH' => 'static::all', 'RPOPLPUSH' => $prefixAll,
'SADD' => 'static::first', 'SADD' => $prefixFirst,
'SREM' => 'static::first', 'SREM' => $prefixFirst,
'SPOP' => 'static::first', 'SPOP' => $prefixFirst,
'SMOVE' => 'static::skipLast', 'SMOVE' => $prefixSkipLast,
'SCARD' => 'static::first', 'SCARD' => $prefixFirst,
'SISMEMBER' => 'static::first', 'SISMEMBER' => $prefixFirst,
'SINTER' => 'static::all', 'SINTER' => $prefixAll,
'SINTERSTORE' => 'static::all', 'SINTERSTORE' => $prefixAll,
'SUNION' => 'static::all', 'SUNION' => $prefixAll,
'SUNIONSTORE' => 'static::all', 'SUNIONSTORE' => $prefixAll,
'SDIFF' => 'static::all', 'SDIFF' => $prefixAll,
'SDIFFSTORE' => 'static::all', 'SDIFFSTORE' => $prefixAll,
'SMEMBERS' => 'static::first', 'SMEMBERS' => $prefixFirst,
'SRANDMEMBER' => 'static::first', 'SRANDMEMBER' => $prefixFirst,
'ZADD' => 'static::first', 'ZADD' => $prefixFirst,
'ZINCRBY' => 'static::first', 'ZINCRBY' => $prefixFirst,
'ZREM' => 'static::first', 'ZREM' => $prefixFirst,
'ZRANGE' => 'static::first', 'ZRANGE' => $prefixFirst,
'ZREVRANGE' => 'static::first', 'ZREVRANGE' => $prefixFirst,
'ZRANGEBYSCORE' => 'static::first', 'ZRANGEBYSCORE' => $prefixFirst,
'ZCARD' => 'static::first', 'ZCARD' => $prefixFirst,
'ZSCORE' => 'static::first', 'ZSCORE' => $prefixFirst,
'ZREMRANGEBYSCORE' => 'static::first', 'ZREMRANGEBYSCORE' => $prefixFirst,
/* ---------------- Redis 2.0 ---------------- */ /* ---------------- Redis 2.0 ---------------- */
'SETEX' => 'static::first', 'SETEX' => $prefixFirst,
'APPEND' => 'static::first', 'APPEND' => $prefixFirst,
'SUBSTR' => 'static::first', 'SUBSTR' => $prefixFirst,
'BLPOP' => 'static::skipLast', 'BLPOP' => $prefixSkipLast,
'BRPOP' => 'static::skipLast', 'BRPOP' => $prefixSkipLast,
'ZUNIONSTORE' => 'static::zsetStore', 'ZUNIONSTORE' => $prefixZsetStore,
'ZINTERSTORE' => 'static::zsetStore', 'ZINTERSTORE' => $prefixZsetStore,
'ZCOUNT' => 'static::first', 'ZCOUNT' => $prefixFirst,
'ZRANK' => 'static::first', 'ZRANK' => $prefixFirst,
'ZREVRANK' => 'static::first', 'ZREVRANK' => $prefixFirst,
'ZREMRANGEBYRANK' => 'static::first', 'ZREMRANGEBYRANK' => $prefixFirst,
'HSET' => 'static::first', 'HSET' => $prefixFirst,
'HSETNX' => 'static::first', 'HSETNX' => $prefixFirst,
'HMSET' => 'static::first', 'HMSET' => $prefixFirst,
'HINCRBY' => 'static::first', 'HINCRBY' => $prefixFirst,
'HGET' => 'static::first', 'HGET' => $prefixFirst,
'HMGET' => 'static::first', 'HMGET' => $prefixFirst,
'HDEL' => 'static::first', 'HDEL' => $prefixFirst,
'HEXISTS' => 'static::first', 'HEXISTS' => $prefixFirst,
'HLEN' => 'static::first', 'HLEN' => $prefixFirst,
'HKEYS' => 'static::first', 'HKEYS' => $prefixFirst,
'HVALS' => 'static::first', 'HVALS' => $prefixFirst,
'HGETALL' => 'static::first', 'HGETALL' => $prefixFirst,
'SUBSCRIBE' => 'static::all', 'SUBSCRIBE' => $prefixAll,
'UNSUBSCRIBE' => 'static::all', 'UNSUBSCRIBE' => $prefixAll,
'PSUBSCRIBE' => 'static::all', 'PSUBSCRIBE' => $prefixAll,
'PUNSUBSCRIBE' => 'static::all', 'PUNSUBSCRIBE' => $prefixAll,
'PUBLISH' => 'static::first', 'PUBLISH' => $prefixFirst,
/* ---------------- Redis 2.2 ---------------- */ /* ---------------- Redis 2.2 ---------------- */
'PERSIST' => 'static::first', 'PERSIST' => $prefixFirst,
'STRLEN' => 'static::first', 'STRLEN' => $prefixFirst,
'SETRANGE' => 'static::first', 'SETRANGE' => $prefixFirst,
'GETRANGE' => 'static::first', 'GETRANGE' => $prefixFirst,
'SETBIT' => 'static::first', 'SETBIT' => $prefixFirst,
'GETBIT' => 'static::first', 'GETBIT' => $prefixFirst,
'RPUSHX' => 'static::first', 'RPUSHX' => $prefixFirst,
'LPUSHX' => 'static::first', 'LPUSHX' => $prefixFirst,
'LINSERT' => 'static::first', 'LINSERT' => $prefixFirst,
'BRPOPLPUSH' => 'static::skipLast', 'BRPOPLPUSH' => $prefixSkipLast,
'ZREVRANGEBYSCORE' => 'static::first', 'ZREVRANGEBYSCORE' => $prefixFirst,
'WATCH' => 'static::all', 'WATCH' => $prefixAll,
/* ---------------- Redis 2.6 ---------------- */ /* ---------------- Redis 2.6 ---------------- */
'PTTL' => 'static::first', 'PTTL' => $prefixFirst,
'PEXPIRE' => 'static::first', 'PEXPIRE' => $prefixFirst,
'PEXPIREAT' => 'static::first', 'PEXPIREAT' => $prefixFirst,
'PSETEX' => 'static::first', 'PSETEX' => $prefixFirst,
'INCRBYFLOAT' => 'static::first', 'INCRBYFLOAT' => $prefixFirst,
'BITOP' => 'static::skipFirst', 'BITOP' => $prefixSkipFirst,
'BITCOUNT' => 'static::first', 'BITCOUNT' => $prefixFirst,
'HINCRBYFLOAT' => 'static::first', 'HINCRBYFLOAT' => $prefixFirst,
'EVAL' => 'static::evalKeys', 'EVAL' => $prefixEvalKeys,
'EVALSHA' => 'static::evalKeys', 'EVALSHA' => $prefixEvalKeys,
'MIGRATE' => 'static::migrate', 'MIGRATE' => $prefixMigrate,
/* ---------------- Redis 2.8 ---------------- */ /* ---------------- Redis 2.8 ---------------- */
'SSCAN' => 'static::first', 'SSCAN' => $prefixFirst,
'ZSCAN' => 'static::first', 'ZSCAN' => $prefixFirst,
'HSCAN' => 'static::first', 'HSCAN' => $prefixFirst,
'PFADD' => 'static::first', 'PFADD' => $prefixFirst,
'PFCOUNT' => 'static::all', 'PFCOUNT' => $prefixAll,
'PFMERGE' => 'static::all', 'PFMERGE' => $prefixAll,
'ZLEXCOUNT' => 'static::first', 'ZLEXCOUNT' => $prefixFirst,
'ZRANGEBYLEX' => 'static::first', 'ZRANGEBYLEX' => $prefixFirst,
'ZREMRANGEBYLEX' => 'static::first', 'ZREMRANGEBYLEX' => $prefixFirst,
'ZREVRANGEBYLEX' => 'static::first', 'ZREVRANGEBYLEX' => $prefixFirst,
'BITPOS' => 'static::first', 'BITPOS' => $prefixFirst,
/* ---------------- Redis 3.2 ---------------- */ /* ---------------- Redis 3.2 ---------------- */
'HSTRLEN' => 'static::first', 'HSTRLEN' => $prefixFirst,
); 'BITFIELD' => $prefixFirst,
'GEOADD' => $prefixFirst,
'GEOHASH' => $prefixFirst,
'GEOPOS' => $prefixFirst,
'GEODIST' => $prefixFirst,
'GEORADIUS' => $prefixGeoradius,
'GEORADIUSBYMEMBER' => $prefixGeoradius,
/* ---------------- Redis 5.0 ---------------- */
'XADD' => $prefixFirst,
'XRANGE' => $prefixFirst,
'XREVRANGE' => $prefixFirst,
'XDEL' => $prefixFirst,
'XLEN' => $prefixFirst,
'XACK' => $prefixFirst,
'XTRIM' => $prefixFirst,
/* ---------------- Redis 6.2 ---------------- */
'GETDEL' => $prefixFirst,
/* ---------------- Redis 7.0 ---------------- */
'EXPIRETIME' => $prefixFirst,
/* RedisJSON */
'JSON.ARRAPPEND' => $prefixFirst,
'JSON.ARRINDEX' => $prefixFirst,
'JSON.ARRINSERT' => $prefixFirst,
'JSON.ARRLEN' => $prefixFirst,
'JSON.ARRPOP' => $prefixFirst,
'JSON.ARRTRIM' => $prefixFirst,
'JSON.CLEAR' => $prefixFirst,
'JSON.DEBUG MEMORY' => $prefixFirst,
'JSON.DEL' => $prefixFirst,
'JSON.FORGET' => $prefixFirst,
'JSON.GET' => $prefixFirst,
'JSON.MGET' => $prefixAll,
'JSON.NUMINCRBY' => $prefixFirst,
'JSON.OBJKEYS' => $prefixFirst,
'JSON.OBJLEN' => $prefixFirst,
'JSON.RESP' => $prefixFirst,
'JSON.SET' => $prefixFirst,
'JSON.STRAPPEND' => $prefixFirst,
'JSON.STRLEN' => $prefixFirst,
'JSON.TOGGLE' => $prefixFirst,
'JSON.TYPE' => $prefixFirst,
/* RedisBloom */
'BF.ADD' => $prefixFirst,
'BF.EXISTS' => $prefixFirst,
'BF.INFO' => $prefixFirst,
'BF.INSERT' => $prefixFirst,
'BF.LOADCHUNK' => $prefixFirst,
'BF.MADD' => $prefixFirst,
'BF.MEXISTS' => $prefixFirst,
'BF.RESERVE' => $prefixFirst,
'BF.SCANDUMP' => $prefixFirst,
'CF.ADD' => $prefixFirst,
'CF.ADDNX' => $prefixFirst,
'CF.COUNT' => $prefixFirst,
'CF.DEL' => $prefixFirst,
'CF.EXISTS' => $prefixFirst,
'CF.INFO' => $prefixFirst,
'CF.INSERT' => $prefixFirst,
'CF.INSERTNX' => $prefixFirst,
'CF.LOADCHUNK' => $prefixFirst,
'CF.MEXISTS' => $prefixFirst,
'CF.RESERVE' => $prefixFirst,
'CF.SCANDUMP' => $prefixFirst,
'CMS.INCRBY' => $prefixFirst,
'CMS.INFO' => $prefixFirst,
'CMS.INITBYDIM' => $prefixFirst,
'CMS.INITBYPROB' => $prefixFirst,
'CMS.QUERY' => $prefixFirst,
'TDIGEST.ADD' => $prefixFirst,
'TDIGEST.BYRANK' => $prefixFirst,
'TDIGEST.BYREVRANK' => $prefixFirst,
'TDIGEST.CDF' => $prefixFirst,
'TDIGEST.CREATE' => $prefixFirst,
'TDIGEST.INFO' => $prefixFirst,
'TDIGEST.MAX' => $prefixFirst,
'TDIGEST.MIN' => $prefixFirst,
'TDIGEST.QUANTILE' => $prefixFirst,
'TDIGEST.RANK' => $prefixFirst,
'TDIGEST.RESET' => $prefixFirst,
'TDIGEST.REVRANK' => $prefixFirst,
'TDIGEST.TRIMMED_MEAN' => $prefixFirst,
'TOPK.ADD' => $prefixFirst,
'TOPK.INCRBY' => $prefixFirst,
'TOPK.INFO' => $prefixFirst,
'TOPK.LIST' => $prefixFirst,
'TOPK.QUERY' => $prefixFirst,
'TOPK.RESERVE' => $prefixFirst,
/* RediSearch */
'FT.AGGREGATE' => $prefixFirst,
'FT.ALTER' => $prefixFirst,
'FT.CREATE' => $prefixFirst,
'FT.CURSOR DEL' => $prefixFirst,
'FT.CURSOR READ' => $prefixFirst,
'FT.DROPINDEX' => $prefixFirst,
'FT.EXPLAIN' => $prefixFirst,
'FT.INFO' => $prefixFirst,
'FT.PROFILE' => $prefixFirst,
'FT.SEARCH' => $prefixFirst,
'FT.SPELLCHECK' => $prefixFirst,
'FT.SYNDUMP' => $prefixFirst,
'FT.SYNUPDATE' => $prefixFirst,
'FT.TAGVALS' => $prefixFirst,
/* Redis TimeSeries */
'TS.ADD' => $prefixFirst,
'TS.ALTER' => $prefixFirst,
'TS.CREATE' => $prefixFirst,
'TS.DECRBY' => $prefixFirst,
'TS.DEL' => $prefixFirst,
'TS.GET' => $prefixFirst,
'TS.INCRBY' => $prefixFirst,
'TS.INFO' => $prefixFirst,
'TS.MGET' => $prefixFirst,
'TS.MRANGE' => $prefixFirst,
'TS.MREVRANGE' => $prefixFirst,
'TS.QUERYINDEX' => $prefixFirst,
'TS.RANGE' => $prefixFirst,
'TS.REVRANGE' => $prefixFirst,
];
} }
/** /**
@ -190,7 +325,7 @@ class KeyPrefixProcessor implements ProcessorInterface
if ($command instanceof PrefixableCommandInterface) { if ($command instanceof PrefixableCommandInterface) {
$command->prefixKeys($this->prefix); $command->prefixKeys($this->prefix);
} elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) { } elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
call_user_func($this->commands[$commandID], $command, $this->prefix); $this->commands[$commandID]($command, $this->prefix);
} }
} }
@ -208,7 +343,7 @@ class KeyPrefixProcessor implements ProcessorInterface
* @param string $commandID The ID of the command to be handled. * @param string $commandID The ID of the command to be handled.
* @param mixed $callback A valid callable object or NULL. * @param mixed $callback A valid callable object or NULL.
* *
* @throws \InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function setCommandHandler($commandID, $callback = null) public function setCommandHandler($commandID, $callback = null)
{ {
@ -221,7 +356,7 @@ class KeyPrefixProcessor implements ProcessorInterface
} }
if (!is_callable($callback)) { if (!is_callable($callback)) {
throw new \InvalidArgumentException( throw new InvalidArgumentException(
'Callback must be a valid callable object or NULL' 'Callback must be a valid callable object or NULL'
); );
} }
@ -338,7 +473,7 @@ class KeyPrefixProcessor implements ProcessorInterface
if (($count = count($arguments)) > 1) { if (($count = count($arguments)) > 1) {
for ($i = 1; $i < $count; ++$i) { for ($i = 1; $i < $count; ++$i) {
switch ($arguments[$i]) { switch (strtoupper($arguments[$i])) {
case 'BY': case 'BY':
case 'STORE': case 'STORE':
$arguments[$i] = "$prefix{$arguments[++$i]}"; $arguments[$i] = "$prefix{$arguments[++$i]}";
@ -351,7 +486,7 @@ class KeyPrefixProcessor implements ProcessorInterface
} }
break; break;
case 'LIMIT'; case 'LIMIT':
$i += 2; $i += 2;
break; break;
} }
@ -412,4 +547,31 @@ class KeyPrefixProcessor implements ProcessorInterface
$command->setRawArguments($arguments); $command->setRawArguments($arguments);
} }
} }
/**
* Applies the specified prefix to the key of a GEORADIUS command.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
*/
public static function georadius(CommandInterface $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$arguments[0] = "$prefix{$arguments[0]}";
$startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
if (($count = count($arguments)) > $startIndex) {
for ($i = $startIndex; $i < $count; ++$i) {
switch (strtoupper($arguments[$i])) {
case 'STORE':
case 'STOREDIST':
$arguments[$i] = "$prefix{$arguments[++$i]}";
break;
}
}
}
$command->setRawArguments($arguments);
}
}
} }

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -11,21 +12,24 @@
namespace Predis\Command\Processor; namespace Predis\Command\Processor;
use ArrayAccess;
use ArrayIterator;
use InvalidArgumentException;
use Predis\Command\CommandInterface; use Predis\Command\CommandInterface;
use ReturnTypeWillChange;
use Traversable;
/** /**
* Default implementation of a command processors chain. * Default implementation of a command processors chain.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
class ProcessorChain implements \ArrayAccess, ProcessorInterface class ProcessorChain implements ArrayAccess, ProcessorInterface
{ {
private $processors = array(); private $processors = [];
/** /**
* @param array $processors List of instances of ProcessorInterface. * @param array $processors List of instances of ProcessorInterface.
*/ */
public function __construct($processors = array()) public function __construct($processors = [])
{ {
foreach ($processors as $processor) { foreach ($processors as $processor) {
$this->add($processor); $this->add($processor);
@ -71,11 +75,11 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/** /**
* Returns an iterator over the list of command processor in the chain. * Returns an iterator over the list of command processor in the chain.
* *
* @return \ArrayIterator * @return Traversable<int, ProcessorInterface>
*/ */
public function getIterator() public function getIterator()
{ {
return new \ArrayIterator($this->processors); return new ArrayIterator($this->processors);
} }
/** /**
@ -89,30 +93,36 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
} }
/** /**
* {@inheritdoc} * @param int $index
* @return bool
*/ */
#[ReturnTypeWillChange]
public function offsetExists($index) public function offsetExists($index)
{ {
return isset($this->processors[$index]); return isset($this->processors[$index]);
} }
/** /**
* {@inheritdoc} * @param int $index
* @return ProcessorInterface
*/ */
#[ReturnTypeWillChange]
public function offsetGet($index) public function offsetGet($index)
{ {
return $this->processors[$index]; return $this->processors[$index];
} }
/** /**
* {@inheritdoc} * @param int $index
* @param ProcessorInterface $processor
* @return void
*/ */
#[ReturnTypeWillChange]
public function offsetSet($index, $processor) public function offsetSet($index, $processor)
{ {
if (!$processor instanceof ProcessorInterface) { if (!$processor instanceof ProcessorInterface) {
throw new \InvalidArgumentException( throw new InvalidArgumentException(
'A processor chain accepts only instances of '. 'Processor chain accepts only instances of `Predis\Command\Processor\ProcessorInterface`'
"'Predis\Command\Processor\ProcessorInterface'."
); );
} }
@ -120,8 +130,10 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
} }
/** /**
* {@inheritdoc} * @param int $index
* @return void
*/ */
#[ReturnTypeWillChange]
public function offsetUnset($index) public function offsetUnset($index)
{ {
unset($this->processors[$index]); unset($this->processors[$index]);

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -15,8 +16,6 @@ use Predis\Command\CommandInterface;
/** /**
* A command processor processes Redis commands before they are sent to Redis. * A command processor processes Redis commands before they are sent to Redis.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
interface ProcessorInterface interface ProcessorInterface
{ {

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/publish
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PubSubPublish extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PUBLISH';
}
}

View file

@ -1,61 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pubsub
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PubSubPubsub extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PUBSUB';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
switch (strtolower($this->getArgument(0))) {
case 'numsub':
return self::processNumsub($data);
default:
return $data;
}
}
/**
* Returns the processed response to PUBSUB NUMSUB.
*
* @param array $channels List of channels
*
* @return array
*/
protected static function processNumsub(array $channels)
{
$processed = array();
$count = count($channels);
for ($i = 0; $i < $count; ++$i) {
$processed[$channels[$i]] = $channels[++$i];
}
return $processed;
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/subscribe
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PubSubSubscribe extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'SUBSCRIBE';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeArguments($arguments);
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/psubscribe
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PubSubSubscribeByPattern extends PubSubSubscribe
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PSUBSCRIBE';
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/unsubscribe
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PubSubUnsubscribe extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'UNSUBSCRIBE';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeArguments($arguments);
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/punsubscribe
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PubSubUnsubscribeByPattern extends PubSubUnsubscribe
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PUNSUBSCRIBE';
}
}

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -12,52 +13,45 @@
namespace Predis\Command; namespace Predis\Command;
/** /**
* Class for generic "anonymous" Redis commands. * Class representing a generic Redis command.
* *
* This command class does not filter input arguments or parse responses, but * Arguments and responses for these commands are not normalized and they follow
* can be used to leverage the standard Predis API to execute any command simply * what is defined by the Redis documentation.
* by providing the needed arguments following the command signature as defined
* by Redis in its documentation.
* *
* @author Daniele Alessandri <suppakilla@gmail.com> * Raw commands can be useful when implementing higher level abstractions on top
* of Predis\Client or managing internals like Redis Sentinel or Cluster as they
* are not potentially subject to hijacking from third party libraries when they
* override command handlers for standard Redis commands.
*/ */
class RawCommand implements CommandInterface final class RawCommand implements CommandInterface
{ {
private $slot; private $slot;
private $commandID; private $commandID;
private $arguments; private $arguments;
/** /**
* @param array $arguments Command ID and its arguments. * @param string $commandID Command ID
* * @param array $arguments Command arguments
* @throws \InvalidArgumentException
*/ */
public function __construct(array $arguments) public function __construct($commandID, array $arguments = [])
{ {
if (!$arguments) { $this->commandID = strtoupper($commandID);
throw new \InvalidArgumentException( $this->setArguments($arguments);
'The arguments array must contain at least the command ID.'
);
}
$this->commandID = strtoupper(array_shift($arguments));
$this->arguments = $arguments;
} }
/** /**
* Creates a new raw command using a variadic method. * Creates a new raw command using a variadic method.
* *
* @param string $commandID Redis command ID. * @param string $commandID Redis command ID
* @param string ... Arguments list for the command. * @param string ...$args Arguments list for the command
* *
* @return CommandInterface * @return CommandInterface
*/ */
public static function create($commandID /* [ $arg, ... */) public static function create($commandID, ...$args)
{ {
$arguments = func_get_args(); $arguments = func_get_args();
$command = new self($arguments);
return $command; return new static(array_shift($arguments), $arguments);
} }
/** /**
@ -116,9 +110,7 @@ class RawCommand implements CommandInterface
*/ */
public function getSlot() public function getSlot()
{ {
if (isset($this->slot)) { return $this->slot ?? null;
return $this->slot;
}
} }
/** /**

View file

@ -3,7 +3,8 @@
/* /*
* This file is part of the Predis package. * This file is part of the Predis package.
* *
* (c) Daniele Alessandri <suppakilla@gmail.com> * (c) 2009-2020 Daniele Alessandri
* (c) 2021-2023 Till Krüss
* *
* For the full copyright and license information, please view the LICENSE * For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. * file that was distributed with this source code.
@ -15,12 +16,18 @@ namespace Predis\Command;
* Base class used to implement an higher level abstraction for commands based * Base class used to implement an higher level abstraction for commands based
* on Lua scripting with EVAL and EVALSHA. * on Lua scripting with EVAL and EVALSHA.
* *
* @link http://redis.io/commands/eval * @see http://redis.io/commands/eval
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/ */
abstract class ScriptCommand extends ServerEvalSHA abstract class ScriptCommand extends Command
{ {
/**
* {@inheritdoc}
*/
public function getId()
{
return 'EVALSHA';
}
/** /**
* Gets the body of a Lua script. * Gets the body of a Lua script.
* *
@ -28,6 +35,16 @@ abstract class ScriptCommand extends ServerEvalSHA
*/ */
abstract public function getScript(); abstract public function getScript();
/**
* Calculates the SHA1 hash of the body of the script.
*
* @return string SHA1 hash.
*/
public function getScriptHash()
{
return sha1($this->getScript());
}
/** /**
* Specifies the number of arguments that should be considered as keys. * Specifies the number of arguments that should be considered as keys.
* *
@ -55,16 +72,20 @@ abstract class ScriptCommand extends ServerEvalSHA
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function filterArguments(array $arguments) public function setArguments(array $arguments)
{ {
if (($numkeys = $this->getKeysCount()) && $numkeys < 0) { if (($numkeys = $this->getKeysCount()) && $numkeys < 0) {
$numkeys = count($arguments) + $numkeys; $numkeys = count($arguments) + $numkeys;
} }
return array_merge(array(sha1($this->getScript()), (int) $numkeys), $arguments); $arguments = array_merge([$this->getScriptHash(), (int) $numkeys], $arguments);
parent::setArguments($arguments);
} }
/** /**
* Returns arguments for EVAL command.
*
* @return array * @return array
*/ */
public function getEvalArguments() public function getEvalArguments()
@ -74,4 +95,14 @@ abstract class ScriptCommand extends ServerEvalSHA
return $arguments; return $arguments;
} }
/**
* Returns the equivalent EVAL command as a raw command instance.
*
* @return RawCommand
*/
public function getEvalCommand()
{
return new RawCommand('EVAL', $this->getEvalArguments());
}
} }

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/bgrewriteaof
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerBackgroundRewriteAOF extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BGREWRITEAOF';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data == 'Background append only file rewriting started';
}
}

View file

@ -1,36 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/bgsave
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerBackgroundSave extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'BGSAVE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data === 'Background saving started' ? true : $data;
}
}

View file

@ -1,74 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/client-list
* @link http://redis.io/commands/client-kill
* @link http://redis.io/commands/client-getname
* @link http://redis.io/commands/client-setname
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerClient extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'CLIENT';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
$args = array_change_key_case($this->getArguments(), CASE_UPPER);
switch (strtoupper($args[0])) {
case 'LIST':
return $this->parseClientList($data);
case 'KILL':
case 'GETNAME':
case 'SETNAME':
default:
return $data;
}
}
/**
* Parses the response to CLIENT LIST and returns a structured list.
*
* @param string $data Response buffer.
*
* @return array
*/
protected function parseClientList($data)
{
$clients = array();
foreach (explode("\n", $data, -1) as $clientData) {
$client = array();
foreach (explode(' ', $clientData) as $kv) {
@list($k, $v) = explode('=', $kv);
$client[$k] = $v;
}
$clients[] = $client;
}
return $clients;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/command
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerCommand extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'COMMAND';
}
}

View file

@ -1,49 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/config-set
* @link http://redis.io/commands/config-get
* @link http://redis.io/commands/config-resetstat
* @link http://redis.io/commands/config-rewrite
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerConfig extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'CONFIG';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
if (is_array($data)) {
$result = array();
for ($i = 0; $i < count($data); ++$i) {
$result[$data[$i]] = $data[++$i];
}
return $result;
}
return $data;
}
}

View file

@ -1,28 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/dbsize
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerDatabaseSize extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DBSIZE';
}
}

Some files were not shown because too many files have changed in this diff Show more