mirror of
https://github.com/predis/predis.git
synced 2026-08-23 13:15:59 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4123fcd85d | |||
| 647cd4c502 | |||
| cab266f359 | |||
| cea6354b55 | |||
| 7155dfdfb4 | |||
| dea768044c | |||
| 70d1c116b6 | |||
| 001fb45a32 | |||
| 8dd7f40d21 | |||
| d5c650d51a | |||
| 7bb85cae1c | |||
| 43a6ae181e | |||
| f538442ad9 | |||
| c2d0737e98 | |||
| d6bd47ca47 | |||
| 0235ba1a30 | |||
| 287b4c76ce | |||
| ba6cfa7887 | |||
| adb4d82ea8 | |||
| 8dac9c097e | |||
| 0df05ae008 | |||
| c85137924c | |||
| b1761c55c5 | |||
| 5b8c2e21f6 | |||
| f6e97d4b94 | |||
| 18f5cea8e6 | |||
| 727072c086 | |||
| 5fde716098 |
+31
-1
@@ -1,7 +1,37 @@
|
||||
v0.8.7 (2014-08-01)
|
||||
================================================================================
|
||||
|
||||
- Added `3.0` in the server profiles aliases list for Redis 3.0. `2.8` is still
|
||||
the default server profile and `dev` still targets Redis 3.0.
|
||||
|
||||
- Added `COMMAND` to the server profile for Redis 2.8.
|
||||
|
||||
- Switched internally to the `CLUSTER SLOTS` command instead of `CLUSTER NODES`
|
||||
to fetch the updated slots map from redis-cluster. This change requires users
|
||||
to upgrade Redis nodes to >= 3.0.0b7.
|
||||
|
||||
- The updated slots map is now fetched automatically from redis-cluster upon the
|
||||
first `-MOVED` response by default. This change makes it possible to feed the
|
||||
client constructor with only a few nodes of the actual cluster composition,
|
||||
without needing a more complex configuration.
|
||||
|
||||
- Implemented support for `PING` in PUB/SUB loop for Redis >= 3.0.0b8.
|
||||
|
||||
- The default client-side sharding strategy and the one for redis-cluster now
|
||||
share the same implementations as they follow the same rules. One difference,
|
||||
aside from the different hashing function used to calculate distribution, is
|
||||
in how empty hash tags like {} are treated by redis-cluster.
|
||||
|
||||
- __FIX__: the patch applied to fix #180 introduced a regression affecting read/
|
||||
write timeouts in `Predis\Connection\PhpiredisStreamConnection`. Unfortunately
|
||||
the only possible solution requires PHP 5.4+. On PHP 5.3, read/write timeouts
|
||||
will be ignored from now on.
|
||||
|
||||
|
||||
v0.8.6 (2014-07-15)
|
||||
================================================================================
|
||||
|
||||
- Redis 2.8 is not the default server profile as there are no changes that would
|
||||
- Redis 2.8 is now the default server profile as there are no changes that would
|
||||
break compatibility with previous releases.
|
||||
|
||||
- Added `PFADD`, `PFCOUNT`, `PFMERGE` to the server profile for Redis 2.8 for
|
||||
|
||||
@@ -10,9 +10,9 @@ By default Predis does not require any additional C extension, but it can be opt
|
||||
Redis protocol. An asynchronous implementation of the client, albeit experimental, is also available
|
||||
through [Predis\Async](https://github.com/nrk/predis-async).
|
||||
|
||||
Predis can be used with [HHVM](http://www.hhvm.com) >= 2.4.0, but there are no guarantees you will
|
||||
not run into unexpected issues (especially with the JIT compiler enabled via `Eval.Jit = true`) due
|
||||
to HHVM being still under heavy development, thus unstable and not yet 100% compatible with PHP.
|
||||
Predis can be used with [HHVM](http://www.hhvm.com) >= 2.3.0, but there are no guarantees you will
|
||||
not run into unexpected issues (especially when the JIT compiler is enabled via `Eval.Jit = true`)
|
||||
due to HHVM being still under heavy development, thus unstable and not yet 100% compatible with PHP.
|
||||
|
||||
More details about the project can be found in our [frequently asked questions](FAQ.md) section or
|
||||
on the online [wiki](https://github.com/nrk/predis/wiki).
|
||||
@@ -20,12 +20,12 @@ on the online [wiki](https://github.com/nrk/predis/wiki).
|
||||
|
||||
## Main features ##
|
||||
|
||||
- Wide range of Redis versions supported (from __1.2__ to __2.8__ and unstable) using profiles.
|
||||
- Clustering via client-side sharding using consistent hashing or custom distributors.
|
||||
- Smart support for [redis-cluster](http://redis.io/topics/cluster-spec) (Redis >= 3.0).
|
||||
- Wide range of Redis versions supported (from __1.2__ to __3.0__ and __unstable__) using profiles.
|
||||
- Cluster of nodes via client-side sharding using consistent hashing or custom distributors.
|
||||
- Smart support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
|
||||
- Support for master-slave replication configurations (write on master, read from slaves).
|
||||
- Transparent key prefixing for all Redis commands.
|
||||
- Command pipelining (works on both single and aggregate connections).
|
||||
- Transparent key prefixing for all known Redis commands.
|
||||
- Command pipelining (works on both single nodes and aggregate connections).
|
||||
- Abstraction for Redis transactions (Redis >= 2.0) supporting CAS operations (Redis >= 2.2).
|
||||
- Abstraction for Lua scripting (Redis >= 2.6) with automatic switching between `EVALSHA` or `EVAL`.
|
||||
- Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators.
|
||||
@@ -49,8 +49,8 @@ Ultimately, archives of each release are [available on GitHub](https://github.co
|
||||
Predis relies on the autoloading features of PHP to load its files when needed and complies with the
|
||||
[PSR-0 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) which makes
|
||||
it compatible with most PHP frameworks. Autoloading is handled automatically when dependencies are
|
||||
managed using Composer, but you can also leverage its own autoloader if you are going to use it in a
|
||||
project or script without any PSR-0 compliant autoloading facility:
|
||||
managed through Composer, but it is also possible to leverage its own autoloader in projects or
|
||||
scripts not having any autoload facility:
|
||||
|
||||
```php
|
||||
// Prepend a base path if Predis is not available in your "include_path".
|
||||
@@ -62,14 +62,14 @@ Predis\Autoloader::register();
|
||||
It is possible to easily create a [phar](http://www.php.net/manual/en/intro.phar.php) archive from
|
||||
the repository just by launching `bin/create-phar`. The generated phar contains a stub defining an
|
||||
autoloader function for Predis, so you just need to require the phar to start using the library.
|
||||
Alternatively, it is also possible to generate one single PHP file that holds every class like older
|
||||
versions of Predis by launching `bin/create-single-file`, but this practice __is not__ encouraged.
|
||||
Alternatively it is possible to generate one single PHP file holding every class like older versions
|
||||
of Predis by launching `bin/create-single-file`, but this practice __is not__ encouraged.
|
||||
|
||||
|
||||
### Connecting to Redis ###
|
||||
|
||||
When not specifying any connection parameter to create a new client, Predis assumes `127.0.0.1` and
|
||||
`6379` as the default host and port and uses a connection timeout of 5 seconds:
|
||||
`6379` as the default host and port with a connection timeout of 5 seconds:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client();
|
||||
@@ -93,9 +93,9 @@ $client = new Predis\Client([
|
||||
$client = new Predis\Client('tcp://10.0.0.1:6379');
|
||||
```
|
||||
|
||||
When an array of connections parameters is provided, Predis automatically works in clustering mode
|
||||
using client-side sharding. Both named arrays and URI strings can be mixed for providing each node
|
||||
configuration:
|
||||
When an array of connection parameters is provided, Predis automatically works in cluster mode using
|
||||
client-side sharding. Both named arrays and URI strings can be mixed when providing configurations
|
||||
for each node:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client([
|
||||
@@ -105,12 +105,12 @@ $client = new Predis\Client([
|
||||
```
|
||||
|
||||
The actual list of supported connection parameters can vary depending on each connection backend so
|
||||
it is recommended to refer to their specific documentation for details.
|
||||
it is recommended to refer to their specific documentation or implementation for details.
|
||||
|
||||
|
||||
### Client configuration ###
|
||||
|
||||
Various aspects of the client can be easily configured by passing options to the second argument of
|
||||
Various aspects of the client can be configured simply by passing options to the second argument of
|
||||
`Predis\Client::__construct()`. Options are managed using a mini DI-alike container and their values
|
||||
are usually lazily initialized only when needed. Predis by default supports the following options:
|
||||
|
||||
@@ -118,7 +118,7 @@ are usually lazily initialized only when needed. Predis by default supports the
|
||||
- `prefix`: a prefix string that is automatically applied to keys found in commands.
|
||||
- `exceptions`: whether the client should throw or return responses upon Redis errors.
|
||||
- `connections`: connection backends or a connection factory to be used by the client.
|
||||
- `cluster`: which backend to use for clustering (predis, redis or custom configuration).
|
||||
- `cluster`: which backend to use for clustering (`predis`, `redis` or custom configuration).
|
||||
- `replication`: which backend to use for replication (predis or custom configuration).
|
||||
|
||||
Users can provide custom option values, they are stored in the options container and can be accessed
|
||||
@@ -127,11 +127,71 @@ later through the library.
|
||||
|
||||
### Aggregate connections ###
|
||||
|
||||
Predis is able to aggregate multiple connections which is the base for clustering and replication.
|
||||
By default the client implements clustering using either client-side sharding (default) or a Redis
|
||||
backed solution using [redis-cluster](http://redis.io/topics/cluster-tutorial). As for replication,
|
||||
Predis can handle single-master and multiple-slaves setups by executing read operations on slaves
|
||||
and switching to the master for write operations. The replication behaviour is fully configurable.
|
||||
Predis is able to aggregate multiple connections which is the base for cluster and replication. By
|
||||
default the client implements a cluster of nodes using either client-side sharding (default) or a
|
||||
Redis-backed solution using [redis-cluster](http://redis.io/topics/cluster-tutorial).
|
||||
As for replication, Predis can handle a single-master and multiple-slaves setup by executing read
|
||||
operations on slaves and switching to the master for write operations. The replication behavior is
|
||||
fully configurable.
|
||||
|
||||
#### Replication ####
|
||||
|
||||
The client can be configured to work in a master / slave replication setup by executing read-only
|
||||
commands on slave nodes and automatically switch to the master node as soon as a command performing
|
||||
a write operation is executed. This is the basic configuration needed to work with replication:
|
||||
|
||||
```php
|
||||
// Parameters require one master node specifically marked with `alias=master`.
|
||||
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2?alias=slave-01'];
|
||||
$options = ['replication' => true];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
```
|
||||
|
||||
While Predis is able to distinguish commands performing write and read-only operations, `EVAL` and
|
||||
`EVALSHA` represent a corner case in which the client switches to the master node because it is not
|
||||
able to tell when a Lua script is safe to be executed on slaves. While this is the default behavior,
|
||||
when certain Lua scripts do not perform write operations it is possible to provide an hint to tell
|
||||
the client to stick with slaves for their execution.
|
||||
|
||||
```php
|
||||
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2?alias=slave-01'];
|
||||
$options = ['replication' => function () {
|
||||
// Set scripts that won't trigger a switch from a slave to the master node.
|
||||
$strategy = new Predis\Replication\ReplicationStrategy();
|
||||
$strategy->setScriptReadOnly($LUA_SCRIPT);
|
||||
|
||||
return new Predis\Connection\Aggregate\MasterSlaveReplication($strategy);
|
||||
}];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
$client->eval($LUA_SCRIPT, 0); // Sticks to slave using `eval`...
|
||||
$client->evalsha(sha1($LUA_SCRIPT), 0); // ... and `evalsha`, too.
|
||||
```
|
||||
|
||||
The `examples` directory contains two complete scripts showing how replication can be configured for
|
||||
[simple](examples/MasterSlaveReplication.php) or [complex](examples/MasterSlaveReplicationComplex.php)
|
||||
scenarios.
|
||||
|
||||
#### Cluster ####
|
||||
|
||||
Simply passing an array of connection parameters to the client constructor configures Predis to work
|
||||
in cluster mode using client-side sharding. If you, on the other hand, want to leverage Redis >= 3.0
|
||||
nodes coordinated by redis-cluster, then the client must be initialized like this:
|
||||
|
||||
```php
|
||||
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2'];
|
||||
$options = ['cluster' => 'redis'];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
```
|
||||
|
||||
When using redis-cluster, it is not necessary to pass all of the nodes that compose your cluster but
|
||||
you can simply specify only a few nodes: Predis will automatically fetch the full and updated slots
|
||||
map directly from Redis by contacting one of the servers.
|
||||
|
||||
__NOTE__: our support for redis-cluster does not currently consider master / slave replication but
|
||||
this feature will be added in a future release of this library.
|
||||
|
||||
|
||||
### Command pipelines ###
|
||||
@@ -179,43 +239,7 @@ __NOTE__: the method `transaction()` is available since `v0.8.5`, older versions
|
||||
for the same purpose but it has been deprecated and will be removed in the next major release.
|
||||
|
||||
|
||||
### Customizable connection backends ###
|
||||
|
||||
Predis can use different connection backends to connect to Redis. Two of them leverage a third party
|
||||
extension such as [phpiredis](https://github.com/nrk/phpiredis) resulting in major performance gains
|
||||
especially when dealing with big multibulk responses. While one is based on PHP streams, the other
|
||||
is based on socket resources provided by `ext-socket`. Both support TCP/IP or UNIX domain sockets:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client('tcp://127.0.0.1', [
|
||||
'connections' => [
|
||||
'tcp' => 'Predis\Connection\PhpiredisStreamConnection', // PHP streams
|
||||
'unix' => 'Predis\Connection\PhpiredisConnection', // ext-socket
|
||||
],
|
||||
]);
|
||||
```
|
||||
|
||||
Developers can create their own connection classes to add support for new network backends, extend
|
||||
existing ones or provide completely different implementations. Connection classes must implement
|
||||
`Predis\Connection\SingleConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
|
||||
|
||||
```php
|
||||
class MyConnectionClass implements Predis\Connection\SingleConnectionInterface
|
||||
{
|
||||
// Implementation goes here...
|
||||
}
|
||||
|
||||
// Use MyConnectionClass to handle connections for the `tcp` scheme:
|
||||
$client = new Predis\Client('tcp://127.0.0.1', [
|
||||
'connections' => ['tcp' => 'MyConnectionClass'],
|
||||
]);
|
||||
```
|
||||
|
||||
For a more in-depth insight on how to create new connection backends you can refer to the actual
|
||||
implementation of the standard connection classes available in the `Predis\Connection` namespace.
|
||||
|
||||
|
||||
### Adding support for new commands ###
|
||||
### Adding new Redis commands ###
|
||||
|
||||
While we try to update Predis to stay up to date with all the commands available in Redis, you might
|
||||
prefer to stick with an older version of the library or provide a different way to filter arguments
|
||||
@@ -240,15 +264,16 @@ $response = $client->newcmd();
|
||||
```
|
||||
|
||||
|
||||
### Scriptable commands ###
|
||||
### Script commands ###
|
||||
|
||||
A scriptable command is just an abstraction for [Lua scripting](http://redis.io/commands/eval) that
|
||||
aims to simplify the usage of scripting with Redis >= 2.6. Scriptable commands can be registered in
|
||||
the server profile used by the client and are accessible as if they were plain Redis commands, but
|
||||
they define a Lua script that gets transmitted to Redis for remote execution. Internally, scriptable
|
||||
commands use by default [EVALSHA](http://redis.io/commands/evalsha) and identify a Lua script by its
|
||||
SHA1 hash to save bandwidth but [EVAL](http://redis.io/commands/eval) is automatically preferred as
|
||||
a fall back when needed:
|
||||
While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using
|
||||
[`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha), Predis
|
||||
offers script commands as an higher level abstraction aiming to make things simple. Script commands
|
||||
can be registered in the server profile used by the client and are accessible as if they were plain
|
||||
Redis commands, but they define Lua scripts that get transmitted to the server for remote execution.
|
||||
Internally they use [`EVALSHA`](http://redis.io/commands/evalsha) by default and identify a script
|
||||
by its SHA1 hash to save bandwidth, but [`EVAL`](http://redis.io/commands/eval) is used as a fall
|
||||
back when needed:
|
||||
|
||||
```php
|
||||
// Define a new scriptable command by extending Predis\Command\ScriptedCommand:
|
||||
@@ -270,7 +295,7 @@ LUA;
|
||||
}
|
||||
}
|
||||
|
||||
// Inject your scriptable command in the current profile:
|
||||
// Inject your script command in the current profile:
|
||||
$client = new Predis\Client();
|
||||
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');
|
||||
|
||||
@@ -278,6 +303,42 @@ $response = $client->lpushrand('random_values', $seed = mt_rand());
|
||||
```
|
||||
|
||||
|
||||
### Customizable connection backends ###
|
||||
|
||||
Predis can use different connection backends to connect to Redis. Two of them leverage a third party
|
||||
extension such as [phpiredis](https://github.com/nrk/phpiredis) resulting in major performance gains
|
||||
especially when dealing with big multibulk responses. While one is based on PHP streams, the other
|
||||
is based on socket resources provided by `ext-socket`. Both support TCP/IP or UNIX domain sockets:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client('tcp://127.0.0.1', [
|
||||
'connections' => [
|
||||
'tcp' => 'Predis\Connection\PhpiredisStreamConnection', // PHP streams
|
||||
'unix' => 'Predis\Connection\PhpiredisConnection', // ext-socket
|
||||
],
|
||||
]);
|
||||
```
|
||||
|
||||
Developers can create their own connection classes to support whole new network backends, extend
|
||||
existing ones or provide completely different implementations. Connection classes must implement
|
||||
`Predis\Connection\SingleConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
|
||||
|
||||
```php
|
||||
class MyConnectionClass implements Predis\Connection\SingleConnectionInterface
|
||||
{
|
||||
// Implementation goes here...
|
||||
}
|
||||
|
||||
// Use MyConnectionClass to handle connections for the `tcp` scheme:
|
||||
$client = new Predis\Client('tcp://127.0.0.1', [
|
||||
'connections' => ['tcp' => 'MyConnectionClass'],
|
||||
]);
|
||||
```
|
||||
|
||||
For a more in-depth insight on how to create new connection backends you can refer to the actual
|
||||
implementation of the standard connection classes available in the `Predis\Connection` namespace.
|
||||
|
||||
|
||||
## Development ##
|
||||
|
||||
|
||||
@@ -296,7 +357,7 @@ in production environments or containing data you are interested in!
|
||||
|
||||
Predis has a comprehensive test suite covering every aspect of the library. This test suite performs
|
||||
integration tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct
|
||||
behaviour of the implementation of each command and automatically skips commands not defined in the
|
||||
behavior of the implementation of each command and automatically skips commands not defined in the
|
||||
specified Redis profile. If you do not have Redis up and running, integration tests can be disabled.
|
||||
By default the test suite is configured to execute integration tests using the profile for Redis 2.8
|
||||
(which is the current stable version of Redis) but can optionally target a Redis instance built from
|
||||
|
||||
@@ -84,6 +84,11 @@ for ($i = 0; $i < 100; $i++) {
|
||||
$server1 = $client->getClientFor('first')->info();
|
||||
$server2 = $client->getClientFor('second')->info();
|
||||
|
||||
if (isset($server1['Keyspace'], $server2['Keyspace'])) {
|
||||
$server1 = $server1['Keyspace'];
|
||||
$server2 = $server2['Keyspace'];
|
||||
}
|
||||
|
||||
printf("Server '%s' has %d keys while server '%s' has %d keys.\n",
|
||||
'first', $server1['db15']['keys'], 'second', $server2['db15']['keys']
|
||||
);
|
||||
|
||||
@@ -20,7 +20,6 @@ require 'SharedConfigurations.php';
|
||||
|
||||
use Predis\Command\ScriptedCommand;
|
||||
use Predis\Connection\MasterSlaveReplication;
|
||||
use Predis\Profile\ServerProfile;
|
||||
use Predis\Replication\ReplicationStrategy;
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
@@ -28,7 +27,8 @@ use Predis\Replication\ReplicationStrategy;
|
||||
// Define a new scripted command that returns all the fields
|
||||
// of a variable number of hashes with a single roundtrip.
|
||||
|
||||
class HashMultipleGetAll extends ScriptedCommand {
|
||||
class HashMultipleGetAll extends ScriptedCommand
|
||||
{
|
||||
const BODY = <<<EOS
|
||||
local hashes = {}
|
||||
for _, key in pairs(KEYS) do
|
||||
@@ -38,7 +38,8 @@ end
|
||||
return hashes
|
||||
EOS;
|
||||
|
||||
public function getScript() {
|
||||
public function getScript()
|
||||
{
|
||||
return self::BODY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ use Predis\Transaction\MultiExecContext;
|
||||
*/
|
||||
class Client implements ClientInterface
|
||||
{
|
||||
const VERSION = '0.8.6';
|
||||
const VERSION = '0.8.7';
|
||||
|
||||
private $options;
|
||||
private $profile;
|
||||
|
||||
@@ -23,8 +23,8 @@ use Predis\Command\ScriptedCommand;
|
||||
*/
|
||||
class PredisClusterHashStrategy implements CommandHashStrategyInterface
|
||||
{
|
||||
private $commands;
|
||||
private $hashGenerator;
|
||||
protected $commands;
|
||||
protected $hashGenerator;
|
||||
|
||||
/**
|
||||
* @param HashGeneratorInterface $hashGenerator Hash generator instance.
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
namespace Predis\Cluster;
|
||||
|
||||
use Predis\Cluster\Hash\CRC16HashGenerator;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\ScriptedCommand;
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
|
||||
/**
|
||||
* Default class used by Predis to calculate hashes out of keys of
|
||||
@@ -21,286 +20,14 @@ use Predis\Command\ScriptedCommand;
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class RedisClusterHashStrategy implements CommandHashStrategyInterface
|
||||
class RedisClusterHashStrategy extends PredisClusterHashStrategy
|
||||
{
|
||||
private $commands;
|
||||
private $hashGenerator;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct(HashGeneratorInterface $hashGenerator = null)
|
||||
{
|
||||
$this->commands = $this->getDefaultCommands();
|
||||
$this->hashGenerator = new CRC16HashGenerator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default map of supported commands with their handlers.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaultCommands()
|
||||
{
|
||||
$getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument');
|
||||
$getKeyFromAllArguments = array($this, 'getKeyFromAllArguments');
|
||||
|
||||
return array(
|
||||
/* commands operating on the key space */
|
||||
'EXISTS' => $getKeyFromFirstArgument,
|
||||
'DEL' => $getKeyFromAllArguments,
|
||||
'TYPE' => $getKeyFromFirstArgument,
|
||||
'EXPIRE' => $getKeyFromFirstArgument,
|
||||
'EXPIREAT' => $getKeyFromFirstArgument,
|
||||
'PERSIST' => $getKeyFromFirstArgument,
|
||||
'PEXPIRE' => $getKeyFromFirstArgument,
|
||||
'PEXPIREAT' => $getKeyFromFirstArgument,
|
||||
'TTL' => $getKeyFromFirstArgument,
|
||||
'PTTL' => $getKeyFromFirstArgument,
|
||||
'SORT' => $getKeyFromFirstArgument, // TODO
|
||||
|
||||
/* commands operating on string values */
|
||||
'APPEND' => $getKeyFromFirstArgument,
|
||||
'DECR' => $getKeyFromFirstArgument,
|
||||
'DECRBY' => $getKeyFromFirstArgument,
|
||||
'GET' => $getKeyFromFirstArgument,
|
||||
'GETBIT' => $getKeyFromFirstArgument,
|
||||
'MGET' => $getKeyFromAllArguments,
|
||||
'SET' => $getKeyFromFirstArgument,
|
||||
'GETRANGE' => $getKeyFromFirstArgument,
|
||||
'GETSET' => $getKeyFromFirstArgument,
|
||||
'INCR' => $getKeyFromFirstArgument,
|
||||
'INCRBY' => $getKeyFromFirstArgument,
|
||||
'INCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'SETBIT' => $getKeyFromFirstArgument,
|
||||
'SETEX' => $getKeyFromFirstArgument,
|
||||
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
|
||||
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
|
||||
'SETNX' => $getKeyFromFirstArgument,
|
||||
'SETRANGE' => $getKeyFromFirstArgument,
|
||||
'STRLEN' => $getKeyFromFirstArgument,
|
||||
'SUBSTR' => $getKeyFromFirstArgument,
|
||||
'BITCOUNT' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on lists */
|
||||
'LINSERT' => $getKeyFromFirstArgument,
|
||||
'LINDEX' => $getKeyFromFirstArgument,
|
||||
'LLEN' => $getKeyFromFirstArgument,
|
||||
'LPOP' => $getKeyFromFirstArgument,
|
||||
'RPOP' => $getKeyFromFirstArgument,
|
||||
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
|
||||
'LPUSH' => $getKeyFromFirstArgument,
|
||||
'LPUSHX' => $getKeyFromFirstArgument,
|
||||
'RPUSH' => $getKeyFromFirstArgument,
|
||||
'RPUSHX' => $getKeyFromFirstArgument,
|
||||
'LRANGE' => $getKeyFromFirstArgument,
|
||||
'LREM' => $getKeyFromFirstArgument,
|
||||
'LSET' => $getKeyFromFirstArgument,
|
||||
'LTRIM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sets */
|
||||
'SADD' => $getKeyFromFirstArgument,
|
||||
'SCARD' => $getKeyFromFirstArgument,
|
||||
'SISMEMBER' => $getKeyFromFirstArgument,
|
||||
'SMEMBERS' => $getKeyFromFirstArgument,
|
||||
'SSCAN' => $getKeyFromFirstArgument,
|
||||
'SPOP' => $getKeyFromFirstArgument,
|
||||
'SRANDMEMBER' => $getKeyFromFirstArgument,
|
||||
'SREM' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'ZADD' => $getKeyFromFirstArgument,
|
||||
'ZCARD' => $getKeyFromFirstArgument,
|
||||
'ZCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZINCRBY' => $getKeyFromFirstArgument,
|
||||
'ZRANGE' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZRANK' => $getKeyFromFirstArgument,
|
||||
'ZREM' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
|
||||
'ZREVRANK' => $getKeyFromFirstArgument,
|
||||
'ZSCORE' => $getKeyFromFirstArgument,
|
||||
'ZSCAN' => $getKeyFromFirstArgument,
|
||||
'ZLEXCOUNT' => $getKeyFromFirstArgument,
|
||||
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on hashes */
|
||||
'HDEL' => $getKeyFromFirstArgument,
|
||||
'HEXISTS' => $getKeyFromFirstArgument,
|
||||
'HGET' => $getKeyFromFirstArgument,
|
||||
'HGETALL' => $getKeyFromFirstArgument,
|
||||
'HMGET' => $getKeyFromFirstArgument,
|
||||
'HMSET' => $getKeyFromFirstArgument,
|
||||
'HINCRBY' => $getKeyFromFirstArgument,
|
||||
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
|
||||
'HKEYS' => $getKeyFromFirstArgument,
|
||||
'HLEN' => $getKeyFromFirstArgument,
|
||||
'HSET' => $getKeyFromFirstArgument,
|
||||
'HSETNX' => $getKeyFromFirstArgument,
|
||||
'HVALS' => $getKeyFromFirstArgument,
|
||||
'HSCAN' => $getKeyFromFirstArgument,
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'PFADD' => $getKeyFromFirstArgument,
|
||||
'PFCOUNT' => $getKeyFromAllArguments,
|
||||
'PFMERGE' => $getKeyFromAllArguments,
|
||||
|
||||
/* scripting */
|
||||
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
|
||||
'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of IDs for the supported commands.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSupportedCommands()
|
||||
{
|
||||
return array_keys($this->commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an handler for the specified command ID.
|
||||
*
|
||||
* The signature of the callback must have a single parameter
|
||||
* of type Predis\Command\CommandInterface.
|
||||
*
|
||||
* When the callback argument is omitted or NULL, the previously
|
||||
* associated handler for the specified command ID is removed.
|
||||
*
|
||||
* @param string $commandId The ID of the command to be handled.
|
||||
* @param mixed $callback A valid callable object or NULL.
|
||||
*/
|
||||
public function setCommandHandler($commandId, $callback = null)
|
||||
{
|
||||
$commandId = strtoupper($commandId);
|
||||
|
||||
if (!isset($callback)) {
|
||||
unset($this->commands[$commandId]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_callable($callback)) {
|
||||
throw new \InvalidArgumentException("Callback must be a valid callable object or NULL");
|
||||
}
|
||||
|
||||
$this->commands[$commandId] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from the first argument of a command instance.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyFromFirstArgument(CommandInterface $command)
|
||||
{
|
||||
return $command->getArgument(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from a command that can accept multiple keys ensuring
|
||||
* that only one key is actually specified to comply with redis-cluster.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyFromAllArguments(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
if (count($arguments) === 1) {
|
||||
return $arguments[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from a command that can accept multiple keys ensuring
|
||||
* that only one key is actually specified to comply with redis-cluster.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyFromInterleavedArguments(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
if (count($arguments) === 2) {
|
||||
return $arguments[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from BLPOP and BRPOP commands ensuring that only one key
|
||||
* is actually specified to comply with redis-cluster.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyFromBlockingListCommands(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
if (count($arguments) === 2) {
|
||||
return $arguments[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the key from EVAL and EVALSHA commands.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @return string
|
||||
*/
|
||||
protected function getKeyFromScriptingCommands(CommandInterface $command)
|
||||
{
|
||||
if ($command instanceof ScriptedCommand) {
|
||||
$keys = $command->getKeys();
|
||||
} else {
|
||||
$keys = array_slice($args = $command->getArguments(), 2, $args[1]);
|
||||
}
|
||||
|
||||
if (count($keys) === 1) {
|
||||
return $keys[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHash(CommandInterface $command)
|
||||
{
|
||||
$hash = $command->getHash();
|
||||
|
||||
if (!isset($hash) && isset($this->commands[$cmdID = $command->getId()])) {
|
||||
$key = call_user_func($this->commands[$cmdID], $command);
|
||||
|
||||
if (isset($key)) {
|
||||
$hash = $this->hashGenerator->hash($key);
|
||||
$command->setHash($hash);
|
||||
}
|
||||
}
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getKeyHash($key)
|
||||
{
|
||||
$key = $this->extractKeyTag($key);
|
||||
$hash = $this->hashGenerator->hash($key);
|
||||
|
||||
return $hash;
|
||||
parent::__construct($hashGenerator ?: new CRC16HashGenerator());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,7 @@ class RawCommand implements CommandInterface
|
||||
/**
|
||||
* Creates a new raw command using a variadic method.
|
||||
*
|
||||
* @param string $commandID Redis command ID.
|
||||
* @param string $commandID Redis command ID.
|
||||
* @param string ... Arguments list for the command.
|
||||
* @return CommandInterface
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/command
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ServerCommand extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'COMMAND';
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ class ServerInfo extends AbstractCommand
|
||||
/**
|
||||
* Parses single row of the reply buffer and returns the key-value pair.
|
||||
*
|
||||
* @param string $row Single row of the reply buffer.
|
||||
* @param string $row Single row of the reply buffer.
|
||||
* @return array
|
||||
*/
|
||||
public function parseRow($row)
|
||||
@@ -62,6 +62,7 @@ class ServerInfo extends AbstractCommand
|
||||
} else {
|
||||
$v = $this->parseDatabaseStats($v);
|
||||
}
|
||||
|
||||
return array($k, $v);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,52 @@ class PhpiredisStreamConnection extends StreamConnection
|
||||
return parent::checkParameters($parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function tcpStreamInitializer(ConnectionParametersInterface $parameters)
|
||||
{
|
||||
$uri = "tcp://{$parameters->host}:{$parameters->port}";
|
||||
$flags = STREAM_CLIENT_CONNECT;
|
||||
$socket = null;
|
||||
|
||||
if (isset($parameters->async_connect) && $parameters->async_connect) {
|
||||
$flags |= STREAM_CLIENT_ASYNC_CONNECT;
|
||||
}
|
||||
|
||||
if (isset($parameters->persistent) && $parameters->persistent) {
|
||||
$flags |= STREAM_CLIENT_PERSISTENT;
|
||||
$uri .= strpos($path = $parameters->path, '/') === 0 ? $path : "/$path";
|
||||
}
|
||||
|
||||
$resource = @stream_socket_client($uri, $errno, $errstr, $parameters->timeout, $flags);
|
||||
|
||||
if (!$resource) {
|
||||
$this->onConnectionError(trim($errstr), $errno);
|
||||
}
|
||||
|
||||
if (isset($parameters->read_write_timeout) && function_exists('socket_import_stream')) {
|
||||
$rwtimeout = (float) $parameters->read_write_timeout;
|
||||
$rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1;
|
||||
|
||||
$timeout = array(
|
||||
'sec' => $timeoutSeconds = floor($rwtimeout),
|
||||
'usec' => ($rwtimeout - $timeoutSeconds) * 1000000,
|
||||
);
|
||||
|
||||
$socket = $socket ?: socket_import_stream($resource);
|
||||
@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout);
|
||||
@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout);
|
||||
}
|
||||
|
||||
if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) {
|
||||
$socket = $socket ?: socket_import_stream($resource);
|
||||
socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay);
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the protocol reader resource.
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,7 @@ use Predis\Command\RawCommand;
|
||||
* Abstraction for a Redis-backed cluster of nodes (Redis >= 3.0.0).
|
||||
*
|
||||
* This connection backend offers smart support for redis-cluster by handling
|
||||
* automatic slots map (re)generation upon -MOVE or -ASK responses returned by
|
||||
* automatic slots map (re)generation upon -MOVED or -ASK responses returned by
|
||||
* Redis when redirecting a client to a different node.
|
||||
*
|
||||
* The cluster can be pre-initialized using only a subset of the actual nodes in
|
||||
@@ -40,13 +40,13 @@ use Predis\Command\RawCommand;
|
||||
* It is also possible to ask for the full and updated slots map directly to one
|
||||
* of the nodes and optionally enable such a behaviour upon -MOVED redirections.
|
||||
* Asking for the cluster configuration to Redis is actually done by issuing a
|
||||
* CLUSTER NODES command to a random node in the pool.
|
||||
* CLUSTER SLOTS command to a random node in the pool.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class RedisCluster implements ClusterConnectionInterface, IteratorAggregate, Countable
|
||||
{
|
||||
private $askClusterNodes = false;
|
||||
private $askClusterNodes = true;
|
||||
private $defaultParameters = array();
|
||||
private $pool = array();
|
||||
private $slots = array();
|
||||
@@ -169,36 +169,35 @@ class RedisCluster implements ClusterConnectionInterface, IteratorAggregate, Cou
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the current slots map by fetching the cluster configuration to
|
||||
* one of the nodes by leveraging the CLUSTER NODES command.
|
||||
* Generates an updated slots map fetching the cluster configuration using
|
||||
* the CLUSTER SLOTS command against the specified node or a random one from
|
||||
* the pool.
|
||||
*
|
||||
* @param SingleConnectionInterface $connection Optional connection instance.
|
||||
* @return array
|
||||
*/
|
||||
public function askClusterNodes()
|
||||
public function askClusterNodes(SingleConnectionInterface $connection = null)
|
||||
{
|
||||
if (!$connection = $this->getRandomConnection()) {
|
||||
if (!$connection && !$connection = $this->getRandomConnection()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$cmdCluster = RawCommand::create('CLUSTER', 'NODES');
|
||||
$response = $connection->executeCommand($cmdCluster);
|
||||
$command = RawCommand::create('CLUSTER', 'SLOTS');
|
||||
$response = $connection->executeCommand($command);
|
||||
|
||||
$nodes = explode("\n", $response, -1);
|
||||
$count = count($nodes);
|
||||
foreach ($response as $slots) {
|
||||
// We only support master servers for now, so we ignore subsequent
|
||||
// elements in the $slots array identifying slaves.
|
||||
list($start, $end, $master) = $slots;
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$node = explode(' ', $nodes[$i], 9);
|
||||
|
||||
if (false === strpos($node[2], 'master')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$slots = explode('-', $node[8], 2);
|
||||
|
||||
if ($node[1] === ':0') {
|
||||
$this->setSlots($slots[0], $slots[1], (string) $connection);
|
||||
if ($master[0] === '') {
|
||||
$this->setSlots($start, $end, (string) $connection);
|
||||
} else {
|
||||
$this->setSlots($slots[0], $slots[1], $node[1]);
|
||||
$this->setSlots($start, $end, "{$master[0]}:{$master[1]}");
|
||||
}
|
||||
}
|
||||
|
||||
return $this->slotsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +404,7 @@ class RedisCluster implements ClusterConnectionInterface, IteratorAggregate, Cou
|
||||
}
|
||||
|
||||
if ($this->askClusterNodes) {
|
||||
$this->askClusterNodes();
|
||||
$this->askClusterNodes($connection);
|
||||
}
|
||||
|
||||
$this->move($connection, $slot);
|
||||
@@ -495,8 +494,8 @@ class RedisCluster implements ClusterConnectionInterface, IteratorAggregate, Cou
|
||||
|
||||
/**
|
||||
* Enables automatic fetching of the current slots map from one of the nodes
|
||||
* using the CLUSTER NODES command. This option is disabled by default but
|
||||
* asking the current slots map to Redis upon -MOVE responses may reduce
|
||||
* using the CLUSTER SLOTS command. This option is disabled by default but
|
||||
* asking the current slots map to Redis upon -MOVED responses may reduce
|
||||
* overhead by eliminating the trial-and-error nature of the node guessing
|
||||
* procedure, mostly when targeting many keys that would end up in a lot of
|
||||
* redirections.
|
||||
@@ -504,7 +503,7 @@ class RedisCluster implements ClusterConnectionInterface, IteratorAggregate, Cou
|
||||
* The slots map can still be manually fetched using the askClusterNodes()
|
||||
* method whether or not this option is enabled.
|
||||
*
|
||||
* @param bool $value Enable or disable the use of CLUSTER NODES.
|
||||
* @param bool $value Enable or disable the use of CLUSTER SLOTS.
|
||||
*/
|
||||
public function enableClusterNodes($value)
|
||||
{
|
||||
|
||||
@@ -76,7 +76,7 @@ class StreamConnection extends AbstractConnection
|
||||
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
|
||||
* @return resource
|
||||
*/
|
||||
private function tcpStreamInitializer(ConnectionParametersInterface $parameters)
|
||||
protected function tcpStreamInitializer(ConnectionParametersInterface $parameters)
|
||||
{
|
||||
$uri = "tcp://{$parameters->host}:{$parameters->port}";
|
||||
$flags = STREAM_CLIENT_CONNECT;
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace Predis\Connection;
|
||||
use Predis\NotSupportedException;
|
||||
use Predis\ResponseError;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Connection\ConnectionException;
|
||||
use Predis\Protocol\ProtocolException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,8 +65,8 @@ class PipelineContext implements BasicClientInterface, ExecutableContextInterfac
|
||||
/**
|
||||
* Queues a command into the pipeline buffer.
|
||||
*
|
||||
* @param string $method Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
* @param string $method Command ID.
|
||||
* @param array $arguments Arguments for the command.
|
||||
* @return $this
|
||||
*/
|
||||
public function __call($method, $arguments)
|
||||
@@ -90,7 +90,7 @@ class PipelineContext implements BasicClientInterface, ExecutableContextInterfac
|
||||
/**
|
||||
* Queues a command instance into the pipeline buffer.
|
||||
*
|
||||
* @param CommandInterface $command Command to queue in the buffer.
|
||||
* @param CommandInterface $command Command to queue in the buffer.
|
||||
* @return $this
|
||||
*/
|
||||
public function executeCommand(CommandInterface $command)
|
||||
|
||||
@@ -78,6 +78,7 @@ abstract class ServerProfile implements ServerProfileInterface, CommandProcessin
|
||||
'2.4' => 'Predis\Profile\ServerVersion24',
|
||||
'2.6' => 'Predis\Profile\ServerVersion26',
|
||||
'2.8' => 'Predis\Profile\ServerVersion28',
|
||||
'3.0' => 'Predis\Profile\ServerVersion30',
|
||||
'default' => 'Predis\Profile\ServerVersion28',
|
||||
'dev' => 'Predis\Profile\ServerVersionNext',
|
||||
);
|
||||
|
||||
@@ -250,6 +250,9 @@ class ServerVersion28 extends ServerProfile
|
||||
'pfadd' => 'Predis\Command\HyperLogLogAdd',
|
||||
'pfcount' => 'Predis\Command\HyperLogLogCount',
|
||||
'pfmerge' => 'Predis\Command\HyperLogLogMerge',
|
||||
|
||||
/* remote server control commands */
|
||||
'command' => 'Predis\Command\ServerCommand',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
<?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\Profile;
|
||||
|
||||
/**
|
||||
* Server profile for Redis v3.0.x.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ServerVersion30 extends ServerProfile
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return '3.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedCommands()
|
||||
{
|
||||
return array(
|
||||
/* ---------------- Redis 1.2 ---------------- */
|
||||
|
||||
/* commands operating on the key space */
|
||||
'exists' => 'Predis\Command\KeyExists',
|
||||
'del' => 'Predis\Command\KeyDelete',
|
||||
'type' => 'Predis\Command\KeyType',
|
||||
'keys' => 'Predis\Command\KeyKeys',
|
||||
'randomkey' => 'Predis\Command\KeyRandom',
|
||||
'rename' => 'Predis\Command\KeyRename',
|
||||
'renamenx' => 'Predis\Command\KeyRenamePreserve',
|
||||
'expire' => 'Predis\Command\KeyExpire',
|
||||
'expireat' => 'Predis\Command\KeyExpireAt',
|
||||
'ttl' => 'Predis\Command\KeyTimeToLive',
|
||||
'move' => 'Predis\Command\KeyMove',
|
||||
'sort' => 'Predis\Command\KeySort',
|
||||
'dump' => 'Predis\Command\KeyDump',
|
||||
'restore' => 'Predis\Command\KeyRestore',
|
||||
|
||||
/* commands operating on string values */
|
||||
'set' => 'Predis\Command\StringSet',
|
||||
'setnx' => 'Predis\Command\StringSetPreserve',
|
||||
'mset' => 'Predis\Command\StringSetMultiple',
|
||||
'msetnx' => 'Predis\Command\StringSetMultiplePreserve',
|
||||
'get' => 'Predis\Command\StringGet',
|
||||
'mget' => 'Predis\Command\StringGetMultiple',
|
||||
'getset' => 'Predis\Command\StringGetSet',
|
||||
'incr' => 'Predis\Command\StringIncrement',
|
||||
'incrby' => 'Predis\Command\StringIncrementBy',
|
||||
'decr' => 'Predis\Command\StringDecrement',
|
||||
'decrby' => 'Predis\Command\StringDecrementBy',
|
||||
|
||||
/* commands operating on lists */
|
||||
'rpush' => 'Predis\Command\ListPushTail',
|
||||
'lpush' => 'Predis\Command\ListPushHead',
|
||||
'llen' => 'Predis\Command\ListLength',
|
||||
'lrange' => 'Predis\Command\ListRange',
|
||||
'ltrim' => 'Predis\Command\ListTrim',
|
||||
'lindex' => 'Predis\Command\ListIndex',
|
||||
'lset' => 'Predis\Command\ListSet',
|
||||
'lrem' => 'Predis\Command\ListRemove',
|
||||
'lpop' => 'Predis\Command\ListPopFirst',
|
||||
'rpop' => 'Predis\Command\ListPopLast',
|
||||
'rpoplpush' => 'Predis\Command\ListPopLastPushHead',
|
||||
|
||||
/* commands operating on sets */
|
||||
'sadd' => 'Predis\Command\SetAdd',
|
||||
'srem' => 'Predis\Command\SetRemove',
|
||||
'spop' => 'Predis\Command\SetPop',
|
||||
'smove' => 'Predis\Command\SetMove',
|
||||
'scard' => 'Predis\Command\SetCardinality',
|
||||
'sismember' => 'Predis\Command\SetIsMember',
|
||||
'sinter' => 'Predis\Command\SetIntersection',
|
||||
'sinterstore' => 'Predis\Command\SetIntersectionStore',
|
||||
'sunion' => 'Predis\Command\SetUnion',
|
||||
'sunionstore' => 'Predis\Command\SetUnionStore',
|
||||
'sdiff' => 'Predis\Command\SetDifference',
|
||||
'sdiffstore' => 'Predis\Command\SetDifferenceStore',
|
||||
'smembers' => 'Predis\Command\SetMembers',
|
||||
'srandmember' => 'Predis\Command\SetRandomMember',
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'zadd' => 'Predis\Command\ZSetAdd',
|
||||
'zincrby' => 'Predis\Command\ZSetIncrementBy',
|
||||
'zrem' => 'Predis\Command\ZSetRemove',
|
||||
'zrange' => 'Predis\Command\ZSetRange',
|
||||
'zrevrange' => 'Predis\Command\ZSetReverseRange',
|
||||
'zrangebyscore' => 'Predis\Command\ZSetRangeByScore',
|
||||
'zcard' => 'Predis\Command\ZSetCardinality',
|
||||
'zscore' => 'Predis\Command\ZSetScore',
|
||||
'zremrangebyscore' => 'Predis\Command\ZSetRemoveRangeByScore',
|
||||
|
||||
/* connection related commands */
|
||||
'ping' => 'Predis\Command\ConnectionPing',
|
||||
'auth' => 'Predis\Command\ConnectionAuth',
|
||||
'select' => 'Predis\Command\ConnectionSelect',
|
||||
'echo' => 'Predis\Command\ConnectionEcho',
|
||||
'quit' => 'Predis\Command\ConnectionQuit',
|
||||
|
||||
/* remote server control commands */
|
||||
'info' => 'Predis\Command\ServerInfoV26x',
|
||||
'slaveof' => 'Predis\Command\ServerSlaveOf',
|
||||
'monitor' => 'Predis\Command\ServerMonitor',
|
||||
'dbsize' => 'Predis\Command\ServerDatabaseSize',
|
||||
'flushdb' => 'Predis\Command\ServerFlushDatabase',
|
||||
'flushall' => 'Predis\Command\ServerFlushAll',
|
||||
'save' => 'Predis\Command\ServerSave',
|
||||
'bgsave' => 'Predis\Command\ServerBackgroundSave',
|
||||
'lastsave' => 'Predis\Command\ServerLastSave',
|
||||
'shutdown' => 'Predis\Command\ServerShutdown',
|
||||
'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF',
|
||||
|
||||
/* ---------------- Redis 2.0 ---------------- */
|
||||
|
||||
/* commands operating on string values */
|
||||
'setex' => 'Predis\Command\StringSetExpire',
|
||||
'append' => 'Predis\Command\StringAppend',
|
||||
'substr' => 'Predis\Command\StringSubstr',
|
||||
|
||||
/* commands operating on lists */
|
||||
'blpop' => 'Predis\Command\ListPopFirstBlocking',
|
||||
'brpop' => 'Predis\Command\ListPopLastBlocking',
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'zunionstore' => 'Predis\Command\ZSetUnionStore',
|
||||
'zinterstore' => 'Predis\Command\ZSetIntersectionStore',
|
||||
'zcount' => 'Predis\Command\ZSetCount',
|
||||
'zrank' => 'Predis\Command\ZSetRank',
|
||||
'zrevrank' => 'Predis\Command\ZSetReverseRank',
|
||||
'zremrangebyrank' => 'Predis\Command\ZSetRemoveRangeByRank',
|
||||
|
||||
/* commands operating on hashes */
|
||||
'hset' => 'Predis\Command\HashSet',
|
||||
'hsetnx' => 'Predis\Command\HashSetPreserve',
|
||||
'hmset' => 'Predis\Command\HashSetMultiple',
|
||||
'hincrby' => 'Predis\Command\HashIncrementBy',
|
||||
'hget' => 'Predis\Command\HashGet',
|
||||
'hmget' => 'Predis\Command\HashGetMultiple',
|
||||
'hdel' => 'Predis\Command\HashDelete',
|
||||
'hexists' => 'Predis\Command\HashExists',
|
||||
'hlen' => 'Predis\Command\HashLength',
|
||||
'hkeys' => 'Predis\Command\HashKeys',
|
||||
'hvals' => 'Predis\Command\HashValues',
|
||||
'hgetall' => 'Predis\Command\HashGetAll',
|
||||
|
||||
/* transactions */
|
||||
'multi' => 'Predis\Command\TransactionMulti',
|
||||
'exec' => 'Predis\Command\TransactionExec',
|
||||
'discard' => 'Predis\Command\TransactionDiscard',
|
||||
|
||||
/* publish - subscribe */
|
||||
'subscribe' => 'Predis\Command\PubSubSubscribe',
|
||||
'unsubscribe' => 'Predis\Command\PubSubUnsubscribe',
|
||||
'psubscribe' => 'Predis\Command\PubSubSubscribeByPattern',
|
||||
'punsubscribe' => 'Predis\Command\PubSubUnsubscribeByPattern',
|
||||
'publish' => 'Predis\Command\PubSubPublish',
|
||||
|
||||
/* remote server control commands */
|
||||
'config' => 'Predis\Command\ServerConfig',
|
||||
|
||||
/* ---------------- Redis 2.2 ---------------- */
|
||||
|
||||
/* commands operating on the key space */
|
||||
'persist' => 'Predis\Command\KeyPersist',
|
||||
|
||||
/* commands operating on string values */
|
||||
'strlen' => 'Predis\Command\StringStrlen',
|
||||
'setrange' => 'Predis\Command\StringSetRange',
|
||||
'getrange' => 'Predis\Command\StringGetRange',
|
||||
'setbit' => 'Predis\Command\StringSetBit',
|
||||
'getbit' => 'Predis\Command\StringGetBit',
|
||||
|
||||
/* commands operating on lists */
|
||||
'rpushx' => 'Predis\Command\ListPushTailX',
|
||||
'lpushx' => 'Predis\Command\ListPushHeadX',
|
||||
'linsert' => 'Predis\Command\ListInsert',
|
||||
'brpoplpush' => 'Predis\Command\ListPopLastPushHeadBlocking',
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'zrevrangebyscore' => 'Predis\Command\ZSetReverseRangeByScore',
|
||||
|
||||
/* transactions */
|
||||
'watch' => 'Predis\Command\TransactionWatch',
|
||||
'unwatch' => 'Predis\Command\TransactionUnwatch',
|
||||
|
||||
/* remote server control commands */
|
||||
'object' => 'Predis\Command\ServerObject',
|
||||
'slowlog' => 'Predis\Command\ServerSlowlog',
|
||||
|
||||
/* ---------------- Redis 2.4 ---------------- */
|
||||
|
||||
/* remote server control commands */
|
||||
'client' => 'Predis\Command\ServerClient',
|
||||
|
||||
/* ---------------- Redis 2.6 ---------------- */
|
||||
|
||||
/* commands operating on the key space */
|
||||
'pttl' => 'Predis\Command\KeyPreciseTimeToLive',
|
||||
'pexpire' => 'Predis\Command\KeyPreciseExpire',
|
||||
'pexpireat' => 'Predis\Command\KeyPreciseExpireAt',
|
||||
|
||||
/* commands operating on string values */
|
||||
'psetex' => 'Predis\Command\StringPreciseSetExpire',
|
||||
'incrbyfloat' => 'Predis\Command\StringIncrementByFloat',
|
||||
'bitop' => 'Predis\Command\StringBitOp',
|
||||
'bitcount' => 'Predis\Command\StringBitCount',
|
||||
|
||||
/* commands operating on hashes */
|
||||
'hincrbyfloat' => 'Predis\Command\HashIncrementByFloat',
|
||||
|
||||
/* scripting */
|
||||
'eval' => 'Predis\Command\ServerEval',
|
||||
'evalsha' => 'Predis\Command\ServerEvalSHA',
|
||||
'script' => 'Predis\Command\ServerScript',
|
||||
|
||||
/* remote server control commands */
|
||||
'time' => 'Predis\Command\ServerTime',
|
||||
|
||||
/* ---------------- Redis 2.8 ---------------- */
|
||||
|
||||
/* commands operating on the key space */
|
||||
'scan' => 'Predis\Command\KeyScan',
|
||||
|
||||
/* commands operating on sets */
|
||||
'sscan' => 'Predis\Command\SetScan',
|
||||
|
||||
/* commands operating on sorted sets */
|
||||
'zscan' => 'Predis\Command\ZSetScan',
|
||||
'zlexcount' => 'Predis\Command\ZSetLexCount',
|
||||
'zrangebylex' => 'Predis\Command\ZSetRangeByLex',
|
||||
'zremrangebylex' => 'Predis\Command\ZSetRemoveRangeByLex',
|
||||
|
||||
/* commands operating on hashes */
|
||||
'hscan' => 'Predis\Command\HashScan',
|
||||
|
||||
/* commands operating on HyperLogLog */
|
||||
'pfadd' => 'Predis\Command\HyperLogLogAdd',
|
||||
'pfcount' => 'Predis\Command\HyperLogLogCount',
|
||||
'pfmerge' => 'Predis\Command\HyperLogLogMerge',
|
||||
|
||||
/* remote server control commands */
|
||||
'command' => 'Predis\Command\ServerCommand',
|
||||
|
||||
/* ---------------- Redis 3.0 ---------------- */
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace Predis\Profile;
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ServerVersionNext extends ServerVersion28
|
||||
class ServerVersionNext extends ServerVersion30
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
||||
@@ -24,6 +24,7 @@ abstract class AbstractPubSubContext implements \Iterator
|
||||
const PUNSUBSCRIBE = 'punsubscribe';
|
||||
const MESSAGE = 'message';
|
||||
const PMESSAGE = 'pmessage';
|
||||
const PONG = 'pong';
|
||||
|
||||
const STATUS_VALID = 1; // 0b0001
|
||||
const STATUS_SUBSCRIBED = 2; // 0b0010
|
||||
@@ -51,6 +52,17 @@ abstract class AbstractPubSubContext implements \Iterator
|
||||
return ($this->statusFlags & $value) === $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* PING the server with an optional payload that will be echoed as a
|
||||
* PONG message in the pub/sub loop.
|
||||
*
|
||||
* @param string $payload Optional PING payload.
|
||||
*/
|
||||
public function ping($payload = null)
|
||||
{
|
||||
$this->writeCommand('PING', array($payload));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to the specified channels.
|
||||
*
|
||||
|
||||
@@ -133,6 +133,12 @@ class PubSubContext extends AbstractPubSubContext
|
||||
'payload' => $response[3],
|
||||
);
|
||||
|
||||
case self::PONG:
|
||||
return (object) array(
|
||||
'kind' => $response[0],
|
||||
'payload' => $response[1],
|
||||
);
|
||||
|
||||
default:
|
||||
$message = "Received an unknown message type {$response[0]} inside of a pubsub context";
|
||||
throw new ClientException($message);
|
||||
|
||||
@@ -123,11 +123,11 @@ class MultiExecContext implements BasicClientInterface, ExecutableContextInterfa
|
||||
|
||||
$profile = $client->getProfile();
|
||||
|
||||
if ($profile->supportsCommands(array('multi', 'exec', 'discard')) === false) {
|
||||
if ($profile->supportsCommands(array('MULTI', 'EXEC', 'DISCARD')) === false) {
|
||||
throw new NotSupportedException('The current profile does not support MULTI, EXEC and DISCARD');
|
||||
}
|
||||
|
||||
$this->canWatch = $profile->supportsCommands(array('watch', 'unwatch'));
|
||||
$this->canWatch = $profile->supportsCommands(array('WATCH', 'UNWATCH'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +200,7 @@ class MultiExecContext implements BasicClientInterface, ExecutableContextInterfa
|
||||
/**
|
||||
* Executes the specified Redis command.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function executeCommand(CommandInterface $command)
|
||||
@@ -215,7 +215,7 @@ class MultiExecContext implements BasicClientInterface, ExecutableContextInterfa
|
||||
|
||||
if ($response instanceof ResponseQueued) {
|
||||
$this->commands->enqueue($command);
|
||||
} else if ($response instanceof ResponseErrorInterface) {
|
||||
} elseif ($response instanceof ResponseErrorInterface) {
|
||||
throw new AbortedMultiExecException($this, $response->getMessage());
|
||||
} else {
|
||||
$this->onProtocolError('The server did not return a +QUEUED status response.');
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ name = "Predis"
|
||||
desc = "Flexible and feature-complete PHP client library for Redis"
|
||||
homepage = "http://github.com/nrk/predis"
|
||||
license = "MIT"
|
||||
version = "0.8.6"
|
||||
version = "0.8.7"
|
||||
stability = "stable"
|
||||
channel = "pear.nrk.io"
|
||||
|
||||
|
||||
@@ -275,6 +275,8 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
'TTL' => 'keys-first',
|
||||
'PTTL' => 'keys-first',
|
||||
'SORT' => 'keys-first', // TODO
|
||||
'DUMP' => 'keys-first',
|
||||
'RESTORE' => 'keys-first',
|
||||
|
||||
/* commands operating on string values */
|
||||
'APPEND' => 'keys-first',
|
||||
@@ -297,6 +299,7 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
'SETRANGE' => 'keys-first',
|
||||
'STRLEN' => 'keys-first',
|
||||
'SUBSTR' => 'keys-first',
|
||||
'BITOP' => 'keys-bitop',
|
||||
'BITCOUNT' => 'keys-first',
|
||||
|
||||
/* commands operating on lists */
|
||||
@@ -305,8 +308,10 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
'LLEN' => 'keys-first',
|
||||
'LPOP' => 'keys-first',
|
||||
'RPOP' => 'keys-first',
|
||||
'RPOPLPUSH' => 'keys-all',
|
||||
'BLPOP' => 'keys-blockinglist',
|
||||
'BRPOP' => 'keys-blockinglist',
|
||||
'BRPOPLPUSH' => 'keys-blockinglist',
|
||||
'LPUSH' => 'keys-first',
|
||||
'LPUSHX' => 'keys-first',
|
||||
'RPUSH' => 'keys-first',
|
||||
@@ -319,6 +324,12 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
/* commands operating on sets */
|
||||
'SADD' => 'keys-first',
|
||||
'SCARD' => 'keys-first',
|
||||
'SDIFF' => 'keys-all',
|
||||
'SDIFFSTORE' => 'keys-all',
|
||||
'SINTER' => 'keys-all',
|
||||
'SINTERSTORE' => 'keys-all',
|
||||
'SUNION' => 'keys-all',
|
||||
'SUNIONSTORE' => 'keys-all',
|
||||
'SISMEMBER' => 'keys-first',
|
||||
'SMEMBERS' => 'keys-first',
|
||||
'SSCAN' => 'keys-first',
|
||||
@@ -331,6 +342,7 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
'ZCARD' => 'keys-first',
|
||||
'ZCOUNT' => 'keys-first',
|
||||
'ZINCRBY' => 'keys-first',
|
||||
'ZINTERSTORE' => 'keys-zaggregated',
|
||||
'ZRANGE' => 'keys-first',
|
||||
'ZRANGEBYSCORE' => 'keys-first',
|
||||
'ZRANK' => 'keys-first',
|
||||
@@ -341,6 +353,7 @@ class RedisClusterHashStrategyTest extends PredisTestCase
|
||||
'ZREVRANGEBYSCORE' => 'keys-first',
|
||||
'ZREVRANK' => 'keys-first',
|
||||
'ZSCORE' => 'keys-first',
|
||||
'ZUNIONSTORE' => 'keys-zaggregated',
|
||||
'ZSCAN' => 'keys-first',
|
||||
'ZLEXCOUNT' => 'keys-first',
|
||||
'ZRANGEBYLEX' => 'keys-first',
|
||||
|
||||
@@ -18,7 +18,7 @@ use Predis\Profile\ServerProfile;
|
||||
/**
|
||||
* @group realm-iterators
|
||||
*/
|
||||
class SortedSetTest extends PredisTestCase
|
||||
class SortedSetKeyTest extends PredisTestCase
|
||||
{
|
||||
/**
|
||||
* @group disconnected
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-server
|
||||
*/
|
||||
class ServerCommandTest extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand()
|
||||
{
|
||||
return 'Predis\Command\ServerCommand';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId()
|
||||
{
|
||||
return 'COMMAND';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments()
|
||||
{
|
||||
$arguments = array('INFO', 'DEL');
|
||||
$expected = array('INFO', 'DEL');
|
||||
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
$this->assertSame($expected, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse()
|
||||
{
|
||||
$raw = array(
|
||||
array('get', 2, array('readonly', 'fast'), 1, 1, 1),
|
||||
array('set', -3, array('write', 'denyoom'), 1, 1, 1),
|
||||
array('watch', -2, array('readonly', 'noscript', 'fast'), 1, -1, 1),
|
||||
array('unwatch', 1, array('readonly', 'noscript', 'fast'), 0, 0, 0),
|
||||
array('info', -1, array('readonly', 'loading', 'stale'), 0, 0, 0),
|
||||
);
|
||||
|
||||
$expected = $raw;
|
||||
|
||||
$command = $this->getCommand();
|
||||
|
||||
$this->assertSame($expected, $command->parseResponse($raw));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseEmptyResponse()
|
||||
{
|
||||
$raw = array(null);
|
||||
$expected = array(null);
|
||||
|
||||
$command = $this->getCommand();
|
||||
|
||||
$this->assertSame($expected, $command->parseResponse($raw));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.13
|
||||
*/
|
||||
public function testReturnsEmptyCommandInfoOnNonExistingCommand()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertCount(1, $response = $redis->command('INFO', 'FOOBAR'));
|
||||
$this->assertSame(array(null), $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.13
|
||||
*/
|
||||
public function testReturnsCommandInfoOnExistingCommand()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$expected = array(array('command', 0, array('readonly', 'loading', 'stale'), 0, 0, 0));
|
||||
$this->assertCount(1, $response = $redis->command('INFO', 'COMMAND'));
|
||||
$this->assertSame($expected, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.8.13
|
||||
*/
|
||||
public function testReturnsListOfCommandInfoWithNoArguments()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertGreaterThan(100, count($response = $redis->command()));
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ namespace Predis\Connection;
|
||||
require_once __DIR__.'/../CommunicationExceptionTest.php';
|
||||
|
||||
use Predis\CommunicationExceptionTest;
|
||||
use Predis\Connection\SingleConnectionInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -16,7 +16,7 @@ use PredisTestCase;
|
||||
* @todo ConnectionParameters::define();
|
||||
* @todo ConnectionParameters::undefine();
|
||||
*/
|
||||
class ParametersTest extends PredisTestCase
|
||||
class ConnectionParametersTest extends PredisTestCase
|
||||
{
|
||||
/**
|
||||
* @group disconnected
|
||||
|
||||
@@ -398,6 +398,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
$connection2->expects($this->never())->method('writeCommand');
|
||||
|
||||
$cluster = new RedisCluster();
|
||||
$cluster->enableClusterNodes(false);
|
||||
$cluster->add($connection1);
|
||||
$cluster->add($connection2);
|
||||
|
||||
@@ -418,6 +419,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
$connection2->expects($this->once())->method('readResponse')->with($command);
|
||||
|
||||
$cluster = new RedisCluster();
|
||||
$cluster->enableClusterNodes(false);
|
||||
$cluster->add($connection1);
|
||||
$cluster->add($connection2);
|
||||
|
||||
@@ -427,7 +429,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testDoesNotSupportKeyTags()
|
||||
public function testSupportsKeyTags()
|
||||
{
|
||||
$profile = ServerProfile::getDefault();
|
||||
|
||||
@@ -445,8 +447,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
|
||||
$set = $profile->createCommand('set', array('{node:1001}:bar', 'foobar'));
|
||||
$get = $profile->createCommand('get', array('{node:1001}:bar'));
|
||||
$this->assertSame($connection2, $cluster->getConnection($set));
|
||||
$this->assertSame($connection2, $cluster->getConnection($get));
|
||||
$this->assertSame($connection1, $cluster->getConnection($set));
|
||||
$this->assertSame($connection1, $cluster->getConnection($get));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -477,6 +479,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
$factory->expects($this->never())->method('create');
|
||||
|
||||
$cluster = new RedisCluster($factory);
|
||||
$cluster->enableClusterNodes(false);
|
||||
$cluster->add($connection1);
|
||||
$cluster->add($connection2);
|
||||
|
||||
@@ -520,6 +523,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
->will($this->returnValue($connection3));
|
||||
|
||||
$cluster = new RedisCluster($factory);
|
||||
$cluster->enableClusterNodes(false);
|
||||
$cluster->add($connection1);
|
||||
$cluster->add($connection2);
|
||||
|
||||
@@ -553,6 +557,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
$factory->expects($this->never())->method('create');
|
||||
|
||||
$cluster = new RedisCluster($factory);
|
||||
$cluster->enableClusterNodes(false);
|
||||
$cluster->add($connection1);
|
||||
$cluster->add($connection2);
|
||||
|
||||
@@ -593,6 +598,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
->will($this->returnValue($connection3));
|
||||
|
||||
$cluster = new RedisCluster($factory);
|
||||
$cluster->enableClusterNodes(false);
|
||||
$cluster->add($connection1);
|
||||
$cluster->add($connection2);
|
||||
|
||||
@@ -604,52 +610,36 @@ class RedisClusterTest extends PredisTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFetchSlotsMapFromClusterWithClusterNodesCommand()
|
||||
public function testFetchSlotsMapFromClusterWithClusterSlotsCommand()
|
||||
{
|
||||
$output =<<<EOS
|
||||
1284a54dc3245d305ae5e3f5507579f9a3e4bd80 10.1.0.51:6387 master - 0 1400000953094 20 connected 12288-13311
|
||||
1fb644aa255ef92db789a93cc834f597e9a3800b 10.1.0.52:6392 master - 0 1400000955139 15 connected 3072-4095
|
||||
fe814c66d4171db548b6dce537a88c3994cd7197 10.1.0.51:6391 slave 7ca7ed3780c405ee50757550e19ade5921097d25 0 1400000954111 5 connected
|
||||
b3a3b7564c75c096bdc72fc314500cdd666adf87 :0 myself,master - 0 0 18 connected 6144-7167
|
||||
67b25b19a07b56a155e25145957dce82a6605f26 10.1.0.51:6388 master - 0 1400000954111 21 connected 14336-15359
|
||||
ba6e596ae5382ad05f1df5abaaa1489a0e0b2449 10.1.0.52:6398 master - 0 1400000953094 13 connected 15360-16383
|
||||
7ca7ed3780c405ee50757550e19ade5921097d25 10.1.0.52:6391 master - 0 1400000954111 5 connected 1024-2047
|
||||
4d90ab772120a49b177eb7bd4799b592f5048e16 10.1.0.51:6394 slave fa9b9fae41e3d53d2a901dbfe6fd1527c874768c 0 1400000955139 26 connected
|
||||
5731adc383f5f7afbfcca2ee75d17609e436b146 10.1.0.51:6398 slave ba6e596ae5382ad05f1df5abaaa1489a0e0b2449 0 1400000953094 21 connected
|
||||
fd0bd0cd8461bd44b759fefaabaf541788ec65cf 10.1.0.52:6396 master - 0 1400000955139 7 connected 11264-12287
|
||||
d012ddf044d0ef5d72d171f8af1e700488b79241 10.1.0.52:6393 master - 0 1400000953604 10 connected 5120-6143
|
||||
462ab20db112a007f3ffc4e7e105b0c963aa8981 10.1.0.52:6382 slave a1948d989b092bc96ff91019d9d3b74361edcdfa 0 1400000954111 18 connected
|
||||
f468a7018ff5a0701a1b614b8ed1b400cddcd578 10.1.0.52:6385 slave 2a1845f27d0f24904d67e2f2cd3e7380949e9d3a 0 1400000953604 25 connected
|
||||
a48e023927edcc0d001be0535130f41d8e93adde 10.1.0.52:6383 slave a547c6de8acfbf4a97a1c341bddc2efffea83032 0 1400000955139 12 connected
|
||||
2530af9ee9b8d4e359cefedd7492ba777dd1e5aa 10.1.0.51:6392 slave 1fb644aa255ef92db789a93cc834f597e9a3800b 0 1400000953603 17 connected
|
||||
2d3934ef56abbe25949063474d2e408ae2fb41af 10.1.0.51:6395 slave 0f70284073a6613343fd9a2da82a0549c1efaaae 0 1400000954627 27 connected
|
||||
6a0430bf8e1beed8911aa69b002ac7a00ad60428 10.1.0.52:6387 slave 1284a54dc3245d305ae5e3f5507579f9a3e4bd80 0 1400000954111 20 connected
|
||||
578ad5ff1970a22419a6afca0cc97bfb729f6b47 10.1.0.51:6397 slave fd637fc700d31395d1f324348b49ad38248ba8d4 0 1400000955139 18 connected
|
||||
c3083761437050f1cd80a5f95b3ff5aabba3bb97 10.1.0.51:6381 master - 0 1400000955139 11 connected 0-1023
|
||||
846d3410ce8e7cea565617686f4685a35d376706 10.1.0.52:6384 slave b3a3b7564c75c096bdc72fc314500cdd666adf87 0 1400000953094 18 connected
|
||||
fd637fc700d31395d1f324348b49ad38248ba8d4 10.1.0.52:6397 master - 0 1400000954627 16 connected 13312-14335
|
||||
a547c6de8acfbf4a97a1c341bddc2efffea83032 10.1.0.51:6383 master - 0 1400000954627 8 connected 4096-5119
|
||||
0f70284073a6613343fd9a2da82a0549c1efaaae 10.1.0.52:6395 master - 0 1400000954627 27 connected 9216-10239
|
||||
2a1845f27d0f24904d67e2f2cd3e7380949e9d3a 10.1.0.51:6385 master - 0 1400000953603 25 connected 8192-9215
|
||||
ba018d6d154205a47b83de33efbbe846cb6f4b1f 10.1.0.52:6381 slave c3083761437050f1cd80a5f95b3ff5aabba3bb97 0 1400000955139 11 connected
|
||||
5a830c9ef0d09eb25b521395466e803052ddbef4 10.1.0.51:6386 master - 0 1400000954627 9 connected 10240-11263
|
||||
91d9840437794ab1ff77b59f831ce80ffaaa4c5a 10.1.0.51:6393 slave d012ddf044d0ef5d72d171f8af1e700488b79241 0 1400000954627 14 connected
|
||||
d6bdbb495fcb66ba3001c2a07c067d5aa354cf55 10.1.0.52:6388 slave 67b25b19a07b56a155e25145957dce82a6605f26 0 1400000954111 21 connected
|
||||
2176380db50d687d37e37c3daeeea6e7f8feed0c 10.1.0.51:6396 slave fd0bd0cd8461bd44b759fefaabaf541788ec65cf 0 1400000953603 22 connected
|
||||
c93ffb4a2ff815f9029156383b00f106234b9546 10.1.0.52:6386 slave 5a830c9ef0d09eb25b521395466e803052ddbef4 0 1400000953604 9 connected
|
||||
a1948d989b092bc96ff91019d9d3b74361edcdfa 10.1.0.51:6382 master - 0 1400000953094 17 connected 2048-3071
|
||||
fa9b9fae41e3d53d2a901dbfe6fd1527c874768c 10.1.0.52:6394 master - 0 1400000953094 1 connected 7168-8191
|
||||
EOS;
|
||||
$response = array(
|
||||
array(12288, 13311, array('10.1.0.51', 6387), array('10.1.0.52', 6387)),
|
||||
array(3072 , 4095, array('10.1.0.52', 6392), array('10.1.0.51', 6392)),
|
||||
array(6144 , 7167, array('', 6384), array('10.1.0.52', 6384)),
|
||||
array(14336, 15359, array('10.1.0.51', 6388), array('10.1.0.52', 6388)),
|
||||
array(15360, 16383, array('10.1.0.52', 6398), array('10.1.0.51', 6398)),
|
||||
array(1024 , 2047, array('10.1.0.52', 6391), array('10.1.0.51', 6391)),
|
||||
array(11264, 12287, array('10.1.0.52', 6396), array('10.1.0.51', 6396)),
|
||||
array( 5120, 6143, array('10.1.0.52', 6393), array('10.1.0.51', 6393)),
|
||||
array( 0, 1023, array('10.1.0.51', 6381), array('10.1.0.52', 6381)),
|
||||
array(13312, 14335, array('10.1.0.52', 6397), array('10.1.0.51', 6397)),
|
||||
array( 4096, 5119, array('10.1.0.51', 6383), array('10.1.0.52', 6383)),
|
||||
array( 9216, 10239, array('10.1.0.52', 6395), array('10.1.0.51', 6395)),
|
||||
array( 8192, 9215, array('10.1.0.51', 6385), array('10.1.0.52', 6385)),
|
||||
array(10240, 11263, array('10.1.0.51', 6386), array('10.1.0.52', 6386)),
|
||||
array( 2048, 3071, array('10.1.0.51', 6382), array('10.1.0.52', 6382)),
|
||||
array( 7168, 8191, array('10.1.0.52', 6394), array('10.1.0.51', 6394)),
|
||||
);
|
||||
|
||||
$command = RawCommand::create('CLUSTER', 'NODES');
|
||||
$command = RawCommand::create('CLUSTER', 'SLOTS');
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://10.1.0.51:6384');
|
||||
$connection1->expects($this->once())
|
||||
->method('executeCommand')
|
||||
->with($command)
|
||||
->will($this->returnValue($output));
|
||||
->will($this->returnValue($response));
|
||||
|
||||
$factory = $this->getMock('Predis\Connection\ConnectionFactoryInterface');
|
||||
$factory = $this->getMock('Predis\Connection\ConnectionFactory');
|
||||
|
||||
$cluster = new RedisCluster($factory);
|
||||
$cluster->add($connection1);
|
||||
@@ -659,6 +649,47 @@ EOS;
|
||||
$this->assertSame($cluster->getConnectionBySlot('6144'), $connection1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testAskSlotsMapToRedisClusterOnMovedResponseByDefault()
|
||||
{
|
||||
$cmdGET = RawCommand::create('GET', 'node:1001');
|
||||
$rspMOVED = new ResponseError('MOVED 1970 127.0.0.1:6380');
|
||||
$rspSlotsArray = array(
|
||||
array(0 , 8191, array('127.0.0.1', 6379)),
|
||||
array(8192, 16383, array('127.0.0.1', 6380)),
|
||||
);
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
|
||||
$connection1->expects($this->once())
|
||||
->method('executeCommand')
|
||||
->with($cmdGET)
|
||||
->will($this->returnValue($rspMOVED));
|
||||
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380');
|
||||
$connection2->expects($this->at(0))
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand('CLUSTER', array('SLOTS')))
|
||||
->will($this->returnValue($rspSlotsArray));
|
||||
$connection2->expects($this->at(2))
|
||||
->method('executeCommand')
|
||||
->with($cmdGET)
|
||||
->will($this->returnValue('foobar'));
|
||||
|
||||
$factory = $this->getMock('Predis\Connection\ConnectionFactory');
|
||||
$factory->expects($this->once())
|
||||
->method('create')
|
||||
->with(array('host' => '127.0.0.1', 'port' => '6380'))
|
||||
->will($this->returnValue($connection2));
|
||||
|
||||
$cluster = new RedisCluster($factory);
|
||||
$cluster->add($connection1);
|
||||
|
||||
$this->assertSame('foobar', $cluster->executeCommand($cmdGET));
|
||||
$this->assertSame(2, count($cluster));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @expectedException Predis\NotSupportedException
|
||||
|
||||
@@ -183,6 +183,7 @@ class ServerVersion28Test extends PredisProfileTestCase
|
||||
142 => 'pfadd',
|
||||
143 => 'pfcount',
|
||||
144 => 'pfmerge',
|
||||
145 => 'command',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
<?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\Profile;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ServerVersion30Test extends PredisProfileTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProfile($version = null)
|
||||
{
|
||||
return new ServerVersion30();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExpectedVersion()
|
||||
{
|
||||
return '3.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getExpectedCommands()
|
||||
{
|
||||
return array(
|
||||
0 => 'exists',
|
||||
1 => 'del',
|
||||
2 => 'type',
|
||||
3 => 'keys',
|
||||
4 => 'randomkey',
|
||||
5 => 'rename',
|
||||
6 => 'renamenx',
|
||||
7 => 'expire',
|
||||
8 => 'expireat',
|
||||
9 => 'ttl',
|
||||
10 => 'move',
|
||||
11 => 'sort',
|
||||
12 => 'dump',
|
||||
13 => 'restore',
|
||||
14 => 'set',
|
||||
15 => 'setnx',
|
||||
16 => 'mset',
|
||||
17 => 'msetnx',
|
||||
18 => 'get',
|
||||
19 => 'mget',
|
||||
20 => 'getset',
|
||||
21 => 'incr',
|
||||
22 => 'incrby',
|
||||
23 => 'decr',
|
||||
24 => 'decrby',
|
||||
25 => 'rpush',
|
||||
26 => 'lpush',
|
||||
27 => 'llen',
|
||||
28 => 'lrange',
|
||||
29 => 'ltrim',
|
||||
30 => 'lindex',
|
||||
31 => 'lset',
|
||||
32 => 'lrem',
|
||||
33 => 'lpop',
|
||||
34 => 'rpop',
|
||||
35 => 'rpoplpush',
|
||||
36 => 'sadd',
|
||||
37 => 'srem',
|
||||
38 => 'spop',
|
||||
39 => 'smove',
|
||||
40 => 'scard',
|
||||
41 => 'sismember',
|
||||
42 => 'sinter',
|
||||
43 => 'sinterstore',
|
||||
44 => 'sunion',
|
||||
45 => 'sunionstore',
|
||||
46 => 'sdiff',
|
||||
47 => 'sdiffstore',
|
||||
48 => 'smembers',
|
||||
49 => 'srandmember',
|
||||
50 => 'zadd',
|
||||
51 => 'zincrby',
|
||||
52 => 'zrem',
|
||||
53 => 'zrange',
|
||||
54 => 'zrevrange',
|
||||
55 => 'zrangebyscore',
|
||||
56 => 'zcard',
|
||||
57 => 'zscore',
|
||||
58 => 'zremrangebyscore',
|
||||
59 => 'ping',
|
||||
60 => 'auth',
|
||||
61 => 'select',
|
||||
62 => 'echo',
|
||||
63 => 'quit',
|
||||
64 => 'info',
|
||||
65 => 'slaveof',
|
||||
66 => 'monitor',
|
||||
67 => 'dbsize',
|
||||
68 => 'flushdb',
|
||||
69 => 'flushall',
|
||||
70 => 'save',
|
||||
71 => 'bgsave',
|
||||
72 => 'lastsave',
|
||||
73 => 'shutdown',
|
||||
74 => 'bgrewriteaof',
|
||||
75 => 'setex',
|
||||
76 => 'append',
|
||||
77 => 'substr',
|
||||
78 => 'blpop',
|
||||
79 => 'brpop',
|
||||
80 => 'zunionstore',
|
||||
81 => 'zinterstore',
|
||||
82 => 'zcount',
|
||||
83 => 'zrank',
|
||||
84 => 'zrevrank',
|
||||
85 => 'zremrangebyrank',
|
||||
86 => 'hset',
|
||||
87 => 'hsetnx',
|
||||
88 => 'hmset',
|
||||
89 => 'hincrby',
|
||||
90 => 'hget',
|
||||
91 => 'hmget',
|
||||
92 => 'hdel',
|
||||
93 => 'hexists',
|
||||
94 => 'hlen',
|
||||
95 => 'hkeys',
|
||||
96 => 'hvals',
|
||||
97 => 'hgetall',
|
||||
98 => 'multi',
|
||||
99 => 'exec',
|
||||
100 => 'discard',
|
||||
101 => 'subscribe',
|
||||
102 => 'unsubscribe',
|
||||
103 => 'psubscribe',
|
||||
104 => 'punsubscribe',
|
||||
105 => 'publish',
|
||||
106 => 'config',
|
||||
107 => 'persist',
|
||||
108 => 'strlen',
|
||||
109 => 'setrange',
|
||||
110 => 'getrange',
|
||||
111 => 'setbit',
|
||||
112 => 'getbit',
|
||||
113 => 'rpushx',
|
||||
114 => 'lpushx',
|
||||
115 => 'linsert',
|
||||
116 => 'brpoplpush',
|
||||
117 => 'zrevrangebyscore',
|
||||
118 => 'watch',
|
||||
119 => 'unwatch',
|
||||
120 => 'object',
|
||||
121 => 'slowlog',
|
||||
122 => 'client',
|
||||
123 => 'pttl',
|
||||
124 => 'pexpire',
|
||||
125 => 'pexpireat',
|
||||
126 => 'psetex',
|
||||
127 => 'incrbyfloat',
|
||||
128 => 'bitop',
|
||||
129 => 'bitcount',
|
||||
130 => 'hincrbyfloat',
|
||||
131 => 'eval',
|
||||
132 => 'evalsha',
|
||||
133 => 'script',
|
||||
134 => 'time',
|
||||
135 => 'scan',
|
||||
136 => 'sscan',
|
||||
137 => 'zscan',
|
||||
138 => 'zlexcount',
|
||||
139 => 'zrangebylex',
|
||||
140 => 'zremrangebylex',
|
||||
141 => 'hscan',
|
||||
142 => 'pfadd',
|
||||
143 => 'pfcount',
|
||||
144 => 'pfmerge',
|
||||
145 => 'command',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -183,6 +183,7 @@ class ServerVersionNextTest extends PredisProfileTestCase
|
||||
142 => 'pfadd',
|
||||
143 => 'pfcount',
|
||||
144 => 'pfmerge',
|
||||
145 => 'command',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,39 @@ class PubSubContextTest extends PredisTestCase
|
||||
$this->assertNull($pubsub->next());
|
||||
}
|
||||
|
||||
public function testHandlesPongMessages()
|
||||
{
|
||||
$rawmessage = array('pong', '');
|
||||
|
||||
$connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
|
||||
$connection->expects($this->once())->method('read')->will($this->returnValue($rawmessage));
|
||||
|
||||
$client = new Client($connection);
|
||||
$pubsub = new PubSubContext($client, array('subscribe' => 'channel:foo'));
|
||||
|
||||
$message = $pubsub->current();
|
||||
$this->assertSame('pong', $message->kind);
|
||||
$this->assertSame('', $message->payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testHandlesPongMessagesWithPayload()
|
||||
{
|
||||
$rawmessage = array('pong', 'foobar');
|
||||
|
||||
$connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
|
||||
$connection->expects($this->once())->method('read')->will($this->returnValue($rawmessage));
|
||||
|
||||
$client = new Client($connection);
|
||||
$pubsub = new PubSubContext($client, array('subscribe' => 'channel:foo'));
|
||||
|
||||
$message = $pubsub->current();
|
||||
$this->assertSame('pong', $message->kind);
|
||||
$this->assertSame('foobar', $message->payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
@@ -302,6 +335,6 @@ class PubSubContextTest extends PredisTestCase
|
||||
|
||||
$this->assertSame(array('message1', 'message2', 'QUIT'), $messages);
|
||||
$this->assertFalse($pubsub->valid());
|
||||
$this->assertTrue($consumer->ping());
|
||||
$this->assertEquals('ECHO', $consumer->echo('ECHO'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,15 @@ class MultiExecContextTest extends PredisTestCase
|
||||
*/
|
||||
public function testThrowsExceptionOnUnsupportedMultiExecInProfile()
|
||||
{
|
||||
$profile = $this->getMock('Predis\Profile\ServerProfileInterface');
|
||||
$profile->expects($this->once())
|
||||
->method('supportsCommands')
|
||||
->with(array('MULTI', 'EXEC', 'DISCARD'))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
|
||||
$client = new Client($connection, array('profile' => '1.2'));
|
||||
$client = new Client($connection, array('profile' => $profile));
|
||||
|
||||
$tx = new MultiExecContext($client);
|
||||
}
|
||||
|
||||
@@ -42,10 +49,20 @@ class MultiExecContextTest extends PredisTestCase
|
||||
*/
|
||||
public function testThrowsExceptionOnUnsupportedWatchUnwatchInProfile()
|
||||
{
|
||||
$connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
|
||||
$client = new Client($connection, array('profile' => '2.0'));
|
||||
$tx = new MultiExecContext($client, array('options' => 'cas'));
|
||||
$profile = $this->getMock('Predis\Profile\ServerProfileInterface');
|
||||
$profile->expects($this->at(0))
|
||||
->method('supportsCommands')
|
||||
->with(array('MULTI', 'EXEC', 'DISCARD'))
|
||||
->will($this->returnValue(true));
|
||||
$profile->expects($this->at(1))
|
||||
->method('supportsCommands')
|
||||
->with(array('WATCH', 'UNWATCH'))
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
|
||||
$client = new Client($connection, array('profile' => $profile));
|
||||
|
||||
$tx = new MultiExecContext($client, array('options' => 'cas'));
|
||||
$tx->watch('foo');
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
if (file_exists(__DIR__.'/../autoload.php')) {
|
||||
require __DIR__.'/../autoload.php';
|
||||
} else if (@include('Predis/Autoloader.php')) {
|
||||
} elseif (@include('Predis/Autoloader.php')) {
|
||||
Predis\Autoloader::register();
|
||||
} else {
|
||||
die('ERROR: Unable to find a suitable mean to register Predis\Autoloader.');
|
||||
|
||||
Reference in New Issue
Block a user