Compare commits

...

40 Commits

Author SHA1 Message Date
Daniele Alessandri 8c0498a893 Bump VERSION and update CHANGELOG. 2013-02-18 15:03:45 +01:00
Daniele Alessandri aba24d0f6f Remove a few more unnecessary strict comparisons. 2013-02-17 20:46:31 +01:00
Daniele Alessandri a1df4f20da Expose private method used to parse URI strings.
This is useful for 3rd party libraries such as PredisServiceProvider
so it makes sense to have it public and static.
2013-02-17 20:38:46 +01:00
Daniele Alessandri 116eaba75e Deprecate the whole Predis\Helpers class. 2013-02-17 15:21:01 +01:00
Daniele Alessandri de4bae3f9b Prevent E_NOTICE messages on __destruct() if $parameters is not set.
This can only happen when throwing on invalid connection parameters,
this is a quick fix that will do for now.
2013-02-17 15:21:01 +01:00
Daniele Alessandri b50a5975ac Use a nicer looking version for the same code. 2013-02-17 15:21:01 +01:00
Daniele Alessandri 1c1b4096f6 Remove a few more unnecessary strict comparisons. 2013-02-17 15:20:57 +01:00
Daniele Alessandri f8e4ba152f Use hexadecimal notation. 2013-02-17 12:27:45 +01:00
Daniele Alessandri 6b6b73f5f4 Add getArgument($index) in Predis\Command\CommandInterface.
This method should have been part of the interface since start since
it is used through the library. We also do not specify a default value
for the index argument since it does not make much sense.
2013-02-17 12:22:00 +01:00
Daniele Alessandri 729e40d6c0 Use 0 to indicate no arguments for KEYS[] in Lua scripted commands.
We previously used FALSE for that but in the end it does not make much
sense. Luckily for us this does not represent a breaking change since
existing code will keep to work, so we can safely push this change into
the next patch release.
2013-02-16 17:40:49 +01:00
Daniele Alessandri fbcfdc343e Do not implicitly set multibulk replies on composable connections.
This should be done externally on the underlying protocol instance.
2013-02-16 17:05:38 +01:00
Daniele Alessandri 8cbcb09c4c Remove a few unnecessary strict comparisons. 2013-02-16 17:05:12 +01:00
Daniele Alessandri 2781bd780f Fetch connection factory directly from client options. 2013-02-16 14:36:04 +01:00
Daniele Alessandri 9675626aac Add "tcp_nodelay" in the list of supported connection parameters.
[ci skip]
2013-02-16 12:14:05 +01:00
Daniele Alessandri f8829985f7 Tweak CHANGELOG.
[ci skip]
2013-02-16 11:59:43 +01:00
Daniele Alessandri e2e809c9d4 Fix test to handle different hashes generated on 64bits builds of PHP.
Just noticed it on Travis CI as they recently switched to 64bits PHP builds,
also great to see that the library seems to work fine anyway.
2013-02-16 11:46:42 +01:00
Daniele Alessandri ef0067e1a4 Use slightly faster version of the same code.
Yet again, micro-optimizations here.
2013-02-16 11:32:29 +01:00
Daniele Alessandri da343046e5 No need for strict comparison here.
Should be also slightly faster (at least on a micro-optimization scale).
2013-02-16 11:14:55 +01:00
Daniele Alessandri 76d6681f68 Fix unserialization of Predis/Connection/PhpiredisStreamConnection. 2013-02-16 11:14:55 +01:00
Daniele Alessandri 8d01be388d Support TCP_NODELAY for stream-based connections on PHP >= 5.4.0.
This cannot be implemented for previous versions of PHP because we
need socket_import_stream() to extract the underlying socket resource
from the stream in order to be able to set the TCP_NODELAY flag.
2013-02-16 11:14:50 +01:00
Daniele Alessandri c38376dcc4 Accepts callable as first argument of Predis\Client::__construct().
Users can then use callables to wrap the creation and initialization
of the underlying connection with custom strategies:
2013-02-15 11:36:19 +01:00
Daniele Alessandri c354d02105 Aggregated connection for redis-cluster now uses 16384 hash slots.
This commit reflects the recent change from the redis unstable branch
in which the number of hash slots was increased from 4096 to 16384.

See https://github.com/antirez/redis/commit/ebd666d for reference.
2013-02-14 21:54:23 +01:00
Daniele Alessandri d6685a424a Update FAQ. 2013-02-09 10:11:16 +01:00
Daniele Alessandri 05ef62ff97 Tweak README. 2013-02-09 10:06:30 +01:00
Daniele Alessandri b76e876b73 No need for "iterable_multibulk" as a default connection parameter.
Furthermore, connections that do not support certain parameters should
check and throw whether they are set, independently from their value.
2013-02-09 09:56:07 +01:00
Daniele Alessandri ebd895a67d Update link to phpiredis. 2013-02-09 09:49:57 +01:00
Daniele Alessandri 923e7ed5fd Add Predis\Connection\PhpiredisStreamConnection.
This class works just like Predis\Connection\PhpiredisConnection but
it does not require the socket extensions since it relies on PHP's
native streams thus allowing the use of persistent connections.
2013-02-09 09:46:48 +01:00
Daniele Alessandri 9e0fe7bdc7 Back to development for the next patch release.
Forgot to do that prior to merge, my bad.

[ci skip]
2013-02-08 14:54:53 +01:00
Daniele Alessandri 5d774dc581 Merge branch 'gh/pr/102' into v0.8 2013-02-08 12:37:43 +01:00
Daniele Alessandri dffbb8b042 [tests] Fix version check to work on Redis < 2.6.0.
INFO does not have the concept of sections prior to 2.6.0 while our
test suite can be run against 2.4.0 (provided that phpunit.xml uses
the correct server profile), so we need to make sure that the check
does not break with prior version of Redis even if this is unlikely
to happen at this point.

Also changed a bit the naming of variables since things are already
pretty much explicit in this context.
2013-02-08 12:33:07 +01:00
Raphael Stolt 06d5475129 Added PHPUnit utility method markTestSkippedOnRedisVersionBelow and applied it where necessary 2013-02-07 22:46:25 +01:00
Raphael Stolt 1536455fea Added CLIENT SET/GETNAME command 2013-02-07 20:21:29 +01:00
Daniele Alessandri aa1b070f9b Bump VERSION and update CHANGELOG. 2013-02-03 13:59:55 +01:00
Daniele Alessandri 3e125c964c Implement Predis\Session\SessionHandler.
This class makes it easy to use Predis to store PHP sessions on Redis.

NOTE: requires PHP >= 5.4.0 or a polyfill for PHP's SessionHandlerInterface.
2013-02-02 15:14:06 +01:00
Daniele Alessandri e9fdc47d3f Minor fix in readme.
[ci skip]
2013-02-02 10:48:22 +01:00
Daniele Alessandri d3930ea298 Update markdown files to use relative links.
See https://help.github.com/articles/relative-links-in-readmes for details.

[ci skip]
2013-02-02 10:44:54 +01:00
Daniele Alessandri 0efcbb7992 Add a way to get the default value of an option from options.
It is not possible to get the default value of a client option using
either its name or instance:

  $options = array(
    'profile' => function ($options, $option) {
      // instance of Predis\Option\OptionInterface
      $profile = $options->getDefault($option);

      // string representing an option handled by $options,
      // returns NULL if the specified name is not handled.
      $profile = $options->getDefault('profile');

      return $profile;
    },
  );

This addition makes it less awkward to get the default value of an
option, especially when not in the context of a callable option
initializer.
2013-02-02 10:30:53 +01:00
Daniele Alessandri f2af247c63 Push minor changes in the standard executor internals. 2013-01-29 12:38:19 +01:00
Daniele Alessandri a5cf6d72cd Fix standard pipeline executor not parsing raw replies.
This bug was actually introduced right before pushing the stable release
of v0.8.0 in which we moved the responsibility of parsing raw replies
with command parser to consumer classes.

This commit closes #101.
2013-01-29 12:13:01 +01:00
Daniele Alessandri 7173f0c80c Get back to development. 2013-01-29 11:55:47 +01:00
67 changed files with 1237 additions and 180 deletions
+44
View File
@@ -1,3 +1,47 @@
v0.8.3 (2013-02-18)
===============================================================================
- Added `CLIENT SETNAME` and `CLIENT GETNAME` (ISSUE #102).
- Implemented the `Predis\Connection\PhpiredisStreamConnection` class using the
`phpiredis` extension like `Predis\Connection\PhpiredisStreamConnection`, but
without requiring the `socket` extension since it relies on PHP's streams.
- Added support for the TCP_NODELAY flag via the `tcp_nodelay` parameter for
stream-based connections, namely `Predis\Connection\StreamConnection` and
`Predis\Connection\PhpiredisStreamConnection` (requires PHP >= 5.4.0).
- Updated the aggregated connection class for redis-cluster to work with 16384
hash slots instead of 4096 to reflect the recent change from redis unstable
([see this commit](https://github.com/antirez/redis/commit/ebd666d)).
- The constructor of `Predis\Client` now accepts a callable as first argument
returning `Predis\Connection\ConnectionInterface`. Users can create their
own self-contained strategies to create and set up the underlying connection.
- Users should return `0` from `Predis\Command\ScriptedCommand::getKeysCount()`
instead of `FALSE` to indicate that all of the arguments of a Lua script must
be used to populate `ARGV[]`. This does not represent a breaking change.
- The `Predis\Helpers` class has been deprecated and it will be removed in
future releases.
v0.8.2 (2013-02-03)
===============================================================================
- Added `Predis\Session\SessionHandler` to make it easy to store PHP sessions
on Redis using Predis. Please note that this class needs either PHP >= 5.4.0
or a polyfill for PHP's `SessionHandlerInterface`.
- Added the ability to get the default value of a client option directly from
`Predis\Option\ClientOption` using the `getDefault()` method by passing the
option name or its instance.
- __FIX__: the standard pipeline executor was not using the response parser
methods associated to commands to process raw responses (ISSUE #101).
v0.8.1 (2013-01-19)
===============================================================================
+9 -4
View File
@@ -68,7 +68,7 @@ $client->hmset('my:hash', array('field1'=>'value1', 'field2'=>'value2'); // valu
```
The only exception to this _rule_ is the [SORT](http://redis.io/commands/sort) command for which modifiers are
[passed using a named array](https://github.com/nrk/predis/blob/master/tests/Predis/Command/KeySortTest.php#L56-77).
[passed using a named array](tests/Predis/Command/KeySortTest.php#L56-77).
@@ -126,12 +126,17 @@ to how your application will use Redis.
Fair enough, but there is actually an option for you if you need even more speed and it consists on
installing __[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name)
and let Predis using it. __phpiredis__ is a C-based extension that wraps __hiredis__ (the official
Redis C client library) with a thin layer that exposes its features to PHP. You will now get the
benefits of a faster protocol parser just by adding a single line of code in your application:
Redis C client library) with a thin layer that exposes its features to PHP. You can choose between
two different connection backend classes: `Predis\Connection\PhpiredisConnection` (it depends on the
`socket` extension) and `Predis\Connection\PhpiredisStreamConnection` (it uses PHP's native streams).
You will now get the benefits of a faster protocol parser just by adding a couple of lines of code:
```php
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array('tcp' => 'Predis\Connection\PhpiredisConnection')
'connections' => array(
'tcp' => 'Predis\Connection\PhpiredisConnection',
'unix' => 'Predis\Connection\PhpiredisConnection',
),
));
```
+13 -7
View File
@@ -8,7 +8,7 @@ serializing and parsing the Redis protocol. Predis is also available in an async
through the experimental client provided by the [Predis\Async](http://github.com/nrk/predis-async)
library.
For a list of frequently asked questions about Predis, see __FAQ.md__ in the root of the repository.
For a list of frequently asked questions about Predis see our [FAQ](FAQ.md).
More details are available on the [official wiki](http://wiki.github.com/nrk/predis) of the project.
@@ -113,13 +113,19 @@ $replies = $redis->pipeline(function ($pipe) {
### Multiple and customizable connection backends ###
Predis can optionally use different connection backends to connect to Redis. One of them leverages
Predis can optionally use different connection backends to connect to Redis. Two of them leverage
the [phpiredis](http://github.com/nrk/phpiredis) C-based extension resulting in a major speed bump
especially when dealing with long multibulk replies (the `socket` extension is also required):
especially when dealing with long multibulk replies, namely `Predis\Connection\PhpiredisConnection`
(the `socket` extension is also required) and `Predis\Connection\StreamPhpiredisConnection` (it
does not require additional extensions since it relies on PHP's native streams). Both of them can
connect to Redis using standard TCP/IP connections or UNIX domain sockets:
```php
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array('tcp' => 'Predis\Connection\PhpiredisConnection')
'connections' => array(
'tcp' => 'Predis\Connection\PhpiredisConnection',
'unix' => 'Predis\Connection\PhpiredisStreamConnection',
)
));
```
@@ -140,7 +146,7 @@ $client = new Predis\Client('tcp://127.0.0.1', array(
```
For a more in-depth insight on how to create new connection backends you can look at the actual
implementation of the classes contained in `Predis\Connection` namespace.
implementation of the classes contained in the `Predis\Connection` namespace.
### Defining and registering new commands on the client at runtime ###
@@ -214,7 +220,7 @@ Redis. If you do not have Redis up and running, integration tests can be disable
suite is configured to execute integration tests using the server profile for Redis v2.4 (which is the
current stable version of Redis). You can optionally run the suite against a Redis instance built from
the `unstable` branch with the development profile by changing the `REDIS_SERVER_VERSION` to `dev` in
the `phpunit.xml` file. More details about testing Predis are available in `tests/README.md`.
the `phpunit.xml` file. More details on testing Predis can be found in [the tests README](tests/README.md).
Predis uses Travis CI for continuous integration. You can find the results of the test suite and the build
history [on its project page](http://travis-ci.org/nrk/predis).
@@ -252,4 +258,4 @@ history [on its project page](http://travis-ci.org/nrk/predis).
## License ##
The code for Predis is distributed under the terms of the MIT license (see LICENSE).
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
+1 -1
View File
@@ -1 +1 @@
0.8.1
0.8.3
+2 -2
View File
@@ -51,8 +51,8 @@ $parameters = array(
);
$options = array(
'profile' => function ($options) {
$profile = ServerProfile::get('2.6');
'profile' => function ($options, $option) {
$profile = $options->getDefault($option);
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
return $profile;
+39
View File
@@ -0,0 +1,39 @@
<?php
require 'SharedConfigurations.php';
// This example demonstrates how to leverage Predis to save PHP sessions on Redis.
//
// The value of `session.gc_maxlifetime` in `php.ini` will be used by default as the
// the TTL for keys holding session data on Redis, but this value can be overridden
// when creating the session handler instance with the `gc_maxlifetime` option.
//
// Note that this class needs PHP >= 5.4 but can be used on PHP 5.3 if a polyfill for
// SessionHandlerInterface (see http://www.php.net/class.sessionhandlerinterface.php)
// is provided either by you or an external package like `symfony/http-foundation`.
if (!interface_exists('SessionHandlerInterface')) {
die("ATTENTION: the session handler implemented by Predis needs PHP >= 5.4.0 or a polyfill ".
"for \SessionHandlerInterface either provided by you or an external package.\n");
}
// Instantiate a new client just like you would normally do. We'll prefix our session keys here.
$client = new Predis\Client($single_server, array('prefix' => 'sessions:'));
// Set `gc_maxlifetime` so that a session will be expired after 5 seconds since last access.
$handler = new Predis\Session\SessionHandler($client, array('gc_maxlifetime' => 5));
// Register our session handler (it uses `session_set_save_handler()` internally).
$handler->register();
// Set a fixed session ID just for the sake of our example.
session_id('example_session_id');
session_start();
if (isset($_SESSION['foo'])) {
echo "Session has `foo` set to {$_SESSION['foo']}\n";
} else {
$_SESSION['foo'] = $value = mt_rand();
echo "Empty session, `foo` has been set with $value\n";
}
+23 -12
View File
@@ -33,12 +33,11 @@ use Predis\Transaction\MultiExecContext;
*/
class Client implements ClientInterface
{
const VERSION = '0.8.1';
const VERSION = '0.8.3';
private $options;
private $profile;
private $connection;
private $connections;
/**
* Initializes a new client with optional connection parameters and client options.
@@ -50,7 +49,6 @@ class Client implements ClientInterface
{
$this->options = $this->filterOptions($options);
$this->profile = $this->options->profile;
$this->connections = $this->options->connections;
$this->connection = $this->initializeConnection($parameters);
}
@@ -64,7 +62,7 @@ class Client implements ClientInterface
*/
protected function filterOptions($options)
{
if ($options === null) {
if (!isset($options)) {
return new ClientOptions();
}
@@ -94,13 +92,26 @@ class Client implements ClientInterface
}
if (is_array($parameters) && isset($parameters[0])) {
$replication = isset($this->options->replication) && $this->options->replication;
$connection = $this->options->{$replication ? 'replication' : 'cluster'};
$options = $this->options;
$replication = isset($options->replication) && $options->replication;
$connection = $options->{$replication ? 'replication' : 'cluster'};
return $this->connections->createAggregated($connection, $parameters);
return $options->connections->createAggregated($connection, $parameters);
}
return $this->connections->create($parameters);
if (is_callable($parameters)) {
$connection = call_user_func($parameters, $this->options);
if (!$connection instanceof ConnectionInterface) {
throw new \InvalidArgumentException(
'Callable parameters must return instances of Predis\Connection\ConnectionInterface'
);
}
return $connection;
}
return $this->options->connections->create($parameters);
}
/**
@@ -126,7 +137,7 @@ class Client implements ClientInterface
*/
public function getConnectionFactory()
{
return $this->connections;
return $this->options->connections;
}
/**
@@ -138,7 +149,7 @@ class Client implements ClientInterface
*/
public function getClientFor($connectionID)
{
if (($connection = $this->getConnectionById($connectionID)) === null) {
if (!$connection = $this->getConnectionById($connectionID)) {
throw new \InvalidArgumentException("Invalid connection ID: '$connectionID'");
}
@@ -269,14 +280,14 @@ class Client implements ClientInterface
$response = $this->executeCommand($eval);
if (false === $response instanceof ResponseObjectInterface) {
if (!$response instanceof ResponseObjectInterface) {
$response = $command->parseResponse($response);
}
return $response;
}
if ($this->options->exceptions === true) {
if ($this->options->exceptions) {
throw new ServerException($response->getMessage());
}
+1 -1
View File
@@ -122,7 +122,7 @@ class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
return;
}
if (count($this->nodes) === 0) {
if (!$this->nodes) {
throw new EmptyRingException('Cannot initialize empty hashring');
}
@@ -64,7 +64,7 @@ class CRC16HashGenerator implements HashGeneratorInterface
$strlen = strlen($value);
for ($i = 0; $i < $strlen; $i++) {
$crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 65535; // 0xFFFF
$crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
}
return $crc;
@@ -294,9 +294,11 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
*/
protected function getKeyFromScriptingCommands(CommandInterface $command)
{
$keys = $command instanceof ScriptedCommand
? $command->getKeys()
: array_slice($args = $command->getArguments(), 2, $args[1]);
if ($command instanceof ScriptedCommand) {
$keys = $command->getKeys();
} else {
$keys = array_slice($args = $command->getArguments(), 2, $args[1]);
}
if ($keys && $this->checkSameHashForKeys($keys)) {
return $keys[0];
@@ -341,7 +343,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
*/
protected function checkSameHashForKeys(Array $keys)
{
if (($count = count($keys)) === 0) {
if (!$count = count($keys)) {
return false;
}
@@ -246,9 +246,11 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
*/
protected function getKeyFromScriptingCommands(CommandInterface $command)
{
$keys = $command instanceof ScriptedCommand
? $command->getKeys()
: array_slice($args = $command->getArguments(), 2, $args[1]);
if ($command instanceof ScriptedCommand) {
$keys = $command->getKeys();
} else {
$keys = array_slice($args = $command->getArguments(), 2, $args[1]);
}
if (count($keys) === 1) {
return $keys[0];
+32 -2
View File
@@ -65,9 +65,9 @@ abstract class AbstractCommand implements CommandInterface
*
* @param array $arguments Position of the argument.
*/
public function getArgument($index = 0)
public function getArgument($index)
{
if (isset($this->arguments[$index]) === true) {
if (isset($this->arguments[$index])) {
return $this->arguments[$index];
}
}
@@ -129,4 +129,34 @@ abstract class AbstractCommand implements CommandInterface
$this->getId()
);
}
/**
* Normalizes the arguments array passed to a Redis command.
*
* @param array $arguments Arguments for a command.
* @return array
*/
public static function normalizeArguments(Array $arguments)
{
if (count($arguments) === 1 && is_array($arguments[0])) {
return $arguments[0];
}
return $arguments;
}
/**
* Normalizes the arguments array passed to a variadic Redis command.
*
* @param array $arguments Arguments for a command.
* @return array
*/
public static function normalizeVariadic(Array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
return array_merge(array($arguments[0]), $arguments[1]);
}
return $arguments;
}
}
+7
View File
@@ -59,6 +59,13 @@ interface CommandInterface
*/
public function getArguments();
/**
* Gets the argument of the command at the specified index.
*
* @return array
*/
public function getArgument($index);
/**
* Parses a reply buffer and returns a PHP object.
*
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/hdel
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,6 +30,6 @@ class HashDelete extends PrefixableCommand
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterVariadicValues($arguments);
return self::normalizeVariadic($arguments);
}
}
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/hmget
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,6 +30,6 @@ class HashGetMultiple extends PrefixableCommand
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterVariadicValues($arguments);
return self::normalizeVariadic($arguments);
}
}
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/del
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,7 +30,7 @@ class KeyDelete extends AbstractCommand implements PrefixableCommandInterface
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterArrayArguments($arguments);
return self::normalizeArguments($arguments);
}
/**
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/rpush
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,6 +30,6 @@ class ListPushTail extends PrefixableCommand
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterVariadicValues($arguments);
return self::normalizeVariadic($arguments);
}
}
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/subscribe
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,7 +30,7 @@ class PubSubSubscribe extends AbstractCommand implements PrefixableCommandInterf
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterArrayArguments($arguments);
return self::normalizeArguments($arguments);
}
/**
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/psubscribe
* @author Daniele Alessandri <suppakilla@gmail.com>
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/unsubscribe
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,7 +30,7 @@ class PubSubUnsubscribe extends AbstractCommand implements PrefixableCommandInte
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterArrayArguments($arguments);
return self::normalizeArguments($arguments);
}
/**
+6 -12
View File
@@ -30,19 +30,15 @@ abstract class ScriptedCommand extends ServerEvalSHA
/**
* Specifies the number of arguments that should be considered as keys.
*
* The default behaviour for the base class is to return FALSE to indicate that
* The default behaviour for the base class is to return 0 to indicate that
* all the elements of the arguments array should be considered as keys, but
* subclasses can enforce a static number of keys.
*
* @todo How about returning 1 by default to make scripted commands act like
* variadic ones where the first argument is the key (KEYS[1]) and the
* rest are values (ARGV)?
*
* @return int|Boolean
* @return int
*/
protected function getKeysCount()
{
return false;
return 0;
}
/**
@@ -60,13 +56,11 @@ abstract class ScriptedCommand extends ServerEvalSHA
*/
protected function filterArguments(Array $arguments)
{
if (false !== $numkeys = $this->getKeysCount()) {
$numkeys = $numkeys >= 0 ? $numkeys : count($arguments) + $numkeys;
} else {
$numkeys = count($arguments);
if (($numkeys = $this->getKeysCount()) && $numkeys < 0) {
$numkeys = count($arguments) + $numkeys;
}
return array_merge(array(sha1($this->getScript()), $numkeys), $arguments);
return array_merge(array(sha1($this->getScript()), (int) $numkeys), $arguments);
}
/**
+2 -1
View File
@@ -35,8 +35,9 @@ class ServerClient extends AbstractCommand
switch (strtoupper($args[0])) {
case 'LIST':
return $this->parseClientList($data);
case 'KILL':
case 'GETNAME':
case 'SETNAME':
default:
return $data;
}
-2
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/object
* @author Daniele Alessandri <suppakilla@gmail.com>
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/sadd
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,6 +30,6 @@ class SetAdd extends PrefixableCommand
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterVariadicValues($arguments);
return self::normalizeVariadic($arguments);
}
}
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/sinter
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,7 +30,7 @@ class SetIntersection extends AbstractCommand implements PrefixableCommandInterf
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterArrayArguments($arguments);
return self::normalizeArguments($arguments);
}
/**
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/srem
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,6 +30,6 @@ class SetRemove extends PrefixableCommand
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterVariadicValues($arguments);
return self::normalizeVariadic($arguments);
}
}
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/mget
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,7 +30,7 @@ class StringGetMultiple extends AbstractCommand implements PrefixableCommandInte
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterArrayArguments($arguments);
return self::normalizeArguments($arguments);
}
/**
-2
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/zadd
* @author Daniele Alessandri <suppakilla@gmail.com>
+1 -3
View File
@@ -11,8 +11,6 @@
namespace Predis\Command;
use Predis\Helpers;
/**
* @link http://redis.io/commands/zrem
* @author Daniele Alessandri <suppakilla@gmail.com>
@@ -32,6 +30,6 @@ class ZSetRemove extends PrefixableCommand
*/
protected function filterArguments(Array $arguments)
{
return Helpers::filterVariadicValues($arguments);
return self::normalizeVariadic($arguments);
}
}
+19
View File
@@ -54,4 +54,23 @@ abstract class CommunicationException extends PredisException
{
return true;
}
/**
* Offers a generic and reusable method to handle exceptions generated by
* a connection object.
*
* @param CommunicationException $exception Exception.
*/
public static function handle(CommunicationException $exception)
{
if ($exception->shouldResetConnection()) {
$connection = $exception->getConnection();
if ($connection->isConnected()) {
$connection->disconnect();
}
}
throw $exception;
}
}
+3 -3
View File
@@ -12,7 +12,7 @@
namespace Predis\Connection;
use Predis\ClientException;
use Predis\Helpers;
use Predis\CommunicationException;
use Predis\NotSupportedException;
use Predis\Command\CommandInterface;
use Predis\Protocol\ProtocolException;
@@ -136,7 +136,7 @@ abstract class AbstractConnection implements SingleConnectionInterface
*/
protected function onConnectionError($message, $code = null)
{
Helpers::onCommunicationException(new ConnectionException($this, $message, $code));
CommunicationException::handle(new ConnectionException($this, $message, $code));
}
/**
@@ -146,7 +146,7 @@ abstract class AbstractConnection implements SingleConnectionInterface
*/
protected function onProtocolError($message)
{
Helpers::onCommunicationException(new ProtocolException($this, $message));
CommunicationException::handle(new ProtocolException($this, $message));
}
/**
@@ -31,12 +31,8 @@ class ComposableStreamConnection extends StreamConnection implements ComposableC
*/
public function __construct(ConnectionParametersInterface $parameters, ProtocolInterface $protocol = null)
{
$protocol = $protocol ?: new TextProtocol();
$protocol->setOption('iterable_multibulk', $parameters->iterable_multibulk);
$this->mbiterable = null;
$this->protocol = $protocol;
$this->parameters = $this->checkParameters($parameters);
$this->protocol = $protocol ?: new TextProtocol();
}
/**
@@ -134,6 +130,6 @@ class ComposableStreamConnection extends StreamConnection implements ComposableC
*/
public function __sleep()
{
return array_merge(parent::__sleep(), array('protocol'));
return array_diff(array_merge(parent::__sleep(), array('protocol')), array('mbiterable'));
}
}
@@ -28,7 +28,6 @@ class ConnectionParameters implements ConnectionParametersInterface
'host' => '127.0.0.1',
'port' => 6379,
'timeout' => 5.0,
'iterable_multibulk' => false,
);
/**
@@ -37,7 +36,7 @@ class ConnectionParameters implements ConnectionParametersInterface
public function __construct($parameters = array())
{
if (!is_array($parameters)) {
$parameters = $this->parseURI($parameters);
$parameters = self::parseURI($parameters);
}
$this->parameters = $this->filter($parameters) + $this->getDefaults();
@@ -109,14 +108,14 @@ class ConnectionParameters implements ConnectionParametersInterface
* @param string $uri Connection string.
* @return array
*/
private function parseURI($uri)
public static function parseURI($uri)
{
if (stripos($uri, 'unix') === 0) {
// Hack to support URIs for UNIX sockets with minimal effort.
$uri = str_ireplace('unix:///', 'unix://localhost/', $uri);
}
if (($parsed = @parse_url($uri)) === false || !isset($parsed['host'])) {
if (!($parsed = @parse_url($uri)) || !isset($parsed['host'])) {
throw new ClientException("Invalid URI: $uri");
}
@@ -140,7 +139,7 @@ class ConnectionParameters implements ConnectionParametersInterface
*/
private function filter(Array $parameters)
{
if (count($parameters) > 0) {
if ($parameters) {
$casters = array_intersect_key($this->getValueCasters(), $parameters);
foreach ($casters as $parameter => $caster) {
@@ -41,7 +41,7 @@ use Predis\Command\CommandInterface;
* - timeout: timeout to perform the connection.
* - read_write_timeout: timeout of read / write operations.
*
* @link http://github.com/seppo0010/phpiredis
* @link http://github.com/nrk/phpiredis
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PhpiredisConnection extends AbstractConnection
@@ -90,10 +90,10 @@ class PhpiredisConnection extends AbstractConnection
*/
protected function checkParameters(ConnectionParametersInterface $parameters)
{
if ($parameters->iterable_multibulk === true) {
if (isset($parameters->iterable_multibulk)) {
$this->onInvalidOption('iterable_multibulk', $parameters);
}
if ($parameters->persistent === true) {
if (isset($parameters->persistent)) {
$this->onInvalidOption('persistent', $parameters);
}
@@ -296,7 +296,7 @@ class PhpiredisConnection extends AbstractConnection
$this->connectWithTimeout($this->parameters);
if (count($this->initCmds) > 0) {
if ($this->initCmds) {
$this->sendInitializationCommands();
}
}
@@ -0,0 +1,197 @@
<?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\Connection;
use Predis\NotSupportedException;
use Predis\ResponseError;
use Predis\ResponseQueued;
use Predis\Command\CommandInterface;
/**
* This class provides the implementation of a Predis connection that uses PHP's
* streams for network communication and wraps the phpiredis C extension (PHP
* bindings for hiredis) to parse and serialize the Redis protocol. Everything
* is highly experimental (even the very same phpiredis since it is quite new),
* so use it at your own risk.
*
* This class is mainly intended to provide an optional low-overhead alternative
* for processing replies from Redis compared to the standard pure-PHP classes.
* Differences in speed when dealing with short inline replies are practically
* nonexistent, the actual speed boost is for long multibulk replies when this
* protocol processor can parse and return replies very fast.
*
* For instructions on how to build and install the phpiredis extension, please
* consult the repository of the project.
*
* The connection parameters supported by this class are:
*
* - scheme: it can be either 'tcp' or 'unix'.
* - host: hostname or IP address of the server.
* - port: TCP port of the server.
* - timeout: timeout to perform the connection.
* - read_write_timeout: timeout of read / write operations.
* - async_connect: performs the connection asynchronously.
* - tcp_nodelay: enables or disables Nagle's algorithm for coalescing.
* - persistent: the connection is left intact after a GC collection.
*
* @link https://github.com/nrk/phpiredis
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PhpiredisStreamConnection extends StreamConnection
{
private $reader;
/**
* {@inheritdoc}
*/
public function __construct(ConnectionParametersInterface $parameters)
{
$this->checkExtensions();
$this->initializeReader();
parent::__construct($parameters);
}
/**
* {@inheritdoc}
*/
public function __destruct()
{
phpiredis_reader_destroy($this->reader);
parent::__destruct();
}
/**
* Checks if the phpiredis extension is loaded in PHP.
*/
protected function checkExtensions()
{
if (!function_exists('phpiredis_reader_create')) {
throw new NotSupportedException(
'The phpiredis extension must be loaded in order to be able to use this connection class'
);
}
}
/**
* {@inheritdoc}
*/
protected function checkParameters(ConnectionParametersInterface $parameters)
{
if (isset($parameters->iterable_multibulk)) {
$this->onInvalidOption('iterable_multibulk', $parameters);
}
return parent::checkParameters($parameters);
}
/**
* Initializes the protocol reader resource.
*/
protected function initializeReader()
{
$reader = phpiredis_reader_create();
phpiredis_reader_set_status_handler($reader, $this->getStatusHandler());
phpiredis_reader_set_error_handler($reader, $this->getErrorHandler());
$this->reader = $reader;
}
/**
* Gets the handler used by the protocol reader to handle status replies.
*
* @return \Closure
*/
protected function getStatusHandler()
{
return function ($payload) {
switch ($payload) {
case 'OK':
return true;
case 'QUEUED':
return new ResponseQueued();
default:
return $payload;
}
};
}
/**
* Gets the handler used by the protocol reader to handle Redis errors.
*
* @param Boolean $throw_errors Specify if Redis errors throw exceptions.
* @return \Closure
*/
protected function getErrorHandler()
{
return function ($errorMessage) {
return new ResponseError($errorMessage);
};
}
/**
* {@inheritdoc}
*/
public function read()
{
$socket = $this->getResource();
$reader = $this->reader;
while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) {
$buffer = fread($socket, 4096);
if ($buffer === false || $buffer === '') {
$this->onConnectionError('Error while reading bytes from the server');
return;
}
phpiredis_reader_feed($reader, $buffer);
}
if ($state === PHPIREDIS_READER_STATE_COMPLETE) {
return phpiredis_reader_get_reply($reader);
} else {
$this->onProtocolError(phpiredis_reader_get_error($reader));
}
}
/**
* {@inheritdoc}
*/
public function writeCommand(CommandInterface $command)
{
$cmdargs = $command->getArguments();
array_unshift($cmdargs, $command->getId());
$this->writeBytes(phpiredis_format_command($cmdargs));
}
/**
* {@inheritdoc}
*/
public function __sleep()
{
return array_diff(parent::__sleep(), array('mbiterable'));
}
/**
* {@inheritdoc}
*/
public function __wakeup()
{
$this->checkExtensions();
$this->initializeReader();
}
}
+4 -4
View File
@@ -135,7 +135,7 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
public function buildSlotsMap()
{
$this->slotsMap = array();
$this->slotsPerNode = (int) (4096 / count($this->pool));
$this->slotsPerNode = (int) (16384 / count($this->pool));
foreach ($this->pool as $connectionID => $connection) {
$parameters = $connection->getParameters();
@@ -177,7 +177,7 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
*/
public function setSlots($first, $last, $connection)
{
if ($first < 0 || $first > 4095 || $last < 0 || $last > 4095 || $last < $first) {
if ($first < 0x0000 || $first > 0x3FFF || $last < 0x0000 || $last > 0x3FFF || $last < $first) {
throw new \OutOfBoundsException("Invalid slot values for $connection: [$first-$last]");
}
@@ -195,7 +195,7 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
throw new NotSupportedException("Cannot use {$command->getId()} with redis-cluster");
}
$slot = $hash & 4095; // 0x0FFF
$slot = $hash & 0x3FFF;
if (isset($this->slots[$slot])) {
return $this->slots[$slot];
@@ -214,7 +214,7 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
*/
public function getConnectionBySlot($slot)
{
if ($slot < 0 || $slot > 4095) {
if ($slot < 0x0000 || $slot > 0x3FFF) {
throw new \OutOfBoundsException("Invalid slot value [$slot]");
}
+12 -6
View File
@@ -27,6 +27,7 @@ use Predis\Iterator\MultiBulkResponseSimple;
* - timeout: timeout to perform the connection.
* - read_write_timeout: timeout of read / write operations.
* - async_connect: performs the connection asynchronously.
* - tcp_nodelay: enables or disables Nagle's algorithm for coalescing.
* - persistent: the connection is left intact after a GC collection.
* - iterable_multibulk: multibulk replies treated as iterable objects.
*
@@ -53,7 +54,7 @@ class StreamConnection extends AbstractConnection
*/
public function __destruct()
{
if (!$this->parameters->persistent) {
if (isset($this->parameters) && !$this->parameters->persistent) {
$this->disconnect();
}
}
@@ -80,10 +81,10 @@ class StreamConnection extends AbstractConnection
$uri = "tcp://{$parameters->host}:{$parameters->port}/";
$flags = STREAM_CLIENT_CONNECT;
if (isset($parameters->async_connect) && $parameters->async_connect === true) {
if (isset($parameters->async_connect) && $parameters->async_connect) {
$flags |= STREAM_CLIENT_ASYNC_CONNECT;
}
if (isset($parameters->persistent) && $parameters->persistent === true) {
if (isset($parameters->persistent) && $parameters->persistent) {
$flags |= STREAM_CLIENT_PERSISTENT;
}
@@ -101,6 +102,11 @@ class StreamConnection extends AbstractConnection
stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds);
}
if (isset($parameters->tcp_nodelay) && version_compare(PHP_VERSION, '5.4.0') >= 0) {
$socket = socket_import_stream($resource);
socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay);
}
return $resource;
}
@@ -115,7 +121,7 @@ class StreamConnection extends AbstractConnection
$uri = "unix://{$parameters->path}";
$flags = STREAM_CLIENT_CONNECT;
if ($parameters->persistent === true) {
if ($parameters->persistent) {
$flags |= STREAM_CLIENT_PERSISTENT;
}
@@ -135,7 +141,7 @@ class StreamConnection extends AbstractConnection
{
parent::connect();
if (count($this->initCmds) > 0){
if ($this->initCmds) {
$this->sendInitializationCommands();
}
}
@@ -244,7 +250,7 @@ class StreamConnection extends AbstractConnection
if ($count === -1) {
return null;
}
if ($this->mbiterable === true) {
if ($this->mbiterable) {
return new MultiBulkResponseSimple($this, $count);
}
+4
View File
@@ -19,6 +19,7 @@ use Predis\Connection\ConnectionInterface;
* Defines a few helper methods.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
* @deprecated Deprecated since v0.8.3.
*/
class Helpers
{
@@ -26,6 +27,7 @@ class Helpers
* Offers a generic and reusable method to handle exceptions generated by
* a connection object.
*
* @deprecated Deprecated since v0.8.3 - moved in Predis\CommunicationException::handle()
* @param CommunicationException $exception Exception.
*/
public static function onCommunicationException(CommunicationException $exception)
@@ -44,6 +46,7 @@ class Helpers
/**
* Normalizes the arguments array passed to a Redis command.
*
* @deprecated Deprecated since v0.8.3 - moved in Predis\Command\AbstractCommand::normalizeArguments()
* @param array $arguments Arguments for a command.
* @return array
*/
@@ -59,6 +62,7 @@ class Helpers
/**
* Normalizes the arguments array passed to a variadic Redis command.
*
* @deprecated Deprecated since v0.8.3 - moved in Predis\Command\AbstractCommand::normalizeVariadic()
* @param array $arguments Arguments for a command.
* @return array
*/
+19
View File
@@ -103,4 +103,23 @@ class ClientOptions implements ClientOptionsInterface
return $value;
}
}
/**
* Returns the default value for the specified option.
*
* @param string|OptionInterface $option Name or instance of the option.
* @return mixed
*/
public function getDefault($option)
{
if ($option instanceof OptionInterface) {
return $option->getDefault($this);
}
$options = $this->getDefaultOptions();
if (isset($options[$option])) {
return $options[$option]->getDefault($this);
}
}
}
-1
View File
@@ -16,7 +16,6 @@ use Predis\BasicClientInterface;
use Predis\ClientException;
use Predis\ClientInterface;
use Predis\ExecutableContextInterface;
use Predis\Helpers;
use Predis\Command\CommandInterface;
/**
+37 -10
View File
@@ -11,9 +11,12 @@
namespace Predis\Pipeline;
use Iterator;
use SplQueue;
use Predis\ResponseErrorInterface;
use Predis\ResponseObjectInterface;
use Predis\ServerException;
use Predis\Command\CommandInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\ReplicationConnectionInterface;
@@ -50,6 +53,27 @@ class StandardExecutor implements PipelineExecutorInterface
}
}
/**
* Handles a response object.
*
* @param ConnectionInterface $connection
* @param CommandInterface $command
* @param ResponseObjectInterface $response
* @return mixed
*/
protected function onResponseObject(ConnectionInterface $connection, CommandInterface $command, ResponseObjectInterface $response)
{
if ($response instanceof ResponseErrorInterface) {
return $this->onResponseError($connection, $response);
}
if ($response instanceof Iterator) {
return $command->parseResponse(iterator_to_array($response));
}
return $response;
}
/**
* Handles -ERR responses returned by Redis.
*
@@ -58,6 +82,10 @@ class StandardExecutor implements PipelineExecutorInterface
*/
protected function onResponseError(ConnectionInterface $connection, ResponseErrorInterface $response)
{
if (!$this->exceptions) {
return $response;
}
// Force disconnection to prevent protocol desynchronization.
$connection->disconnect();
$message = $response->getMessage();
@@ -70,24 +98,23 @@ class StandardExecutor implements PipelineExecutorInterface
*/
public function execute(ConnectionInterface $connection, SplQueue $commands)
{
$size = count($commands);
$values = array();
$exceptions = $this->exceptions;
$this->checkConnection($connection);
foreach ($commands as $command) {
$connection->writeCommand($command);
}
for ($i = 0; $i < $size; $i++) {
$response = $connection->readResponse($commands->dequeue());
$values = array();
if ($response instanceof ResponseErrorInterface && $exceptions === true) {
$this->onResponseError($connection, $response);
while (!$commands->isEmpty()) {
$command = $commands->dequeue();
$response = $connection->readResponse($command);
if ($response instanceof ResponseObjectInterface) {
$values[] = $this->onResponseObject($connection, $command, $response);
} else {
$values[] = $command->parseResponse($response);
}
$values[$i] = $response instanceof \Iterator ? iterator_to_array($response) : $response;
}
return $values;
+1 -1
View File
@@ -130,7 +130,7 @@ abstract class ServerProfile implements ServerProfileInterface, CommandProcessin
public function supportsCommands(Array $commands)
{
foreach ($commands as $command) {
if ($this->supportsCommand($command) === false) {
if (!$this->supportsCommand($command)) {
return false;
}
}
@@ -11,7 +11,7 @@
namespace Predis\Protocol\Text;
use Predis\Helpers;
use Predis\CommunicationException;
use Predis\Connection\ComposableConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Protocol\ResponseHandlerInterface;
@@ -37,7 +37,7 @@ class ResponseBulkHandler implements ResponseHandlerInterface
$length = (int) $lengthString;
if ("$length" !== $lengthString) {
Helpers::onCommunicationException(new ProtocolException(
CommunicationException::handle(new ProtocolException(
$connection, "Cannot parse '$lengthString' as bulk length"
));
}
@@ -11,7 +11,7 @@
namespace Predis\Protocol\Text;
use Predis\Helpers;
use Predis\CommunicationException;
use Predis\Connection\ComposableConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Protocol\ResponseHandlerInterface;
@@ -39,7 +39,7 @@ class ResponseIntegerHandler implements ResponseHandlerInterface
}
if ($number !== 'nil') {
Helpers::onCommunicationException(new ProtocolException(
CommunicationException::handle(new ProtocolException(
$connection, "Cannot parse '$number' as numeric response"
));
}
@@ -11,7 +11,7 @@
namespace Predis\Protocol\Text;
use Predis\Helpers;
use Predis\CommunicationException;
use Predis\Connection\ComposableConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Protocol\ResponseHandlerInterface;
@@ -37,7 +37,7 @@ class ResponseMultiBulkHandler implements ResponseHandlerInterface
$length = (int) $lengthString;
if ("$length" !== $lengthString) {
Helpers::onCommunicationException(new ProtocolException(
CommunicationException::handle(new ProtocolException(
$connection, "Cannot parse '$lengthString' as multi-bulk length"
));
}
@@ -11,7 +11,7 @@
namespace Predis\Protocol\Text;
use Predis\Helpers;
use Predis\CommunicationException;
use Predis\Connection\ComposableConnectionInterface;
use Predis\Iterator\MultiBulkResponseSimple;
use Predis\Protocol\ProtocolException;
@@ -38,7 +38,7 @@ class ResponseMultiBulkStreamHandler implements ResponseHandlerInterface
$length = (int) $lengthString;
if ("$length" != $lengthString) {
Helpers::onCommunicationException(new ProtocolException(
CommunicationException::handle(new ProtocolException(
$connection, "Cannot parse '$lengthString' as multi-bulk length"
));
}
+3 -3
View File
@@ -11,7 +11,7 @@
namespace Predis\Protocol\Text;
use Predis\Helpers;
use Predis\CommunicationException;
use Predis\ResponseError;
use Predis\ResponseQueued;
use Predis\ServerException;
@@ -98,7 +98,7 @@ class TextProtocol implements ProtocolInterface
if ($count === -1) {
return null;
}
if ($this->mbiterable == true) {
if ($this->mbiterable) {
return new MultiBulkResponseSimple($connection, $count);
}
@@ -117,7 +117,7 @@ class TextProtocol implements ProtocolInterface
return new ResponseError($payload);
default:
Helpers::onCommunicationException(new ProtocolException(
CommunicationException::handle(new ProtocolException(
$connection, "Unknown prefix: '$prefix'"
));
}
@@ -11,7 +11,7 @@
namespace Predis\Protocol\Text;
use Predis\Helpers;
use Predis\CommunicationException;
use Predis\Connection\ComposableConnectionInterface;
use Predis\Protocol\ProtocolException;
use Predis\Protocol\ResponseHandlerInterface;
@@ -108,6 +108,6 @@ class TextResponseReader implements ResponseReaderInterface
*/
private function protocolError(ComposableConnectionInterface $connection, $message)
{
Helpers::onCommunicationException(new ProtocolException($connection, $message));
CommunicationException::handle(new ProtocolException($connection, $message));
}
}
@@ -13,7 +13,6 @@ namespace Predis\PubSub;
use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Helpers;
use Predis\NotSupportedException;
/**
+2 -2
View File
@@ -13,7 +13,7 @@ namespace Predis\PubSub;
use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Helpers;
use Predis\Command\AbstractCommand as Command;
use Predis\NotSupportedException;
use Predis\Connection\AggregatedConnectionInterface;
@@ -77,7 +77,7 @@ class PubSubContext extends AbstractPubSubContext
*/
protected function writeCommand($method, $arguments)
{
$arguments = Helpers::filterArrayArguments($arguments);
$arguments = Command::normalizeArguments($arguments);
$command = $this->client->createCommand($method, $arguments);
$this->client->getConnection()->writeCommand($command);
}
+141
View File
@@ -0,0 +1,141 @@
<?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\Session;
use SessionHandlerInterface;
use Predis\ClientInterface;
/**
* Session handler class that relies on Predis\Client to store PHP's sessions
* data into one or multiple Redis servers.
*
* This class is mostly intended for PHP 5.4 but it can be used under PHP 5.3 provided
* that a polyfill for `SessionHandlerInterface` is defined by either you or an external
* package such as `symfony/http-foundation`.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class SessionHandler implements SessionHandlerInterface
{
protected $client;
protected $ttl;
/**
* @param ClientInterface $client Fully initialized client instance.
* @param array $options Session handler options.
*/
public function __construct(ClientInterface $client, Array $options = array())
{
$this->client = $client;
$this->ttl = (int) (isset($options['gc_maxlifetime']) ? $options['gc_maxlifetime'] : ini_get('session.gc_maxlifetime'));
}
/**
* Registers the handler instance as the current session handler.
*/
public function register()
{
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
session_set_save_handler($this, true);
} else {
session_set_save_handler(
array($this, 'open'),
array($this, 'close'),
array($this, 'read'),
array($this, 'write'),
array($this, 'destroy'),
array($this, 'gc')
);
}
}
/**
* {@inheritdoc}
*/
public function open($save_path, $session_id)
{
// NOOP
return true;
}
/**
* {@inheritdoc}
*/
public function close()
{
// NOOP
return true;
}
/**
* {@inheritdoc}
*/
public function gc($maxlifetime)
{
// NOOP
return true;
}
/**
* {@inheritdoc}
*/
public function read($session_id)
{
if ($data = $this->client->get($session_id)) {
return $data;
}
return '';
}
/**
* {@inheritdoc}
*/
public function write($session_id, $session_data)
{
$this->client->setex($session_id, $this->ttl, $session_data);
return true;
}
/**
* {@inheritdoc}
*/
public function destroy($session_id)
{
$this->client->del($session_id);
return true;
}
/**
* Returns the underlying client instance.
*
* @return ClientInterface
*/
public function getClient()
{
return $this->client;
}
/**
* Returns the session max lifetime value.
*
* @return int
*/
public function getMaxLifeTime()
{
return $this->ttl;
}
}
+1 -2
View File
@@ -17,7 +17,6 @@ use Predis\ClientException;
use Predis\ClientInterface;
use Predis\CommunicationException;
use Predis\ExecutableContextInterface;
use Predis\Helpers;
use Predis\NotSupportedException;
use Predis\ResponseErrorInterface;
use Predis\ResponseQueued;
@@ -443,7 +442,7 @@ class MultiExecContext implements BasicClientInterface, ExecutableContextInterfa
// Since a MULTI/EXEC block cannot be initialized when using aggregated
// connections, we can safely assume that Predis\Client::getConnection()
// will always return an instance of Predis\Connection\SingleConnectionInterface.
Helpers::onCommunicationException(new ProtocolException(
CommunicationException::handle(new ProtocolException(
$this->client->getConnection(), $message
));
}
+1 -1
View File
@@ -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.1"
version = "0.8.3"
stability = "stable"
channel = "pear.nrk.io"
+26
View File
@@ -159,6 +159,32 @@ abstract class CommandTestCase extends StandardTestCase
$this->assertEquals($this->getExpectedId(), $command->getId());
}
/**
* @param string $expectedVersion
* @param string $message Optional message.
* @throws \RuntimeException when unable to retrieve server info or redis version
* @throws \PHPUnit_Framework_SkippedTestError when expected redis version is not met
*/
protected function markTestSkippedOnRedisVersionBelow($expectedVersion, $message = '')
{
$client = $this->getClient();
$info = array_change_key_case($client->info());
if (isset($info['server']['redis_version'])) {
// Redis >= 2.6
$version = $info['server']['redis_version'];
} else if (isset($info['redis_version'])) {
// Redis < 2.6
$version = $info['redis_version'];
} else {
throw new \RuntimeException('Unable to retrieve server info');
}
if (version_compare($version, $expectedVersion) <= -1) {
$this->markTestSkipped($message ?: "Test requires Redis $expectedVersion, current is $version.");
}
}
/**
* @group disconnected
*/
+36
View File
@@ -197,6 +197,42 @@ class ClientTest extends StandardTestCase
$this->assertSame($replication, $client->getConnection());
}
/**
* @group disconnected
*/
public function testConstructorWithCallableArgument()
{
$connection = $this->getMock('Predis\Connection\ConnectionInterface');
$callable = $this->getMock('stdClass', array('__invoke'));
$callable->expects($this->once())
->method('__invoke')
->with($this->isInstanceOf('Predis\Option\ClientOptions'))
->will($this->returnValue($connection));
$client = new Client($callable);
$this->assertSame($connection, $client->getConnection());
}
/**
* @group disconnected
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Callable parameters must return instances of Predis\Connection\ConnectionInterface
*/
public function testConstructorWithCallableArgumentButInvalidReturnType()
{
$wrongType = $this->getMock('stdClass');
$callable = $this->getMock('stdClass', array('__invoke'));
$callable->expects($this->once())
->method('__invoke')
->with($this->isInstanceOf('Predis\Option\ClientOptions'))
->will($this->returnValue($wrongType));
$client = new Client($callable);
}
/**
* @group disconnected
*/
@@ -26,7 +26,9 @@ class PredisClusterHashStrategyTest extends StandardTestCase
*/
public function testSupportsKeyTags()
{
$expected = -1938594527;
// NOTE: 32 and 64 bits PHP runtimes can produce different hash values.
$expected = PHP_INT_SIZE == 4 ? -1938594527 : 2356372769;
$strategy = $this->getHashStrategy();
$this->assertSame($expected, $strategy->getKeyHash('{foo}'));
+31
View File
@@ -140,4 +140,35 @@ class CommandTest extends StandardTestCase
$this->assertEquals($expected, (string) $command);
}
/**
* @group disconnected
*/
public function testNormalizeArguments()
{
$arguments = array('arg1', 'arg2', 'arg3', 'arg4');
$this->assertSame($arguments, AbstractCommand::normalizeArguments($arguments));
$this->assertSame($arguments, AbstractCommand::normalizeArguments(array($arguments)));
$arguments = array(array(), array());
$this->assertSame($arguments, AbstractCommand::normalizeArguments($arguments));
$arguments = array(new \stdClass());
$this->assertSame($arguments, AbstractCommand::normalizeArguments($arguments));
}
/**
* @group disconnected
*/
public function testNormalizeVariadic()
{
$arguments = array('key', 'value1', 'value2', 'value3');
$this->assertSame($arguments, AbstractCommand::normalizeVariadic($arguments));
$this->assertSame($arguments, AbstractCommand::normalizeVariadic(array('key', array('value1', 'value2', 'value3'))));
$arguments = array(new \stdClass());
$this->assertSame($arguments, AbstractCommand::normalizeVariadic($arguments));
}
}
@@ -60,6 +60,25 @@ class ScriptedCommandTest extends StandardTestCase
$this->assertSame(array_merge(array(self::LUA_SCRIPT_SHA1, 2), $arguments), $command->getArguments());
}
/**
* @group disconnected
*/
public function testGetArgumentsWithZeroKeysCount()
{
$arguments = array('value1', 'value2', 'value3');
$command = $this->getMock('Predis\Command\ScriptedCommand', array('getScript', 'getKeysCount'));
$command->expects($this->once())
->method('getScript')
->will($this->returnValue(self::LUA_SCRIPT));
$command->expects($this->once())
->method('getKeysCount')
->will($this->returnValue(0));
$command->setArguments($arguments);
$this->assertSame(array_merge(array(self::LUA_SCRIPT_SHA1, 0), $arguments), $command->getArguments());
}
/**
* @group disconnected
*/
@@ -79,6 +98,25 @@ class ScriptedCommandTest extends StandardTestCase
$this->assertSame(array('key1', 'key2'), $command->getKeys());
}
/**
* @group disconnected
*/
public function testGetKeysWithZeroKeysCount()
{
$arguments = array('value1', 'value2', 'value3');
$command = $this->getMock('Predis\Command\ScriptedCommand', array('getScript', 'getKeysCount'));
$command->expects($this->once())
->method('getScript')
->will($this->returnValue(self::LUA_SCRIPT));
$command->expects($this->exactly(2))
->method('getKeysCount')
->will($this->returnValue(0));
$command->setArguments($arguments);
$this->assertSame(array(), $command->getKeys());
}
/**
* @group disconnected
*/
+81
View File
@@ -63,6 +63,32 @@ class ServerClientTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testFilterArgumentsOfClientGetname()
{
$arguments = $expected = array('getname');
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testFilterArgumentsOfClientSetname()
{
$arguments = $expected = array('setname', 'connection-a');
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
@@ -117,6 +143,61 @@ BUFFER;
$this->assertArrayHasKey('psub', $clients[0]);
}
/**
* @group connected
*/
public function testGetsNameOfConnection()
{
$this->markTestSkippedOnRedisVersionBelow('2.6.9');
$redis = $this->getClient();
$clientName = $redis->client('GETNAME');
$this->assertNull($clientName);
$expectedConnectionName = 'foo-bar';
$this->assertTrue($redis->client('SETNAME', $expectedConnectionName));
$this->assertEquals($expectedConnectionName, $redis->client('GETNAME'));
}
/**
* @group connected
*/
public function testSetsNameOfConnection()
{
$this->markTestSkippedOnRedisVersionBelow('2.6.9');
$redis = $this->getClient();
$expectedConnectionName = 'foo-baz';
$this->assertTrue($redis->client('SETNAME', $expectedConnectionName));
$this->assertEquals($expectedConnectionName, $redis->client('GETNAME'));
}
/**
* @return array
*/
public function invalidConnectionNameProvider()
{
return array(
array('foo space'),
array('foo \n'),
array('foo $'),
);
}
/**
* @group connected
* @expectedException Predis\ServerException
* @dataProvider invalidConnectionNameProvider
*/
public function testInvalidSetNameOfConnection($invalidConnectionName)
{
$this->markTestSkippedOnRedisVersionBelow('2.6.9');
$redis = $this->getClient();
$redis->client('SETNAME', $invalidConnectionName);
}
/**
* @group connected
* @expectedException Predis\ServerException
@@ -56,6 +56,22 @@ class CommunicationExceptionTest extends StandardTestCase
$this->assertTrue($exception->shouldResetConnection());
}
/**
* @group disconnected
* @expectedException Predis\CommunicationException
* @expectedExceptionMessage Communication error
*/
public function testCommunicationExceptionHandling()
{
$connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
$connection->expects($this->once())->method('isConnected')->will($this->returnValue(true));
$connection->expects($this->once())->method('disconnect');
$exception = $this->getException($connection, 'Communication error');
CommunicationException::handle($exception);
}
// ******************************************************************** //
// ---- HELPER METHODS ------------------------------------------------ //
// ******************************************************************** //
@@ -74,7 +74,8 @@ class ComposableStreamConnectionTest extends ConnectionTestCase
*/
public function testReadsMultibulkRepliesAsIterators()
{
$connection = $this->getConnection($profile, true, array('iterable_multibulk' => true));
$connection = $this->getConnection($profile, true);
$connection->getProtocol()->setOption('iterable_multibulk', true);
$connection->executeCommand($profile->createCommand('rpush', array('metavars', 'foo', 'hoge', 'lol')));
$connection->writeCommand($profile->createCommand('lrange', array('metavars', 0, -1)));
@@ -30,7 +30,6 @@ class ParametersTest extends StandardTestCase
$this->assertEquals($defaults['scheme'], $parameters->scheme);
$this->assertEquals($defaults['host'], $parameters->host);
$this->assertEquals($defaults['port'], $parameters->port);
$this->assertEquals($defaults['iterable_multibulk'], $parameters->iterable_multibulk);
$this->assertEquals($defaults['timeout'], $parameters->timeout);
}
@@ -124,6 +123,52 @@ class ParametersTest extends StandardTestCase
$this->assertNull($unserialized->unknown);
}
/**
* @group disconnected
*/
public function testParsingURI()
{
$uri = 'tcp://10.10.10.10:6400?timeout=0.5&persistent=1';
$expected = array(
'scheme' => 'tcp',
'host' => '10.10.10.10',
'port' => 6400,
'timeout' => '0.5',
'persistent' => '1',
);
$this->assertSame($expected, ConnectionParameters::parseURI($uri));
}
/**
* @group disconnected
*/
public function testParsingUnixDomainURI()
{
$uri = 'unix:///tmp/redis.sock?timeout=0.5&persistent=1';
$expected = array(
'scheme' => 'unix',
'host' => 'localhost',
'path' => '/tmp/redis.sock',
'timeout' => '0.5',
'persistent' => '1',
);
$this->assertSame($expected, ConnectionParameters::parseURI($uri));
}
/**
* @group disconnected
* @expectedException Predis\ClientException
* @expectedExceptionMessage Invalid URI: tcp://invalid:uri
*/
public function testParsingURIThrowOnInvalidURI()
{
ConnectionParameters::parseURI('tcp://invalid:uri');
}
// ******************************************************************** //
// ---- HELPER METHODS ------------------------------------------------ //
// ******************************************************************** //
@@ -140,7 +185,6 @@ class ParametersTest extends StandardTestCase
'host' => '127.0.0.1',
'port' => 6379,
'timeout' => 5.0,
'iterable_multibulk' => false,
);
}
@@ -0,0 +1,148 @@
<?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\Connection;
use \PHPUnit_Framework_TestCase as StandardTestCase;
use Predis\Profile\ServerProfile;
/**
* @group ext-phpiredis
*/
class PhpiredisStreamConnectionTest extends ConnectionTestCase
{
/**
* @group disconnected
*/
public function testConstructorDoesNotOpenConnection()
{
$connection = new PhpiredisStreamConnection($this->getParameters());
$this->assertFalse($connection->isConnected());
}
/**
* @group disconnected
*/
public function testExposesParameters()
{
$parameters = $this->getParameters();
$connection = new PhpiredisStreamConnection($parameters);
$this->assertSame($parameters, $connection->getParameters());
}
/**
* @group disconnected
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Invalid scheme: udp
*/
public function testThrowsExceptionOnInvalidScheme()
{
$parameters = $this->getParameters(array('scheme' => 'udp'));
$connection = new PhpiredisStreamConnection($parameters);
}
/**
* @group disconnected
*/
public function testCanBeSerialized()
{
$parameters = $this->getParameters(array('alias' => 'redis', 'read_write_timeout' => 10));
$connection = new PhpiredisStreamConnection($parameters);
$unserialized = unserialize(serialize($connection));
$this->assertInstanceOf('Predis\Connection\PhpiredisStreamConnection', $unserialized);
$this->assertEquals($parameters, $unserialized->getParameters());
}
// ******************************************************************** //
// ---- INTEGRATION TESTS --------------------------------------------- //
// ******************************************************************** //
/**
* @group connected
*/
public function testAcceptsTcpNodelayParameter()
{
if (!version_compare(PHP_VERSION, '5.4.0', '>=')) {
$this->markTestSkipped('Setting TCP_NODELAY on PHP socket streams works on PHP >= 5.4.0');
}
$connection = new PhpiredisStreamConnection($this->getParameters(array('tcp_nodelay' => false)));
$connection->connect();
$this->assertTrue($connection->isConnected());
$connection = new PhpiredisStreamConnection($this->getParameters(array('tcp_nodelay' => true)));
$connection->connect();
$this->assertTrue($connection->isConnected());
}
/**
* @group connected
*/
public function testExecutesCommandsOnServer()
{
$connection = $this->getConnection($profile, true);
$cmdPing = $profile->createCommand('ping');
$cmdEcho = $profile->createCommand('echo', array('echoed'));
$cmdGet = $profile->createCommand('get', array('foobar'));
$cmdRpush = $profile->createCommand('rpush', array('metavars', 'foo', 'hoge', 'lol'));
$cmdLrange = $profile->createCommand('lrange', array('metavars', 0, -1));
$this->assertSame('PONG', $connection->executeCommand($cmdPing));
$this->assertSame('echoed', $connection->executeCommand($cmdEcho));
$this->assertNull($connection->executeCommand($cmdGet));
$this->assertSame(3, $connection->executeCommand($cmdRpush));
$this->assertSame(array('foo', 'hoge', 'lol'), $connection->executeCommand($cmdLrange));
}
/**
* @group connected
* @expectedException Predis\Protocol\ProtocolException
* @expectedExceptionMessage Protocol error, got "P" as reply type byte
*/
public function testThrowsExceptionOnProtocolDesynchronizationErrors()
{
$connection = $this->getConnection($profile);
$socket = $connection->getResource();
$connection->writeCommand($profile->createCommand('ping'));
fread($socket, 1);
$connection->read();
}
// ******************************************************************** //
// ---- HELPER METHODS ------------------------------------------------ //
// ******************************************************************** //
/**
* {@inheritdoc}
*/
protected function getConnection(&$profile = null, $initialize = false, Array $parameters = array())
{
$parameters = $this->getParameters($parameters);
$profile = $this->getProfile();
$connection = new PhpiredisStreamConnection($parameters);
if ($initialize) {
$connection->pushInitCommand($profile->createCommand('select', array($parameters->database)));
$connection->pushInitCommand($profile->createCommand('flushdb'));
}
return $connection;
}
}
+26 -26
View File
@@ -279,9 +279,9 @@ class RedisClusterTest extends StandardTestCase
*/
public function testCanAssignConnectionsToCustomSlotsFromParameters()
{
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-1364');
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=1365-2729');
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=2730-4095');
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-5460');
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-10921');
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10922-16383');
$cluster = new RedisCluster();
$cluster->add($connection1);
@@ -289,9 +289,9 @@ class RedisClusterTest extends StandardTestCase
$cluster->add($connection3);
$expectedMap = array_merge(
array_fill(0, 1365, '127.0.0.1:6379'),
array_fill(1364, 1365, '127.0.0.1:6380'),
array_fill(2729, 1366, '127.0.0.1:6381')
array_fill(0, 5461, '127.0.0.1:6379'),
array_fill(5460, 5461, '127.0.0.1:6380'),
array_fill(10921, 5462, '127.0.0.1:6381')
);
$cluster->buildSlotsMap();
@@ -314,11 +314,11 @@ class RedisClusterTest extends StandardTestCase
$cluster->add($connection3);
$this->assertSame($connection1, $cluster->getConnectionBySlot(0));
$this->assertSame($connection2, $cluster->getConnectionBySlot(1365));
$this->assertSame($connection3, $cluster->getConnectionBySlot(2730));
$this->assertSame($connection2, $cluster->getConnectionBySlot(5461));
$this->assertSame($connection3, $cluster->getConnectionBySlot(10922));
$cluster->setSlots(1365, 3000, '127.0.0.1:6380');
$this->assertSame($connection2, $cluster->getConnectionBySlot(2730));
$cluster->setSlots(5461, 7096, '127.0.0.1:6380');
$this->assertSame($connection2, $cluster->getConnectionBySlot(5461));
}
/**
@@ -337,8 +337,8 @@ class RedisClusterTest extends StandardTestCase
$cluster->add($connection2);
$cluster->add($connection3);
$set = $profile->createCommand('set', array('node:1024', 'foobar'));
$get = $profile->createCommand('get', array('node:1024'));
$set = $profile->createCommand('set', array('node:1001', 'foobar'));
$get = $profile->createCommand('get', array('node:1001'));
$this->assertSame($connection1, $cluster->getConnection($set));
$this->assertSame($connection1, $cluster->getConnection($get));
@@ -358,7 +358,7 @@ class RedisClusterTest extends StandardTestCase
*/
public function testWritesCommandToCorrectConnection()
{
$command = ServerProfile::getDefault()->createCommand('get', array('node:1024'));
$command = ServerProfile::getDefault()->createCommand('get', array('node:1001'));
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
$connection1->expects($this->once())->method('writeCommand')->with($command);
@@ -378,7 +378,7 @@ class RedisClusterTest extends StandardTestCase
*/
public function testReadsCommandFromCorrectConnection()
{
$command = ServerProfile::getDefault()->createCommand('get', array('node:1048'));
$command = ServerProfile::getDefault()->createCommand('get', array('node:1050'));
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
$connection1->expects($this->never())->method('readResponse');
@@ -407,13 +407,13 @@ class RedisClusterTest extends StandardTestCase
$cluster->add($connection1);
$cluster->add($connection2);
$set = $profile->createCommand('set', array('{node:1024}:foo', 'foobar'));
$get = $profile->createCommand('get', array('{node:1024}:foo'));
$set = $profile->createCommand('set', array('{node:1001}:foo', 'foobar'));
$get = $profile->createCommand('get', array('{node:1001}:foo'));
$this->assertSame($connection1, $cluster->getConnection($set));
$this->assertSame($connection1, $cluster->getConnection($get));
$set = $profile->createCommand('set', array('{node:1024}:bar', 'foobar'));
$get = $profile->createCommand('get', array('{node:1024}:bar'));
$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));
}
@@ -423,9 +423,9 @@ class RedisClusterTest extends StandardTestCase
*/
public function testAskResponseWithConnectionInPool()
{
$askResponse = new ResponseError('ASK 373 127.0.0.1:6380');
$askResponse = new ResponseError('ASK 1970 127.0.0.1:6380');
$command = ServerProfile::getDefault()->createCommand('get', array('node:1024'));
$command = ServerProfile::getDefault()->createCommand('get', array('node:1001'));
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
$connection1->expects($this->exactly(2))
@@ -456,9 +456,9 @@ class RedisClusterTest extends StandardTestCase
*/
public function testAskResponseWithConnectionNotInPool()
{
$askResponse = new ResponseError('ASK 373 127.0.0.1:6381');
$askResponse = new ResponseError('ASK 1970 127.0.0.1:6381');
$command = ServerProfile::getDefault()->createCommand('get', array('node:1024'));
$command = ServerProfile::getDefault()->createCommand('get', array('node:1001'));
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
$connection1->expects($this->exactly(2))
@@ -496,9 +496,9 @@ class RedisClusterTest extends StandardTestCase
*/
public function testMovedResponseWithConnectionInPool()
{
$movedResponse = new ResponseError('MOVED 373 127.0.0.1:6380');
$movedResponse = new ResponseError('MOVED 1970 127.0.0.1:6380');
$command = ServerProfile::getDefault()->createCommand('get', array('node:1024'));
$command = ServerProfile::getDefault()->createCommand('get', array('node:1001'));
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
$connection1->expects($this->exactly(1))
@@ -530,9 +530,9 @@ class RedisClusterTest extends StandardTestCase
*/
public function testMovedResponseWithConnectionNotInPool()
{
$movedResponse = new ResponseError('MOVED 373 127.0.0.1:6381');
$movedResponse = new ResponseError('MOVED 1970 127.0.0.1:6381');
$command = ServerProfile::getDefault()->createCommand('get', array('node:1024'));
$command = ServerProfile::getDefault()->createCommand('get', array('node:1001'));
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379');
$connection1->expects($this->once())
@@ -69,6 +69,24 @@ class StreamConnectionTest extends ConnectionTestCase
// ---- INTEGRATION TESTS --------------------------------------------- //
// ******************************************************************** //
/**
* @group connected
*/
public function testAcceptsTcpNodelayParameter()
{
if (!version_compare(PHP_VERSION, '5.4.0', '>=')) {
$this->markTestSkipped('Setting TCP_NODELAY on PHP socket streams works on PHP >= 5.4.0');
}
$connection = new StreamConnection($this->getParameters(array('tcp_nodelay' => false)));
$connection->connect();
$this->assertTrue($connection->isConnected());
$connection = new StreamConnection($this->getParameters(array('tcp_nodelay' => true)));
$connection->connect();
$this->assertTrue($connection->isConnected());
}
/**
* @group connected
*/
+47
View File
@@ -78,4 +78,51 @@ class ClientOptionsTest extends StandardTestCase
$this->assertTrue(isset($options->custom));
$this->assertFalse(isset($options->profile));
}
/**
* @group disconnected
*/
public function testGetDefaultUsingOptionName()
{
$options = new ClientOptions();
$this->assertInstanceOf('Predis\Connection\PredisCluster', $options->getDefault('cluster'));
}
/**
* @group disconnected
*/
public function testGetDefaultUsingUnhandledOptionName()
{
$options = new ClientOptions();
$option = new ClientCluster();
$this->assertNull($options->getDefault('foo'));
}
/**
* @group disconnected
*/
public function testGetDefaultUsingOptionInstance()
{
$options = new ClientOptions();
$option = new ClientCluster();
$this->assertInstanceOf('Predis\Connection\PredisCluster', $options->getDefault($option));
}
/**
* @group disconnected
*/
public function testGetDefaultUsingUnhandledOptionInstance()
{
$options = new ClientOptions();
$option = new CustomOption(array(
'default' => function ($options) {
return 'foo';
},
));
$this->assertSame('foo', $options->getDefault($option));
}
}
+53 -2
View File
@@ -15,6 +15,7 @@ use \PHPUnit_Framework_TestCase as StandardTestCase;
use SplQueue;
use Predis\ResponseError;
use Predis\ResponseObjectInterface;
use Predis\Profile\ServerProfile;
/**
@@ -40,7 +41,7 @@ class StandardExecutorTest extends StandardTestCase
$replies = $executor->execute($connection, $pipeline);
$this->assertTrue($pipeline->isEmpty());
$this->assertSame(array('PONG', 'PONG', 'PONG'), $replies);
$this->assertSame(array(true, true, true), $replies);
}
/**
@@ -64,7 +65,29 @@ class StandardExecutorTest extends StandardTestCase
$replies = $executor->execute($connection, $pipeline);
$this->assertTrue($pipeline->isEmpty());
$this->assertSame(array('PONG', 'PONG', 'PONG'), $replies);
$this->assertSame(array(true, true, true), $replies);
}
/**
* @group disconnected
*/
public function testExecutorDoesNotParseResponseObjects()
{
$executor = new StandardExecutor();
$response = $this->getMock('Predis\ResponseObjectInterface');
$this->simpleResponseObjectTest($executor, $response);
}
/**
* @group disconnected
*/
public function testExecutorCanReturnRedisErrors()
{
$executor = new StandardExecutor(false);
$response = $this->getMock('Predis\ResponseErrorInterface');
$this->simpleResponseObjectTest($executor, $response);
}
/**
@@ -90,6 +113,34 @@ class StandardExecutorTest extends StandardTestCase
// ---- HELPER METHODS ------------------------------------------------ //
// ******************************************************************** //
/**
* Executes a test for the Predis\ResponseObjectInterface type.
*
* @param PipelineExecutorInterface $executor
* @param ResponseObjectInterface $response
*/
protected function simpleResponseObjectTest(PipelineExecutorInterface $executor, ResponseObjectInterface $response)
{
$pipeline = new SplQueue();
$command = $this->getMock('Predis\Command\CommandInterface');
$command->expects($this->never())
->method('parseResponse');
$connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
$connection->expects($this->once())
->method('writeCommand');
$connection->expects($this->once())
->method('readResponse')
->will($this->returnValue($response));
$pipeline->enqueue($command);
$replies = $executor->execute($connection, $pipeline);
$this->assertTrue($pipeline->isEmpty());
$this->assertSame(array($response), $replies);
}
/**
* Returns a list of queued command instances.
*