Compare commits

..

46 Commits

Author SHA1 Message Date
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
Till Krüss 99c253733d tweak changelog format 2022-06-08 06:14:56 -07:00
Till Krüss 69bb8fa082 bump version to v2.0.0 stable 2022-06-08 06:11:35 -07:00
Josias Montag e5221fa13b Allow PubSub / MultiExec with Replication 2022-05-28 08:18:01 -07:00
Till Krüss 15244c9642 cleanup readme 2022-05-26 12:01:30 -07:00
Till Krüss 4cc119a03c tag 2.0.0-beta.1 2022-05-26 11:48:12 -07:00
Till Krüss 619890f715 fix test 2022-05-26 11:45:35 -07:00
Till Krüss b060d43157 port changes from v1.1 branch 2022-05-26 11:29:45 -07:00
Till Krüss a5ab2bce6b truncate changelog 2022-05-26 11:12:41 -07:00
Hubert Lenoir 89f20dc968 add TOUCH command (#767) 2022-05-23 08:46:48 -07:00
Michal Lulco 6af5394a89 Added ZPOPMIN and ZPOPMAX (#758)
* Added ZPOPMIN and ZPOPMAX

* Added ZPOPMIN and ZPOPMAX

* Applied patch
2022-05-16 08:00:06 -07:00
Zaher Ghaibeh c508086b0e remove redundant variables (#765) 2022-05-15 09:51:28 -07:00
Zaher Ghaibeh 3c01ccc94c Testing code against Redis 7 (#764)
* redis 7 init

fix typo

add the new returns

* having a full sentence for the exception message
2022-05-12 12:23:51 -07:00
Jordi Boggiano e382bedb27 Update method tags (#762) 2022-05-12 09:13:06 -07:00
Zaher Ghaibeh a2449da7c8 PHP 8.1 tests (#760)
* - init 8.1 tests
- adding `#[\ReturnTypeWillChange]`
- Fix deprecation message: strlen(): Passing null to parameter #1 ($string) of type string is deprecated
- null is not allowed
- cleanup
- default values should be same type

* Mirror `v1.1` fixes for these files

* add null as type too since we also accept null as default value

Signed-off-by: Zaher Ghaibeh <zaher@zaher.dev>

* revert back to assertNull

* Update CHANGELOG

* Update CHANGELOG.md

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-05-07 08:03:53 -07:00
♚ PH⑦ de Soria™♛ b3a5bdf940 Remove invalid input for actions/cache (#737)
https://github.com/actions/cache/blob/main/examples.md#php---composer
2022-01-14 08:22:59 -08:00
Georges.L 4c1aada5ae Updated set, setex, setexp type hint (#710) 2021-09-08 09:05:55 -07:00
Mehdi Baaboura 54775a0295 Update LICENSE (#704) 2021-07-19 09:27:37 -07:00
Ruslan Bekenev 367bd72a0b Test optional count argument for LPOP (#700)
First added in Redis 6.2
2021-06-09 09:46:24 -07:00
Till Krüss 1d7ccb64ab Merge pull request #670 from charescape/patch-1
Update README.md
2021-04-04 10:33:08 -07:00
Till Krüss e29ae9065f some spacing 2021-04-04 10:31:53 -07:00
Till Krüss 64fc7774c6 stick to PHPUnit 9.4.* for now
`getAnnotations()` was removed in 9.5:

https://github.com/sebastianbergmann/phpunit/commit/68582043e149039cfa3596b42ed35753dcf54fb2
2021-04-04 10:31:45 -07:00
Till Krüss 554c36b329 Merge pull request #688 from open-source-contributions/improve_assertions
Improve PHPUnit assertions
2021-04-04 10:20:39 -07:00
Till Krüss be9d477564 fix for Redis 6 error message 2021-04-04 10:16:44 -07:00
Till Krüss 0e4e8f41cc fix 2021-04-04 10:14:13 -07:00
Till Krüss aa0c30809b fix test when using Redis 6 2021-04-04 10:11:29 -07:00
Peter 20f8faee6f Improve PHPUnit assertions 2021-04-04 23:30:14 +08:00
Till Krüss aa436263f8 Merge pull request #684 from andrew-demb/patch-2
CommandInterface::parseResponse() can accept array and null argument
2021-03-09 08:43:59 -08:00
Till Krüss 3c8856b05a Merge pull request #683 from andrew-demb/patch-1
Status::get() always return self
2021-03-09 08:43:39 -08:00
Andrii Dembitskyi d935f6e5c2 CommandInterface::parseResponse() can accept array and null argument 2021-03-09 07:15:08 -05:00
Andrii Dembitskyi 87fcc69e7a Status::get() always return self 2021-03-09 07:09:15 -05:00
charescape 407ddeca92 Update README.md 2020-11-12 11:39:48 +08:00
Till Krüss 338ba6d73d Merge pull request #665 from danwall/patch-1
Fix typo
2020-09-26 19:08:13 -07:00
Dan Wall 25db9bfec5 Fix typo 2020-09-26 11:52:44 +10:00
Daniele Alessandri 0e9aeee90f Fix PhpiredisSocketConnection for PHP 8.
Removed is_resource() check as socket_create() now returns an object.
2020-09-20 15:29:16 +02:00
66 changed files with 1158 additions and 1817 deletions
+16 -4
View File
@@ -1,40 +1,52 @@
name: Tests
on: [push, pull_request]
on:
- push
- pull_request
jobs:
predis:
name: PHP ${{ matrix.php-versions }} (Redis ${{ matrix.redis-versions }})
runs-on: ubuntu-latest
services:
redis:
image: redis:${{ matrix.redis-versions }}
ports:
- 6379:6379
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-versions: ['7.2', '7.3', '7.4', '8.0']
redis-versions: ['3', '4', '5', '6']
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
redis-versions: ['3', '4', '5', '6', '7']
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP with Composer and extensions
with:
php-version: ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
- name: Get Composer cache directory
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache Composer dependencies
uses: actions/cache@v2
with:
php-version: ${{ matrix.php-versions }}
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
env:
PHP_VERSION: ${{ matrix.php-versions }}
run: composer install --no-progress --prefer-dist --optimize-autoloader $(if [ "$PHP_VERSION" == "8.0" ]; then echo "--ignore-platform-reqs"; fi;)
- name: Test with PHPUnit
run: vendor/bin/phpunit
+34 -1072
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009-2020 Daniele Alessandri
Copyright (c) 2009-2021 Daniele Alessandri
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
+12 -67
View File
@@ -8,21 +8,12 @@
A flexible and feature-complete [Redis](http://redis.io) client for PHP 7.2 and newer.
__ATTENTION:__ you are on the README file of an unstable branch of Predis specifically meant for the
development of future releases. This means that the code on this branch is potentially unstable, and
breaking change may happen without any prior notice. Do not use it in production environments or use
it at your own risk!
Predis does not require any additional C extension by default, but it can be optionally paired with
[phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of the serialization and parsing
of the [Redis RESP Protocol](http://redis.io/topics/protocol).
More details about this project can be found on the [frequently asked questions](FAQ.md).
## Main features ##
- Support for Redis from __2.0__ to __3.2__.
- Support for Redis from __3.0__ to __7.0__.
- Support for clustering using client-side sharding and pluggable keyspace distributors.
- Support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
- Support for master-slave replication setups and [redis-sentinel](http://redis.io/topics/sentinel).
@@ -44,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 ###
@@ -169,40 +164,6 @@ Users can also provide custom options with values or callable objects (for lazy
are stored in the options container for later use through the library.
### Global and role-specific default connection parameters ###
While the `parameters` client option is useful to apply a set of default parameters and their values
to connections created by the underlying connection factory, sometimes it is useful to set different
values depending on the actual role of the target node (just to make an example, for sentinel nodes
it is common to use a lower connect() timeout compared to the default value for normal Redis nodes).
To make this possible `parameters` allows passing role-specific default values as named arrays using
three special keys: `role.sentinel`, `role.master` and `role.slave`. These role-specific parameters
take precedence over global default parameters passed at the root level of `parameters` but they do
not override parameters explicitly set by the user for each single node just like global defaults.
```php
$options = [
'parameters' => [
// Root level is for global default parameters.
'database' => 10,
'password' => $redisSecretPassword,
// ...
'role.master' => [
// Sub-key for default parameters targeting master Redis nodes.
],
'role.slave' => [
// Sub-key for default parameters targeting replica Redis nodes.
],
'role.sentinel' => [
// Sub-key for default parameters targeting Redis Sentinel nodes.
],
],
];
```
### Aggregate connections ###
Aggregate connections are the foundation upon which Predis implements clustering and replication and
@@ -268,38 +229,22 @@ the `service` option set to the name of the service:
```php
$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => 'sentinel', 'service' => 'myservice'];
$options = ['replication' => 'sentinel', 'service' => 'mymaster'];
$client = new Predis\Client($sentinels, $options);
```
When master and replica nodes are configured to require authentication from clients, users must pass
`password` (password-based authentication) or `username` and `password` (ACL-based authentication on
Redis >= 6.0) via the global `parameters` client option.
If the master and slave nodes are configured to require an authentication from clients, a password
must be provided via the global `parameters` client option. This option can also be used to specify
a different database index. The client options array would then look like this:
```php
$options = [
'replication' => 'sentinel',
'service' => 'myservice',
'service' => 'mymaster',
'parameters' => [
'password' => $secretRedisPassword,
],
];
```
For sentinels protected by a password (supported since Redis >= 5.0) its value is not inherited from
the global `parameters` client option so a `password` must be set using the `role.sentinel` sub-key:
```php
$options = [
'replication' => 'sentinel',
'service' => 'myservice',
'parameters' => [
'password' => $secretRedisPassword,
'role.sentinel' => [
'password' => $secretSentinelPassword
]
'password' => $secretpassword,
'database' => 10,
],
];
```
+1 -1
View File
@@ -1 +1 @@
2.0.0-dev
2.0.2
+1 -1
View File
@@ -31,7 +31,7 @@
"php": "^7.2 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^8.0 || ^9.0"
"phpunit/phpunit": "^8.0 || ~9.4.4"
},
"suggest": {
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
+3 -4
View File
@@ -40,7 +40,7 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
*/
class Client implements ClientInterface, \IteratorAggregate
{
const VERSION = '2.0.0-dev';
const VERSION = '2.0.2';
/** @var OptionsInterface */
private $options;
@@ -209,9 +209,7 @@ class Client implements ClientInterface, \IteratorAggregate
throw new \InvalidArgumentException("Cannot find a connection by $selector matching `$value`");
}
$client = new static($connection, $this->getOptions());
return $client;
return new static($connection, $this->getOptions());
}
/**
@@ -515,6 +513,7 @@ class Client implements ClientInterface, \IteratorAggregate
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
$clients = array();
+152 -148
View File
@@ -15,6 +15,7 @@ use Predis\Command\CommandInterface;
use Predis\Command\FactoryInterface;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Response\Status;
/**
* Interface defining a client able to execute commands against Redis.
@@ -24,154 +25,157 @@ use Predis\Connection\ConnectionInterface;
* and more friendly interface to ease programming which is described in the
* following list of methods:
*
* @method int del(array|string $keys)
* @method string|null dump($key)
* @method int exists($key)
* @method int expire($key, $seconds)
* @method int expireat($key, $timestamp)
* @method array keys($pattern)
* @method int move($key, $db)
* @method mixed object($subcommand, $key)
* @method int persist($key)
* @method int pexpire($key, $milliseconds)
* @method int pexpireat($key, $timestamp)
* @method int pttl($key)
* @method string|null randomkey()
* @method mixed rename($key, $target)
* @method int renamenx($key, $target)
* @method array scan($cursor, array $options = null)
* @method array sort($key, array $options = null)
* @method int ttl($key)
* @method mixed type($key)
* @method int append($key, $value)
* @method int bitcount($key, $start = null, $end = null)
* @method int bitop($operation, $destkey, $key)
* @method array|null bitfield($key, $subcommand, ...$subcommandArg)
* @method int bitpos($key, $bit, $start = null, $end = null)
* @method int decr($key)
* @method int decrby($key, $decrement)
* @method string|null get($key)
* @method int getbit($key, $offset)
* @method string getrange($key, $start, $end)
* @method string|null getset($key, $value)
* @method int incr($key)
* @method int incrby($key, $increment)
* @method string incrbyfloat($key, $increment)
* @method array mget(array $keys)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method mixed psetex($key, $milliseconds, $value)
* @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit($key, $offset, $value)
* @method int setex($key, $seconds, $value)
* @method int setnx($key, $value)
* @method int setrange($key, $offset, $value)
* @method int strlen($key)
* @method int hdel($key, array $fields)
* @method int hexists($key, $field)
* @method string|null hget($key, $field)
* @method array hgetall($key)
* @method int hincrby($key, $field, $increment)
* @method string hincrbyfloat($key, $field, $increment)
* @method array hkeys($key)
* @method int hlen($key)
* @method array hmget($key, array $fields)
* @method mixed hmset($key, array $dictionary)
* @method array hscan($key, $cursor, array $options = null)
* @method int hset($key, $field, $value)
* @method int hsetnx($key, $field, $value)
* @method array hvals($key)
* @method int hstrlen($key, $field)
* @method array|null blpop(array|string $keys, $timeout)
* @method array|null brpop(array|string $keys, $timeout)
* @method string|null brpoplpush($source, $destination, $timeout)
* @method string|null lindex($key, $index)
* @method int linsert($key, $whence, $pivot, $value)
* @method int llen($key)
* @method string|null lpop($key)
* @method int lpush($key, array $values)
* @method int lpushx($key, array $values)
* @method array lrange($key, $start, $stop)
* @method int lrem($key, $count, $value)
* @method mixed lset($key, $index, $value)
* @method mixed ltrim($key, $start, $stop)
* @method string|null rpop($key)
* @method string|null rpoplpush($source, $destination)
* @method int rpush($key, array $values)
* @method int rpushx($key, array $values)
* @method int sadd($key, array $members)
* @method int scard($key)
* @method array sdiff(array|string $keys)
* @method int sdiffstore($destination, array|string $keys)
* @method array sinter(array|string $keys)
* @method int sinterstore($destination, array|string $keys)
* @method int sismember($key, $member)
* @method array smembers($key)
* @method int smove($source, $destination, $member)
* @method string|null spop($key, $count = null)
* @method string|null srandmember($key, $count = null)
* @method int srem($key, $member)
* @method array sscan($key, $cursor, array $options = null)
* @method array sunion(array|string $keys)
* @method int sunionstore($destination, array|string $keys)
* @method int zadd($key, array $membersAndScoresDictionary)
* @method int zcard($key)
* @method string zcount($key, $min, $max)
* @method string zincrby($key, $increment, $member)
* @method int zinterstore($destination, array|string $keys, array $options = null)
* @method array zrange($key, $start, $stop, array $options = null)
* @method array zrangebyscore($key, $min, $max, array $options = null)
* @method int|null zrank($key, $member)
* @method int zrem($key, $member)
* @method int zremrangebyrank($key, $start, $stop)
* @method int zremrangebyscore($key, $min, $max)
* @method array zrevrange($key, $start, $stop, array $options = null)
* @method array zrevrangebyscore($key, $max, $min, array $options = null)
* @method int|null zrevrank($key, $member)
* @method int zunionstore($destination, array|string $keys, array $options = null)
* @method string|null zscore($key, $member)
* @method array zscan($key, $cursor, array $options = null)
* @method array zrangebylex($key, $start, $stop, array $options = null)
* @method array zrevrangebylex($key, $start, $stop, array $options = null)
* @method int zremrangebylex($key, $min, $max)
* @method int zlexcount($key, $min, $max)
* @method int pfadd($key, array $elements)
* @method mixed pfmerge($destinationKey, array|string $sourceKeys)
* @method int pfcount(array|string $keys)
* @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($key)
* @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method mixed script($subcommand, $argument = null)
* @method mixed auth($password)
* @method string echo($message)
* @method mixed ping($message = null)
* @method mixed select($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($host, $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method array time()
* @method array command()
* @method int geoadd($key, $longitude, $latitude, $member)
* @method array geohash($key, array $members)
* @method array geopos($key, array $members)
* @method string|null geodist($key, $member1, $member2, $unit = null)
* @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method array georadiusbymember($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 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>
*/
+1 -2
View File
@@ -239,9 +239,8 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
public function get($value)
{
$hash = $this->hash($value);
$node = $this->getByHash($hash);
return $node;
return $this->getByHash($hash);
}
/**
+1 -2
View File
@@ -40,9 +40,8 @@ class PredisStrategy extends ClusterStrategy
{
$key = $this->extractKeyTag($key);
$hash = $this->distributor->hash($key);
$slot = $this->distributor->getSlot($hash);
return $slot;
return $this->distributor->getSlot($hash);
}
/**
+1 -2
View File
@@ -41,9 +41,8 @@ class RedisStrategy extends ClusterStrategy
public function getSlotByKey($key)
{
$key = $this->extractKeyTag($key);
$slot = $this->hashGenerator->hash($key) & 0x3FFF;
return $slot;
return $this->hashGenerator->hash($key) & 0x3FFF;
}
/**
+6
View File
@@ -125,6 +125,7 @@ class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($slot)
{
return isset($this->slots[$slot]);
@@ -137,6 +138,7 @@ class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
*
* @return string
*/
#[\ReturnTypeWillChange]
public function offsetGet($slot)
{
if (isset($this->slots[$slot])) {
@@ -152,6 +154,7 @@ class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
*
* @return string
*/
#[\ReturnTypeWillChange]
public function offsetSet($slot, $connection)
{
if (!static::isValid($slot)) {
@@ -168,6 +171,7 @@ class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
*
* @return string
*/
#[\ReturnTypeWillChange]
public function offsetUnset($slot)
{
unset($this->slots[$slot]);
@@ -178,6 +182,7 @@ class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return count($this->slots);
@@ -188,6 +193,7 @@ class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
*
* @return \ArrayIterator
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->slots);
@@ -92,7 +92,7 @@ abstract class CursorBasedIterator implements \Iterator
{
$options = array();
if (strlen($this->match) > 0) {
if (strlen(strval($this->match)) > 0) {
$options['MATCH'] = $this->match;
}
@@ -139,6 +139,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -148,6 +149,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -156,6 +158,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -164,6 +167,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
tryFetch: {
@@ -184,6 +188,7 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+5
View File
@@ -128,6 +128,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -137,6 +138,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -145,6 +147,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -153,6 +156,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
if (!$this->elements && $this->fetchmore) {
@@ -169,6 +173,7 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+1 -1
View File
@@ -92,7 +92,7 @@ abstract class Command implements CommandInterface
*/
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];
}
+1 -1
View File
@@ -73,7 +73,7 @@ interface CommandInterface
/**
* 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
*/
+8 -1
View File
@@ -197,7 +197,14 @@ 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);
$callable = $this->commands[$commandID];
if (is_string($callable) && strpos($callable, 'static::') === 0) {
$callable = explode('::', $this->commands[$commandID]);
$callable[0] = $this;
}
call_user_func($callable, $command, $this->prefix);
}
}
+5 -1
View File
@@ -71,7 +71,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* Returns an iterator over the list of command processor in the chain.
*
* @return \ArrayIterator
* @return \Traversable<int, ProcessorInterface>
*/
public function getIterator()
{
@@ -91,6 +91,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function offsetExists($index)
{
return isset($this->processors[$index]);
@@ -99,6 +100,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function offsetGet($index)
{
return $this->processors[$index];
@@ -107,6 +109,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function offsetSet($index, $processor)
{
if (!$processor instanceof ProcessorInterface) {
@@ -121,6 +124,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function offsetUnset($index)
{
unset($this->processors[$index]);
+1 -2
View File
@@ -51,9 +51,8 @@ final class RawCommand implements CommandInterface
public static function create($commandID /* [ $arg, ... */)
{
$arguments = func_get_args();
$command = new static(array_shift($arguments), $arguments);
return $command;
return new static(array_shift($arguments), $arguments);
}
/**
+4 -1
View File
@@ -33,7 +33,10 @@ class SENTINEL extends RedisCommand
*/
public function parseResponse($data)
{
switch (strtolower($this->getArgument(0))) {
$argument = $this->getArgument(0);
$argument = is_null($argument) ? null : strtolower($argument);
switch ($argument) {
case 'masters':
case 'slaves':
return self::processMastersOrSlaves($data);
+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/touch
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class TOUCH extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'TOUCH';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);
parent::setArguments($arguments);
}
}
+44
View File
@@ -0,0 +1,44 @@
<?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/zpopmax
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ZPOPMAX extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'ZPOPMAX';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
$result = array();
for ($i = 0; $i < count($data); ++$i) {
$result[$data[$i]] = $data[++$i];
}
return $result;
}
}
+44
View File
@@ -0,0 +1,44 @@
<?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/zpopmin
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ZPOPMIN extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'ZPOPMIN';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
$result = array();
for ($i = 0; $i < count($data); ++$i) {
$result[$data[$i]] = $data[++$i];
}
return $result;
}
}
+9 -5
View File
@@ -26,17 +26,21 @@ abstract class CommunicationException extends PredisException
* @param NodeConnectionInterface $connection Connection that generated the exception.
* @param string $message Error message.
* @param int $code Error code.
* @param \Exception $innerException Inner exception for wrapping the original error.
* @param \Exception|null $innerException Inner exception for wrapping the original error.
*/
public function __construct(
NodeConnectionInterface $connection,
$message = null,
$code = null,
$message = "",
$code = 0,
\Exception $innerException = null
) {
$this->connection = $connection;
parent::__construct(
is_null($message) ? '' : $message,
is_null($code) ? 0 : $code,
$innerException
);
parent::__construct($message, $code, $innerException);
$this->connection = $connection;
}
/**
+1 -1
View File
@@ -126,7 +126,7 @@ abstract class AbstractConnection implements NodeConnectionInterface
* @param string $message Error message.
* @param int $code Error code.
*/
protected function onConnectionError($message, $code = null)
protected function onConnectionError($message, $code = 0)
{
CommunicationException::handle(
new ConnectionException($this, "$message [{$this->getParameters()}]", $code)
+4 -6
View File
@@ -138,9 +138,7 @@ class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable
);
}
$node = $this->distributor->getBySlot($slot);
return $node;
return $this->distributor->getBySlot($slot);
}
/**
@@ -189,9 +187,7 @@ class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable
public function getConnectionByKey($key)
{
$hash = $this->strategy->getSlotByKey($key);
$node = $this->distributor->getBySlot($hash);
return $node;
return $this->distributor->getBySlot($hash);
}
/**
@@ -208,6 +204,7 @@ class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function count()
{
return count($this->pool);
@@ -216,6 +213,7 @@ class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->pool);
+54 -13
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;
}
}
@@ -442,9 +469,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
}
$this->move($connection, $slot);
$response = $this->executeCommand($command);
return $response;
return $this->executeCommand($command);
}
/**
@@ -465,9 +490,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
}
$connection->executeCommand(RawCommand::create('ASKING'));
$response = $connection->executeCommand($command);
return $response;
return $connection->executeCommand($command);
}
/**
@@ -486,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;
}
@@ -545,6 +584,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function count()
{
return count($this->pool);
@@ -553,6 +593,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
if ($this->slotmap->isEmpty()) {
+2 -64
View File
@@ -11,7 +11,6 @@
namespace Predis\Connection;
use InvalidArgumentException;
use Predis\Command\RawCommand;
/**
@@ -120,28 +119,6 @@ class Factory implements FactoryInterface
*/
public function setDefaultParameters(array $parameters)
{
if (isset($parameters['role.master']) && !is_array($parameters['role.master'])) {
throw new InvalidArgumentException('Default parameters for `role.master` must be passed as a named array');
}
if (isset($parameters['role.slave']) && !is_array($parameters['role.slave'])) {
throw new InvalidArgumentException('Default parameters for `role.slave` must be passed as a named array');
}
if (isset($parameters['role.sentinel'])) {
if (!is_array($parameters['role.sentinel'])) {
throw new InvalidArgumentException('Default parameters for `role.sentinel` must be passed as a named array');
}
// NOTE: sentinels do not support "SELECT" and ACL "AUTH" commands
// so we must strip "database" and "username" from "role.sentinel"
// to prevent spurious commands from being sent to sentinel nodes.
unset(
$parameters['role.sentinel']['username'],
$parameters['role.sentinel']['database']
);
}
$this->defaults = $parameters;
}
@@ -155,37 +132,6 @@ class Factory implements FactoryInterface
return $this->defaults;
}
/**
* Applies default connection parameters to the user supplied parameters.
*
* @param array $parameters Input connection parameters
*
* @return array
*/
protected function applyDefaultParameters(array $parameters)
{
static $stripInternal = ['role.sentinel' => null, 'role.master' => null, 'role.slave' => null];
$stripAdditional = [];
if (isset($parameters['role'])) {
switch ($role = $parameters['role']) {
case 'sentinel':
// NOTE: we strip these from global defaults when dealing with sentinel nodes.
$stripAdditional = ['username' => null, 'password' => null, 'database' => null];
case 'master':
case 'slave':
if (isset($this->defaults["role.$role"])) {
$parameters += $this->defaults["role.$role"];
}
}
}
$parameters += array_diff_key($this->defaults, $stripInternal, $stripAdditional);
return $parameters;
}
/**
* Creates a connection parameters instance from the supplied argument.
*
@@ -198,19 +144,11 @@ class Factory implements FactoryInterface
if (is_string($parameters)) {
$parameters = Parameters::parse($parameters);
} else {
$parameters = $parameters ?? [];
}
if (isset($parameters['role']) && $parameters['role'] === 'sentinel') {
// NOTE: sentinels do not support "SELECT" and ACL "AUTH" commands so we must strip
// "database" and "username" from input parameters to prevent spurious commands from
// being sent to sentinel nodes but they can still accept "password" when explicitly
// set (password-based authentication for sentinels is supported on Redis >= 5.0).
unset($parameters['username'], $parameters['database']);
$parameters = $parameters ?: array();
}
if ($this->defaults) {
$parameters = $this->applyDefaultParameters($parameters);
$parameters += $this->defaults;
}
return new Parameters($parameters);
+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.
*/
+5 -5
View File
@@ -66,9 +66,9 @@ class PhpiredisSocketConnection extends AbstractConnection
*/
public function __destruct()
{
phpiredis_reader_destroy($this->reader);
parent::__destruct();
phpiredis_reader_destroy($this->reader);
}
/**
@@ -227,9 +227,7 @@ class PhpiredisSocketConnection extends AbstractConnection
$protocol = SOL_TCP;
}
$socket = @socket_create($domain, SOCK_STREAM, $protocol);
if (!is_resource($socket)) {
if (false === $socket = @socket_create($domain, SOCK_STREAM, $protocol)) {
$this->emitSocketError();
}
@@ -344,7 +342,9 @@ class PhpiredisSocketConnection extends AbstractConnection
public function disconnect()
{
if ($this->isConnected()) {
phpiredis_reader_reset($this->reader);
socket_close($this->getResource());
parent::disconnect();
}
}
+12 -2
View File
@@ -67,9 +67,19 @@ class PhpiredisStreamConnection extends StreamConnection
*/
public function __destruct()
{
phpiredis_reader_destroy($this->reader);
parent::__destruct();
phpiredis_reader_destroy($this->reader);
}
/**
* {@inheritdoc}
*/
public function disconnect()
{
phpiredis_reader_reset($this->reader);
parent::disconnect();
}
/**
@@ -20,6 +20,7 @@ use Predis\Connection\NodeConnectionInterface;
use Predis\Connection\Parameters;
use Predis\Replication\ReplicationStrategy;
use Predis\Replication\RoleException;
use Predis\Response\Error;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ServerException;
@@ -69,6 +70,11 @@ class SentinelReplication implements ReplicationInterface
*/
protected $sentinels = array();
/**
* @var int
*/
protected $sentinelIndex = 0;
/**
* @var NodeConnectionInterface
*/
@@ -151,14 +157,14 @@ class SentinelReplication implements ReplicationInterface
}
/**
* Sets the time to wait (in seconds) before fetching a new configuration
* Sets the time to wait (in milliseconds) before fetching a new configuration
* from one of the sentinels.
*
* @param float $seconds Time to wait before the next attempt.
* @param float $milliseconds Time to wait before the next attempt.
*/
public function setRetryWait($seconds)
public function setRetryWait($milliseconds)
{
$this->retryWait = (float) $seconds;
$this->retryWait = (float) $milliseconds;
}
/**
@@ -241,8 +247,6 @@ class SentinelReplication implements ReplicationInterface
/**
* Creates a new connection to a sentinel server.
*
* @param mixed $parameters Connection parameters or connection instance
*
* @return NodeConnectionInterface
*/
protected function createSentinelConnection($parameters)
@@ -256,19 +260,21 @@ class SentinelReplication implements ReplicationInterface
}
if (is_array($parameters)) {
// NOTE: we enforce the "sentinel" role so that appropriate default
// parameters are applied when creating the new connection instance
// and blacklisted ones are stripped off from input parameters.
$parameters['role'] = 'sentinel';
// NOTE: sentinels do not accept AUTH and SELECT commands so we must
// explicitly set them to NULL to avoid problems when using default
// parameters set via client options. Actually AUTH is supported for
// sentinels starting with Redis 5 but we have to differentiate from
// sentinels passwords and nodes passwords, this will be implemented
// in a later release.
$parameters['database'] = null;
$parameters['username'] = null;
if (!isset($parameters['timeout'])) {
$parameters['timeout'] = $this->sentinelTimeout;
}
}
$connection = $this->connectionFactory->create($parameters);
return $connection;
return $this->connectionFactory->create($parameters);
}
/**
@@ -281,11 +287,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);
}
@@ -306,6 +314,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();
@@ -536,13 +545,17 @@ class SentinelReplication implements ReplicationInterface
* @param NodeConnectionInterface $connection Connection to a redis server.
* @param string $role Expected role of the server ("master", "slave" or "sentinel").
*
* @throws RoleException
* @throws RoleException|ConnectionException
*/
protected function assertConnectionRole(NodeConnectionInterface $connection, $role)
{
$role = strtolower($role);
$actualRole = $connection->executeCommand(RawCommand::create('ROLE'));
if ($actualRole instanceof Error) {
throw new ConnectionException($connection, $actualRole->getMessage());
}
if ($role !== $actualRole[0]) {
throw new RoleException($connection, "Expected $role but got $actualRole[0] [$connection]");
}
+3 -7
View File
@@ -151,9 +151,7 @@ class StreamConnection extends AbstractConnection
}
}
$resource = $this->createStreamSocket($parameters, $address, $flags);
return $resource;
return $this->createStreamSocket($parameters, $address, $flags);
}
/**
@@ -183,9 +181,7 @@ class StreamConnection extends AbstractConnection
}
}
$resource = $this->createStreamSocket($parameters, "unix://{$parameters->path}", $flags);
return $resource;
return $this->createStreamSocket($parameters, "unix://{$parameters->path}", $flags);
}
/**
@@ -364,7 +360,7 @@ class StreamConnection extends AbstractConnection
$buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";
foreach ($arguments as $argument) {
$arglen = strlen($argument);
$arglen = strlen(strval($argument));
$buffer .= "\${$arglen}\r\n{$argument}\r\n";
}
+8 -3
View File
@@ -12,7 +12,7 @@
namespace Predis\Monitor;
use Predis\ClientInterface;
use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\Cluster\ClusterInterface;
use Predis\NotSupportedException;
/**
@@ -56,9 +56,9 @@ class Consumer implements \Iterator
*/
private function assertClient(ClientInterface $client)
{
if ($client->getConnection() instanceof AggregateConnectionInterface) {
if ($client->getConnection() instanceof ClusterInterface) {
throw new NotSupportedException(
'Cannot initialize a monitor consumer over aggregate connections.'
'Cannot initialize a monitor consumer over cluster connections.'
);
}
@@ -91,6 +91,7 @@ class Consumer implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function rewind()
{
// NOOP
@@ -101,6 +102,7 @@ class Consumer implements \Iterator
*
* @return object
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->getValue();
@@ -109,6 +111,7 @@ class Consumer implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -117,6 +120,7 @@ class Consumer implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
++$this->position;
@@ -127,6 +131,7 @@ class Consumer implements \Iterator
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+1 -1
View File
@@ -14,7 +14,7 @@ namespace Predis\Protocol;
use Predis\CommunicationException;
/**
* Exception used to indentify errors encountered while parsing the Redis wire
* Exception used to identify errors encountered while parsing the Redis wire
* protocol.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
+1 -3
View File
@@ -95,9 +95,7 @@ class ResponseReader implements ResponseReaderInterface
$this->onProtocolError($connection, "Unknown response prefix: '$prefix'");
}
$payload = $this->handlers[$prefix]->handle($connection, substr($header, 1));
return $payload;
return $this->handlers[$prefix]->handle($connection, substr($header, 1));
}
/**
+5
View File
@@ -151,6 +151,7 @@ abstract class AbstractConsumer implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function rewind()
{
// NOOP
@@ -162,6 +163,7 @@ abstract class AbstractConsumer implements \Iterator
*
* @return array
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->getValue();
@@ -170,6 +172,7 @@ abstract class AbstractConsumer implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -178,6 +181,7 @@ abstract class AbstractConsumer implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
if ($this->valid()) {
@@ -192,6 +196,7 @@ abstract class AbstractConsumer implements \Iterator
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
$isValid = $this->isFlagSet(self::STATUS_VALID);
+3 -3
View File
@@ -14,7 +14,7 @@ namespace Predis\PubSub;
use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Command\Command;
use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\Cluster\ClusterInterface;
use Predis\NotSupportedException;
/**
@@ -62,9 +62,9 @@ class Consumer extends AbstractConsumer
*/
private function checkCapabilities(ClientInterface $client)
{
if ($client->getConnection() instanceof AggregateConnectionInterface) {
if ($client->getConnection() instanceof ClusterInterface) {
throw new NotSupportedException(
'Cannot initialize a PUB/SUB consumer over aggregate connections.'
'Cannot initialize a PUB/SUB consumer over cluster connections.'
);
}
+2 -1
View File
@@ -62,7 +62,8 @@ class ReplicationStrategy
}
if (($eval = $id === 'EVAL') || $id === 'EVALSHA') {
$sha1 = $eval ? sha1($command->getArgument(0)) : $command->getArgument(0);
$argument = $command->getArgument(0);
$sha1 = $eval ? sha1(strval($argument)) : $argument;
if (isset($this->readonlySHA1[$sha1])) {
if (true === $readonly = $this->readonlySHA1[$sha1]) {
@@ -34,6 +34,7 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function rewind()
{
// NOOP
@@ -42,6 +43,7 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -50,6 +52,7 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -58,6 +61,7 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
if (++$this->position < $this->size) {
@@ -68,6 +72,7 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->position < $this->size;
@@ -82,6 +87,7 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return $this->size;
+1
View File
@@ -61,6 +61,7 @@ class MultiBulkTuple extends MultiBulk implements \OuterIterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function getInnerIterator()
{
return $this->iterator;
+1 -1
View File
@@ -59,7 +59,7 @@ class Status implements ResponseInterface
*
* @param string $payload Status response payload.
*
* @return string
* @return self
*/
public static function get($payload)
{
+6
View File
@@ -54,6 +54,7 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function open($save_path, $session_id)
{
// NOOP
@@ -63,6 +64,7 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function close()
{
// NOOP
@@ -72,6 +74,7 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function gc($maxlifetime)
{
// NOOP
@@ -81,6 +84,7 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function read($session_id)
{
if ($data = $this->client->get($session_id)) {
@@ -92,6 +96,7 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function write($session_id, $session_data)
{
$this->client->setex($session_id, $this->ttl, $session_data);
@@ -102,6 +107,7 @@ class Handler implements \SessionHandlerInterface
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function destroy($session_id)
{
$this->client->del($session_id);
@@ -27,9 +27,10 @@ class AbortedMultiExecException extends PredisException
* @param string $message Error message.
* @param int $code Error code.
*/
public function __construct(MultiExec $transaction, $message, $code = null)
public function __construct(MultiExec $transaction, $message, $code = 0)
{
parent::__construct($message, $code);
parent::__construct($message, is_null($code) ? 0 : $code);
$this->transaction = $transaction;
}
+3 -3
View File
@@ -16,7 +16,7 @@ use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Command\CommandInterface;
use Predis\CommunicationException;
use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\Cluster\ClusterInterface;
use Predis\NotSupportedException;
use Predis\Protocol\ProtocolException;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
@@ -66,9 +66,9 @@ class MultiExec implements ClientContextInterface
*/
private function assertClient(ClientInterface $client)
{
if ($client->getConnection() instanceof AggregateConnectionInterface) {
if ($client->getConnection() instanceof ClusterInterface) {
throw new NotSupportedException(
'Cannot initialize a MULTI/EXEC transaction over aggregate connections.'
'Cannot initialize a MULTI/EXEC transaction over cluster connections.'
);
}
+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']) &&
+4 -4
View File
@@ -236,7 +236,7 @@ class SlotMapTest extends PredisTestCase
{
$slotmap = new SlotMap();
$this->assertSame(0, count($slotmap));
$this->assertCount(0, $slotmap);
}
/**
@@ -247,13 +247,13 @@ class SlotMapTest extends PredisTestCase
$slotmap = new SlotMap();
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
$this->assertSame(5461, count($slotmap));
$this->assertCount(5461, $slotmap);
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
$this->assertSame(10923, count($slotmap));
$this->assertCount(10923, $slotmap);
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
$this->assertSame(16384, count($slotmap));
$this->assertCount(16384, $slotmap);
}
/**
+22 -1
View File
@@ -106,13 +106,34 @@ class COMMAND_Test extends PredisCommandTestCase
// NOTE: starting with Redis 6.0 and the introduction of Access Control
// Lists, COMMAND INFO returns an additional array for each specified
// command in yhe request with a list of the ACL categories associated
// command in the request with a list of the ACL categories associated
// to a command. We simply append this additional array in the expected
// response if the test suite is executed against Redis >= 6.0.
if ($this->isRedisServerVersion('>=', '6.0')) {
$expected[0][] = array('@read', '@string', '@fast');
}
// NOTE: starting with Redis 7.0 COMMAND INFO returns an additional arrays:
// - Command tips: https://redis.io/topics/command-tips.
// - Key specifications: https://redis.io/topics/key-specs.
// - Subcommands: https://redis.io/commands/command/#subcommands.
// We simply append this additional array in the expected response if the
// test suite is executed against Redis >= 7.0.
if ($this->isRedisServerVersion('>=', '7.0')) {
$expected[0][] = array();
$expected[0][] = array(
array(
'flags',
array('RO','access'),
'begin_search',
array('type','index','spec', array('index',1)),
'find_keys',
array('type','range','spec', array('lastkey',0,'keystep',1,'limit',0))
)
);
$expected[0][] = array();
}
$this->assertCount(1, $response = $redis->command('INFO', 'GET'));
// NOTE: we use assertEquals instead of assertSame because Redis returns
+8 -2
View File
@@ -104,7 +104,7 @@ class CONFIG_Test extends PredisCommandTestCase
$redis = $this->getClient();
$this->assertIsArray($configs = $redis->config('GET', 'dbfilename'));
$this->assertEquals(1, count($configs));
$this->assertCount(1, $configs);
$this->assertArrayHasKey('dbfilename', $configs);
}
@@ -143,7 +143,13 @@ class CONFIG_Test extends PredisCommandTestCase
public function testThrowsExceptionWhenSettingUnknownConfiguration(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('ERR Unsupported CONFIG parameter: foo');
if ($this->isRedisServerVersion('<=', '6.0')) {
$this->expectExceptionMessage('ERR Unsupported CONFIG parameter: foo');
}
if ($this->isRedisServerVersion('>=', '7.0')) {
$this->expectExceptionMessage("ERR Unknown option or number of arguments for CONFIG SET - 'foo'");
}
$redis = $this->getClient();
+1 -1
View File
@@ -320,7 +320,7 @@ BUFFER;
$redis = $this->getClient();
$command = $this->getCommand();
$this->assertInternalType('array', $info = $redis->executeCommand($command));
$this->assertIsArray($info = $redis->executeCommand($command));
$this->assertArrayHasKey('redis_version', $info);
}
}
+15
View File
@@ -92,4 +92,19 @@ class LPOP_Test extends PredisCommandTestCase
$redis->set('foo', 'bar');
$redis->lpop('foo');
}
/**
* @group connected
* @requiresRedisVersion >= 6.2
*/
public function testPopsSpecifiedNumberOfElements(): void
{
$redis = $this->getClient();
$redis->rpush('letters', 'a', 'b', 'c', 'd', 'e', 'f');
$this->assertSame(array('a', 'b'), $redis->lpop('letters', 2));
$this->assertSame(array('c', 'd'), $redis->lpop('letters', 2));
$this->assertSame(array('e', 'f'), $redis->lrange('letters', 0, -1));
}
}
+1 -1
View File
@@ -85,7 +85,7 @@ class MOVE_Test extends PredisCommandTestCase
public function testThrowsExceptionOnInvalidDatabases(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('ERR index out of range');
$this->expectExceptionMessageMatches('/ERR.*out of range/');
$redis = $this->getClient();
+1 -1
View File
@@ -87,7 +87,7 @@ class SELECT_Test extends PredisCommandTestCase
public function testThrowsExceptionOnUnexpectedDatabaseName(): void
{
$this->expectException('Predis\Response\ServerException');
$this->expectExceptionMessage('ERR invalid DB index');
$this->expectExceptionMessageMatches('/ERR.*(invalid DB index|value is not an integer or out of range)/');
$redis = $this->getClient();
+3 -3
View File
@@ -185,11 +185,11 @@ class SORT_Test extends PredisCommandTestCase
$redis = $this->getClient();
$redis->lpush('list:unordered', $unordered = array(2, 100, 3, 1, 30, 10));
$this->assertEquals(
count($unordered),
$this->assertCount(
$redis->sort('list:unordered', array(
'store' => 'list:ordered',
))
)),
$unordered
);
$this->assertEquals(array(1, 2, 3, 10, 30, 100), $redis->lrange('list:ordered', 0, -1));
+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
*/
+98
View File
@@ -0,0 +1,98 @@
<?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-server
*/
class TOUCH_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\TOUCH';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'TOUCH';
}
/**
* @requiresRedisVersion >= 3.2.1
*
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = ['key1', 'key2', 'key3'];
$expected = ['key1', 'key2', 'key3'];
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @requiresRedisVersion >= 3.2.1
*
* @group disconnected
*/
public function testFilterArgumentsAsSingleArray(): void
{
$arguments = [['key1', 'key2', 'key3']];
$expected = ['key1', 'key2', 'key3'];
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @requiresRedisVersion >= 3.2.1
*
* @group disconnected
*/
public function testParseResponse(): void
{
$command = $this->getCommand();
$this->assertSame(10, $command->parseResponse(10));
}
/**
* @requiresRedisVersion >= 3.2.1
*
* @group connected
*/
public function testReturnsNumberOfDeletedKeys(): void
{
$redis = $this->getClient();
$this->assertSame(0, $redis->touch('foo'));
$redis->set('foo', 'bar');
$this->assertSame(1, $redis->touch('foo'));
$this->assertSame(1, $redis->touch('foo', 'hoge'));
$redis->set('hoge', 'piyo');
$this->assertSame(1, $redis->touch('foo'));
$this->assertSame(2, $redis->touch('foo', 'hoge'));
}
}
+102
View File
@@ -0,0 +1,102 @@
<?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-zset
*/
class ZPOPMAX_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\ZPOPMAX';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'ZPOPMAX';
}
/**
* @requiresRedisVersion >= 5.0.0
*
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = array('zset', 2);
$expected = array('zset', 2);
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @requiresRedisVersion >= 5.0.0
*
* @group disconnected
*/
public function testParseResponse(): void
{
$raw = array('element1', '1', 'element2', '2', 'element3', '3');
$expected = array('element1' => '1', 'element2' => '2', 'element3' => '3');
$command = $this->getCommand();
$this->assertSame($expected, $command->parseResponse($raw));
}
/**
* @requiresRedisVersion >= 5.0.0
*
* @group connected
*/
public function testReturnsElements(): void
{
$redis = $this->getClient();
$this->assertSame(array(), $redis->zpopmax('letters'));
$this->assertSame(array(), $redis->zpopmax('letters', 3));
$redis->zadd('letters', -10, 'a', 0, 'b', 10, 'c', 20, 'd', 20, 'e', 30, 'f');
$this->assertSame(array('f' => '30'), $redis->zpopmax('letters'));
$this->assertSame(array('e' => '20', 'd' => '20', 'c' => '10'), $redis->zpopmax('letters', 3));
$this->assertSame(array('b' => '0', 'a' => '-10'), $redis->zpopmax('letters', 3));
}
/**
* @requiresRedisVersion >= 5.0.0
*
* @group connected
*/
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->zpopmax('foo');
}
}
+101
View File
@@ -0,0 +1,101 @@
<?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-zset
*/
class ZPOPMIN_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return 'Predis\Command\Redis\ZPOPMIN';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'ZPOPMIN';
}
/**
* @requiresRedisVersion >= 5.0.0
*
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = array('zset', 2);
$expected = array('zset', 2);
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @requiresRedisVersion >= 5.0.0
*
* @group disconnected
*/
public function testParseResponse(): void
{
$raw = array('element1', '1', 'element2', '2', 'element3', '3');
$expected = array('element1' => '1', 'element2' => '2', 'element3' => '3');
$command = $this->getCommand();
$this->assertSame($expected, $command->parseResponse($raw));
}
/**
* @requiresRedisVersion >= 5.0.0
*
* @group connected
*/
public function testReturnsElements(): void
{
$redis = $this->getClient();
$this->assertSame(array(), $redis->zpopmin('letters'));
$this->assertSame(array(), $redis->zpopmin('letters', 3));
$redis->zadd('letters', -10, 'a', 0, 'b', 10, 'c', 20, 'd', 20, 'e', 30, 'f');
$this->assertSame(array('a' => '-10'), $redis->zpopmin('letters'));
$this->assertSame(array('b' => '0', 'c' => '10', 'd' => '20'), $redis->zpopmin('letters', 3));
$this->assertSame(array('e' => '20', 'f' => '30'), $redis->zpopmin('letters', 3));
}
/**
* @requiresRedisVersion >= 5.0.0
*
* @group connected
*/
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->zpopmin('foo');
}
}
+1 -1
View File
@@ -90,7 +90,7 @@ class RedisFactoryTest extends PredisTestCase
$this->assertTrue($factory->supports('mock'));
$this->assertTrue($factory->supports('MOCK'));
$this->assertSame(get_class($command), $factory->getCommandClass('mock'));
$this->assertInstanceOf($factory->getCommandClass('mock'), $command);
}
/**
@@ -40,7 +40,7 @@ class PredisClusterTest extends PredisTestCase
$cluster->add($connection1);
$cluster->add($connection2);
$this->assertSame(2, count($cluster));
$this->assertCount(2, $cluster);
$this->assertSame($connection1, $cluster->getConnectionById('127.0.0.1:7001'));
$this->assertSame($connection2, $cluster->getConnectionById('127.0.0.1:7002'));
}
@@ -58,7 +58,7 @@ class PredisClusterTest extends PredisTestCase
$cluster->add($connection1);
$cluster->add($connection2);
$this->assertSame(2, count($cluster));
$this->assertCount(2, $cluster);
$this->assertSame($connection1, $cluster->getConnectionByAlias('node01'));
$this->assertSame($connection2, $cluster->getConnectionByAlias('node02'));
}
@@ -71,7 +71,7 @@ class RedisClusterTest extends PredisTestCase
$cluster->add($connection1);
$cluster->add($connection2);
$this->assertSame(2, count($cluster));
$this->assertCount(2, $cluster);
$this->assertSame($connection1, $cluster->getConnectionById('127.0.0.1:6379'));
$this->assertSame($connection2, $cluster->getConnectionById('127.0.0.1:6380'));
}
@@ -92,7 +92,7 @@ class RedisClusterTest extends PredisTestCase
$this->assertTrue($cluster->remove($connection1));
$this->assertFalse($cluster->remove($connection3));
$this->assertSame(1, count($cluster));
$this->assertCount(1, $cluster);
}
/**
@@ -110,7 +110,7 @@ class RedisClusterTest extends PredisTestCase
$this->assertTrue($cluster->removeById('127.0.0.1:6380'));
$this->assertFalse($cluster->removeById('127.0.0.1:6390'));
$this->assertSame(1, count($cluster));
$this->assertCount(1, $cluster);
}
/**
@@ -128,11 +128,11 @@ class RedisClusterTest extends PredisTestCase
$cluster->add($connection2);
$cluster->add($connection3);
$this->assertSame(3, count($cluster));
$this->assertCount(3, $cluster);
$cluster->remove($connection3);
$this->assertSame(2, count($cluster));
$this->assertCount(2, $cluster);
}
/**
@@ -977,7 +977,7 @@ class RedisClusterTest extends PredisTestCase
$this->assertSame('foobar', $cluster->executeCommand($command));
$this->assertSame('foobar', $cluster->executeCommand($command));
$this->assertSame(2, count($cluster));
$this->assertCount(2, $cluster);
}
/**
@@ -1033,7 +1033,7 @@ class RedisClusterTest extends PredisTestCase
$this->assertSame('foobar', $cluster->executeCommand($command));
$this->assertSame('foobar', $cluster->executeCommand($command));
$this->assertSame(2, count($cluster));
$this->assertCount(2, $cluster);
}
/**
@@ -1071,7 +1071,7 @@ class RedisClusterTest extends PredisTestCase
$this->assertSame('foobar', $cluster->executeCommand($command));
$this->assertSame('foobar', $cluster->executeCommand($command));
$this->assertSame(2, count($cluster));
$this->assertCount(2, $cluster);
}
/**
@@ -1121,7 +1121,7 @@ class RedisClusterTest extends PredisTestCase
$this->assertSame('foobar', $cluster->executeCommand($command));
$this->assertSame('foobar', $cluster->executeCommand($command));
$this->assertSame(3, count($cluster));
$this->assertCount(3, $cluster);
}
/**
@@ -1259,7 +1259,7 @@ class RedisClusterTest extends PredisTestCase
$cluster->add($connection1);
$this->assertSame('foobar', $cluster->executeCommand($cmdGET));
$this->assertSame(2, count($cluster));
$this->assertCount(2, $cluster);
}
/**
@@ -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());
}
}
+2 -314
View File
@@ -44,108 +44,8 @@ class FactoryTest extends PredisTestCase
));
$this->assertSame($defaults, $factory->getDefaultParameters());
}
/**
* @group disconnected
*/
public function testSettingDefaultParametersForMasterRole(): void
{
$factory = new Factory();
$factory->setDefaultParameters($expected = array(
'role.master' => [
'username' => 'myusername',
'password' => 'secret',
'database' => 10,
]
));
$this->assertSame($expected, $factory->getDefaultParameters());
}
/**
* @group disconnected
*/
public function testSettingDefaultParametersForMasterRoleAcceptsArrayOnly(): void
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('Default parameters for `role.master` must be passed as a named array');
$factory = new Factory();
$factory->setDefaultParameters(array(
'role.master' => 'invalid value',
));
}
/**
* @group disconnected
*/
public function testSettingDefaultParametersForSlaveRole(): void
{
$factory = new Factory();
$factory->setDefaultParameters($expected = array(
'role.slave' => [
'username' => 'myusername',
'password' => 'secret',
'database' => 10,
]
));
$this->assertSame($expected, $factory->getDefaultParameters());
}
/**
* @group disconnected
*/
public function testSettingDefaultParametersForSlaveRoleAcceptsArrayOnly(): void
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('Default parameters for `role.slave` must be passed as a named array');
$factory = new Factory();
$factory->setDefaultParameters(array(
'role.slave' => 'invalid value',
));
}
/**
* @group disconnected
*/
public function testSettingDefaultParametersForSentinelRoleIgnoresUsernameAndPassword(): void
{
$factory = new Factory();
$factory->setDefaultParameters(array(
'role.sentinel' => [
'username' => 'myusername',
'password' => 'secret',
'database' => 10,
]
));
$expected = array(
'role.sentinel' => [
'password' => 'secret',
]
);
$this->assertSame($expected, $factory->getDefaultParameters());
}
/**
* @group disconnected
*/
public function testSettingDefaultParametersForSentinelRoleAcceptsArrayOnly(): void
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('Default parameters for `role.sentinel` must be passed as a named array');
$factory = new Factory();
$factory->setDefaultParameters(array(
'role.sentinel' => 'invalid value',
));
$parameters = array('database' => 10, 'persistent' => true);
}
/**
@@ -295,7 +195,7 @@ class FactoryTest extends PredisTestCase
/**
* @group disconnected
*/
public function testCreateConnectionWithDefaultParametersDoNotOverrideExplicitInputParameters(): void
public function testCreateConnectionWithArrayParametersAndDefaults(): void
{
$factory = new Factory();
@@ -323,218 +223,6 @@ class FactoryTest extends PredisTestCase
$this->assertNull($parameters->path);
}
/**
* @group disconnected
*/
public function testCreateConnectionForSentinelRoleIgnoresUsernameAndDatabase(): void
{
$factory = new Factory();
$connection = $factory->create($inputParams = array(
'role' => 'sentinel',
'username' => 'myusername',
'password' => 'mypassword',
'database' => 10,
));
$parameters = $connection->getParameters();
$this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
$this->assertEquals($inputParams['role'], $parameters->role);
$this->assertEquals($inputParams['password'], $parameters->password);
$this->assertNull($parameters->username);
$this->assertNull($parameters->database);
}
/**
* @group disconnected
*/
public function testCreateConnectionForSentinelRoleDoesNotInheritPasswordFromGlobalDefaultParameters(): void
{
$factory = new Factory();
$factory->setDefaultParameters($defaultParams = array(
'password' => 'pwd.default',
));
$connectionSentinelRole = $factory->create($inputParamsSentinelRole = array(
'role' => 'sentinel',
));
$parameters = $connectionSentinelRole->getParameters();
$this->assertNull($parameters->password);
}
/**
* @group disconnected
*/
public function testCreateConnectionForSentinelRoleDoesNotInheritUsernameFromGlobalDefaultParameters(): void
{
$factory = new Factory();
$factory->setDefaultParameters($defaultParams = array(
'username' => 'usr.default',
));
$connectionSentinelRole = $factory->create($inputParamsSentinelRole = array(
'role' => 'sentinel',
));
$parameters = $connectionSentinelRole->getParameters();
$this->assertNull($parameters->username);
}
/**
* @group disconnected
*/
public function testCreateConnectionForSentinelRoleDoesNotInheritDatabaseFromGlobalDefaultParameters(): void
{
$factory = new Factory();
$factory->setDefaultParameters($defaultParams = array(
'database' => 15,
));
$connectionSentinelRole = $factory->create($inputParamsSentinelRole = array(
'role' => 'sentinel',
));
$parameters = $connectionSentinelRole->getParameters();
$this->assertNull($parameters->database);
}
/**
* @group disconnected
*/
public function testCreateConnectionWithDefaultRoleParametersDoNotOverrideExplicitInputParameters(): void
{
$factory = new Factory();
$factory->setDefaultParameters($defaultParams = array(
'timeout' => 20,
'password' => 'pwd.default.norole',
'role.master' => [
'password' => 'pwd.role.master',
'timeout' => 10,
],
'role.slave' => [
'password' => 'pwd.role.slave',
'timeout' => 5,
],
'role.sentinel' => [
'password' => 'pwd.role.sentinel',
'timeout' => 1,
],
));
// NO ROLE
$connectionNoRole = $factory->create($inputParamsNoRole = array(
'password' => 'pwd.local.norole',
'timeout' => 30,
));
$parameters = $connectionNoRole->getParameters();
$this->assertEquals('pwd.local.norole', $parameters->password);
$this->assertEquals(30, $parameters->timeout);
// ROLE MASTER
$connectionMasterRole = $factory->create($inputParamsMasterRole = array(
'role' => 'master',
'password' => 'pwd.local.master',
'timeout' => 30,
));
$parameters = $connectionMasterRole->getParameters();
$this->assertEquals('pwd.local.master', $parameters->password);
$this->assertEquals(30, $parameters->timeout);
// ROLE SLAVE
$connectionSlaveRole = $factory->create($inputParamsSlaveRole = array(
'role' => 'slave',
'password' => 'pwd.local.slave',
'timeout' => 30,
));
$parameters = $connectionSlaveRole->getParameters();
$this->assertEquals('pwd.local.slave', $parameters->password);
$this->assertEquals(30, $parameters->timeout);
// ROLE SENTINEL
$connectionSentinelRole = $factory->create($inputParamsSentinelRole = array(
'role' => 'slave',
'password' => 'pwd.local.sentinel',
'timeout' => 30,
));
$parameters = $connectionSentinelRole->getParameters();
$this->assertEquals('pwd.local.sentinel', $parameters->password);
$this->assertEquals(30, $parameters->timeout);
}
/**
* @group disconnected
*/
public function testCreateConnectionWithDefaultRoleParametersOverridesDefaultGlobalParameters(): void
{
$factory = new Factory();
$factory->setDefaultParameters($defaultParams = array(
'timeout' => 20,
'password' => 'pwd.default.norole',
'role.master' => [
'password' => 'pwd.role.master',
'timeout' => 10,
],
'role.slave' => [
'password' => 'pwd.role.slave',
'timeout' => 5,
],
'role.sentinel' => [
'password' => 'pwd.role.sentinel',
'timeout' => 1,
],
));
// NO ROLE
$connectionNoRole = $factory->create($inputParamsNoRole = array(
// EMPTY
));
$parameters = $connectionNoRole->getParameters();
$this->assertEquals('pwd.default.norole', $parameters->password);
$this->assertEquals(20, $parameters->timeout);
// ROLE MASTER
$connectionMasterRole = $factory->create($inputParamsMasterRole = array(
'role' => 'master',
));
$parameters = $connectionMasterRole->getParameters();
$this->assertEquals('pwd.role.master', $parameters->password);
$this->assertEquals(10, $parameters->timeout);
// ROLE SLAVE
$connectionSlaveRole = $factory->create($inputParamsSlaveRole = array(
'role' => 'slave',
));
$parameters = $connectionSlaveRole->getParameters();
$this->assertEquals('pwd.role.slave', $parameters->password);
$this->assertEquals(5, $parameters->timeout);
// ROLE SENTINEL
$connectionSentinelRole = $factory->create($inputParamsSentinelRole = array(
'role' => 'sentinel',
));
$parameters = $connectionSentinelRole->getParameters();
$this->assertEquals('pwd.role.sentinel', $parameters->password);
$this->assertEquals(1, $parameters->timeout);
}
/**
* @group disconnected
*/
@@ -41,7 +41,7 @@ class SentinelReplicationTest extends PredisTestCase
public function testParametersForSentinelConnectionShouldUsePasswordForAuthentication(): void
{
$replication = $this->getReplicationConnection('svc', array(
'tcp://127.0.0.1:5381?password=secret',
'tcp://127.0.0.1:5381?alias=sentinel1&password=secret',
));
$parameters = $replication->getSentinelConnection()->getParameters()->toArray();
@@ -122,9 +122,9 @@ class SentinelReplicationTest extends PredisTestCase
*/
public function testMethodGetSentinelConnectionReturnsFirstAvailableSentinel(): void
{
$sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
$sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel');
$sentinel3 = $this->getMockSentinelConnection('tcp://127.0.0.1:5383?role=sentinel');
$sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel&alias=sentinel1');
$sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel&alias=sentinel2');
$sentinel3 = $this->getMockSentinelConnection('tcp://127.0.0.1:5383?role=sentinel&alias=sentinel3');
$replication = $this->getReplicationConnection('svc', array($sentinel1, $sentinel2, $sentinel3));
@@ -305,16 +305,15 @@ class SentinelReplicationTest extends PredisTestCase
// TODO: sorry for the smell...
$reflection = new \ReflectionProperty($replication, 'sentinels');
$reflection->setAccessible(true);
$retrievedSentinels = $reflection->getValue($replication);
$expectedSentinels = array(
array('scheme' => 'tcp', 'host' => '127.0.0.1', 'port' => '5381', 'role' => 'sentinel'),
array('host' => '127.0.0.1', 'port' => '5382', 'role' => 'sentinel'),
array('host' => '127.0.0.1', 'port' => '5383', 'role' => 'sentinel'),
$expected = array(
array('host' => '127.0.0.1', 'port' => '5381'),
array('host' => '127.0.0.1', 'port' => '5382'),
array('host' => '127.0.0.1', 'port' => '5383'),
);
$this->assertSame($sentinel1, $replication->getSentinelConnection());
$this->assertEquals($expectedSentinels, $retrievedSentinels);
$this->assertSame($expected, array_intersect_key($expected, $reflection->getValue($replication)));
}
/**
@@ -322,7 +321,7 @@ class SentinelReplicationTest extends PredisTestCase
*/
public function testMethodUpdateSentinelsRemovesCurrentSentinelAndRetriesNextOneOnFailure(): void
{
$sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
$sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel&alias=sentinel1');
$sentinel1
->expects($this->once())
->method('executeCommand')
@@ -333,7 +332,7 @@ class SentinelReplicationTest extends PredisTestCase
new Connection\ConnectionException($sentinel1, 'Unknown connection error [127.0.0.1:5381]')
);
$sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel');
$sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel&alias=sentinel2');
$sentinel2
->expects($this->once())
->method('executeCommand')
@@ -358,15 +357,14 @@ class SentinelReplicationTest extends PredisTestCase
// TODO: sorry for the smell...
$reflection = new \ReflectionProperty($replication, 'sentinels');
$reflection->setAccessible(true);
$retrievedSentinels = $reflection->getValue($replication);
$expectedSentinels = array(
array('scheme' => 'tcp', 'host' => '127.0.0.1', 'port' => '5382', 'role' => 'sentinel'),
array('host' => '127.0.0.1', 'port' => '5383', 'role' => 'sentinel'),
$expected = array(
array('host' => '127.0.0.1', 'port' => '5382'),
array('host' => '127.0.0.1', 'port' => '5383'),
);
$this->assertSame($sentinel2, $replication->getSentinelConnection());
$this->assertEquals($expectedSentinels, $retrievedSentinels);
$this->assertSame($expected, array_intersect_key($expected, $reflection->getValue($replication)));
}
/**
@@ -397,7 +395,7 @@ class SentinelReplicationTest extends PredisTestCase
*/
public function testMethodQuerySentinelFetchesMasterNodeSlaveNodesAndSentinelNodes(): void
{
$sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel');
$sentinel1 = $this->getMockSentinelConnection('tcp://127.0.0.1:5381?role=sentinel&alias=sentinel1');
$sentinel1
->expects($this->exactly(3))
->method('executeCommand')
@@ -444,7 +442,7 @@ class SentinelReplicationTest extends PredisTestCase
)
);
$sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel');
$sentinel2 = $this->getMockSentinelConnection('tcp://127.0.0.1:5382?role=sentinel&alias=sentinel2');
$master = $this->getMockConnection('tcp://127.0.0.1:6381?role=master');
$slave1 = $this->getMockConnection('tcp://127.0.0.1:6382?role=slave');
@@ -456,15 +454,14 @@ class SentinelReplicationTest extends PredisTestCase
// TODO: sorry for the smell...
$reflection = new \ReflectionProperty($replication, 'sentinels');
$reflection->setAccessible(true);
$retrievedSentinels = $reflection->getValue($replication);
$expectedSentinels = array(
array('scheme' => 'tcp', 'host' => '127.0.0.1', 'port' => '5381', 'role' => 'sentinel'),
array('host' => '127.0.0.1', 'port' => '5382', 'role' => 'sentinel'),
$sentinels = array(
array('host' => '127.0.0.1', 'port' => '5381'),
array('host' => '127.0.0.1', 'port' => '5382'),
);
$this->assertSame($sentinel1, $replication->getSentinelConnection());
$this->assertEquals($expectedSentinels, $retrievedSentinels);
$this->assertSame($sentinels, array_intersect_key($sentinels, $reflection->getValue($replication)));
$master = $replication->getMaster();
$slaves = $replication->getSlaves();
@@ -1450,6 +1447,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 ------------------------------------------------ //
// ******************************************************************** //
+2 -2
View File
@@ -46,9 +46,9 @@ class ConsumerTest extends PredisTestCase
public function testMonitorConsumerDoesNotWorkOnClusters(): void
{
$this->expectException('Predis\NotSupportedException');
$this->expectExceptionMessage('Cannot initialize a monitor consumer over aggregate connections');
$this->expectExceptionMessage('Cannot initialize a monitor consumer over cluster connections');
$cluster = $this->getMockBuilder('Predis\Connection\AggregateConnectionInterface')->getMock();
$cluster = $this->getMockBuilder('Predis\Connection\Cluster\ClusterInterface')->getMock();
$client = new Client($cluster);
new MonitorConsumer($client);
+1 -1
View File
@@ -45,7 +45,7 @@ class ConsumerTest extends PredisTestCase
public function testPubSubConsumerDoesNotWorkOnClusters(): void
{
$this->expectException('Predis\NotSupportedException');
$this->expectExceptionMessage('Cannot initialize a PUB/SUB consumer over aggregate connections');
$this->expectExceptionMessage('Cannot initialize a PUB/SUB consumer over cluster connections');
$cluster = $this->getMockBuilder('Predis\Connection\Cluster\ClusterInterface')->getMock();
$client = new Client($cluster);
+1 -1
View File
@@ -749,7 +749,7 @@ class MultiExecTest extends PredisTestCase
$tx->set('hoge', 'piyo');
});
$this->assertSame(1, count($responses));
$this->assertCount(1, $responses);
$this->assertSame(0, $client->exists('foo'));
$this->assertSame(1, $client->exists('hoge'));
}