Compare commits

..

67 Commits

Author SHA1 Message Date
Daniele Alessandri cb18d67b6a Update CHANGELOG and bump VERSION. 2013-07-27 11:13:54 +02:00
Daniele Alessandri 4576dc029f Add LICENSE and phpunit.xml.dist in package.ini for Pear.
This commit addresses issues #124 and #126. LICENSE will be installed
with the "doc" role while phpunit.xml.dist will be installed with the
"test" role.

[ci skip]
2013-06-17 11:16:07 +02:00
Daniele Alessandri aa458a1922 Merge pull request #123 from drealecs/doctype_fixes
some PHPDoc fixes in lib
2013-06-03 03:04:10 -07:00
Alexandru Patranescu 2d2930d24f some PHPDoc fixes in lib
and removed some unused imports also
2013-06-03 00:06:41 +03:00
Daniele Alessandri 7e0ec1a265 Update CHANGELOG.
[ci skip]
2013-05-31 11:52:47 +02:00
Daniele Alessandri 04e597ebba Merge remote-tracking branch 'origin/pr/116' into v0.8 2013-05-31 11:30:30 +02:00
Daniele Alessandri d681bff4ab Merge branch 'github/pr/114' into v0.8 2013-05-31 10:47:58 +02:00
Daniele Alessandri 3dea41aa66 Add tests for prefixed Predis\PubSub\DispatcherLoop. 2013-05-31 10:39:46 +02:00
Daniele Alessandri 69c66e157f Make callbacks properties protected in the DispatcherLoop class.
This makes easier to extend the class. The underlying pub/sub iterator
property is still private, but can be accessed via public getter method.
2013-05-31 10:21:21 +02:00
Daniele Alessandri dd50cfe47e Slightly rework original pull request. 2013-05-31 10:17:54 +02:00
Daniele Alessandri 8451146d38 Expose underlying client object from pub/sub iterators. 2013-05-31 10:17:08 +02:00
Thomas Orozco d45ce35923 Use a random IP when a host has several IPs.
Using gethostbyname, we will reuse the same (first) IP address for
each request. Here, we choose the IP we use randomly.

This is practical in a case where we have multiple redis read-only
slaves that can't invidually support the full application load, but are
accessible through a single hostname.
2013-05-11 14:08:51 -07:00
Eloi Poch 6cd124cc59 Fix DispatcherLoop error with client prefix keys
DispatcherLoop works properly if a client have configured a
prefix for the keys or not
2013-04-20 13:02:17 +02:00
Daniele Alessandri d6fa4a3292 Augment exception message with basic server details on connection error.
Providing a basic hint in the exception message about the server that caused
a connection exception could be useful especially with aggregated connections.

This is in response to issue #110.
2013-03-30 16:16:13 +01:00
Daniele Alessandri 5b771704d3 Handle DUMP and RESTORE with Predis\Connection\PredisCluster. 2013-03-30 14:38:49 +01:00
Daniele Alessandri 68d972a1b1 Merge remote-tracking branch 'origin/pr/111' into v0.8 2013-03-27 15:11:00 +01:00
marcosQuesada 4bf035918d setting medium timeout on slow tests 2013-03-23 00:57:36 +01:00
Daniele Alessandri 94c4e2ffee Fix handling of scripted commands with redis-cluster hash strategy.
A missing "use" directive was preventing the hash strategy to properly
use the specific methods of Predis\Command\ScriptedCommand, falling back
to analyzing the raw arguments array of the command.
2013-03-16 16:24:59 +01:00
Daniele Alessandri 6e2fd181f1 Make sure key prefixing is skipped when command has no arguments.
Actually this was already the case for certain commands, but some of them
was left unguarded for such cases. This commit also fixes #109.

The behaviour of silently skipping key prefixing when a command has no
arguments may change in the future so we added explicit tests as guards
for future changes. Predis\Command\Processor\KeyPrefixProcessor will
continue to skip key prefixing on empty arguments, regardless.
2013-03-16 16:00:33 +01:00
Daniele Alessandri 1c21fcc5b3 Fix strict warnings running command test generator script. 2013-03-16 13:23:46 +01:00
Daniele Alessandri 531261b0fc Add DUMP and RESTORE to the server profile for Redis 2.6.
Closes issue #108.
2013-03-16 13:21:24 +01:00
Daniele Alessandri 7434a9bd0e Add the WITHSCORES modifier only when option value is true.
Fixes issue #107.
2013-03-16 12:37:00 +01:00
Daniele Alessandri c4123ce7ce Update CHANGELOG. 2013-03-16 12:16:52 +01:00
Daniele Alessandri 536992e277 Merge remote-tracking branch 'nitper/cluster_hmset' into v0.8 2013-03-16 12:11:44 +01:00
Daniele Alessandri 5a735aa670 Back to development for the next patch release. 2013-03-16 12:11:37 +01:00
nitper 15f2776d77 update cluster tests 2013-03-11 23:08:10 +00:00
nitper dc1103582e allow HMSET in clusters 2013-03-11 22:50:03 +00:00
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
204 changed files with 3210 additions and 543 deletions
+68
View File
@@ -1,3 +1,71 @@
v0.8.4 (2013-07-27)
===============================================================================
- Added `DUMP` and `RESTORE` to the server profile for Redis 2.6.
- Connection exceptions now report basic host details in their messages.
- Allow `Predis\Connection\PhpiredisConnection` to use a random IP when a host
actually has several IPs (ISSUE #116).
- __FIX__: allow `HMSET` when using a cluster of Redis nodes with client-side
sharding or redis-cluster (ISSUE #106).
- __FIX__: set `WITHSCORES` modifer for `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`
and `ZREVRANGEBYSCORE` only when the options array passed to these commands
has `WITHSCORES` set to `true` (ISSUE #107).
- __FIX__: scripted commands falling back from `EVALSHA` to `EVAL` resulted in
PHP errors when using a prefixed client (ISSUE #109).
- __FIX__: `Predis\PubSub\DispatcherLoop` now works properly when using key
prefixing (ISSUE #114).
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.4
+15 -2
View File
@@ -116,7 +116,8 @@ class CommandTestCaseGenerator
return $this->options['realm'];
}
$class = array_pop(explode('\\', $this->options['fqn']));
$fqnParts = explode('\\', $this->options['fqn']);
$class = array_pop($fqnParts);
list($realm,) = preg_split('/([[:upper:]][[:lower:]]+)/', $class, 2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
return strtolower($realm);
@@ -152,7 +153,8 @@ class CommandTestCaseGenerator
{
$id = $instance->getId();
$fqn = get_class($instance);
$class = array_pop(explode('\\', $fqn)) . "Test";
$fqnParts = explode('\\', $fqn);
$class = array_pop($fqnParts) . "Test";
$realm = $this->getTestRealm();
$buffer =<<<PHP
@@ -245,6 +247,17 @@ PHP;
\$this->assertSame(\$expected, \$command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
\$command = \$this->getCommand();
\$command->prefixKeys('prefix:');
\$this->assertSame(array(), \$command->getArguments());
}
PHP;
}
+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";
}
+24 -15
View File
@@ -15,14 +15,12 @@ use Predis\Command\CommandInterface;
use Predis\Command\ScriptedCommand;
use Predis\Connection\AggregatedConnectionInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\ConnectionFactory;
use Predis\Connection\ConnectionFactoryInterface;
use Predis\Monitor\MonitorContext;
use Predis\Option\ClientOptions;
use Predis\Option\ClientOptionsInterface;
use Predis\Pipeline\PipelineContext;
use Predis\Profile\ServerProfile;
use Predis\Profile\ServerProfileInterface;
use Predis\PubSub\PubSubContext;
use Predis\Transaction\MultiExecContext;
@@ -33,12 +31,11 @@ use Predis\Transaction\MultiExecContext;
*/
class Client implements ClientInterface
{
const VERSION = '0.8.1';
const VERSION = '0.8.4';
private $options;
private $profile;
private $connection;
private $connections;
/**
* Initializes a new client with optional connection parameters and client options.
@@ -50,7 +47,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 +60,7 @@ class Client implements ClientInterface
*/
protected function filterOptions($options)
{
if ($options === null) {
if (!isset($options)) {
return new ClientOptions();
}
@@ -94,13 +90,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 +135,7 @@ class Client implements ClientInterface
*/
public function getConnectionFactory()
{
return $this->connections;
return $this->options->connections;
}
/**
@@ -138,7 +147,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'");
}
@@ -194,7 +203,7 @@ class Client implements ClientInterface
* Retrieves a single connection out of an aggregated connections instance.
*
* @param string $connectionId Index or alias of the connection.
* @return SingleConnectionInterface
* @return Connection\SingleConnectionInterface
*/
public function getConnectionById($connectionId)
{
@@ -269,14 +278,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
@@ -60,7 +60,7 @@ interface ClientInterface extends BasicClientInterface
*
* @param string $method The name of a Redis command.
* @param array $arguments The arguments for the command.
* @return CommandInterface
* @return Command\CommandInterface
*/
public function createCommand($method, $arguments = array());
}
@@ -11,6 +11,8 @@
namespace Predis\Cluster\Distribution;
use Predis\Cluster\Hash\HashGeneratorInterface;
/**
* A distributor implements the logic to automatically distribute
* keys among several nodes for client-side sharding.
@@ -44,7 +46,7 @@ interface DistributionStrategyInterface
/**
* Returns the underlying hash generator instance.
*
* @return Predis\Cluster\Hash\HashGeneratorInterface
* @return HashGeneratorInterface
*/
public function getHashGenerator();
}
+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;
@@ -58,6 +58,8 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
'TTL' => $keyIsFirstArgument,
'PTTL' => $keyIsFirstArgument,
'SORT' => $keyIsFirstArgument, // TODO
'DUMP' => $keyIsFirstArgument,
'RESTORE' => $keyIsFirstArgument,
/* commands operating on string values */
'APPEND' => $keyIsFirstArgument,
@@ -140,6 +142,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
'HGET' => $keyIsFirstArgument,
'HGETALL' => $keyIsFirstArgument,
'HMGET' => $keyIsFirstArgument,
'HMSET' => $keyIsFirstArgument,
'HINCRBY' => $keyIsFirstArgument,
'HINCRBYFLOAT' => $keyIsFirstArgument,
'HKEYS' => $keyIsFirstArgument,
@@ -294,9 +297,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 +346,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
*/
protected function checkSameHashForKeys(Array $keys)
{
if (($count = count($keys)) === 0) {
if (!$count = count($keys)) {
return false;
}
@@ -13,6 +13,7 @@ namespace Predis\Cluster;
use Predis\Cluster\Hash\CRC16HashGenerator;
use Predis\Command\CommandInterface;
use Predis\Command\ScriptedCommand;
/**
* Default class used by Predis to calculate hashes out of keys of
@@ -127,6 +128,7 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
'HGET' => $keyIsFirstArgument,
'HGETALL' => $keyIsFirstArgument,
'HMGET' => $keyIsFirstArgument,
'HMSET' => $keyIsFirstArgument,
'HINCRBY' => $keyIsFirstArgument,
'HINCRBYFLOAT' => $keyIsFirstArgument,
'HKEYS' => $keyIsFirstArgument,
@@ -246,9 +248,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);
}
/**
+27
View File
@@ -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/dump
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDump extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'DUMP';
}
}
+27
View File
@@ -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/restore
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRestore extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'RESTORE';
}
}
+21 -20
View File
@@ -86,31 +86,32 @@ class KeySort extends AbstractCommand implements PrefixableCommandInterface
*/
public function prefixKeys($prefix)
{
$arguments = $this->getArguments();
$arguments[0] = "$prefix{$arguments[0]}";
if ($arguments = $this->getArguments()) {
$arguments[0] = "$prefix{$arguments[0]}";
if (($count = count($arguments)) > 1) {
for ($i = 1; $i < $count; $i++) {
switch ($arguments[$i]) {
case 'BY':
case 'STORE':
$arguments[$i] = "$prefix{$arguments[++$i]}";
break;
if (($count = count($arguments)) > 1) {
for ($i = 1; $i < $count; $i++) {
switch ($arguments[$i]) {
case 'BY':
case 'STORE':
$arguments[$i] = "$prefix{$arguments[++$i]}";
break;
case 'GET':
$value = $arguments[++$i];
if ($value !== '#') {
$arguments[$i] = "$prefix$value";
}
break;
case 'GET':
$value = $arguments[++$i];
if ($value !== '#') {
$arguments[$i] = "$prefix$value";
}
break;
case 'LIMIT';
$i += 2;
break;
case 'LIMIT';
$i += 2;
break;
}
}
}
}
$this->setRawArguments($arguments);
$this->setRawArguments($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);
}
}
+26 -23
View File
@@ -40,13 +40,13 @@ class PrefixHelpers
*/
public static function all(CommandInterface $command, $prefix)
{
$arguments = $command->getArguments();
if ($arguments = $command->getArguments()) {
foreach ($arguments as &$key) {
$key = "$prefix$key";
}
foreach ($arguments as &$key) {
$key = "$prefix$key";
$command->setRawArguments($arguments);
}
$command->setRawArguments($arguments);
}
/**
@@ -57,14 +57,15 @@ class PrefixHelpers
*/
public static function interleaved(CommandInterface $command, $prefix)
{
$arguments = $command->getArguments();
$length = count($arguments);
if ($arguments = $command->getArguments()) {
$length = count($arguments);
for ($i = 0; $i < $length; $i += 2) {
$arguments[$i] = "$prefix{$arguments[$i]}";
for ($i = 0; $i < $length; $i += 2) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
$command->setRawArguments($arguments);
}
$command->setRawArguments($arguments);
}
/**
@@ -75,14 +76,15 @@ class PrefixHelpers
*/
public static function skipFirst(CommandInterface $command, $prefix)
{
$arguments = $command->getArguments();
$length = count($arguments);
if ($arguments = $command->getArguments()) {
$length = count($arguments);
for ($i = 1; $i < $length; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
for ($i = 1; $i < $length; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
$command->setRawArguments($arguments);
}
$command->setRawArguments($arguments);
}
/**
@@ -93,13 +95,14 @@ class PrefixHelpers
*/
public static function skipLast(CommandInterface $command, $prefix)
{
$arguments = $command->getArguments();
$length = count($arguments);
if ($arguments = $command->getArguments()) {
$length = count($arguments);
for ($i = 0; $i < $length - 1; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
for ($i = 0; $i < $length - 1; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
$command->setRawArguments($arguments);
}
$command->setRawArguments($arguments);
}
}
@@ -57,7 +57,7 @@ class KeyPrefixProcessor implements CommandProcessorInterface
*/
public function process(CommandInterface $command)
{
if ($command instanceof PrefixableCommandInterface) {
if ($command instanceof PrefixableCommandInterface && $command->getArguments()) {
$command->prefixKeys($this->prefix);
}
}
+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;
}
+5 -5
View File
@@ -30,13 +30,13 @@ class ServerEval extends AbstractCommand implements PrefixableCommandInterface
*/
public function prefixKeys($prefix)
{
$arguments = $this->getArguments();
if ($arguments = $this->getArguments()) {
for ($i = 2; $i < $arguments[1] + 2; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
for ($i = 2; $i < $arguments[1] + 2; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
$this->setRawArguments($arguments);
}
$this->setRawArguments($arguments);
}
/**
-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>
+2 -2
View File
@@ -33,7 +33,7 @@ class ZSetRange extends PrefixableCommand
if (count($arguments) === 4) {
$lastType = gettype($arguments[3]);
if ($lastType === 'string' && strtolower($arguments[3]) === 'withscores') {
if ($lastType === 'string' && strtoupper($arguments[3]) === 'WITHSCORES') {
// Used for compatibility with older versions
$arguments[3] = array('WITHSCORES' => true);
$lastType = 'array';
@@ -59,7 +59,7 @@ class ZSetRange extends PrefixableCommand
$opts = array_change_key_case($options, CASE_UPPER);
$finalizedOpts = array();
if (isset($opts['WITHSCORES'])) {
if (!empty($opts['WITHSCORES'])) {
$finalizedOpts[] = 'WITHSCORES';
}
+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);
}
}
+7 -7
View File
@@ -79,15 +79,15 @@ class ZSetUnionStore extends PrefixableCommand
*/
public function prefixKeys($prefix)
{
$arguments = $this->getArguments();
if ($arguments = $this->getArguments()) {
$arguments[0] = "$prefix{$arguments[0]}";
$length = ((int) $arguments[1]) + 2;
$arguments[0] = "$prefix{$arguments[0]}";
$length = ((int) $arguments[1]) + 2;
for ($i = 2; $i < $length; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
}
for ($i = 2; $i < $length; $i++) {
$arguments[$i] = "$prefix{$arguments[$i]}";
$this->setRawArguments($arguments);
}
$this->setRawArguments($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 [{$this->parameters->scheme}://{$this->getIdentifier()}]", $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 [{$this->parameters->scheme}://{$this->getIdentifier()}]"));
}
/**
@@ -11,8 +11,6 @@
namespace Predis\Connection;
use Predis\Command\CommandInterface;
/**
* Defines a cluster of Redis servers formed by aggregating multiple
* connection objects.
@@ -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'));
}
}
@@ -11,8 +11,6 @@
namespace Predis\Connection;
use Predis\Profile\ServerProfileInterface;
/**
* Interface that must be implemented by classes that provide their own mechanism
* to create and initialize new instances of Predis\Connection\SingleConnectionInterface.
@@ -40,16 +38,16 @@ interface ConnectionFactoryInterface
* Creates a new connection object.
*
* @param mixed $parameters Parameters for the connection.
* @return Predis\Connection\SingleConnectionInterface
* @return SingleConnectionInterface
*/
public function create($parameters);
/**
* Prepares an aggregation of connection objects.
*
* @param AggregatedConnectionInterface Instance of an aggregated connection class.
* @param AggregatedConnectionInterface $cluster Instance of an aggregated connection class.
* @param array $parameters List of parameters for each connection object.
* @return Predis\Connection\AggregatedConnectionInterface
* @return AggregatedConnectionInterface
*/
public function createAggregated(AggregatedConnectionInterface $cluster, Array $parameters);
}
@@ -12,7 +12,6 @@
namespace Predis\Connection;
use Predis\ClientException;
use Predis\Option\OptionInterface;
/**
* Handles parsing and validation of connection parameters.
@@ -28,7 +27,6 @@ class ConnectionParameters implements ConnectionParametersInterface
'host' => '127.0.0.1',
'port' => 6379,
'timeout' => 5.0,
'iterable_multibulk' => false,
);
/**
@@ -37,7 +35,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 +107,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 +138,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) {
@@ -11,7 +11,6 @@
namespace Predis\Connection;
use Predis\NotSupportedException;
use Predis\Command\CommandInterface;
use Predis\Replication\ReplicationStrategy;
@@ -11,7 +11,6 @@
namespace Predis\Connection;
use Predis\ClientException;
use Predis\NotSupportedException;
use Predis\ResponseError;
use Predis\ResponseQueued;
@@ -41,7 +40,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 +89,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);
}
@@ -236,10 +235,10 @@ class PhpiredisConnection extends AbstractConnection
$host = $parameters->host;
if (ip2long($host) === false) {
if (($address = gethostbyname($host)) === $host) {
if (($addresses = gethostbynamel($host)) === false) {
$this->onConnectionError("Cannot resolve the address of $host");
}
return $address;
return $addresses[array_rand($addresses)];
}
return $host;
@@ -296,7 +295,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();
}
}
+2 -2
View File
@@ -11,7 +11,7 @@
namespace Predis\Connection;
use Predis\ClientException;
use Predis\Cluster\CommandHashStrategyInterface;
use Predis\NotSupportedException;
use Predis\Cluster\PredisClusterHashStrategy;
use Predis\Cluster\Distribution\DistributionStrategyInterface;
@@ -166,7 +166,7 @@ class PredisCluster implements ClusterConnectionInterface, \IteratorAggregate, \
* Returns the underlying command hash strategy used to hash
* commands by their keys.
*
* @return Predis\Cluster\CommandHashStrategyInterface
* @return CommandHashStrategyInterface
*/
public function getCommandHashStrategy()
{
+6 -5
View File
@@ -12,6 +12,7 @@
namespace Predis\Connection;
use Predis\ClientException;
use Predis\Cluster\CommandHashStrategyInterface;
use Predis\NotSupportedException;
use Predis\ResponseErrorInterface;
use Predis\Cluster\RedisClusterHashStrategy;
@@ -135,7 +136,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 +178,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 +196,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 +215,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]");
}
@@ -306,7 +307,7 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
* Returns the underlying command hash strategy used to hash
* commands by their keys.
*
* @return Predis\Cluster\CommandHashStrategyInterface
* @return CommandHashStrategyInterface
*/
public function getCommandHashStrategy()
{
@@ -11,8 +11,6 @@
namespace Predis\Connection;
use Predis\Command\CommandInterface;
/**
* Defines a group of Redis servers in a master/slave replication configuration.
*
+12 -7
View File
@@ -11,7 +11,6 @@
namespace Predis\Connection;
use Predis\NotSupportedException;
use Predis\ResponseError;
use Predis\ResponseQueued;
use Predis\Command\CommandInterface;
@@ -27,6 +26,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 +53,7 @@ class StreamConnection extends AbstractConnection
*/
public function __destruct()
{
if (!$this->parameters->persistent) {
if (isset($this->parameters) && !$this->parameters->persistent) {
$this->disconnect();
}
}
@@ -80,10 +80,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 +101,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 +120,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 +140,7 @@ class StreamConnection extends AbstractConnection
{
parent::connect();
if (count($this->initCmds) > 0){
if ($this->initCmds) {
$this->sendInitializationCommands();
}
}
@@ -244,7 +249,7 @@ class StreamConnection extends AbstractConnection
if ($count === -1) {
return null;
}
if ($this->mbiterable === true) {
if ($this->mbiterable) {
return new MultiBulkResponseSimple($this, $count);
}
+4 -4
View File
@@ -11,14 +11,11 @@
namespace Predis;
use Predis\Connection\AggregatedConnectionInterface;
use Predis\Connection\ClusterConnectionInterface;
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 +23,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 +42,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 +58,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
*/
+2 -2
View File
@@ -27,7 +27,7 @@ class MonitorContext implements \Iterator
private $position;
/**
* @param ClientInterface Client instance used by the context.
* @param ClientInterface $client Client instance used by the context.
*/
public function __construct(ClientInterface $client)
{
@@ -48,7 +48,7 @@ class MonitorContext implements \Iterator
* Checks if the passed client instance satisfies the required conditions
* needed to initialize a monitor context.
*
* @param ClientInterface Client instance used by the context.
* @param ClientInterface $client Client instance used by the context.
*/
private function checkCapabilities(ClientInterface $client)
{
+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);
}
}
}
@@ -28,7 +28,7 @@ class FireAndForgetExecutor implements PipelineExecutorInterface
* connection before starting to execute the commands stored
* in the pipeline.
*
* @param ConnectionInterface Connection instance.
* @param ConnectionInterface $connection Connection instance.
*/
protected function checkConnection(ConnectionInterface $connection)
{
+1 -2
View File
@@ -16,7 +16,6 @@ use SplQueue;
use Predis\ClientException;
use Predis\ResponseErrorInterface;
use Predis\ResponseObjectInterface;
use Predis\ResponseQueued;
use Predis\ServerException;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\SingleConnectionInterface;
@@ -47,7 +46,7 @@ class MultiExecExecutor implements PipelineExecutorInterface
* connection before starting to execute the commands stored
* in the pipeline.
*
* @param ConnectionInterface Connection instance.
* @param ConnectionInterface $connection Connection instance.
*/
protected function checkConnection(ConnectionInterface $connection)
{
+1 -2
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;
/**
@@ -49,7 +48,7 @@ class PipelineContext implements BasicClientInterface, ExecutableContextInterfac
* Returns a pipeline executor depending on the kind of the underlying
* connection and the passed options.
*
* @param ClientInterface Client instance used by the context.
* @param ClientInterface $client Client instance used by the context.
* @return PipelineExecutorInterface
*/
protected function createExecutor(ClientInterface $client)
-1
View File
@@ -13,7 +13,6 @@ namespace Predis\Pipeline;
use SplQueue;
use Predis\CommunicationException;
use Predis\ServerException;
use Predis\Connection\ConnectionInterface;
/**
+38 -11
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;
@@ -41,7 +44,7 @@ class StandardExecutor implements PipelineExecutorInterface
* connection before starting to execute the commands stored
* in the pipeline.
*
* @param ConnectionInterface Connection instance.
* @param ConnectionInterface $connection Connection instance.
*/
protected function checkConnection(ConnectionInterface $connection)
{
@@ -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,6 +11,7 @@
namespace Predis\Profile;
use Predis\Command\CommandInterface;
/**
* A server profile defines features and commands supported by certain
@@ -49,7 +50,7 @@ interface ServerProfileInterface
*
* @param string $method Command ID.
* @param array $arguments Arguments for the command.
* @return Predis\Command\CommandInterface
* @return CommandInterface
*/
public function createCommand($method, $arguments = array());
}
+2
View File
@@ -47,6 +47,8 @@ class ServerVersion26 extends ServerProfile
'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',
@@ -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 -4
View File
@@ -11,10 +11,9 @@
namespace Predis\Protocol\Text;
use Predis\Helpers;
use Predis\CommunicationException;
use Predis\ResponseError;
use Predis\ResponseQueued;
use Predis\ServerException;
use Predis\Command\CommandInterface;
use Predis\Connection\ComposableConnectionInterface;
use Predis\Iterator\MultiBulkResponseSimple;
@@ -98,7 +97,7 @@ class TextProtocol implements ProtocolInterface
if ($count === -1) {
return null;
}
if ($this->mbiterable == true) {
if ($this->mbiterable) {
return new MultiBulkResponseSimple($connection, $count);
}
@@ -117,7 +116,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));
}
}
@@ -11,11 +11,6 @@
namespace Predis\PubSub;
use Predis\ClientException;
use Predis\ClientInterface;
use Predis\Helpers;
use Predis\NotSupportedException;
/**
* Client-side abstraction of a Publish / Subscribe context.
*
+29 -10
View File
@@ -21,26 +21,25 @@ use Predis\ClientInterface;
*/
class DispatcherLoop
{
private $client;
private $pubSubContext;
private $callbacks;
private $defaultCallback;
private $subscriptionCallback;
protected $callbacks;
protected $defaultCallback;
protected $subscriptionCallback;
/**
* @param ClientInterface Client instance used by the context.
* @param ClientInterface $client Client instance used by the context.
*/
public function __construct(ClientInterface $client)
{
$this->callbacks = array();
$this->client = $client;
$this->pubSubContext = $client->pubSub();
}
/**
* Checks if the passed argument is a valid callback.
*
* @param mixed A callback.
* @param mixed $callable A callback.
*/
protected function validateCallback($callable)
{
@@ -96,8 +95,10 @@ class DispatcherLoop
*/
public function attachCallback($channel, $callback)
{
$callbackName = $this->getPrefixKeys() . $channel;
$this->validateCallback($callback);
$this->callbacks[$channel] = $callback;
$this->callbacks[$callbackName] = $callback;
$this->pubSubContext->subscribe($channel);
}
@@ -108,8 +109,10 @@ class DispatcherLoop
*/
public function detachCallback($channel)
{
if (isset($this->callbacks[$channel])) {
unset($this->callbacks[$channel]);
$callbackName = $this->getPrefixKeys() . $channel;
if (isset($this->callbacks[$callbackName])) {
unset($this->callbacks[$callbackName]);
$this->pubSubContext->unsubscribe($channel);
}
}
@@ -148,4 +151,20 @@ class DispatcherLoop
{
$this->pubSubContext->closeContext();
}
/**
* Return the prefix of the keys
*
* @return string
*/
protected function getPrefixKeys()
{
$options = $this->pubSubContext->getClient()->getOptions();
if (isset($options->prefix)) {
return $options->prefix->getPrefix();
}
return '';
}
}
+15 -5
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;
@@ -28,8 +28,8 @@ class PubSubContext extends AbstractPubSubContext
private $options;
/**
* @param ClientInterface Client instance used by the context.
* @param array Options for the context initialization.
* @param ClientInterface $client Client instance used by the context.
* @param array $options Options for the context initialization.
*/
public function __construct(ClientInterface $client, Array $options = null)
{
@@ -41,11 +41,21 @@ class PubSubContext extends AbstractPubSubContext
$this->genericSubscribeInit('psubscribe');
}
/**
* Returns the underlying client instance used by the pub/sub iterator.
*
* @return ClientInterface
*/
public function getClient()
{
return $this->client;
}
/**
* Checks if the passed client instance satisfies the required conditions
* needed to initialize a Publish / Subscribe context.
*
* @param ClientInterface Client instance used by the context.
* @param ClientInterface $client Client instance used by the context.
*/
private function checkCapabilities(ClientInterface $client)
{
@@ -77,7 +87,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;
}
}
+4 -5
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;
@@ -48,8 +47,8 @@ class MultiExecContext implements BasicClientInterface, ExecutableContextInterfa
protected $commands;
/**
* @param ClientInterface Client instance used by the context.
* @param array Options for the context initialization.
* @param ClientInterface $client Client instance used by the context.
* @param array $options Options for the context initialization.
*/
public function __construct(ClientInterface $client, Array $options = null)
{
@@ -114,7 +113,7 @@ class MultiExecContext implements BasicClientInterface, ExecutableContextInterfa
* Checks if the passed client instance satisfies the required conditions
* needed to initialize a transaction context.
*
* @param ClientInterface Client instance used by the context.
* @param ClientInterface $client Client instance used by the context.
*/
private function checkCapabilities(ClientInterface $client)
{
@@ -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
));
}
+3 -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.4"
stability = "stable"
channel = "pear.nrk.io"
@@ -21,7 +21,9 @@ php = ">= 5.3.2"
pearinstaller = "1.4.1"
[roles]
*.xml.dist = test
*.md = doc
LICENSE = doc
lib = php
[optional phpiredis]
+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}'));
@@ -261,6 +263,8 @@ class PredisClusterHashStrategyTest extends StandardTestCase
'TTL' => 'keys-first',
'PTTL' => 'keys-first',
'SORT' => 'keys-first', // TODO
'DUMP' => 'keys-first',
'RESTORE' => 'keys-first',
/* commands operating on string values */
'APPEND' => 'keys-first',
@@ -343,6 +347,7 @@ class PredisClusterHashStrategyTest extends StandardTestCase
'HGET' => 'keys-first',
'HGETALL' => 'keys-first',
'HMGET' => 'keys-first',
'HMSET' => 'keys-first',
'HINCRBY' => 'keys-first',
'HINCRBYFLOAT' => 'keys-first',
'HKEYS' => 'keys-first',
@@ -186,7 +186,7 @@ class RedisClusterHashStrategyTest extends StandardTestCase
$command->expects($this->once())
->method('getScript')
->will($this->returnValue('return true'));
$command->expects($this->exactly(1))
$command->expects($this->exactly(2))
->method('getKeysCount')
->will($this->returnValue(1));
$command->setArguments($arguments);
@@ -340,6 +340,7 @@ class RedisClusterHashStrategyTest extends StandardTestCase
'HGET' => 'keys-first',
'HGETALL' => 'keys-first',
'HMGET' => 'keys-first',
'HMSET' => 'keys-first',
'HINCRBY' => 'keys-first',
'HINCRBYFLOAT' => 'keys-first',
'HKEYS' => 'keys-first',
+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));
}
}
+11
View File
@@ -85,6 +85,17 @@ class HashDeleteTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+11
View File
@@ -74,6 +74,17 @@ class HashExistsTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+11
View File
@@ -76,6 +76,17 @@ class HashGetAllTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
@@ -90,6 +90,17 @@ class HashGetMultipleTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+11
View File
@@ -71,6 +71,17 @@ class HashGetTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
@@ -71,6 +71,17 @@ class HashIncrementByFloatTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
@@ -71,6 +71,17 @@ class HashIncrementByTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+11
View File
@@ -76,6 +76,17 @@ class HashKeysTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+11
View File
@@ -71,6 +71,17 @@ class HashLengthTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
@@ -85,6 +85,17 @@ class HashSetMultipleTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
@@ -74,6 +74,17 @@ class HashSetPreserveTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+11
View File
@@ -74,6 +74,17 @@ class HashSetTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+11
View File
@@ -76,6 +76,17 @@ class HashValuesTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+11
View File
@@ -86,6 +86,17 @@ class KeyDeleteTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+89
View File
@@ -0,0 +1,89 @@
<?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;
use \PHPUnit_Framework_TestCase as StandardTestCase;
/**
* @group commands
* @group realm-key
*/
class KeyDumpTest extends CommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand()
{
return 'Predis\Command\KeyDump';
}
/**
* {@inheritdoc}
*/
protected function getExpectedId()
{
return 'DUMP';
}
/**
* @group disconnected
*/
public function testFilterArguments()
{
$arguments = array('key');
$expected = array('key');
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse()
{
$raw = "\x00\xC0\n\x06\x00\xF8r?\xC5\xFB\xFB_(";
$expected = "\x00\xC0\n\x06\x00\xF8r?\xC5\xFB\xFB_(";
$command = $this->getCommand();
$this->assertSame($expected, $command->parseResponse($raw));
}
/**
* @group disconnected
*/
public function testPrefixKeys()
{
$arguments = array('key');
$expected = array('prefix:key');
$command = $this->getCommandWithArgumentsArray($arguments);
$command->prefixKeys('prefix:');
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
}
+11
View File
@@ -74,6 +74,17 @@ class KeyExistsTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
+12
View File
@@ -74,6 +74,17 @@ class KeyExpireAtTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
@@ -85,6 +96,7 @@ class KeyExpireAtTest extends CommandTestCase
}
/**
* @medium
* @group connected
* @group slow
*/
+13
View File
@@ -74,6 +74,17 @@ class KeyExpireTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/
@@ -85,6 +96,7 @@ class KeyExpireTest extends CommandTestCase
}
/**
* @medium
* @group connected
* @group slow
*/
@@ -103,6 +115,7 @@ class KeyExpireTest extends CommandTestCase
}
/**
* @medium
* @group connected
* @group slow
*/
+11
View File
@@ -74,6 +74,17 @@ class KeyKeysTest extends CommandTestCase
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments()
{
$command = $this->getCommand();
$command->prefixKeys('prefix:');
$this->assertSame(array(), $command->getArguments());
}
/**
* @group connected
*/

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