Compare commits

...

18 Commits

Author SHA1 Message Date
Till Krüss ff59f74581 bump version 2022-10-11 09:52:29 -07:00
Mehmet Tolga Avcioglu 1a0eb7ee85 sentinel default to no password when replica authentication is used (#807)
* set default nopass if password is not provided

* remove debug output

* added tests for noauth sentinel

* Update SentinelReplication.php

Co-authored-by: Mehmet Tolga Avcioglu <mehmet.avcioglu@pusula.net.tr>
Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-10-06 08:14:47 -07:00
Tobias Bengtsson bb6c1a7b6d Add partial support for Stream commands (#804)
Adds support for the following commands not related to groups/consumers:
XADD, XDEL, XLEN, XRANGE, XREVRANGE, XTRIM.
2022-09-27 09:39:17 -07:00
streamingsystems 42c33a406f Check if socket is resource before reading (#802) 2022-09-18 15:01:02 -07:00
Stefano Borghi 7ad74217c2 check resource before writing, w/ test (#800) 2022-09-16 17:03:13 -07:00
Alexandru Pătrănescu 72d7675563 improve PHP 8.2 support (#797)
* use static::class . '::<method>' instead of 'static::method'

* extract concatenation so it can be improved easier at a later point

one improvement at some point could be to replace static::class . '::<method>' with static::<method>(...) in PHP 8.1+

Co-authored-by: Alexandru Patranescu <alexandru.patranescu@payu.com>
2022-09-08 11:25:37 -07:00
Till Krüss 8b5fa92856 mention php version [no ci] 2022-09-06 07:34:14 -07:00
Till Krüss 076a62e3d3 bump version to 2.0.2 2022-09-06 07:32:44 -07:00
Dries Vints 85dc1752b7 PHP 8.2 Support (#796) 2022-09-06 07:13:46 -07:00
Till Krüss 7683215023 bump tag to v2.0.1 2022-09-04 14:57:36 -07:00
Till Krüss dd4ddf6504 Update CHANGELOG.md 2022-08-03 11:53:59 -07:00
Till Krüss 6468e6cc2e Update CHANGELOG.md 2022-08-03 11:51:22 -07:00
digital2real 12f6d30502 Add retry logic to support temporary redis cluster failure (#788)
* Add retry logic to support temporary redis cluster failure

* formatting

* Use exponential backoff retries.
$minRetryAfter cant be changed if needed with RedisCluster::setMinRetryAfter

* fix formating

* (hotfix) formaing + import import  \Predis\Response\Error and \Predis\Connection\ConnectionException

* Update RedisCluster.php

* formatting

* tweaks

* Update RedisCluster.php

* Update RedisCluster.php

* Update RedisCluster.php

* Update unit tests regarding renaming of "retryInterval"

* spacing

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-08-03 11:49:56 -07:00
Linc a602fc7425 Fix sentinel reconnect issue on long-running PHP processes (#784)
* Fix no sentinel server available for autodiscovery on Swoole

* Fix errors in PHPUnit 9.5

the reason: https://github.com/sebastianbergmann/phpunit/commit/68582043e149039cfa3596b42ed35753dcf54fb2?fbclid=IwAR1qy1jFNFeJ3hUt5k7XAflqyCTUTWZhEqVSeizkybVHUEqXjBZ1gJM739U

* add restart sentinel test
2022-07-12 09:44:28 -07:00
John Paul E. Balandan, CPA 4ec74cc5fe Declare $parameters property of Predis\Connection\Parameters (#781) 2022-06-29 07:58:14 -07:00
BrightQi 78fd4cb998 bugfix for srem type to accept array #779 (#780)
* bugfix for srem  type to accept array #779

* add test to test SREM accept members as array type #779
2022-06-28 08:23:35 -07:00
Tomáš Fedor 2a8dd6a7f1 Add array as possible return type to spop (#774)
* Add array as possible return type to spop

* Update ClientInterface.php

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-06-14 19:59:10 -07:00
Jayin Taung 7d7b3416a8 Add example for installing predis by Composer (#772)
* Add example for installing predis by Composer

* Update README.md

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-06-13 07:54:46 -07:00
28 changed files with 1934 additions and 297 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1']
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
redis-versions: ['3', '4', '5', '6', '7']
steps:
+17
View File
@@ -1,5 +1,22 @@
## Changelog
## v2.0.3 (2022-10-11)
- Improved PHP 8.2 support
- Call `is_resource()` before reading/writing
- Added partial support for Redis Stream commands
- Fixed Sentinel authentication issue
## v2.0.2 (2022-09-06)
- Fixed PHP 8.2 deprecation notice: Use of "static" in callables
## v2.0.1 (2022-09-04)
- Added retry interval to `RedisCluster` with a default of `10ms`
- Avoid PHP 8.2 warning in `Connection\Parameters`
- Fixed Sentinel reconnect issue in long-running PHP processes
## v2.0.0 (2022-06-08)
- Dropped support for PHP 7.1 and older
+4
View File
@@ -35,6 +35,10 @@ This library can be found on [Packagist](http://packagist.org/packages/predis/pr
management of projects dependencies using [Composer](http://packagist.org/about-composer).
Compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
```shell
composer require predis/predis
```
### Loading the library ###
+1 -1
View File
@@ -1 +1 @@
2.0.0
2.0.3
+1 -1
View File
@@ -40,7 +40,7 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
*/
class Client implements ClientInterface, \IteratorAggregate
{
const VERSION = '2.0.0';
const VERSION = '2.0.3';
/** @var OptionsInterface */
private $options;
+157 -151
View File
@@ -25,157 +25,163 @@ use Predis\Response\Status;
* and more friendly interface to ease programming which is described in the
* following list of methods:
*
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
* @method string|null dump(string $key)
* @method int exists(string $key)
* @method int expire(string $key, int $seconds)
* @method int expireat(string $key, int $timestamp)
* @method array keys(string $pattern)
* @method int move(string $key, int $db)
* @method mixed object($subcommand, string $key)
* @method int persist(string $key)
* @method int pexpire(string $key, int $milliseconds)
* @method int pexpireat(string $key, int $timestamp)
* @method int pttl(string $key)
* @method string|null randomkey()
* @method mixed rename(string $key, string $target)
* @method int renamenx(string $key, string $target)
* @method array scan($cursor, array $options = null)
* @method array sort(string $key, array $options = null)
* @method int ttl(string $key)
* @method mixed type(string $key)
* @method int append(string $key, $value)
* @method int bitcount(string $key, $start = null, $end = null)
* @method int bitop($operation, $destkey, $key)
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
* @method int bitpos(string $key, $bit, $start = null, $end = null)
* @method int decr(string $key)
* @method int decrby(string $key, int $decrement)
* @method string|null get(string $key)
* @method int getbit(string $key, $offset)
* @method string getrange(string $key, $start, $end)
* @method string|null getset(string $key, $value)
* @method int incr(string $key)
* @method int incrby(string $key, int $increment)
* @method string incrbyfloat(string $key, int|float $increment)
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method Status psetex(string $key, $milliseconds, $value)
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit(string $key, $offset, $value)
* @method Status setex(string $key, $seconds, $value)
* @method int setnx(string $key, $value)
* @method int setrange(string $key, $offset, $value)
* @method int strlen(string $key)
* @method int hdel(string $key, array $fields)
* @method int hexists(string $key, string $field)
* @method string|null hget(string $key, string $field)
* @method array hgetall(string $key)
* @method int hincrby(string $key, string $field, int $increment)
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
* @method array hkeys(string $key)
* @method int hlen(string $key)
* @method array hmget(string $key, array $fields)
* @method mixed hmset(string $key, array $dictionary)
* @method array hscan(string $key, $cursor, array $options = null)
* @method int hset(string $key, string $field, string $value)
* @method int hsetnx(string $key, string $field, string $value)
* @method array hvals(string $key)
* @method int hstrlen(string $key, string $field)
* @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 string|null lindex(string $key, int $index)
* @method int linsert(string $key, $whence, $pivot, $value)
* @method int llen(string $key)
* @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 sinterstore(string $destination, array|string $keys)
* @method int sismember(string $key, string $member)
* @method string[] smembers(string $key)
* @method int smove(string $source, string $destination, string $member)
* @method string|null spop(string $key, int $count = null)
* @method string|null srandmember(string $key, int $count = null)
* @method int srem(string $key, 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 int zadd(string $key, array $membersAndScoresDictionary)
* @method int zcard(string $key)
* @method string zcount(string $key, int|string $min, int|string $max)
* @method string zincrby(string $key, int $increment, string $member)
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
* @method array zpopmin(string $key, int $count = 1)
* @method array zpopmax(string $key, int $count = 1)
* @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|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 int zunionstore(string $destination, array|string $keys, array $options = null)
* @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 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 mixed watch(string $key)
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed script($subcommand, $argument = null)
* @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 int del(string[]|string $keyOrKeys, string ...$keys = null)
* @method string|null dump(string $key)
* @method int exists(string $key)
* @method int expire(string $key, int $seconds)
* @method int expireat(string $key, int $timestamp)
* @method array keys(string $pattern)
* @method int move(string $key, int $db)
* @method mixed object($subcommand, string $key)
* @method int persist(string $key)
* @method int pexpire(string $key, int $milliseconds)
* @method int pexpireat(string $key, int $timestamp)
* @method int pttl(string $key)
* @method string|null randomkey()
* @method mixed rename(string $key, string $target)
* @method int renamenx(string $key, string $target)
* @method array scan($cursor, array $options = null)
* @method array sort(string $key, array $options = null)
* @method int ttl(string $key)
* @method mixed type(string $key)
* @method int append(string $key, $value)
* @method int bitcount(string $key, $start = null, $end = null)
* @method int bitop($operation, $destkey, $key)
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
* @method int bitpos(string $key, $bit, $start = null, $end = null)
* @method int decr(string $key)
* @method int decrby(string $key, int $decrement)
* @method string|null get(string $key)
* @method int getbit(string $key, $offset)
* @method string getrange(string $key, $start, $end)
* @method string|null getset(string $key, $value)
* @method int incr(string $key)
* @method int incrby(string $key, int $increment)
* @method string incrbyfloat(string $key, int|float $increment)
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method Status psetex(string $key, $milliseconds, $value)
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit(string $key, $offset, $value)
* @method Status setex(string $key, $seconds, $value)
* @method int setnx(string $key, $value)
* @method int setrange(string $key, $offset, $value)
* @method int strlen(string $key)
* @method int hdel(string $key, array $fields)
* @method int hexists(string $key, string $field)
* @method string|null hget(string $key, string $field)
* @method array hgetall(string $key)
* @method int hincrby(string $key, string $field, int $increment)
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
* @method array hkeys(string $key)
* @method int hlen(string $key)
* @method array hmget(string $key, array $fields)
* @method mixed hmset(string $key, array $dictionary)
* @method array hscan(string $key, $cursor, array $options = null)
* @method int hset(string $key, string $field, string $value)
* @method int hsetnx(string $key, string $field, string $value)
* @method array hvals(string $key)
* @method int hstrlen(string $key, string $field)
* @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 string|null lindex(string $key, int $index)
* @method int linsert(string $key, $whence, $pivot, $value)
* @method int llen(string $key)
* @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 sinterstore(string $destination, array|string $keys)
* @method int sismember(string $key, string $member)
* @method string[] smembers(string $key)
* @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 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 string zincrby(string $key, int $increment, string $member)
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
* @method array zpopmin(string $key, int $count = 1)
* @method array zpopmax(string $key, int $count = 1)
* @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|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 int zunionstore(string $destination, array|string $keys, array $options = null)
* @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 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 mixed watch(string $key)
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed script($subcommand, $argument = null)
* @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)
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
+143 -131
View File
@@ -31,142 +31,154 @@ class KeyPrefixProcessor implements ProcessorInterface
public function __construct($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 ---------------- */
'EXISTS' => 'static::all',
'DEL' => 'static::all',
'TYPE' => 'static::first',
'KEYS' => 'static::first',
'RENAME' => 'static::all',
'RENAMENX' => 'static::all',
'EXPIRE' => 'static::first',
'EXPIREAT' => 'static::first',
'TTL' => 'static::first',
'MOVE' => 'static::first',
'SORT' => 'static::sort',
'DUMP' => 'static::first',
'RESTORE' => 'static::first',
'SET' => 'static::first',
'SETNX' => 'static::first',
'MSET' => 'static::interleaved',
'MSETNX' => 'static::interleaved',
'GET' => 'static::first',
'MGET' => 'static::all',
'GETSET' => 'static::first',
'INCR' => 'static::first',
'INCRBY' => 'static::first',
'DECR' => 'static::first',
'DECRBY' => 'static::first',
'RPUSH' => 'static::first',
'LPUSH' => 'static::first',
'LLEN' => 'static::first',
'LRANGE' => 'static::first',
'LTRIM' => 'static::first',
'LINDEX' => 'static::first',
'LSET' => 'static::first',
'LREM' => 'static::first',
'LPOP' => 'static::first',
'RPOP' => 'static::first',
'RPOPLPUSH' => 'static::all',
'SADD' => 'static::first',
'SREM' => 'static::first',
'SPOP' => 'static::first',
'SMOVE' => 'static::skipLast',
'SCARD' => 'static::first',
'SISMEMBER' => 'static::first',
'SINTER' => 'static::all',
'SINTERSTORE' => 'static::all',
'SUNION' => 'static::all',
'SUNIONSTORE' => 'static::all',
'SDIFF' => 'static::all',
'SDIFFSTORE' => 'static::all',
'SMEMBERS' => 'static::first',
'SRANDMEMBER' => 'static::first',
'ZADD' => 'static::first',
'ZINCRBY' => 'static::first',
'ZREM' => 'static::first',
'ZRANGE' => 'static::first',
'ZREVRANGE' => 'static::first',
'ZRANGEBYSCORE' => 'static::first',
'ZCARD' => 'static::first',
'ZSCORE' => 'static::first',
'ZREMRANGEBYSCORE' => 'static::first',
'EXISTS' => $prefixAll,
'DEL' => $prefixAll,
'TYPE' => $prefixFirst,
'KEYS' => $prefixFirst,
'RENAME' => $prefixAll,
'RENAMENX' => $prefixAll,
'EXPIRE' => $prefixFirst,
'EXPIREAT' => $prefixFirst,
'TTL' => $prefixFirst,
'MOVE' => $prefixFirst,
'SORT' => $prefixSort,
'DUMP' => $prefixFirst,
'RESTORE' => $prefixFirst,
'SET' => $prefixFirst,
'SETNX' => $prefixFirst,
'MSET' => $prefixInterleaved,
'MSETNX' => $prefixInterleaved,
'GET' => $prefixFirst,
'MGET' => $prefixAll,
'GETSET' => $prefixFirst,
'INCR' => $prefixFirst,
'INCRBY' => $prefixFirst,
'DECR' => $prefixFirst,
'DECRBY' => $prefixFirst,
'RPUSH' => $prefixFirst,
'LPUSH' => $prefixFirst,
'LLEN' => $prefixFirst,
'LRANGE' => $prefixFirst,
'LTRIM' => $prefixFirst,
'LINDEX' => $prefixFirst,
'LSET' => $prefixFirst,
'LREM' => $prefixFirst,
'LPOP' => $prefixFirst,
'RPOP' => $prefixFirst,
'RPOPLPUSH' => $prefixAll,
'SADD' => $prefixFirst,
'SREM' => $prefixFirst,
'SPOP' => $prefixFirst,
'SMOVE' => $prefixSkipLast,
'SCARD' => $prefixFirst,
'SISMEMBER' => $prefixFirst,
'SINTER' => $prefixAll,
'SINTERSTORE' => $prefixAll,
'SUNION' => $prefixAll,
'SUNIONSTORE' => $prefixAll,
'SDIFF' => $prefixAll,
'SDIFFSTORE' => $prefixAll,
'SMEMBERS' => $prefixFirst,
'SRANDMEMBER' => $prefixFirst,
'ZADD' => $prefixFirst,
'ZINCRBY' => $prefixFirst,
'ZREM' => $prefixFirst,
'ZRANGE' => $prefixFirst,
'ZREVRANGE' => $prefixFirst,
'ZRANGEBYSCORE' => $prefixFirst,
'ZCARD' => $prefixFirst,
'ZSCORE' => $prefixFirst,
'ZREMRANGEBYSCORE' => $prefixFirst,
/* ---------------- Redis 2.0 ---------------- */
'SETEX' => 'static::first',
'APPEND' => 'static::first',
'SUBSTR' => 'static::first',
'BLPOP' => 'static::skipLast',
'BRPOP' => 'static::skipLast',
'ZUNIONSTORE' => 'static::zsetStore',
'ZINTERSTORE' => 'static::zsetStore',
'ZCOUNT' => 'static::first',
'ZRANK' => 'static::first',
'ZREVRANK' => 'static::first',
'ZREMRANGEBYRANK' => 'static::first',
'HSET' => 'static::first',
'HSETNX' => 'static::first',
'HMSET' => 'static::first',
'HINCRBY' => 'static::first',
'HGET' => 'static::first',
'HMGET' => 'static::first',
'HDEL' => 'static::first',
'HEXISTS' => 'static::first',
'HLEN' => 'static::first',
'HKEYS' => 'static::first',
'HVALS' => 'static::first',
'HGETALL' => 'static::first',
'SUBSCRIBE' => 'static::all',
'UNSUBSCRIBE' => 'static::all',
'PSUBSCRIBE' => 'static::all',
'PUNSUBSCRIBE' => 'static::all',
'PUBLISH' => 'static::first',
'SETEX' => $prefixFirst,
'APPEND' => $prefixFirst,
'SUBSTR' => $prefixFirst,
'BLPOP' => $prefixSkipLast,
'BRPOP' => $prefixSkipLast,
'ZUNIONSTORE' => $prefixZsetStore,
'ZINTERSTORE' => $prefixZsetStore,
'ZCOUNT' => $prefixFirst,
'ZRANK' => $prefixFirst,
'ZREVRANK' => $prefixFirst,
'ZREMRANGEBYRANK' => $prefixFirst,
'HSET' => $prefixFirst,
'HSETNX' => $prefixFirst,
'HMSET' => $prefixFirst,
'HINCRBY' => $prefixFirst,
'HGET' => $prefixFirst,
'HMGET' => $prefixFirst,
'HDEL' => $prefixFirst,
'HEXISTS' => $prefixFirst,
'HLEN' => $prefixFirst,
'HKEYS' => $prefixFirst,
'HVALS' => $prefixFirst,
'HGETALL' => $prefixFirst,
'SUBSCRIBE' => $prefixAll,
'UNSUBSCRIBE' => $prefixAll,
'PSUBSCRIBE' => $prefixAll,
'PUNSUBSCRIBE' => $prefixAll,
'PUBLISH' => $prefixFirst,
/* ---------------- Redis 2.2 ---------------- */
'PERSIST' => 'static::first',
'STRLEN' => 'static::first',
'SETRANGE' => 'static::first',
'GETRANGE' => 'static::first',
'SETBIT' => 'static::first',
'GETBIT' => 'static::first',
'RPUSHX' => 'static::first',
'LPUSHX' => 'static::first',
'LINSERT' => 'static::first',
'BRPOPLPUSH' => 'static::skipLast',
'ZREVRANGEBYSCORE' => 'static::first',
'WATCH' => 'static::all',
'PERSIST' => $prefixFirst,
'STRLEN' => $prefixFirst,
'SETRANGE' => $prefixFirst,
'GETRANGE' => $prefixFirst,
'SETBIT' => $prefixFirst,
'GETBIT' => $prefixFirst,
'RPUSHX' => $prefixFirst,
'LPUSHX' => $prefixFirst,
'LINSERT' => $prefixFirst,
'BRPOPLPUSH' => $prefixSkipLast,
'ZREVRANGEBYSCORE' => $prefixFirst,
'WATCH' => $prefixAll,
/* ---------------- Redis 2.6 ---------------- */
'PTTL' => 'static::first',
'PEXPIRE' => 'static::first',
'PEXPIREAT' => 'static::first',
'PSETEX' => 'static::first',
'INCRBYFLOAT' => 'static::first',
'BITOP' => 'static::skipFirst',
'BITCOUNT' => 'static::first',
'HINCRBYFLOAT' => 'static::first',
'EVAL' => 'static::evalKeys',
'EVALSHA' => 'static::evalKeys',
'MIGRATE' => 'static::migrate',
'PTTL' => $prefixFirst,
'PEXPIRE' => $prefixFirst,
'PEXPIREAT' => $prefixFirst,
'PSETEX' => $prefixFirst,
'INCRBYFLOAT' => $prefixFirst,
'BITOP' => $prefixSkipFirst,
'BITCOUNT' => $prefixFirst,
'HINCRBYFLOAT' => $prefixFirst,
'EVAL' => $prefixEvalKeys,
'EVALSHA' => $prefixEvalKeys,
'MIGRATE' => $prefixMigrate,
/* ---------------- Redis 2.8 ---------------- */
'SSCAN' => 'static::first',
'ZSCAN' => 'static::first',
'HSCAN' => 'static::first',
'PFADD' => 'static::first',
'PFCOUNT' => 'static::all',
'PFMERGE' => 'static::all',
'ZLEXCOUNT' => 'static::first',
'ZRANGEBYLEX' => 'static::first',
'ZREMRANGEBYLEX' => 'static::first',
'ZREVRANGEBYLEX' => 'static::first',
'BITPOS' => 'static::first',
'SSCAN' => $prefixFirst,
'ZSCAN' => $prefixFirst,
'HSCAN' => $prefixFirst,
'PFADD' => $prefixFirst,
'PFCOUNT' => $prefixAll,
'PFMERGE' => $prefixAll,
'ZLEXCOUNT' => $prefixFirst,
'ZRANGEBYLEX' => $prefixFirst,
'ZREMRANGEBYLEX' => $prefixFirst,
'ZREVRANGEBYLEX' => $prefixFirst,
'BITPOS' => $prefixFirst,
/* ---------------- Redis 3.2 ---------------- */
'HSTRLEN' => 'static::first',
'BITFIELD' => 'static::first',
'GEOADD' => 'static::first',
'GEOHASH' => 'static::first',
'GEOPOS' => 'static::first',
'GEODIST' => 'static::first',
'GEORADIUS' => 'static::georadius',
'GEORADIUSBYMEMBER' => 'static::georadius',
);
'HSTRLEN' => $prefixFirst,
'BITFIELD' => $prefixFirst,
'GEOADD' => $prefixFirst,
'GEOHASH' => $prefixFirst,
'GEOPOS' => $prefixFirst,
'GEODIST' => $prefixFirst,
'GEORADIUS' => $prefixGeoradius,
'GEORADIUSBYMEMBER' => $prefixGeoradius,
];
}
/**
@@ -197,7 +209,7 @@ class KeyPrefixProcessor implements ProcessorInterface
if ($command instanceof PrefixableCommandInterface) {
$command->prefixKeys($this->prefix);
} elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
call_user_func($this->commands[$commandID], $command, $this->prefix);
$this->commands[$commandID]($command, $this->prefix);
}
}
+65
View File
@@ -0,0 +1,65 @@
<?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\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xadd
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XADD extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XADD';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$args = array();
$args[] = $arguments[0];
$options = $arguments[3] ?? [];
if (isset($options['nomkstream']) && $options['nomkstream']) {
$args[] = 'NOMKSTREAM';
}
if (isset($options['trim']) && is_array($options['trim'])) {
array_push($args, ...$options['trim']);
if (isset($options['limit'])) {
$args[] = 'LIMIT';
$args[] = $options['limit'];
}
}
// ID, default to * to let Redis set it
$args[] = $arguments[2] ?? '*';
if (isset($arguments[1]) && is_array($arguments[1])) {
foreach ($arguments[1] as $key => $val) {
$args[] = $key;
$args[] = $val;
}
}
parent::setArguments($args);
}
}
+40
View File
@@ -0,0 +1,40 @@
<?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\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xdel
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XDEL extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XDEL';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);
parent::setArguments($arguments);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?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\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XLEN extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XLEN';
}
}
+63
View File
@@ -0,0 +1,63 @@
<?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\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xrange
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XRANGE extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XRANGE';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
if (count($arguments) === 4) {
$arguments[] = $arguments[3];
$arguments[3] = 'COUNT';
}
parent::setArguments($arguments);
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
$result = array();
foreach ($data as $entry) {
$processed = array();
$count = count($entry[1]);
for ($i = 0; $i < $count; ++$i) {
$processed[$entry[1][$i]] = $entry[1][++$i];
}
$result[$entry[0]] = $processed;
}
return $result;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?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\Redis;
/**
* @link http://redis.io/commands/xrevrange
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XREVRANGE extends XRANGE
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XREVRANGE';
}
}
+55
View File
@@ -0,0 +1,55 @@
<?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\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xtrim
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XTRIM extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XTRIM';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$args = [];
$options = $arguments[3] ?? [];
$args[] = $arguments[0];
// Either e.g. 'MAXLEN' or ['MAXLEN', '~']
if (is_array($arguments[1])) {
array_push($args, ...$arguments[1]);
} else {
$args[] = $arguments[1];
}
$args[] = $arguments[2];
if (isset($options['limit'])) {
$args[] = 'LIMIT';
$args[] = $options['limit'];
}
parent::setArguments($args);
}
}
+50 -7
View File
@@ -22,6 +22,8 @@ use Predis\Connection\FactoryInterface;
use Predis\Connection\NodeConnectionInterface;
use Predis\NotSupportedException;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ServerException;
use Predis\Response\Error as ErrorResponse;
/**
* Abstraction for a Redis-backed cluster of nodes (Redis >= 3.0.0).
@@ -54,6 +56,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
private $strategy;
private $connections;
private $retryLimit = 5;
private $retryInterval = 10;
/**
* @param FactoryInterface $connections Optional connection factory.
@@ -82,6 +85,26 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
$this->retryLimit = (int) $retry;
}
/**
* Sets the initial retry interval (milliseconds).
*
* @param int $retryInterval Milliseconds between retries.
*/
public function setRetryInterval($retryInterval)
{
$this->retryInterval = (int) $retryInterval;
}
/**
* Returns the retry interval (milliseconds).
*
* @return int Milliseconds between retries.
*/
public function getRetryInterval()
{
return (int) $this->retryInterval;
}
/**
* {@inheritdoc}
*/
@@ -207,6 +230,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
private function queryClusterNodeForSlotMap(NodeConnectionInterface $connection)
{
$retries = 0;
$retryAfter = $this->retryInterval;
$command = RawCommand::create('CLUSTER', 'SLOTS');
RETRY_COMMAND: {
@@ -226,7 +250,10 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
throw new ClientException('No connections left in the pool for `CLUSTER SLOTS`');
}
usleep($retryAfter * 1000);
$retryAfter = $retryAfter * 2;
++$retries;
goto RETRY_COMMAND;
}
}
@@ -482,24 +509,40 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
*/
private function retryCommandOnFailure(CommandInterface $command, $method)
{
$failure = false;
$retries = 0;
$retryAfter = $this->retryInterval;
RETRY_COMMAND: {
try {
$response = $this->getConnectionByCommand($command)->$method($command);
} catch (ConnectionException $exception) {
$connection = $exception->getConnection();
$connection->disconnect();
$this->remove($connection);
if ($response instanceof ErrorResponse) {
$message = $response->getMessage();
if ($failure) {
if (strpos($message, 'CLUSTERDOWN') !== false) {
throw new ServerException($message);
}
}
} catch (\Throwable $exception) {
usleep($retryAfter * 1000);
$retryAfter = $retryAfter * 2;
if ($exception instanceof ConnectionException) {
$connection = $exception->getConnection();
if ($connection) {
$connection->disconnect();
$this->remove($connection);
}
}
if ($retries === $this->retryLimit) {
throw $exception;
} elseif ($this->useClusterSlots) {
$this->askSlotMap();
}
$failure = true;
++$retries;
goto RETRY_COMMAND;
}
+8
View File
@@ -26,6 +26,14 @@ class Parameters implements ParametersInterface
'port' => 6379,
);
/**
* Set of connection paramaters already filtered
* for NULL or 0-length string values.
*
* @var array
*/
protected $parameters;
/**
* @param array $parameters Named array of connection parameters.
*/
@@ -70,6 +70,11 @@ class SentinelReplication implements ReplicationInterface
*/
protected $sentinels = array();
/**
* @var int
*/
protected $sentinelIndex = 0;
/**
* @var NodeConnectionInterface
*/
@@ -264,6 +269,12 @@ class SentinelReplication implements ReplicationInterface
$parameters['database'] = null;
$parameters['username'] = null;
// don't leak password from between configurations
// https://github.com/predis/predis/pull/807/#discussion_r985764770
if (! isset($parameters['password'])) {
$parameters['password'] = null;
}
if (!isset($parameters['timeout'])) {
$parameters['timeout'] = $this->sentinelTimeout;
}
@@ -282,11 +293,13 @@ class SentinelReplication implements ReplicationInterface
public function getSentinelConnection()
{
if (!$this->sentinelConnection) {
if (!$this->sentinels) {
if ($this->sentinelIndex >= count($this->sentinels)) {
$this->sentinelIndex = 0;
throw new \Predis\ClientException('No sentinel server available for autodiscovery.');
}
$sentinel = array_shift($this->sentinels);
$sentinel = $this->sentinels[$this->sentinelIndex];
++$this->sentinelIndex;
$this->sentinelConnection = $this->createSentinelConnection($sentinel);
}
@@ -307,6 +320,7 @@ class SentinelReplication implements ReplicationInterface
);
$this->sentinels = array();
$this->sentinelIndex = 0;
// NOTE: sentinel server does not return itself, so we add it back.
$this->sentinels[] = $sentinel->getParameters()->toArray();
+2 -2
View File
@@ -260,7 +260,7 @@ class StreamConnection extends AbstractConnection
$socket = $this->getResource();
while (($length = strlen($buffer)) > 0) {
$written = @fwrite($socket, $buffer);
$written = is_resource($socket) ? @fwrite($socket, $buffer) : false;
if ($length === $written) {
return;
@@ -304,7 +304,7 @@ class StreamConnection extends AbstractConnection
$bytesLeft = ($size += 2);
do {
$chunk = fread($socket, min($bytesLeft, 4096));
$chunk = is_resource($socket) ? fread($socket, min($bytesLeft, 4096)) : false;
if ($chunk === false || $chunk === '') {
$this->onConnectionError('Error while reading bytes from the server.');
+5 -1
View File
@@ -10,6 +10,7 @@
*/
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Util\Test as TestUtil;
use Predis\Client;
use Predis\Command;
use Predis\Connection;
@@ -315,7 +316,10 @@ abstract class PredisTestCase extends \PHPUnit\Framework\TestCase
*/
protected function getRequiredRedisServerVersion(): ?string
{
$annotations = $this->getAnnotations();
$annotations = TestUtil::parseTestMethodAnnotations(
get_class($this),
$this->getName(false)
);
if (isset($annotations['method']['requiresRedisVersion'], $annotations['method']['group']) &&
!empty($annotations['method']['requiresRedisVersion']) &&
+16
View File
@@ -101,6 +101,22 @@ class SREM_Test extends PredisCommandTestCase
$this->assertSame(0, $redis->srem('digits', 1));
}
/**
* @group connected
* @requiresRedisVersion >= 2.4.0
*/
public function testRemovesMembersInArrayTypeFromSetVariadic(): void
{
$redis = $this->getClient();
$redis->sadd('letters', 'a', 'b', 'c', 'd');
$this->assertSame(2, $redis->srem('letters', ['b', 'd', 'z']));
$this->assertSameValues(array('a', 'c'), $redis->smembers('letters'));
$this->assertSame(0, $redis->srem('digits', [1]));
}
/**
* @group connected
*/
+290
View File
@@ -0,0 +1,290 @@
<?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\Redis;
/**
* @group commands
* @group realm-stream
*/
class XADD_Test extends PredisCommandTestCase
{
/**
* @group disconnected
* @dataProvider dataFilterArguments
*/
public function testFilterArguments(array $arguments, array $expected): void
{
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
public function dataFilterArguments(): array
{
$data = [];
$data[] = [
[
'stream',
['key' => 'val'],
'*',
['trim' => ['MINID', '~', '0-1'], 'limit' => 5, 'nomkstream' => true]
],
['stream', 'NOMKSTREAM', 'MINID', '~', '0-1', 'LIMIT', 5, '*', 'key', 'val'],
];
$data[] = [
[
'stream',
['key1' => 'val1', 'key2' => 'val2'],
'*',
['trim' => ['MINID', '~', '0-1'], 'limit' => 5, 'nomkstream' => true]
],
['stream', 'NOMKSTREAM', 'MINID', '~', '0-1', 'LIMIT', 5, '*', 'key1', 'val1', 'key2', 'val2'],
];
$data[] = [
[
'stream',
['key' => 'val'],
'*',
['trim' => ['MINID', '~', '0-1'], 'limit' => 5]
],
['stream', 'MINID', '~', '0-1', 'LIMIT', 5, '*', 'key', 'val'],
];
$data[] = [
[
'stream',
['key' => 'val'],
'*',
['trim' => ['MINID', '~', '0-1']]
],
['stream', 'MINID', '~', '0-1', '*', 'key', 'val'],
];
$data[] = [
[
'stream',
['key' => 'val'],
'*',
['trim' => ['MINID', '0-1']]
],
['stream', 'MINID', '0-1', '*', 'key', 'val'],
];
$data[] = [
['stream', ['key' => 'val'], '2-3'],
['stream', '2-3', 'key', 'val'],
];
$data[] = [
['stream', ['key' => 'val']],
['stream', '*', 'key', 'val'],
];
return $data;
}
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\XADD';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'XADD';
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testAddsToStreamWithDefaults(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key' => 'val']);
$this->assertSame(1, $redis->xlen('stream'));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testAddsToStreamWithSpecificId(): void
{
$redis = $this->getClient();
$id = time() . '-123';
$redis->xadd('stream', ['key' => 'val'], $id);
$response = $redis->xrange('stream', $id, $id);
$this->assertCount(1, $response);
$this->assertNotNull($response[$id]);
}
/**
* @group connected
* @requiresRedisVersion >= 6.2.0
*/
public function testNomkstreamWhenStreamDoesNotExist(): void
{
$redis = $this->getClient();
$redis->xadd('new-stream', ['key' => 'val'], '*', ['nomkstream' => true]);
$this->assertSame(0, $redis->exists('new-stream'));
}
/**
* @group connected
* @requiresRedisVersion >= 6.2.0
*/
public function testNomkstreamWhenStreamExists(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val'], '*', ['nomkstream' => true]);
$this->assertSame(1, $redis->exists('stream'));
$this->assertSame(2, $redis->xlen('stream'));
}
/**
* @group connected
* @requiresRedisVersion >= 6.2.0
*/
public function testTrimOnMinidExact(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key' => 'val']);
$id = $redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val'], '*', ['trim' => ['MINID', $id]]);
$this->assertSame(2, $redis->xlen('stream'));
}
/**
* @group connected
* @requiresRedisVersion >= 6.2.0
*/
public function testTrimOnMinidInexact(): void
{
$redis = $this->getClient();
$config = $redis->config('get', 'stream-node-max-entries');
$oldStreamNodeMaxEntries = (int) array_pop($config);
$redis->config('set', 'stream-node-max-entries', 2);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$id = $redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val'], '*', ['trim' => ['MINID', '~', $id]]);
$this->assertSame(3, $redis->xlen('stream'));
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testTrimOnMaxlenExact(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val'], '*', ['trim' => ['MAXLEN', 2]]);
$this->assertSame(2, $redis->xlen('stream'));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testTrimOnMaxlenInexact(): void
{
$redis = $this->getClient();
$config = $redis->config('get', 'stream-node-max-entries');
$oldStreamNodeMaxEntries = (int) array_pop($config);
$redis->config('set', 'stream-node-max-entries', 2);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val'], '*', ['trim' => ['MAXLEN', '~', 2]]);
$this->assertSame(3, $redis->xlen('stream'));
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
}
/**
* @group connected
* @requiresRedisVersion >= 6.2.0
*/
public function testTrimOnMaxlenWithLimit(): void
{
$redis = $this->getClient();
$config = $redis->config('get', 'stream-node-max-entries');
$oldStreamNodeMaxEntries = (int) array_pop($config);
$redis->config('set', 'stream-node-max-entries', 2);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd(
'stream',
['key' => 'val'],
'*',
['trim' => ['MAXLEN', '~', 2], 'limit' => 2]
);
$this->assertSame(4, $redis->xlen('stream'));
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->xadd('foo', ['key' => 'val']);
}
}
+90
View File
@@ -0,0 +1,90 @@
<?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\Redis;
/**
* @group commands
* @group realm-stream
*/
class XDEL_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\XDEL';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'XDEL';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = array('stream', 'id1', 'id2', 'id3');
$expected = array('stream', 'id1', 'id2', 'id3');
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testRemovesSpecifiedMembers(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key0' => 'val0'], '0-1');
$redis->xadd('stream', ['key1' => 'val1'], '1-1');
$redis->xadd('stream', ['key2' => 'val2'], '2-1');
$this->assertSame(2, $redis->xdel('stream', '0-1', '2-1', '99-1'));
$this->assertSame(['1-1' => ['key1' => 'val1']], $redis->xrange('stream', '-', '+'));
$this->assertSame(0, $redis->xdel('stream', '0-1'));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->xdel('foo', 'bar');
}
}
+99
View File
@@ -0,0 +1,99 @@
<?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\Redis;
/**
* @group commands
* @group realm-stream
*/
class XLEN_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\XLEN';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'XLEN';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = array('key');
$expected = array('key');
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testReturnsLengthOfList(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$this->assertSame(2, $redis->xlen('stream'));
$redis->xadd('stream', ['key' => 'val']);
$this->assertSame(3, $redis->xlen('stream'));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testReturnsZeroLengthOnNonExistingList(): void
{
$redis = $this->getClient();
$this->assertSame(0, $redis->llen('stream'));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->xlen('foo');
}
}
+159
View File
@@ -0,0 +1,159 @@
<?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\Redis;
/**
* @group commands
* @group realm-stream
*/
class XRANGE_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\XRANGE';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'XRANGE';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = array('stream', '0-1', '1-2', 123);
$expected = array('stream', '0-1', '1-2', 'COUNT', 123);
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testFilterArgumentsNoCount(): void
{
$arguments = array('stream', '0-1', '1-2');
$expected = array('stream', '0-1', '1-2');
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$raw = array(array('0-1', ['key', 'val']));
$expected = array('0-1' => ['key' => 'val']);
$command = $this->getCommand();
$this->assertSame($expected, $command->parseResponse($raw));
}
/**
* @group disconnected
*/
public function testParseResponseMultipleKeys(): void
{
$raw = array(array('0-1', ['key1', 'val1', 'key2', 'val2']));
$expected = array('0-1' => ['key1' => 'val1', 'key2' => 'val2']);
$command = $this->getCommand();
$this->assertSame($expected, $command->parseResponse($raw));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testReturnsElementsInRange(): void
{
$redis = $this->getClient();
for ($i = 0; $i < 10; $i++) {
$redis->xadd('stream', ['key' . $i => 'val' . $i], $i . '-1');
}
$this->assertSame(array(), $redis->xrange('stream', '1-1', '0-1'));
$this->assertSame(
array('0-1' => ['key0' => 'val0']),
$redis->xrange('stream', '0-1', '0-1')
);
$this->assertSame(
array('0-1' => ['key0' => 'val0'], '1-1' => ['key1' => 'val1']),
$redis->xrange('stream', '0-1', '1-1')
);
$this->assertSame(
array('0-1' => ['key0' => 'val0'], '1-1' => ['key1' => 'val1']),
$redis->xrange('stream', '-', '1-1')
);
$this->assertSame(
array('8-1' => ['key8' => 'val8'], '9-1' => ['key9' => 'val9']),
$redis->xrange('stream', '8-1', '+')
);
$this->assertSame(
array('5-1' => ['key5' => 'val5'], '6-1' => ['key6' => 'val6']),
$redis->xrange('stream', '5-1', '6-1')
);
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testMultipleKeys(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key1' => 'val1', 'key2' => 'val2'], '0-1');
$redis->xadd('stream', ['key1' => 'val1', 'key2' => 'val2'], '1-1');
$this->assertSame(
array(
'0-1' => ['key1' => 'val1', 'key2' => 'val2'],
'1-1' => ['key1' => 'val1', 'key2' => 'val2'],
),
$redis->xrange('stream', '-', '+')
);
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->xrange('foo', '0-1', '1-1');
}
}
@@ -0,0 +1,159 @@
<?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\Redis;
/**
* @group commands
* @group realm-stream
*/
class XREVRANGE_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\XREVRANGE';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'XREVRANGE';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = array('stream', '1-1', '0-1', 123);
$expected = array('stream', '1-1', '0-1', 'COUNT', 123);
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testFilterArgumentsNoCount(): void
{
$arguments = array('stream', '1-1', '0-1');
$expected = array('stream', '1-1', '0-1');
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$raw = array(array('0-1', ['key', 'val']));
$expected = array('0-1' => ['key' => 'val']);
$command = $this->getCommand();
$this->assertSame($expected, $command->parseResponse($raw));
}
/**
* @group disconnected
*/
public function testParseResponseMultipleKeys(): void
{
$raw = array(array('0-1', ['key1', 'val1', 'key2', 'val2']));
$expected = array('0-1' => ['key1' => 'val1', 'key2' => 'val2']);
$command = $this->getCommand();
$this->assertSame($expected, $command->parseResponse($raw));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testReturnsElementsInRange(): void
{
$redis = $this->getClient();
for ($i = 0; $i < 10; $i++) {
$redis->xadd('stream', ['key' . $i => 'val' . $i], $i . '-1');
}
$this->assertSame(array(), $redis->xrevrange('stream', '0-1', '1-1'));
$this->assertSame(
array('0-1' => ['key0' => 'val0']),
$redis->xrevrange('stream', '0-1', '0-1')
);
$this->assertSame(
array('1-1' => ['key1' => 'val1'], '0-1' => ['key0' => 'val0']),
$redis->xrevrange('stream', '1-1', '0-1')
);
$this->assertSame(
array('1-1' => ['key1' => 'val1'], '0-1' => ['key0' => 'val0']),
$redis->xrevrange('stream', '1-1', '-')
);
$this->assertSame(
array('9-1' => ['key9' => 'val9'], '8-1' => ['key8' => 'val8']),
$redis->xrevrange('stream', '+', '8-1')
);
$this->assertSame(
array('6-1' => ['key6' => 'val6'], '5-1' => ['key5' => 'val5']),
$redis->xrevrange('stream', '6-1', '5-1')
);
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testMultipleKeys(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key1' => 'val1', 'key2' => 'val2'], '0-1');
$redis->xadd('stream', ['key1' => 'val1', 'key2' => 'val2'], '1-1');
$this->assertSame(
array(
'1-1' => ['key1' => 'val1', 'key2' => 'val2'],
'0-1' => ['key1' => 'val1', 'key2' => 'val2'],
),
$redis->xrevrange('stream', '+', '-')
);
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->xrevrange('foo', '1-1', '0-1');
}
}
+201
View File
@@ -0,0 +1,201 @@
<?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\Redis;
/**
* @group commands
* @group realm-stream
*/
class XTRIM_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\XTRIM';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'XTRIM';
}
/**
* @group disconnected
* @dataProvider dataFilterArguments
*/
public function testFilterArguments(array $arguments, array $expected): void
{
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
public function dataFilterArguments(): array
{
return [
[
['stream', ['MINID', '~'], '0-1', ['limit' => 10]],
['stream', 'MINID', '~', '0-1', 'LIMIT', 10],
],
[
['stream', ['MINID'], '0-1', ['limit' => 10]],
['stream', 'MINID', '0-1', 'LIMIT', 10],
],
[
['stream', 'MINID', '0-1', ['limit' => 10]],
['stream', 'MINID', '0-1', 'LIMIT', 10],
],
[
['stream', 'MINID', '0-1'],
['stream', 'MINID', '0-1'],
],
];
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testTrimOnMaxlenExact(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$res = $redis->xtrim('stream', 'MAXLEN', 2);
$this->assertSame(1, $res);
$this->assertSame(2, $redis->xlen('stream'));
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testTrimOnMaxlenInexact(): void
{
$redis = $this->getClient();
$config = $redis->config('get', 'stream-node-max-entries');
$oldStreamNodeMaxEntries = (int) array_pop($config);
$redis->config('set', 'stream-node-max-entries', 2);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$res = $redis->xtrim('stream', ['MAXLEN', '~'], 2);
$this->assertSame(2, $res);
$this->assertSame(3, $redis->xlen('stream'));
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
}
/**
* @group connected
* @requiresRedisVersion >= 6.2.0
*/
public function testTrimOnMinidExact(): void
{
$redis = $this->getClient();
$redis->xadd('stream', ['key' => 'val']);
$id = $redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$res = $redis->xtrim('stream', 'MINID', $id);
$this->assertSame(1, $res);
$this->assertSame(2, $redis->xlen('stream'));
}
/**
* @group connected
* @requiresRedisVersion >= 6.2.0
*/
public function testTrimOnMinidInexact(): void
{
$redis = $this->getClient();
$config = $redis->config('get', 'stream-node-max-entries');
$oldStreamNodeMaxEntries = (int) array_pop($config);
$redis->config('set', 'stream-node-max-entries', 2);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$id = $redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$res = $redis->xtrim('stream', ['MINID', '~'], $id);
$this->assertSame(2, $res);
$this->assertSame(3, $redis->xlen('stream'));
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
}
/**
* @group connected
* @requiresRedisVersion >= 6.2.0
*/
public function testTrimOnMaxlenWithLimit(): void
{
$redis = $this->getClient();
$config = $redis->config('get', 'stream-node-max-entries');
$oldStreamNodeMaxEntries = (int) array_pop($config);
$redis->config('set', 'stream-node-max-entries', 2);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$redis->xadd('stream', ['key' => 'val']);
$res = $redis->xtrim('stream', ['MAXLEN', '~'], 2, ['limit' => 2]);
$this->assertSame(2, $res);
$this->assertSame(4, $redis->xlen('stream'));
$redis->config('set', 'stream-node-max-entries', $oldStreamNodeMaxEntries);
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('key', 'foo');
$redis->xtrim('key', 'MAXLEN', 2);
}
}
@@ -1301,4 +1301,144 @@ class RedisClusterTest extends PredisTestCase
$this->assertEquals($cluster, $unserialized);
}
/**
* @medium
* @group disconnected
* @group slow
*/
public function testRetryCommandSuccessOnClusterDownErrors()
{
$clusterDownError= new Response\Error("CLUSTERDOWN") ;
$command = Command\RawCommand::create('get', 'node:1001');
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
$connection1->expects($this->exactly(3))
->method('executeCommand')
->with($command)
->will($this->onConsecutiveCalls(
$clusterDownError,
$clusterDownError,
'foobar'));
$cluster = new RedisCluster(new Connection\Factory());
$cluster->useClusterSlots(false);
$cluster->setRetryLimit(2);
$cluster->add($connection1);
$this->assertSame('foobar', $cluster->executeCommand($command));
}
/**
* @medium
* @group disconnected
* @group slow
*/
public function testRetryCommandFailureOnClusterDownErrors()
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('CLUSTERDOWN');
$clusterDownError= new Response\Error("CLUSTERDOWN") ;
$command = Command\RawCommand::create('get', 'node:1001');
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
$connection1->expects($this->exactly(3))
->method('executeCommand')
->with($command)
->will($this->onConsecutiveCalls(
$clusterDownError,
$clusterDownError,
$clusterDownError
));
$cluster = new RedisCluster(new Connection\Factory());
$cluster->useClusterSlots(false);
$cluster->setRetryLimit(2);
$cluster->add($connection1);
$cluster->executeCommand($command);
}
/**
* @medium
* @group disconnected
* @group slow
*/
public function testQueryClusterNodeForSlotMapPauseDurationOnRetry()
{
$slotsmap = array(
array(0, 5460, array('127.0.0.1', 9381), array()),
array(5461, 10922, array('127.0.0.1', 6382), array()),
array(10923, 16383, array('127.0.0.1', 6383), array()),
);
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
$connection1
->expects($this->once())
->method('executeCommand')
->with($this->isRedisCommand(
'CLUSTER', array('SLOTS')
))
->willThrowException(
new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
);
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10922');
$connection2
->expects($this->once())
->method('executeCommand')
->with($this->isRedisCommand(
'CLUSTER', array('SLOTS')
))
->willThrowException(
new Connection\ConnectionException($connection2, 'Unknown connection error [127.0.0.1:6383]')
);
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10923-16383');
$connection3
->expects($this->once())
->method('executeCommand')
->with($this->isRedisCommand(
'CLUSTER', array('SLOTS')
))
->willReturn($slotsmap);
$factory = $this->getMockBuilder('Predis\Connection\FactoryInterface')->getMock();
$factory
->expects($this->never())
->method('create');
// TODO: I'm not sure about mocking a protected method, but it'll do for now
/** @var Connection\Cluster\RedisCluster|MockObject */
$cluster = $this->getMockBuilder('Predis\Connection\Cluster\RedisCluster')
->onlyMethods(array('getRandomConnection'))
->setConstructorArgs(array($factory))
->getMock();
$cluster
->expects($this->exactly(3))
->method('getRandomConnection')
->willReturnOnConsecutiveCalls($connection1, $connection2, $connection3);
$cluster->add($connection1);
$cluster->add($connection2);
$cluster->add($connection3);
$cluster->setRetryInterval(2000);
$startTime = time() ;
$cluster->askSlotMap();
$endTime = time();
$totalTime=$endTime-$startTime;
$t1 = $cluster->getRetryInterval() ;
$t2 = $t1 * 2;
$expectedTime = ($t1 + $t2 )/1000 ; // expected time for 2 retries (fail 1=wait 2s, fail 2=wait 4s , OK)
$this->AssertEqualsWithDelta($expectedTime, $totalTime, 1, "Unexpected execution time") ;
$this->assertCount(16384, $cluster->getSlotMap());
}
}
@@ -62,6 +62,7 @@ class SentinelReplicationTest extends PredisTestCase
$this->assertArrayNotHasKey('database', $parameters, 'Parameter `database` was expected to not exist in connection parameters');
$this->assertArrayNotHasKey('username', $parameters, 'Parameter `username` was expected to not exist in connection parameters');
$this->assertArrayNotHasKey('password', $parameters, 'Parameter `password` was expected to not exist in connection parameters');
}
/**
@@ -117,6 +118,30 @@ class SentinelReplicationTest extends PredisTestCase
$this->assertNotNull($parameters->database);
}
/**
* @group disconnected
*/
public function testConnectionParametersInstanceForSentinelConnectionIsNotModifiedEmptyPassword(): void
{
$sentinel1 = Connection\Parameters::create('tcp://127.0.0.1:5381?role=sentinel&database=1&password=');
$sentinel2 = Connection\Parameters::create('tcp://127.0.0.1:5381?role=sentinel&database=1');
$replication1 = $this->getReplicationConnection('svc', array($sentinel1));
$replication2 = $this->getReplicationConnection('svc', array($sentinel2));
$parameters1 = $replication1->getSentinelConnection()->getParameters();
$parameters2 = $replication2->getSentinelConnection()->getParameters();
$this->assertSame($sentinel1, $parameters1);
$this->assertSame($sentinel2, $parameters2);
$this->assertNull($parameters1->password);
$this->assertNull($parameters2->password);
$this->assertNotNull($parameters1->database);
$this->assertNotNull($parameters2->database);
}
/**
* @group disconnected
*/
@@ -1447,6 +1472,52 @@ class SentinelReplicationTest extends PredisTestCase
$this->assertEquals($strategy, $unserialized->getReplicationStrategy());
}
/**
* @group disconnected
*/
public function testMethodGetSentinelConnectionAfterSentinelRestart(): void
{
$sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel&alias=sentinel1');
$sentinel1
->expects($this->exactly(2))
->method('executeCommand')
->with($this->isRedisCommand(
'SENTINEL', array('sentinels', 'svc')
))
->willReturnOnConsecutiveCalls(
$this->throwException(new Connection\ConnectionException($sentinel1, 'Unknown connection error [127.0.0.1:5381]')),
array(
array(
'name', '127.0.0.1:5382',
'ip', '127.0.0.1',
'port', '5382',
'runid', 'f53b52d281be5cdd4873700c94846af8dbe47209',
'flags', 'sentinel',
)
)
);
$sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel&alias=sentinel2');
$sentinel2
->expects($this->once())
->method('executeCommand')
->with($this->isRedisCommand(
'SENTINEL', array('sentinels', 'svc')
))
->willThrowException(
new Connection\ConnectionException($sentinel2, 'Unknown connection error [127.0.0.1:5382]')
);
$replication = $this->getReplicationConnection('svc', array($sentinel1, $sentinel2));
try {
$replication->updateSentinels();
} catch (\Predis\ClientException $exception){
$this->assertEquals('No sentinel server available for autodiscovery.', $exception->getMessage());
}
$replication->updateSentinels();
}
// ******************************************************************** //
// ---- HELPER METHODS ------------------------------------------------ //
// ******************************************************************** //
@@ -57,6 +57,29 @@ class StreamConnectionTest extends PredisConnectionTestCase
$connection->connect();
}
/**
* @group disconnected
*/
public function testDoesntThrowErrorOnInvalidResource(): void
{
$this->expectException('Predis\Connection\ConnectionException');
$cmdSelect = RawCommand::create('SELECT', '1000');
$invalidResource = null;
/** @var NodeConnectionInterface|MockObject */
$connection = $this
->getMockBuilder($this->getConnectionClass())
->onlyMethods(array('getResource'))
->setConstructorArgs(array(new Parameters()))
->getMock();
$connection
->method('getResource')
->willReturn($invalidResource);
$connection->writeRequest($cmdSelect);
}
// ******************************************************************** //
// ---- INTEGRATION TESTS --------------------------------------------- //
// ******************************************************************** //