Compare commits

..

32 Commits

Author SHA1 Message Date
Daniele Alessandri 4bc6f588fc Bump version in files for release. 2012-06-01 11:17:31 +02:00
Daniele Alessandri 2522a249f3 Minor CS fixes. 2012-06-01 11:16:34 +02:00
Daniele Alessandri b4e1cfcee7 Update CHANGELOG. 2012-06-01 11:15:47 +02:00
Daniele Alessandri 105bf8a2ee Merge remote-tracking branch 'inadarei/v0.7' into v0.7 2012-05-27 20:56:51 +02:00
Irakli Nadareishvili 4f16e232a8 Updated file_exists() to is_file() and changed require_once() to require() per Predis' author's suggestions 2012-05-26 08:14:29 -03:00
Irakli Nadareishvili 703c3ceb90 Making autoloader act nicer to autoloaders that may be running in parallel 2012-05-22 13:39:44 -03:00
Daniele Alessandri ca2e4aa4a1 Update CHANGELOG.
[ci skip]
2012-05-17 17:33:21 +02:00
Daniele Alessandri 477056b862 For some reason, class and tests for BITCOUNT were missing... 2012-05-17 16:52:21 +02:00
Daniele Alessandri b94e002e90 TIME can be considered a read-only command in MASTER / SLAVE. 2012-05-17 16:37:26 +02:00
Daniele Alessandri 3aff21f8cc New commands: BITOP, BITCOUNT (Redis v2.6-dev). 2012-05-17 16:34:50 +02:00
Daniele Alessandri 1e142a63e6 Fix comment in package.ini.
[ci skip]
2012-05-17 09:52:46 +02:00
Daniele Alessandri b67dab9ab1 Fix Predis\Commands\ServerEvalSHA::getScriptHash().
"Predis\Commands\ServerEvalSHA::getScriptHash()" was calculating the hash
while it just needs to return the first argument of the command.
2012-04-28 01:25:17 +02:00
Daniele Alessandri 6ba1890da9 Do not assume that the position of a MultiBulkResponse iterator is 0. 2012-04-27 16:10:58 +02:00
Daniele Alessandri ca8ba35fb5 Switch to master inside executor when pipelining in replication mode.
The actual code to switch to master has been implemented only in the
standard and the fire-and-forget executor classes since it does not
make sense to have it in special executors used exclusively when in
client-side clustering mode.
2012-04-25 11:53:08 +02:00
Daniele Alessandri 5af2b628f1 Scripting abstraction can use negatives to calculate number of keys.
With a negative number Predis will count from the end of the arguments list
to calculate the actual number of keys that will be interpreted as elements
for `KEYS` by the underlying `EVAL` command.
2012-04-25 11:20:57 +02:00
Daniele Alessandri f4c58b926b Fix example for custom distribution strategy.
See ISSUE #63 for details.
2012-04-13 11:51:02 +02:00
Daniele Alessandri cb390cf292 Back to development for the next patch release. 2012-04-13 11:49:30 +02:00
Daniele Alessandri 74cc0e3225 Bump version in files for release. 2012-04-01 10:09:20 +02:00
Daniele Alessandri b5ae1b9a2b Update README. 2012-03-31 11:58:38 +02:00
Daniele Alessandri 31c47362fb Add a couple of paragraphs in the FAQ. 2012-03-31 11:29:59 +02:00
Daniele Alessandri 871f1a3a1d Update TODO. 2012-03-31 10:45:16 +02:00
Daniele Alessandri ba0338e3d8 Update CHANGELOG. 2012-03-31 10:44:29 +02:00
Daniele Alessandri a7c906ab4c Apparently the next major version or Redis will be 2.8. 2012-03-31 10:43:50 +02:00
Daniele Alessandri 7675bb040a Make it possible to prepend the autoloader. 2012-03-19 11:31:39 +01:00
Daniele Alessandri 930af24c7f Make Predis\Autoloader faster. 2012-03-18 18:00:00 +01:00
Daniele Alessandri 23ed563e45 Add missing tests for Predis\Profiles\ServerProfile::defineCommand(). 2012-03-18 12:58:19 +01:00
Daniele Alessandri f00bf6443d Update CHANGELOG.
[ci skip]
2012-03-11 14:01:49 +01:00
Daniele Alessandri 6d73f4a538 Promote 2.6 as an independent server profile for Redis 2.6.
The development server profile now targets Redis 3.0.
2012-03-11 13:53:28 +01:00
Daniele Alessandri f0f3d7814c Fix MONITOR and Predis\MonitorContext with Redis 2.6.
Starting with 2.6, Redis uses a slightly different format for the payload
returned by MONITOR for each command. Predis can now adapt to this difference
and returns a new `client` field in the payload object. This new field is
always defined but is set to NULL when connected to Redis < 2.6.

Please note that Redis 2.6 now does not echo the `MONITOR` command as the
first payload when opening a new MONITOR context.
2012-03-11 13:30:30 +01:00
Daniele Alessandri 5c2704c9d4 Add a not about MONITOR failing the test with Redis unstable. 2012-03-07 13:04:28 +01:00
Daniele Alessandri effefcee34 New command: TIME (Redis v2.6-dev). 2012-03-07 12:30:39 +01:00
Daniele Alessandri 86bf223ec0 Enable integration tests for Redis commands on Travis CI.
This was not possible before since our test suite requires Redis >= 2.4 but
their environment was running Redis 2.2 until a month ago or so.
2012-02-25 11:04:31 +01:00
474 changed files with 4762 additions and 9231 deletions
-2
View File
@@ -6,6 +6,4 @@ branches:
only:
- master
- v0.7
- v0.8
services: redis-server
script: phpunit -c phpunit.xml.travisci
-53
View File
@@ -1,53 +0,0 @@
# Namespaces, interfaces and classes renamed in Predis v0.8 #
____________________________________________
Some namespaces, interfaces and classes in Predis v0.8 have been renamed to follow a common rule inspired
by the naming conventions adopted by the Symfony2 project. This is a list of all the changes:
### Renamed namespaces ###
- `Predis\Network` => `Predis\Connection`
- `Predis\Profiles` => `Predis\Profile`
- `Predis\Iterators` => `Predis\Iterator`
- `Predis\Options` => `Predis\Option`
- `Predis\Commands` => `Predis\Command`
- `Predis\Commands\Processors` => `Predis\Command\Processor`
### Renamed interfaces ###
- `Predis\IReplyObject` => `Predis\ResponseObjectInterface`
- `Predis\IRedisServerError` => `Predis\ResponseErrorInterface`
- `Predis\Options\IOption` => `Predis\Option\OptionInterface`
- `Predis\Options\IClientOptions` => `Predis\Option\ClientOptionsInterface`
- `Predis\Profile\IServerProfile` => `Predis\Profile\ServerProfileInterface`
- `Predis\Pipeline\IPipelineExecutor` => `Predis\Pipeline\PipelineExecutorInterface`
- `Predis\Distribution\INodeKeyGenerator` => `Predis\Distribution\HashGeneratorInterface`
- `Predis\Distribution\IDistributionStrategy` => `Predis\Distribution\DistributionStrategyInterface`
- `Predis\Protocol\IProtocolProcessor` => `Predis\Protocol\ProtocolInterface`
- `Predis\Protocol\IResponseReader` => `Predis\Protocol\ResponseReaderInterface`
- `Predis\Protocol\IResponseHandler` => `Predis\Protocol\ResponseHandlerInterface`
- `Predis\Protocol\ICommandSerializer` => `Predis\Protocol\CommandSerializerInterface`
- `Predis\Protocol\IComposableProtocolProcessor` => `Predis\Protocol\ComposableProtocolInterface`
- `Predis\Network\IConnection` => `Predis\Connection\ConnectionInterface`
- `Predis\Network\IConnectionSingle` => `Predis\Connection\SingleConnectionInterface`
- `Predis\Network\IConnectionComposable` => `Predis\Connection\ComposableConnectionInterface`
- `Predis\Network\IConnectionCluster` => `Predis\Connection\ClusterConnectionInterface`
- `Predis\Network\IConnectionReplication` => `Predis\Connection\ReplicationConnectionInterface`
- `Predis\Commands\ICommand` => `Predis\Command\CommandInterface`
- `Predis\Commands\IPrefixable` => `Predis\Command\PrefixableCommandInterface`
- `Predis\Command\Processor\ICommandProcessor` => `Predis\Command\Processor\CommandProcessorInterface`
- `Predis\Command\Processor\ICommandProcessorChain` => `Predis\Command\Processor\CommandProcessorChainInterface`
- `Predis\Command\Processor\IProcessingSupport` => `Predis\Command\Processor\CommandProcessingInterface`
### Renamed classes ###
- `Predis\Commands\Command` => `Predis\Command\AbstractCommand`
- `Predis\Network\ConnectionBase` => `Predis\Connection\AbstractConnection`
### Classes or interfaces moved to different namespaces ###
- `Predis\MonitorContext` => `Predis\Monitor\MonitorContext`
- `Predis\ConnectionParameters` => `Predis\Connection\ConnectionParameters`
- `Predis\ConnectionParametersInterface` => `Predis\Connection\ConnectionParametersInterface`
- `Predis\ConnectionFactory` => `Predis\Connection\ConnectionFactory`
- `Predis\ConnectionFactoryInterface` => `Predis\Connection\ConnectionFactoryInterface`
-65
View File
@@ -1,68 +1,3 @@
v0.8.0 (2012-10-23)
===============================================================================
- The default server profile for Redis is now `2.6`.
- Certain connection parameters have been renamed:
- `connection_async` is now `async_connect`
- `connection_timeout` is now `timeout`
- `connection_persistent` is now `persistent`
- The `throw_errors` connection parameter has been removed and replaced by the
new `exceptions` client option since exceptions on `-ERR` replies returned by
Redis are not generated by connection classes anymore but instead are thrown
by the client class and other abstractions such as pipeline contexts.
- Added smart support for redis-cluster (Redis v3.0) in addition to the usual
cluster implementation that uses client-side sharding.
- Various namespaces and classes have been renamed to follow rules inspired by
the Symfony2 naming conventions.
- The second argument of the constructor of `Predis\Client` does not accept
strings or instances of `Predis\Profile\ServerProfileInterface` anymore.
To specify a server profile you must explicitly set `profile` in the array
of client options.
- `Predis\Command\ScriptedCommand` internally relies on `EVALSHA` instead of
`EVAL` thus avoiding to send Lua scripts bodies on each request. The client
automatically resends the command falling back to `EVAL` when Redis returns a
`-NOSCRIPT` error. Automatic fallback to `EVAL` does not work with pipelines,
inside a `MULTI / EXEC` context or with plain `EVALSHA` commands.
- Complex responses are no more parsed by connection classes as they must be
processed by consumer classes using the handler associated to the issued
command. This means that executing commands directly on connections only
returns simple Redis types, but nothing changes when using `Predis\Client`
or the provided abstractions for pipelines and transactions.
- Iterators for multi-bulk replies now skip the response parsing method of the
command that generated the response and are passed directly to user code.
Pipeline and transaction objects still consume automatically iterators.
- Cluster and replication connections now extend a new common interface,
`Predis\Connection\AggregatedConnectionInterface`.
- `Predis\Connection\MasterSlaveReplication` now uses an external strategy
class to handle the logic for checking readable / writable commands and Lua
scripts.
- Command pipelines have been optimized for both speed and code cleanness, but
at the cost of bringing a breaking change in the signature of the interface
for pipeline executors.
- Added a new pipeline executor that sends commands wrapped in a MULTI / EXEC
context to make the execution atomic: if a pipeline fails at a certain point
then the whole pipeline is discarded.
- The key-hashing mechanism for commands is now handled externally and is no
more a competence of each command class. This change is neeeded to support
both client-side sharding and Redis cluster.
- `Predis\Options\Option` is now abstract, see `Predis\Option\AbstractOption`.
v0.7.3 (2012-06-01)
===============================================================================
-39
View File
@@ -1,39 +0,0 @@
## Filing bug reports ##
Bugs or feature requests can be posted online on the [GitHub issues](http://github.com/nrk/predis/issues)
section of the project.
When reporting bugs, in addition to the obvious description of your issue you __must__ always provide
some essential information about your environment such as:
1. version of Predis.
2. version of PHP.
3. name and version of the operating system.
4. when possible, a small snippet of code that reproduces the issue.
__Think about it__: we do not have a crystal ball and cannot predict things and peer into the unknown,
so please provide as much details as possible to help us isolating issues and fix them.
__Never__ use GitHub issues to post generic questions about Predis! When you have questions about
how Predis works or how it can be used, please just hop me an email and I will get back to you as
soon as possible.
## Contributing code ##
If you want to work on Predis, it is highly recommended that you first run the test suite in order to
check that everything is OK, and report strange behaviours or bugs. When modifying Predis please make
sure that no warnings or notices are emitted by PHP by running the interpreter in your development
environment with the `error_reporting` variable set to `E_ALL | E_STRICT`.
The recommended way to contribute to Predis is to fork the project on GitHub, create new topic branches
on your newly created repository to fix or add features (possibly with tests covering your modifications)
and then open a new pull request with a description of the applied changes. Obviously you can use any
other Git hosting provider of your preference.
When writing code please follow the [basic coding (PSR-1)](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
and [coding style (PSR-2)](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
standards and stick with the conventions used in Predis to name classes and interfaces.
Please also follow some basic [commit guidelines](http://git-scm.com/book/ch5-2.html#Commit-Guidelines)
before opening pull requests.
+79
View File
@@ -0,0 +1,79 @@
# Some frequently asked questions about the performances of Predis #
____________________________________________
### Predis is a pure-PHP implementation: it can not be fast enough! ###
It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you
a couple of easy numbers using a single Predis client with PHP 5.3.5 (custom build) and Redis 2.2
(localhost) under Ubuntu 11.04 (running on a Intel Q6600):
19600 SET/sec using 12 bytes for both key and value
18900 GET/sec while retrieving the very same values
0.200 seconds to fetch 30000 keys using _KEYS *_.
How does it compare with a nice C-based extension such as [__phpredis__](http://github.com/nicolasff/phpredis)?
30500 SET/sec using 12 bytes for both key and value
31000 GET/sec while retrieving the very same values
0.030 seconds to fetch 30000 keys using "KEYS *"".
Wow, __phpredis__ looks so much faster! Well we are comparing a C extension with a pure-PHP library so
lower numbers are quite expected, but there is a fundamental flaw in them: is this really how you are
going to use Redis in your application? Are you really going to send thousands of commands in a for-loop
for each page request using a single client instance? If so, well I guess you are probably doing something
wrong. Also, if you need to SET or GET multiple keys you should definitely use commands such as MSET and
MGET. You can also use pipelining to get more performances when this technique can be used.
There is one more thing. We have tested the overhead of Predis by connecting on a localhost instance of
Redis, but how these numbers change when we hit the network by connecting to instances of Redis that
reside on other servers?
Using Predis:
3600 SET/sec using 12 bytes for both key and value
3600 GET/sec while retrieving the very same values
0.210 seconds to fetch 30000 keys using "KEYS *".
Using phpredis:
4000 SET/sec using 12 bytes for both key and value
4000 GET/sec while retrieving the very same values
0.051 seconds to fetch 30000 keys using "KEYS *".
There you go, you get almost the same average numbers and the reason is quite simple: network latency
is a real performance killer and you cannot do (almost) anything about that. As a disclaimer, please
remember that we are measuring the overhead of client libraries implementations and the effects of the
network round-trip time, we are not really measuring how fast Redis is. Redis shines the best with
thousands of concurrent clients doing requests! Also, actual performances should be measured according
to how your application will use Redis.
### I am convinced, but performances for multi-bulk replies (e.g. _KEYS *_) are still worse ###
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/seppo0010/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:
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array('tcp' => 'Predis\Network\PhpiredisConnection')
));
As simple as it is, nothing will really change in the way you use the library in your application. So,
how fast is it now? There are not much improvements for inline or short bulk replies (e.g. _SET_ or
_GET_), but the speed for parsing multi-bulk replies is now on par with phpredis:
Using Predis with a phpiredis-based connection to fetch 30000 keys using _KEYS *_:
0.031 seconds from a local Redis instance
0.058 seconds from a remote Redis instance
### If I need to install a C extension to get better performances, why not using phpredis? ###
Good question. Generically speaking, if you need absolute uber-speed using localhost instances of Redis
and you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you
do not need any kind of extensibility or guaranteed backwards compatibility with different versions of
Redis (Predis currently supports from 1.2 up to 2.2, and even the current development version), then
using __phpredis__ can make sense for you. Otherwise, Predis is for you. Using __phpiredis__ gives you
a nice speed bump, but it is not mandatory.
+32 -85
View File
@@ -1,5 +1,5 @@
# Some frequently asked questions about Predis #
_________________________________________________
____________________________________________
### What is the point of Predis? ###
@@ -14,6 +14,34 @@ features, this can be a great asset that allows you to add new and still missing
or change the behaviour of the library without the need to break your dependencies in production code
(well, at least to some degree).
### How about performances? ###
Please refer to the dedicated __FAQ.PERFORMANCES__ file.
### Why PHP 5.3? ###
Seriously, are you still using PHP 5.2 to build new applications? I assume that if you are throwing Redis
in the mix, then you are probably coding something new after all. PHP 5.3 is faster, less memory hungry
and has a few nice features such as namespaces and closures (kind of). More importantly, PHP 5.2 is not
even officially supported anymore (aside from security patches). PHP 5.3 is not the future of PHP, but
its current present. Furthermore, most of the existing frameworks out there are also making the switch
with their respective new major versions. If you still insist on using PHP 5.2, you can get any recent
backported release of Predis 0.6.x, or just use a different library.
### Why so many files for just one library? ###
Before v0.7, Predis used the one-big-file approach to distribute the library. As much as you prefer having
just one file for everything, this kind of solution is actually not that good. Predis now complies with the
[PSR-0](http://groups.google.com/group/php-standards/web/psr-0-final-proposal) standard to play nice with
the major recent frameworks and libraries, so it needs an autoloader function to be defined. If you still
want to have just one file grouping all the classes for whatever reason, then the __bin/create-single-file.php__
script in the repository can generate it for you. There is also the __bin/create-phar.php__ script that
generates a single [Phar archive](http://www.php.net/manual/en/intro.phar.php) of the whole library.
### Does Predis support UNIX domain sockets and persistent connections? ###
Yes. Obviously, persistent connections actually work when using PHP configured as a persistent process that
@@ -42,7 +70,8 @@ $client = new Predis\Client();
try {
$client->connect();
} catch (Predis\Connection\ConnectionException $exception) {
}
catch (Predis\Network\ConnectionException $exception) {
// We could not connect to Redis! Your handling code goes here.
}
@@ -68,86 +97,4 @@ $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).
# Frequently asked questions about performances #
_________________________________________________
### Predis is a pure-PHP implementation: it can not be fast enough! ###
It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you
a couple of easy numbers using a single Predis client with PHP 5.3.5 (custom build) and Redis 2.2
(localhost) under Ubuntu 11.04 (running on a Intel Q6600):
19600 SET/sec using 12 bytes for both key and value
18900 GET/sec while retrieving the very same values
0.200 seconds to fetch 30000 keys using _KEYS *_.
How does it compare with a nice C-based extension such as [__phpredis__](http://github.com/nicolasff/phpredis)?
30500 SET/sec using 12 bytes for both key and value
31000 GET/sec while retrieving the very same values
0.030 seconds to fetch 30000 keys using "KEYS *"".
Wow, __phpredis__ looks so much faster! Well we are comparing a C extension with a pure-PHP library so
lower numbers are quite expected, but there is a fundamental flaw in them: is this really how you are
going to use Redis in your application? Are you really going to send thousands of commands in a for-loop
for each page request using a single client instance? If so, well I guess you are probably doing something
wrong. Also, if you need to SET or GET multiple keys you should definitely use commands such as MSET and
MGET. You can also use pipelining to get more performances when this technique can be used.
There is one more thing. We have tested the overhead of Predis by connecting on a localhost instance of
Redis, but how these numbers change when we hit the network by connecting to instances of Redis that
reside on other servers?
Using Predis:
3600 SET/sec using 12 bytes for both key and value
3600 GET/sec while retrieving the very same values
0.210 seconds to fetch 30000 keys using "KEYS *".
Using phpredis:
4000 SET/sec using 12 bytes for both key and value
4000 GET/sec while retrieving the very same values
0.051 seconds to fetch 30000 keys using "KEYS *".
There you go, you get almost the same average numbers and the reason is quite simple: network latency
is a real performance killer and you cannot do (almost) anything about that. As a disclaimer, please
remember that we are measuring the overhead of client libraries implementations and the effects of the
network round-trip time, we are not really measuring how fast Redis is. Redis shines the best with
thousands of concurrent clients doing requests! Also, actual performances should be measured according
to how your application will use Redis.
### I am convinced, but performances for multi-bulk replies (e.g. _KEYS *_) are still worse ###
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/seppo0010/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:
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array('tcp' => 'Predis\Connection\PhpiredisConnection')
));
As simple as it is, nothing will really change in the way you use the library in your application. So,
how fast is it now? There are not much improvements for inline or short bulk replies (e.g. _SET_ or
_GET_), but the speed for parsing multi-bulk replies is now on par with phpredis:
Using Predis with a phpiredis-based connection to fetch 30000 keys using _KEYS *_:
0.031 seconds from a local Redis instance
0.058 seconds from a remote Redis instance
### If I need to install a C extension to get better performances, why not using phpredis? ###
Good question. Generically speaking, if you need absolute uber-speed using localhost instances of Redis
and you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you
do not need any kind of extensibility or guaranteed backwards compatibility with different versions of
Redis (Predis currently supports from 1.2 up to 2.6, and even the current development version), then
using __phpredis__ can make sense for you. Otherwise, Predis is perfect for the job. __phpiredis__
can give you a nice speed bump, but using it is not mandatory.
[passed using a named array](https://github.com/nrk/predis/blob/v0.7.1/tests/Predis/Commands/KeySortTest.php#L56-77).
+48 -88
View File
@@ -2,29 +2,23 @@
Predis is a flexible and feature-complete PHP (>= 5.3) client library for the Redis key-value store.
The library does not require any additional extension loaded in PHP but it can be optionally paired
with the [phpiredis](https://github.com/seppo0010/phpiredis) C extension to lower the overhead of
serializing and parsing the Redis protocol. Predis is also available in an asynchronous fashion
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.
More details are available on the [official wiki](http://wiki.github.com/nrk/predis) of the project.
For a list of frequently asked questions about Predis, see the __FAQ__ file in the root of the repository.
For a version compatible with PHP 5.2 you must use the backported version from the latest release in the
0.6.x series. More details are available on the [official wiki](http://wiki.github.com/nrk/predis) of the
project.
## Main features ##
- Wide range of Redis versions supported (from __1.2__ to __2.6__ and unstable) using server profiles.
- Smart support for [redis-cluster](http://redis.io/topics/cluster-spec) (Redis >= 3.0).
- Client-side sharding via consistent hashing or custom distribution strategies.
- Complete support for Redis from __1.2__ to __2.6__ and unstable versions using different server profiles.
- Client-side sharding with support for consistent hashing or custom distribution strategies.
- Support for master / slave replication configurations (write on master, read from slaves).
- Transparent key prefixing strategy capable of handling any command known that has keys in its arguments.
- Command pipelining on single and aggregated connections.
- Transparent key prefixing strategy capable of handling any command known that has keys in its arguments.
- Abstraction for Redis transactions (Redis >= 2.0) with support for CAS operations (Redis >= 2.2).
- Abstraction for Lua scripting (Redis >= 2.6) capable of automatically switching between `EVAL` and `EVALSHA`.
- Connections to Redis instances are lazily established upon the first call to a command by the client.
- Connections to Redis instances are automatically and lazily estabilished upon the first call to a command.
- Ability to connect to Redis using TCP/IP or UNIX domain sockets with support for persistent connections.
- Ability to specify alternative connection classes to use different types of network or protocol backends.
- Ability to use alternative connection classes to use different types of network or protocol backends.
- Flexible system to define and register your own set of commands or server profiles to client instances.
@@ -40,11 +34,11 @@ by browsing the list of [tagged releases](http://github.com/nrk/predis/tags).
### Loading the library ###
Predis relies on the autoloading features of PHP to load its files when needed and complies with the
[PSR-0 standard](http://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) which makes it
compatible with most of the major frameworks and libraries. Autoloading in your application is handled
automatically when managing the dependencies with Composer, but you can also leverage its own autoloader
class if you are going to use it in a project or script without any PSR-0 compliant autoloading facility:
Predis relies on the autoloading features of PHP to automatically load the needed files and complies
with the [PSR-0 standard](http://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) for
interoperability with most of the major frameworks and libraries. Everything is transparently handled
for you when installing the library using Composer, but you can also leverage its own autoloader class
if you are going to use it in a project or script without any PSR-0 compliant autoloading facility:
``` php
<?php
@@ -54,19 +48,20 @@ require 'Predis/Autoloader.php';
Predis\Autoloader::register();
```
It is possible to create a single [Phar](http://www.php.net/manual/en/intro.phar.php) archive from the
repository just by launching `bin/create-phar.php`. The generated Phar archive ships with a stub defining
an autoloader function for Predis, so you just need to require the Phar to be able to use the library.
You can create a single [Phar](http://www.php.net/manual/en/intro.phar.php) archive from the repository
just by launching the `bin/create-phar.php` executable script. The generated Phar archive ships with a
stub defining an autoloader function for Predis, so you just need to require the Phar to be able to use
the library.
Alternatively it is possible to generate a single PHP file that holds every class, just like older versions
of Predis, using `bin/create-single-file.php`. In this way you can load Predis in your scripts simply by
using functions such as `require` and `include`, but this practice is not encouraged.
Alternatively you can generate a single PHP file that holds every class, just like older versions of
Predis, using the `bin/create-single-file.php` executable script. In this way you can load Predis in your
scripts simply by using functions such as `require` and `include`, but this practice is not encouraged.
### Connecting to Redis ###
### Connecting to a local instance of Redis ###
By default Predis uses `127.0.0.1` and `6379` as the default host and port when creating a new client
instance without specifying any connection parameter:
When connecting to local instance of Redis (`127.0.0.1` on port `6379`), you do not have to specify any
additional parameter to create a new client instance:
``` php
<?php
@@ -75,7 +70,7 @@ $redis->set('foo', 'bar');
$value = $redis->get('foo');
```
It is possible to specify the various connection parameters using URI strings or named arrays:
However you can use an URI string or a named array to specify the needed connection parameters:
``` php
<?php
@@ -91,7 +86,7 @@ $redis = new Predis\Client(array(
```
### Pipelining commands to multiple instances of Redis with client-side sharding ###
### Pipelining multiple commands to multiple instances of Redis with client-side sharding ###
Pipelining helps with performances when there is the need to send many commands to a server in one go.
Furthermore, pipelining works transparently even on aggregated connections. To achieve this, Predis
@@ -105,7 +100,7 @@ $redis = new Predis\Client(array(
array('host' => '10.0.0.2', 'port' => 6379)
));
$replies = $redis->pipeline(function ($pipe) {
$replies = $redis->pipeline(function($pipe) {
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
$pipe->get("key:$i");
@@ -114,26 +109,15 @@ $replies = $redis->pipeline(function ($pipe) {
```
### Multiple and customizable connection backends ###
### Overriding standard connection classes with custom ones ###
Predis can optionally use different connection backends to connect to Redis. One of them leverages
the [phpiredis](http://github.com/seppo0010/phpiredis) C extension resulting in a major speed bump
especially when dealing with long multibulk replies (the `socket` extension is also required):
Predis allows developers to create new connection classes to add support for new protocols or override
the existing ones and provide a different implementation compared to the default classes. This can be
obtained by subclassing the `Predis\Network\IConnectionSingle` interface.
``` php
<?php
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array('tcp' => 'Predis\Connection\PhpiredisConnection')
));
```
Developers can also create their own connection backends to add support for new protocols, extend
existing ones or provide different implementations. Connection backend classes must implement
`Predis\Connection\SingleConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
``` php
<?php
class MyConnectionClass implements Predis\Connection\SingleConnectionInterface
class MyConnectionClass implements Predis\Network\IConnectionSingle
{
// implementation goes here
}
@@ -144,8 +128,8 @@ $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.
The classes contained in the `Predis\Network` namespace give you a better insight with actual code on
how to create new connection classes.
### Defining and registering new commands on the client at runtime ###
@@ -158,7 +142,7 @@ runtime. Actually, it is easier done than said:
``` php
<?php
class BrandNewRedisCommand extends Predis\Command\AbstractCommand
class BrandNewRedisCommand extends Predis\Commands\Command
{
public function getId()
{
@@ -172,43 +156,6 @@ $redis->newcmd();
```
### Abstraction for handling Lua scripts as plain Redis commands ###
A scripted command in Predis is an abstraction for [Lua scripting](http://redis.io/commands/eval)
with Redis >= 2.6 that allows to use a Lua script as if it was a plain Redis command registered
in the server profile being used by the client instance. Internally, scripted commands use
[EVALSHA](http://redis.io/commands/evalsha) to refer to a Lua script by its SHA1 hash in order
to save bandwidth, but they are capable of falling back to [EVAL](http://redis.io/commands/eval)
when needed:
``` php
<?php
class ListPushRandomValue extends Predis\Command\ScriptedCommand
{
public function getKeysCount()
{
return 1;
}
public function getScript()
{
return
<<<LUA
math.randomseed(ARGV[1])
local rnd = tostring(math.random())
redis.call('lpush', KEYS[1], rnd)
return rnd
LUA;
}
}
$client = new Predis\Client();
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');
$value = $client->lpushrand('random_values', $seed = mt_rand());
```
## Test suite ##
__ATTENTION__: Do not ever run the test suite shipped with Predis against instances of Redis running in
@@ -227,6 +174,19 @@ Predis uses Travis CI for continuous integration. You can find the results of th
history [on its project page](http://travis-ci.org/nrk/predis).
## Contributing ##
If you want to work on Predis, it is highly recommended that you first run the test suite in order to
check that everything is OK, and report strange behaviours or bugs. When modifying Predis please make
sure that no warnings or notices are emitted by PHP by running the interpreter in your development
environment with the `error_reporting` variable set to `E_ALL | E_STRICT`.
The recommended way to contribute to Predis is to fork the project on GitHub, create new topic branches
on your newly created repository to fix or add features (possibly with tests covering your modifications)
and then open a new pull request with a description of the applied changes. Obviously you can use any
other Git hosting provider of your preference.
## Dependencies ##
- PHP >= 5.3.2
+2
View File
@@ -0,0 +1,2 @@
* Documentation! The README is obviously not enought to show how to use Predis
as it does not cover all of its features.
+1 -1
View File
@@ -1 +1 @@
0.8.0
0.7.3
+1 -1
View File
@@ -50,7 +50,7 @@ function getPharStub($options)
return <<<EOSTUB
<?php
Phar::mapPhar('predis.phar');
spl_autoload_register(function (\$class) {
spl_autoload_register(function(\$class) {
if (strpos(\$class, 'Predis\\\\') === 0) {
\$file = 'phar://predis.phar/'.strtr(\$class, '\\\', '/').'.php';
if (file_exists(\$file)) {
+9 -10
View File
@@ -184,7 +184,7 @@ class PredisFile
{
$namespaces = array_fill_keys(array_unique(
array_map(
function ($fqn) { return PhpNamespace::extractName($fqn); },
function($fqn) { return PhpNamespace::extractName($fqn); },
array_keys($dependencyScores)
)
), 0);
@@ -200,7 +200,7 @@ class PredisFile
private function getOrderedClasses(PhpNamespace $phpNamespace, $classes)
{
$nsClassesFQNs = array_map(function ($cl) { return $cl->getFQN(); }, $phpNamespace->getClasses());
$nsClassesFQNs = array_map(function($cl) { return $cl->getFQN(); }, $phpNamespace->getClasses());
$nsOrderedClasses = array();
foreach ($nsClassesFQNs as $nsClassFQN) {
@@ -352,9 +352,7 @@ class PhpUseDirectives implements Countable, IteratorAggregate
public function getPhpCode()
{
$reducer = function ($str, $use) {
return $str .= "use $use;\n";
};
$reducer = function($str, $use) { return $str .= "use $use;\n"; };
return array_reduce($this->getList(), $reducer, '');
}
@@ -432,7 +430,7 @@ LICENSE;
{
$useDirectives = $this->getNamespace()->getUseDirectives();
$useExtractor = function ($m) use ($useDirectives) {
$useExtractor = function($m) use($useDirectives) {
$useDirectives->add(($namespacedPath = $m[1]));
};
@@ -455,7 +453,7 @@ LICENSE;
$implements = array();
$extends = array();
$extractor = function ($iterator, $callback) {
$extractor = function($iterator, $callback) {
$className = '';
$iterator->seek($iterator->key() + 1);
@@ -466,7 +464,8 @@ LICENSE;
if (preg_match('/\s?,\s?/', $token)) {
$callback(trim($className));
$className = '';
} else if ($token == '{') {
}
else if ($token == '{') {
$callback(trim($className));
return;
}
@@ -511,13 +510,13 @@ LICENSE;
break;
case T_IMPLEMENTS:
$extractor($iterator, function ($fqn) use (&$implements) {
$extractor($iterator, function($fqn) use (&$implements) {
$implements[] = $fqn;
});
break;
case T_EXTENDS:
$extractor($iterator, function ($fqn) use (&$extends) {
$extractor($iterator, function($fqn) use (&$extends) {
$extends[] = $fqn;
});
break;
+11 -11
View File
@@ -13,9 +13,9 @@
// -------------------------------------------------------------------------- //
// This script can be used to automatically generate a file with the scheleton
// of a test case to test a Redis command by specifying the name of the class
// in the Predis\Command namespace (only classes in this namespace are valid).
// in the Predis\Commands namespace (only classes in this namespace are valid).
// For example, to generate a test case for SET (which is represented by the
// Predis\Command\StringSet class):
// Predis\Commands\StringSet class):
//
// $ ./bin/generate-command-test.php --class=StringSet
//
@@ -33,8 +33,8 @@
// is explicitly specified.
// -------------------------------------------------------------------------- //
use Predis\Command\CommandInterface;
use Predis\Command\PrefixableCommandInterface;
use Predis\Commands\ICommand;
use Predis\Commands\IPrefixable;
class CommandTestCaseGenerator
{
@@ -92,8 +92,8 @@ class CommandTestCaseGenerator
throw new RuntimeException("Missing 'class' option.");
}
$options['fqn'] = "Predis\\Command\\{$options['class']}";
$options['path'] = "Predis/Command/{$options['class']}.php";
$options['fqn'] = "Predis\\Commands\\{$options['class']}";
$options['path'] = "Predis/Commands/{$options['class']}.php";
$source = __DIR__.'/../lib/'.$options['path'];
if (!file_exists($source)) {
@@ -129,8 +129,8 @@ class CommandTestCaseGenerator
if (!$reflection->isInstantiable()) {
throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed.");
}
if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) {
throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface.");
if (!$reflection->implementsInterface('Predis\Commands\ICommand')) {
throw new RuntimeException("Class $class must implement the Predis\Commands\ICommand interface.");
}
$instance = $reflection->newInstance();
@@ -148,7 +148,7 @@ class CommandTestCaseGenerator
file_put_contents($options['output'], $this->generate());
}
protected function getTestCaseBuffer(CommandInterface $instance)
protected function getTestCaseBuffer(ICommand $instance)
{
$id = $instance->getId();
$fqn = get_class($instance);
@@ -167,7 +167,7 @@ class CommandTestCaseGenerator
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use \PHPUnit_Framework_TestCase as StandardTestCase;
@@ -226,7 +226,7 @@ class $class extends CommandTestCase
PHP;
if ($instance instanceof PrefixableCommandInterface) {
if ($instance instanceof IPrefixable) {
$buffer .=<<<PHP
/**
+1 -5
View File
@@ -13,11 +13,7 @@
}
],
"require": {
"php": ">=5.3.2"
},
"suggest": {
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
"ext-curl": "Allows access to Webdis when paired with phpiredis"
"php": ">=5.3.0"
},
"autoload": {
"psr-0": {"Predis": "lib/"}
+10 -17
View File
@@ -13,13 +13,12 @@ require 'SharedConfigurations.php';
// Developers can customize the distribution strategy used by the client
// to distribute keys among a cluster of servers simply by creating a class
// that implements Predis\Distribution\DistributionStrategyInterface.
// that implements the Predis\Distribution\IDistributionStrategy interface.
use Predis\Connection\PredisCluster;
use Predis\Cluster\Distribution\DistributionStrategyInterface;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\Distribution\IDistributionStrategy;
use Predis\Network\PredisCluster;
class NaiveDistributionStrategy implements DistributionStrategyInterface, HashGeneratorInterface
class NaiveDistributionStrategy implements IDistributionStrategy
{
private $nodes;
private $nodesCount;
@@ -38,7 +37,7 @@ class NaiveDistributionStrategy implements DistributionStrategyInterface, HashGe
public function remove($node)
{
$this->nodes = array_filter($this->nodes, function ($n) use ($node) {
$this->nodes = array_filter($this->nodes, function($n) use($node) {
return $n !== $node;
});
@@ -47,30 +46,24 @@ class NaiveDistributionStrategy implements DistributionStrategyInterface, HashGe
public function get($key)
{
if (0 === $count = $this->nodesCount) {
$count = $this->nodesCount;
if ($count === 0) {
throw new RuntimeException('No connections');
}
return $this->nodes[$count > 1 ? abs($key % $count) : 0];
}
public function hash($value)
public function generateKey($value)
{
return crc32($value);
}
public function getHashGenerator()
{
return $this;
}
}
$options = array(
'cluster' => function () {
'cluster' => function() {
$distributor = new NaiveDistributionStrategy();
$cluster = new PredisCluster($distributor);
return $cluster;
return new PredisCluster($distributor);
},
);
+1 -1
View File
@@ -60,7 +60,7 @@ class EventsListener implements Countable
$dispatcher->attachCallback('events', ($events = new EventsListener()));
// Attach a function to control the dispatcher loop termination with a message.
$dispatcher->attachCallback('control', function ($payload) use ($dispatcher) {
$dispatcher->attachCallback('control', function($payload) use ($dispatcher) {
if ($payload === 'terminate_dispatcher') {
$dispatcher->stop();
}
+8 -11
View File
@@ -13,15 +13,14 @@ require 'SharedConfigurations.php';
// Predis allows to set Lua scripts as read-only operations in the context of
// replication. This works for both EVAL and EVALSHA and also for the client-side
// abstraction built upon them (Predis\Command\ScriptedCommand). This example
// abstraction built upon them (Predis\Commands\ScriptedCommand). This example
// shows a slightly more complex configuration that injects a new scripted command
// in the server profile used by the new client instance and marks it marks it as
// a read-only operation for replication so that it will be executed on slaves.
use Predis\Command\ScriptedCommand;
use Predis\Connection\MasterSlaveReplication;
use Predis\Profile\ServerProfile;
use Predis\Replication\ReplicationStrategy;
use Predis\Profiles\ServerProfile;
use Predis\Commands\ScriptedCommand;
use Predis\Network\MasterSlaveReplication;
// ------------------------------------------------------------------------- //
@@ -51,17 +50,15 @@ $parameters = array(
);
$options = array(
'profile' => function ($options) {
'profile' => function($options) {
$profile = ServerProfile::get('2.6');
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
return $profile;
},
'replication' => function ($options) {
$strategy = new ReplicationStrategy();
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
$replication = new MasterSlaveReplication($strategy);
'replication' => function($options) {
$replication = new MasterSlaveReplication();
$replication->setScriptReadOnly(HashMultipleGetAll::BODY);
return $replication;
},
-56
View File
@@ -1,56 +0,0 @@
<?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.
*/
require 'SharedConfigurations.php';
// Operations such as LRANGE, ZRANGE and others can potentially generate replies
// containing a huge number of items. In some corner cases, such replies might
// end up exhausting the maximum allowed memory allocated for a PHP process.
// Multibulk iterators can be handy because they allow you to stream multibulk
// replies using plain old PHP iterators, making it possible to iterate them with
// a classic `foreach` loop and avoiding to consume an excessive amount of memory.
//
// PS: please note that multibulk iterators are supported only by the standard
// connection backend class (Predis\Connection\StreamConnection) and not the
// phpiredis-based one (Predis\Connection\PhpiredisConnection).
// Create a client and force the connection to use iterable multibulk responses.
$client = new Predis\Client($single_server + array('iterable_multibulk' => true));
// Prepare an hash with some fields and their respective values.
$client->hmset('metavars', array('foo' => 'bar', 'hoge' => 'piyo', 'lol' => 'wut'));
// By default multibulk iterators iterate over the reply as a list of items...
foreach ($client->hgetall('metavars') as $index => $item) {
echo "[$index] $item\n";
}
/* OUTPUT:
[0] foo
[1] bar
[2] hoge
[3] piyo
[4] lol
[5] wut
*/
// ... but certain multibulk replies are better represented as lists of tuples.
foreach ($client->hgetall('metavars')->asTuple() as $index => $kv) {
list($key, $value) = $kv;
echo "[$index] $key => $value\n";
}
/* OUTPUT:
[0] foo => bar
[1] hoge => piyo
[2] lol => wut
*/
+1 -1
View File
@@ -33,7 +33,7 @@ function zpop($client, $key)
// which the client bails out with an exception.
);
$client->multiExec($options, function ($tx) use ($key, &$element) {
$client->multiExec($options, function($tx) use ($key, &$element) {
@list($element) = $tx->zrange($key, 0, 0);
if (isset($element)) {
+1 -1
View File
@@ -16,7 +16,7 @@ require 'SharedConfigurations.php';
$client = new Predis\Client($single_server);
$replies = $client->pipeline(function ($pipe) {
$replies = $client->pipeline(function($pipe) {
$pipe->ping();
$pipe->flushdb();
$pipe->incrby('counter', 10);
+4 -2
View File
@@ -38,10 +38,12 @@ foreach ($pubsub as $message) {
if ($message->payload == 'quit_loop') {
echo "Aborting pubsub loop...\n";
$pubsub->unsubscribe();
} else {
}
else {
echo "Received an unrecognized command: {$message->payload}.\n";
}
} else {
}
else {
echo "Received the following message from {$message->channel}:\n",
" {$message->payload}\n\n";
}
+3 -3
View File
@@ -14,11 +14,11 @@ require 'SharedConfigurations.php';
// This example will not work with versions of Redis < 2.6.
//
// Additionally to the EVAL command defined in the current development profile, the new
// Predis\Command\ScriptedCommand base class can be used to build an higher abstraction
// Predis\Commands\ScriptedCommand base class can be used to build an higher abstraction
// for our "scripted" commands so that they will appear just like any other command on
// the client-side. This is a quick example used to implement INCREX.
use Predis\Command\ScriptedCommand;
use Predis\Commands\ScriptedCommand;
class IncrementExistingKeysBy extends ScriptedCommand
{
@@ -49,7 +49,7 @@ LUA;
}
}
$client = new Predis\Client($single_server);
$client = new Predis\Client($single_server, '2.6');
$client->getProfile()->defineCommand('increxby', 'IncrementExistingKeysBy');
+6 -5
View File
@@ -11,8 +11,9 @@
require 'SharedConfigurations.php';
use Predis\Command\CommandInterface;
use Predis\Connection\StreamConnection;
use Predis\ConnectionParameters;
use Predis\Commands\ICommand;
use Predis\Network\StreamConnection;
class SimpleDebuggableConnection extends StreamConnection
{
@@ -26,7 +27,7 @@ class SimpleDebuggableConnection extends StreamConnection
parent::connect();
}
private function storeDebug(CommandInterface $command, $direction)
private function storeDebug(ICommand $command, $direction)
{
$firtsArg = $command->getArgument(0);
$timestamp = round(microtime(true) - $this->tstart, 4);
@@ -39,14 +40,14 @@ class SimpleDebuggableConnection extends StreamConnection
$this->debugBuffer[] = $debug;
}
public function writeCommand(CommandInterface $command)
public function writeCommand(ICommand $command)
{
parent::writeCommand($command);
$this->storeDebug($command, '->');
}
public function readResponse(CommandInterface $command)
public function readResponse(ICommand $command)
{
$reply = parent::readResponse($command);
$this->storeDebug($command, '<-');
-1
View File
@@ -53,7 +53,6 @@ class Autoloader
if (0 === strpos($className, $this->prefix)) {
$parts = explode('\\', substr($className, $this->prefixLength));
$filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
if (is_file($filepath)) {
require($filepath);
}
-31
View File
@@ -1,31 +0,0 @@
<?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;
use Predis\Command\CommandInterface;
/**
* Defines the interface of a basic client object or abstraction that
* can send commands to Redis.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface BasicClientInterface
{
/**
* Executes the specified Redis command.
*
* @param CommandInterface $command A Redis command.
* @return mixed
*/
public function executeCommand(CommandInterface $command);
}
+80 -102
View File
@@ -11,19 +11,15 @@
namespace Predis;
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\Commands\ICommand;
use Predis\Options\IClientOptions;
use Predis\Network\IConnection;
use Predis\Network\IConnectionSingle;
use Predis\Profiles\IServerProfile;
use Predis\Options\ClientOptions;
use Predis\Profiles\ServerProfile;
use Predis\PubSub\PubSubContext;
use Predis\Pipeline\PipelineContext;
use Predis\Transaction\MultiExecContext;
/**
@@ -31,9 +27,9 @@ use Predis\Transaction\MultiExecContext;
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class Client implements ClientInterface
class Client
{
const VERSION = '0.8.0';
const VERSION = '0.7.3';
private $options;
private $profile;
@@ -48,16 +44,17 @@ class Client implements ClientInterface
*/
public function __construct($parameters = null, $options = null)
{
$this->options = $this->filterOptions($options);
$this->profile = $this->options->profile;
$this->connections = $this->options->connections;
$options = $this->filterOptions($options);
$this->options = $options;
$this->profile = $options->profile;
$this->connections = $options->connections;
$this->connection = $this->initializeConnection($parameters);
}
/**
* Creates an instance of Predis\Option\ClientOptions from various types of
* arguments (string, array, Predis\Profile\ServerProfile) or returns the
* passed object if it is an instance of Predis\Option\ClientOptions.
* Creates an instance of Predis\Options\ClientOptions from various types of
* arguments (string, array, Predis\Profiles\ServerProfile) or returns the
* passed object if it is an instance of Predis\Options\ClientOptions.
*
* @param mixed $options Client options.
* @return ClientOptions
@@ -67,14 +64,15 @@ class Client implements ClientInterface
if ($options === null) {
return new ClientOptions();
}
if (is_array($options)) {
return new ClientOptions($options);
}
if ($options instanceof ClientOptionsInterface) {
if ($options instanceof IClientOptions) {
return $options;
}
if ($options instanceof IServerProfile || is_string($options)) {
return new ClientOptions(array('profile' => $options));
}
throw new \InvalidArgumentException("Invalid type for client options");
}
@@ -82,29 +80,33 @@ class Client implements ClientInterface
/**
* Initializes one or multiple connection (cluster) objects from various
* types of arguments (string, array) or returns the passed object if it
* implements Predis\Connection\ConnectionInterface.
* implements the Predis\Network\IConnection interface.
*
* @param mixed $parameters Connection parameters or instance.
* @return ConnectionInterface
* @return IConnection
*/
protected function initializeConnection($parameters)
{
if ($parameters instanceof ConnectionInterface) {
if ($parameters instanceof IConnection) {
return $parameters;
}
if (is_array($parameters) && isset($parameters[0])) {
$replication = isset($this->options->replication) && $this->options->replication;
$connection = $this->options->{$replication ? 'replication' : 'cluster'};
return $this->connections->createAggregated($connection, $parameters);
$connection = $this->options->{$replication ? 'replication' : 'cluster'};
$initializer = $replication ? 'createReplication' : 'createCluster';
return $this->connections->$initializer($connection, $parameters, $this->profile);
}
return $this->connections->create($parameters);
return $this->connections->create($parameters, $this->profile);
}
/**
* {@inheritdoc}
* Returns the server profile used by the client.
*
* @return IServerProfile
*/
public function getProfile()
{
@@ -112,7 +114,9 @@ class Client implements ClientInterface
}
/**
* {@inheritdoc}
* Returns the client options specified upon initialization.
*
* @return ClientOptions
*/
public function getOptions()
{
@@ -122,7 +126,7 @@ class Client implements ClientInterface
/**
* Returns the connection factory object used by the client.
*
* @return ConnectionFactoryInterface
* @return IConnectionFactory
*/
public function getConnectionFactory()
{
@@ -136,10 +140,10 @@ class Client implements ClientInterface
*
* @return Client
*/
public function getClientFor($connectionID)
public function getClientFor($connectionAlias)
{
if (($connection = $this->getConnectionById($connectionID)) === null) {
throw new \InvalidArgumentException("Invalid connection ID: '$connectionID'");
if (($connection = $this->getConnection($connectionAlias)) === null) {
throw new \InvalidArgumentException("Invalid connection alias: '$connectionAlias'");
}
return new Client($connection, $this->options);
@@ -183,30 +187,27 @@ class Client implements ClientInterface
}
/**
* {@inheritdoc}
* Returns the underlying connection instance or, when connected to a cluster,
* one of the connection instances identified by its alias.
*
* @param string $id The alias of a connection when connected to a cluster.
* @return IConnection
*/
public function getConnection()
public function getConnection($id = null)
{
if (isset($id)) {
if (!Helpers::isAggregated($this->connection)) {
$message = 'Retrieving connections by alias is supported only with aggregated connections (cluster or replication)';
throw new NotSupportedException($message);
}
return $this->connection->getConnectionById($id);
}
return $this->connection;
}
/**
* Retrieves a single connection out of an aggregated connections instance.
*
* @param string $connectionId Index or alias of the connection.
* @return SingleConnectionInterface
*/
public function getConnectionById($connectionId)
{
if (!$this->connection instanceof AggregatedConnectionInterface) {
throw new NotSupportedException('Retrieving connections by ID is supported only when using aggregated connections');
}
return $this->connection->getConnectionById($connectionId);
}
/**
* Dynamically invokes a Redis command with the specified arguments.
* Dinamically invokes a Redis command with the specified arguments.
*
* @param string $method The name of a Redis command.
* @param array $arguments The arguments for the command.
@@ -215,21 +216,15 @@ class Client implements ClientInterface
public function __call($method, $arguments)
{
$command = $this->profile->createCommand($method, $arguments);
$response = $this->connection->executeCommand($command);
if ($response instanceof ResponseObjectInterface) {
if ($response instanceof ResponseErrorInterface) {
$response = $this->onResponseError($command, $response);
}
return $response;
}
return $command->parseResponse($response);
return $this->connection->executeCommand($command);
}
/**
* {@inheritdoc}
* Creates a new instance of the specified Redis command.
*
* @param string $method The name of a Redis command.
* @param array $arguments The arguments for the command.
* @return ICommand
*/
public function createCommand($method, $arguments = array())
{
@@ -237,44 +232,35 @@ class Client implements ClientInterface
}
/**
* {@inheritdoc}
* Executes the specified Redis command.
*
* @param ICommand $command A Redis command.
* @return mixed
*/
public function executeCommand(CommandInterface $command)
public function executeCommand(ICommand $command)
{
$response = $this->connection->executeCommand($command);
if ($response instanceof ResponseObjectInterface) {
if ($response instanceof ResponseErrorInterface) {
$response = $this->onResponseError($command, $response);
}
return $response;
}
return $command->parseResponse($response);
return $this->connection->executeCommand($command);
}
/**
* Handles -ERR responses returned by Redis.
* Executes the specified Redis command on all the nodes of a cluster.
*
* @param CommandInterface $command The command that generated the error.
* @param ResponseErrorInterface $response The error response instance.
* @return mixed
* @param ICommand $command A Redis command.
* @return array
*/
protected function onResponseError(CommandInterface $command, ResponseErrorInterface $response)
public function executeCommandOnShards(ICommand $command)
{
if ($command instanceof ScriptedCommand && $response->getErrorType() === 'NOSCRIPT') {
$eval = $this->createCommand('eval');
$eval->setRawArguments($command->getEvalArguments());
if (Helpers::isCluster($this->connection)) {
$replies = array();
return $this->executeCommand($eval);
foreach ($this->connection as $connection) {
$replies[] = $connection->executeCommand($command);
}
return $replies;
}
if ($this->options->exceptions === true) {
throw new ServerException($response->getMessage());
}
return $response;
return array($this->connection->executeCommand($command));
}
/**
@@ -326,16 +312,8 @@ class Client implements ClientInterface
*/
protected function initPipeline(Array $options = null, $callable = null)
{
$executor = isset($options['executor']) ? $options['executor'] : null;
if (is_callable($executor)) {
$executor = call_user_func($executor, $this, $options);
}
$pipeline = new PipelineContext($this, $executor);
$replies = $this->pipelineExecute($pipeline, $callable);
return $replies;
$pipeline = new PipelineContext($this, $options);
return $this->pipelineExecute($pipeline, $callable);
}
/**
-66
View File
@@ -1,66 +0,0 @@
<?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;
use Predis\Connection\ConnectionInterface;
use Predis\Option\ClientOptionsInterface;
use Predis\Profile\ServerProfileInterface;
/**
* Interface defining the most important parts needed to create an
* high-level Redis client object that can interact with other
* building blocks of Predis.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface ClientInterface extends BasicClientInterface
{
/**
* Returns the server profile used by the client.
*
* @return ServerProfileInterface
*/
public function getProfile();
/**
* Returns the client options specified upon initialization.
*
* @return ClientOptionsInterface
*/
public function getOptions();
/**
* Opens the connection to the server.
*/
public function connect();
/**
* Disconnects from the server.
*/
public function disconnect();
/**
* Returns the underlying connection instance.
*
* @return ConnectionInterface
*/
public function getConnection();
/**
* Creates a new instance of the specified Redis command.
*
* @param string $method The name of a Redis command.
* @param array $arguments The arguments for the command.
* @return CommandInterface
*/
public function createCommand($method, $arguments = array());
}
@@ -1,42 +0,0 @@
<?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\Cluster;
use Predis\Command\CommandInterface;
/**
* Interface for classes defining the strategy used to calculate an hash
* out of keys extracted from supported commands.
*
* This is mostly useful to support clustering via client-side sharding.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface CommandHashStrategyInterface
{
/**
* Returns the hash for the given command using the specified algorithm, or null
* if the command cannot be hashed.
*
* @param CommandInterface $command Command to be hashed.
* @return int
*/
public function getHash(CommandInterface $command);
/**
* Returns the hash for the given key using the specified algorithm.
*
* @param string $key Key to be hashed.
* @return string
*/
public function getKeyHash($key);
}
@@ -1,72 +0,0 @@
<?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\Cluster\Hash;
/**
* This class implements the CRC-CCITT-16 algorithm used by redis-cluster.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class CRC16HashGenerator implements HashGeneratorInterface
{
private static $CCITT_16 = array(
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
);
/**
* {@inheritdoc}
*/
public function hash($value)
{
// CRC-CCITT-16 algorithm
$crc = 0;
$CCITT_16 = self::$CCITT_16;
$strlen = strlen($value);
for ($i = 0; $i < $strlen; $i++) {
$crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 65535; // 0xFFFF
}
return $crc;
}
}
@@ -1,380 +0,0 @@
<?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\Cluster;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\Command\CommandInterface;
use Predis\Command\ScriptedCommand;
/**
* Default class used by Predis for client-side sharding to calculate
* hashes out of keys of supported commands.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PredisClusterHashStrategy implements CommandHashStrategyInterface
{
private $commands;
private $hashGenerator;
/**
* @param HashGeneratorInterface $hashGenerator Hash generator instance.
*/
public function __construct(HashGeneratorInterface $hashGenerator)
{
$this->commands = $this->getDefaultCommands();
$this->hashGenerator = $hashGenerator;
}
/**
* Returns the default map of supported commands with their handlers.
*
* @return array
*/
protected function getDefaultCommands()
{
$keyIsFirstArgument = array($this, 'getKeyFromFirstArgument');
$keysAreAllArguments = array($this, 'getKeyFromAllArguments');
return array(
/* commands operating on the key space */
'EXISTS' => $keyIsFirstArgument,
'DEL' => $keysAreAllArguments,
'TYPE' => $keyIsFirstArgument,
'EXPIRE' => $keyIsFirstArgument,
'EXPIREAT' => $keyIsFirstArgument,
'PERSIST' => $keyIsFirstArgument,
'PEXPIRE' => $keyIsFirstArgument,
'PEXPIREAT' => $keyIsFirstArgument,
'TTL' => $keyIsFirstArgument,
'PTTL' => $keyIsFirstArgument,
'SORT' => $keyIsFirstArgument, // TODO
/* commands operating on string values */
'APPEND' => $keyIsFirstArgument,
'DECR' => $keyIsFirstArgument,
'DECRBY' => $keyIsFirstArgument,
'GET' => $keyIsFirstArgument,
'GETBIT' => $keyIsFirstArgument,
'MGET' => $keysAreAllArguments,
'SET' => $keyIsFirstArgument,
'GETRANGE' => $keyIsFirstArgument,
'GETSET' => $keyIsFirstArgument,
'INCR' => $keyIsFirstArgument,
'INCRBY' => $keyIsFirstArgument,
'SETBIT' => $keyIsFirstArgument,
'SETEX' => $keyIsFirstArgument,
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
'SETNX' => $keyIsFirstArgument,
'SETRANGE' => $keyIsFirstArgument,
'STRLEN' => $keyIsFirstArgument,
'SUBSTR' => $keyIsFirstArgument,
'BITOP' => array($this, 'getKeyFromBitOp'),
'BITCOUNT' => $keyIsFirstArgument,
/* commands operating on lists */
'LINSERT' => $keyIsFirstArgument,
'LINDEX' => $keyIsFirstArgument,
'LLEN' => $keyIsFirstArgument,
'LPOP' => $keyIsFirstArgument,
'RPOP' => $keyIsFirstArgument,
'RPOPLPUSH' => $keysAreAllArguments,
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'),
'LPUSH' => $keyIsFirstArgument,
'LPUSHX' => $keyIsFirstArgument,
'RPUSH' => $keyIsFirstArgument,
'RPUSHX' => $keyIsFirstArgument,
'LRANGE' => $keyIsFirstArgument,
'LREM' => $keyIsFirstArgument,
'LSET' => $keyIsFirstArgument,
'LTRIM' => $keyIsFirstArgument,
/* commands operating on sets */
'SADD' => $keyIsFirstArgument,
'SCARD' => $keyIsFirstArgument,
'SDIFF' => $keysAreAllArguments,
'SDIFFSTORE' => $keysAreAllArguments,
'SINTER' => $keysAreAllArguments,
'SINTERSTORE' => $keysAreAllArguments,
'SUNION' => $keysAreAllArguments,
'SUNIONSTORE' => $keysAreAllArguments,
'SISMEMBER' => $keyIsFirstArgument,
'SMEMBERS' => $keyIsFirstArgument,
'SPOP' => $keyIsFirstArgument,
'SRANDMEMBER' => $keyIsFirstArgument,
'SREM' => $keyIsFirstArgument,
/* commands operating on sorted sets */
'ZADD' => $keyIsFirstArgument,
'ZCARD' => $keyIsFirstArgument,
'ZCOUNT' => $keyIsFirstArgument,
'ZINCRBY' => $keyIsFirstArgument,
'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
'ZRANGE' => $keyIsFirstArgument,
'ZRANGEBYSCORE' => $keyIsFirstArgument,
'ZRANK' => $keyIsFirstArgument,
'ZREM' => $keyIsFirstArgument,
'ZREMRANGEBYRANK' => $keyIsFirstArgument,
'ZREMRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANGE' => $keyIsFirstArgument,
'ZREVRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANK' => $keyIsFirstArgument,
'ZSCORE' => $keyIsFirstArgument,
'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
/* commands operating on hashes */
'HDEL' => $keyIsFirstArgument,
'HEXISTS' => $keyIsFirstArgument,
'HGET' => $keyIsFirstArgument,
'HGETALL' => $keyIsFirstArgument,
'HMGET' => $keyIsFirstArgument,
'HINCRBY' => $keyIsFirstArgument,
'HINCRBYFLOAT' => $keyIsFirstArgument,
'HKEYS' => $keyIsFirstArgument,
'HLEN' => $keyIsFirstArgument,
'HSET' => $keyIsFirstArgument,
'HSETNX' => $keyIsFirstArgument,
'HVALS' => $keyIsFirstArgument,
/* scripting */
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
);
}
/**
* Returns the list of IDs for the supported commands.
*
* @return array
*/
public function getSupportedCommands()
{
return array_keys($this->commands);
}
/**
* Sets an handler for the specified command ID.
*
* The signature of the callback must have a single parameter
* of type Predis\Command\CommandInterface.
*
* When the callback argument is omitted or NULL, the previously
* associated handler for the specified command ID is removed.
*
* @param string $commandId The ID of the command to be handled.
* @param mixed $callback A valid callable object or NULL.
*/
public function setCommandHandler($commandId, $callback = null)
{
$commandId = strtoupper($commandId);
if (!isset($callback)) {
unset($this->commands[$commandId]);
return;
}
if (!is_callable($callback)) {
throw new \InvalidArgumentException("Callback must be a valid callable object or NULL");
}
$this->commands[$commandId] = $callback;
}
/**
* Extracts the key from the first argument of a command instance.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromFirstArgument(CommandInterface $command)
{
return $command->getArgument(0);
}
/**
* Extracts the key from a command with multiple keys only when all keys
* in the arguments array produce the same hash.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromAllArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
if ($this->checkSameHashForKeys($arguments)) {
return $arguments[0];
}
}
/**
* Extracts the key from a command with multiple keys only when all keys
* in the arguments array produce the same hash.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromInterleavedArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
$keys = array();
for ($i = 0; $i < count($arguments); $i += 2) {
$keys[] = $arguments[$i];
}
if ($this->checkSameHashForKeys($keys)) {
return $arguments[0];
}
}
/**
* Extracts the key from BLPOP and BRPOP commands.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromBlockingListCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
if ($this->checkSameHashForKeys(array_slice($arguments, 0, count($arguments) - 1))) {
return $arguments[0];
}
}
/**
* Extracts the key from BITOP command.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromBitOp(CommandInterface $command)
{
$arguments = $command->getArguments();
if ($this->checkSameHashForKeys(array_slice($arguments, 1, count($arguments)))) {
return $arguments[1];
}
}
/**
* Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
$keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1]));
if ($this->checkSameHashForKeys($keys)) {
return $arguments[0];
}
}
/**
* Extracts the key from EVAL and EVALSHA commands.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromScriptingCommands(CommandInterface $command)
{
$keys = $command instanceof ScriptedCommand
? $command->getKeys()
: array_slice($args = $command->getArguments(), 2, $args[1]);
if ($keys && $this->checkSameHashForKeys($keys)) {
return $keys[0];
}
}
/**
* {@inheritdoc}
*/
public function getHash(CommandInterface $command)
{
$hash = $command->getHash();
if (!isset($hash) && isset($this->commands[$cmdID = $command->getId()])) {
$key = call_user_func($this->commands[$cmdID], $command);
if (isset($key)) {
$hash = $this->getKeyHash($key);
$command->setHash($hash);
}
}
return $hash;
}
/**
* {@inheritdoc}
*/
public function getKeyHash($key)
{
$key = $this->extractKeyTag($key);
$hash = $this->hashGenerator->hash($key);
return $hash;
}
/**
* Checks if the specified array of keys will generate the same hash.
*
* @param array $keys Array of keys.
* @return Boolean
*/
protected function checkSameHashForKeys(Array $keys)
{
if (($count = count($keys)) === 0) {
return false;
}
$currentKey = $this->extractKeyTag($keys[0]);
for ($i = 1; $i < $count; $i++) {
$nextKey = $this->extractKeyTag($keys[$i]);
if ($currentKey !== $nextKey) {
return false;
}
$currentKey = $nextKey;
}
return true;
}
/**
* Returns only the hashable part of a key (delimited by "{...}"), or the
* whole key if a key tag is not found in the string.
*
* @param string $key A key.
* @return string
*/
protected function extractKeyTag($key)
{
if (false !== $start = strpos($key, '{')) {
if (false !== $end = strpos($key, '}', $start)) {
$key = substr($key, ++$start, $end - $start);
}
}
return $key;
}
}
@@ -1,284 +0,0 @@
<?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\Cluster;
use Predis\Cluster\Hash\CRC16HashGenerator;
use Predis\Command\CommandInterface;
/**
* Default class used by Predis to calculate hashes out of keys of
* commands supported by redis-cluster.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RedisClusterHashStrategy implements CommandHashStrategyInterface
{
private $commands;
private $hashGenerator;
/**
*
*/
public function __construct()
{
$this->commands = $this->getDefaultCommands();
$this->hashGenerator = new CRC16HashGenerator();
}
/**
* Returns the default map of supported commands with their handlers.
*
* @return array
*/
protected function getDefaultCommands()
{
$keyIsFirstArgument = array($this, 'getKeyFromFirstArgument');
return array(
/* commands operating on the key space */
'EXISTS' => $keyIsFirstArgument,
'DEL' => array($this, 'getKeyFromAllArguments'),
'TYPE' => $keyIsFirstArgument,
'EXPIRE' => $keyIsFirstArgument,
'EXPIREAT' => $keyIsFirstArgument,
'PERSIST' => $keyIsFirstArgument,
'PEXPIRE' => $keyIsFirstArgument,
'PEXPIREAT' => $keyIsFirstArgument,
'TTL' => $keyIsFirstArgument,
'PTTL' => $keyIsFirstArgument,
'SORT' => $keyIsFirstArgument, // TODO
/* commands operating on string values */
'APPEND' => $keyIsFirstArgument,
'DECR' => $keyIsFirstArgument,
'DECRBY' => $keyIsFirstArgument,
'GET' => $keyIsFirstArgument,
'GETBIT' => $keyIsFirstArgument,
'MGET' => array($this, 'getKeyFromAllArguments'),
'SET' => $keyIsFirstArgument,
'GETRANGE' => $keyIsFirstArgument,
'GETSET' => $keyIsFirstArgument,
'INCR' => $keyIsFirstArgument,
'INCRBY' => $keyIsFirstArgument,
'SETBIT' => $keyIsFirstArgument,
'SETEX' => $keyIsFirstArgument,
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
'SETNX' => $keyIsFirstArgument,
'SETRANGE' => $keyIsFirstArgument,
'STRLEN' => $keyIsFirstArgument,
'SUBSTR' => $keyIsFirstArgument,
'BITCOUNT' => $keyIsFirstArgument,
/* commands operating on lists */
'LINSERT' => $keyIsFirstArgument,
'LINDEX' => $keyIsFirstArgument,
'LLEN' => $keyIsFirstArgument,
'LPOP' => $keyIsFirstArgument,
'RPOP' => $keyIsFirstArgument,
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
'LPUSH' => $keyIsFirstArgument,
'LPUSHX' => $keyIsFirstArgument,
'RPUSH' => $keyIsFirstArgument,
'RPUSHX' => $keyIsFirstArgument,
'LRANGE' => $keyIsFirstArgument,
'LREM' => $keyIsFirstArgument,
'LSET' => $keyIsFirstArgument,
'LTRIM' => $keyIsFirstArgument,
/* commands operating on sets */
'SADD' => $keyIsFirstArgument,
'SCARD' => $keyIsFirstArgument,
'SISMEMBER' => $keyIsFirstArgument,
'SMEMBERS' => $keyIsFirstArgument,
'SPOP' => $keyIsFirstArgument,
'SRANDMEMBER' => $keyIsFirstArgument,
'SREM' => $keyIsFirstArgument,
/* commands operating on sorted sets */
'ZADD' => $keyIsFirstArgument,
'ZCARD' => $keyIsFirstArgument,
'ZCOUNT' => $keyIsFirstArgument,
'ZINCRBY' => $keyIsFirstArgument,
'ZRANGE' => $keyIsFirstArgument,
'ZRANGEBYSCORE' => $keyIsFirstArgument,
'ZRANK' => $keyIsFirstArgument,
'ZREM' => $keyIsFirstArgument,
'ZREMRANGEBYRANK' => $keyIsFirstArgument,
'ZREMRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANGE' => $keyIsFirstArgument,
'ZREVRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANK' => $keyIsFirstArgument,
'ZSCORE' => $keyIsFirstArgument,
/* commands operating on hashes */
'HDEL' => $keyIsFirstArgument,
'HEXISTS' => $keyIsFirstArgument,
'HGET' => $keyIsFirstArgument,
'HGETALL' => $keyIsFirstArgument,
'HMGET' => $keyIsFirstArgument,
'HINCRBY' => $keyIsFirstArgument,
'HINCRBYFLOAT' => $keyIsFirstArgument,
'HKEYS' => $keyIsFirstArgument,
'HLEN' => $keyIsFirstArgument,
'HSET' => $keyIsFirstArgument,
'HSETNX' => $keyIsFirstArgument,
'HVALS' => $keyIsFirstArgument,
/* scripting */
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
);
}
/**
* Returns the list of IDs for the supported commands.
*
* @return array
*/
public function getSupportedCommands()
{
return array_keys($this->commands);
}
/**
* Sets an handler for the specified command ID.
*
* The signature of the callback must have a single parameter
* of type Predis\Command\CommandInterface.
*
* When the callback argument is omitted or NULL, the previously
* associated handler for the specified command ID is removed.
*
* @param string $commandId The ID of the command to be handled.
* @param mixed $callback A valid callable object or NULL.
*/
public function setCommandHandler($commandId, $callback = null)
{
$commandId = strtoupper($commandId);
if (!isset($callback)) {
unset($this->commands[$commandId]);
return;
}
if (!is_callable($callback)) {
throw new \InvalidArgumentException("Callback must be a valid callable object or NULL");
}
$this->commands[$commandId] = $callback;
}
/**
* Extracts the key from the first argument of a command instance.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromFirstArgument(CommandInterface $command)
{
return $command->getArgument(0);
}
/**
* Extracts the key from a command that can accept multiple keys ensuring
* that only one key is actually specified to comply with redis-cluster.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromAllArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
if (count($arguments) === 1) {
return $arguments[0];
}
}
/**
* Extracts the key from a command that can accept multiple keys ensuring
* that only one key is actually specified to comply with redis-cluster.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromInterleavedArguments(CommandInterface $command)
{
$arguments = $command->getArguments();
if (count($arguments) === 2) {
return $arguments[0];
}
}
/**
* Extracts the key from BLPOP and BRPOP commands ensuring that only one key
* is actually specified to comply with redis-cluster.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromBlockingListCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
if (count($arguments) === 2) {
return $arguments[0];
}
}
/**
* Extracts the key from EVAL and EVALSHA commands.
*
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromScriptingCommands(CommandInterface $command)
{
$keys = $command instanceof ScriptedCommand
? $command->getKeys()
: array_slice($args = $command->getArguments(), 2, $args[1]);
if (count($keys) === 1) {
return $keys[0];
}
}
/**
* {@inheritdoc}
*/
public function getHash(CommandInterface $command)
{
$hash = $command->getHash();
if (!isset($hash) && isset($this->commands[$cmdID = $command->getId()])) {
$key = call_user_func($this->commands[$cmdID], $command);
if (isset($key)) {
$hash = $this->hashGenerator->hash($key);
$command->setHash($hash);
}
}
return $hash;
}
/**
* {@inheritdoc}
*/
public function getKeyHash($key)
{
return $this->hashGenerator->hash($key);
}
}
@@ -9,14 +9,17 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Helpers;
use Predis\Distribution\INodeKeyGenerator;
/**
* Base class for Redis commands.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
abstract class AbstractCommand implements CommandInterface
abstract class Command implements ICommand
{
private $hash;
private $arguments = array();
@@ -73,21 +76,57 @@ abstract class AbstractCommand implements CommandInterface
}
/**
* {@inheritdoc}
* Checks if the command can return an hash for client-side sharding.
*
* @return Boolean
*/
public function setHash($hash)
protected function canBeHashed()
{
$this->hash = $hash;
return isset($this->arguments[0]);
}
/**
* Checks if the specified array of keys will generate the same hash.
*
* @param array $keys Array of keys.
* @return Boolean
*/
protected function checkSameHashForKeys(Array $keys)
{
if (($count = count($keys)) === 0) {
return false;
}
$currentKey = Helpers::extractKeyTag($keys[0]);
for ($i = 1; $i < $count; $i++) {
$nextKey = Helpers::extractKeyTag($keys[$i]);
if ($currentKey !== $nextKey) {
return false;
}
$currentKey = $nextKey;
}
return true;
}
/**
* {@inheritdoc}
*/
public function getHash()
public function getHash(INodeKeyGenerator $distributor)
{
if (isset($this->hash)) {
return $this->hash;
}
if ($this->canBeHashed()) {
$key = Helpers::extractKeyTag($this->arguments[0]);
$this->hash = $distributor->generateKey($key);
return $this->hash;
}
return null;
}
/**
@@ -110,7 +149,6 @@ abstract class AbstractCommand implements CommandInterface
if (strlen($argument) > 32) {
$argument = substr($argument, 0, 32) . '[...]';
}
$accumulator .= " $argument";
return $accumulator;
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/auth
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionAuth extends AbstractCommand
class ConnectionAuth extends Command
{
/**
* {@inheritdoc}
@@ -24,4 +24,12 @@ class ConnectionAuth extends AbstractCommand
{
return 'AUTH';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
}
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/echo
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionEcho extends AbstractCommand
class ConnectionEcho extends Command
{
/**
* {@inheritdoc}
@@ -24,4 +24,12 @@ class ConnectionEcho extends AbstractCommand
{
return 'ECHO';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
}
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/ping
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionPing extends AbstractCommand
class ConnectionPing extends Command
{
/**
* {@inheritdoc}
@@ -25,6 +25,14 @@ class ConnectionPing extends AbstractCommand
return 'PING';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
/**
* {@inheritdoc}
*/
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/quit
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionQuit extends AbstractCommand
class ConnectionQuit extends Command
{
/**
* {@inheritdoc}
@@ -24,4 +24,12 @@ class ConnectionQuit extends AbstractCommand
{
return 'QUIT';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
}
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/select
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionSelect extends AbstractCommand
class ConnectionSelect extends Command
{
/**
* {@inheritdoc}
@@ -24,4 +24,12 @@ class ConnectionSelect extends AbstractCommand
{
return 'SELECT';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Helpers;
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hexists
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hget
@@ -9,7 +9,9 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Iterators\MultiBulkResponseTuple;
/**
* @link http://redis.io/commands/hgetall
@@ -30,8 +32,11 @@ class HashGetAll extends PrefixableCommand
*/
public function parseResponse($data)
{
$result = array();
if ($data instanceof \Iterator) {
return new MultiBulkResponseTuple($data);
}
$result = array();
for ($i = 0; $i < count($data); $i++) {
$result[$data[$i]] = $data[++$i];
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Helpers;
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hincrby
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hincrbyfloat
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hkeys
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hlen
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hset
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hmset
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hsetnx
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/hvals
@@ -9,13 +9,15 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Distribution\INodeKeyGenerator;
/**
* Defines an abstraction representing a Redis command.
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface CommandInterface
interface ICommand
{
/**
* Gets the ID of a Redis command.
@@ -25,33 +27,21 @@ interface CommandInterface
public function getId();
/**
* Set the hash for the command.
*
* @param int $hash Calculated hash.
*/
public function setHash($hash);
/**
* Returns the hash of the command.
* Returns an hash of the command using the provided algorithm against the
* key (used to calculate the distribution of keys with client-side sharding).
*
* @param INodeKeyGenerator $distributor Distribution algorithm.
* @return int
*/
public function getHash();
public function getHash(INodeKeyGenerator $distributor);
/**
* Sets the arguments for the command.
* Sets the arguments of the command.
*
* @param array $arguments List of arguments.
*/
public function setArguments(Array $arguments);
/**
* Sets the raw arguments for the command without processing them.
*
* @param array $arguments List of arguments.
*/
public function setRawArguments(Array $arguments);
/**
* Gets the arguments of the command.
*
@@ -9,14 +9,16 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Distribution\INodeKeyGenerator;
/**
* Defines a command whose keys can be prefixed.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface PrefixableCommandInterface
interface IPrefixable
{
/**
* Prefixes all the keys found in the arguments of the command.
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Helpers;
@@ -17,7 +17,7 @@ use Predis\Helpers;
* @link http://redis.io/commands/del
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDelete extends AbstractCommand implements PrefixableCommandInterface
class KeyDelete extends Command implements IPrefixable
{
/**
* {@inheritdoc}
@@ -42,4 +42,17 @@ class KeyDelete extends AbstractCommand implements PrefixableCommandInterface
{
PrefixHelpers::all($this, $prefix);
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
$args = $this->getArguments();
if (count($args) === 1) {
return true;
}
return $this->checkSameHashForKeys($args);
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/exists
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/expire
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/expireat
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/keys
@@ -24,4 +24,12 @@ class KeyKeys extends PrefixableCommand
{
return 'KEYS';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/keys
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/move
@@ -25,6 +25,14 @@ class KeyMove extends PrefixableCommand
return 'MOVE';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
/**
* {@inheritdoc}
*/
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/persist
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/pexpire
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/pexpireat
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/pttl
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/randomkey
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRandom extends AbstractCommand
class KeyRandom extends Command
{
/**
* {@inheritdoc}
@@ -25,6 +25,14 @@ class KeyRandom extends AbstractCommand
return 'RANDOMKEY';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
/**
* {@inheritdoc}
*/
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/rename
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRename extends AbstractCommand implements PrefixableCommandInterface
class KeyRename extends Command implements IPrefixable
{
/**
* {@inheritdoc}
@@ -32,4 +32,12 @@ class KeyRename extends AbstractCommand implements PrefixableCommandInterface
{
PrefixHelpers::all($this, $prefix);
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/renamenx
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/sort
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeySort extends AbstractCommand implements PrefixableCommandInterface
class KeySort extends Command implements IPrefixable
{
/**
* {@inheritdoc}
@@ -44,21 +44,20 @@ class KeySort extends AbstractCommand implements PrefixableCommandInterface
if (isset($sortParams['GET'])) {
$getargs = $sortParams['GET'];
if (is_array($getargs)) {
foreach ($getargs as $getarg) {
$query[] = 'GET';
$query[] = $getarg;
}
} else {
}
else {
$query[] = 'GET';
$query[] = $getargs;
}
}
if (isset($sortParams['LIMIT']) &&
is_array($sortParams['LIMIT']) &&
count($sortParams['LIMIT']) == 2) {
if (isset($sortParams['LIMIT']) && is_array($sortParams['LIMIT'])
&& count($sortParams['LIMIT']) == 2) {
$query[] = 'LIMIT';
$query[] = $sortParams['LIMIT'][0];
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/ttl
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/type
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/lindex
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/linsert
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/llen
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/lpop
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/blpop
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopFirstBlocking extends AbstractCommand implements PrefixableCommandInterface
class ListPopFirstBlocking extends Command implements IPrefixable
{
/**
* {@inheritdoc}
@@ -34,7 +34,6 @@ class ListPopFirstBlocking extends AbstractCommand implements PrefixableCommandI
list($arguments, $timeout) = $arguments;
array_push($arguments, $timeout);
}
return $arguments;
}
@@ -45,4 +44,14 @@ class ListPopFirstBlocking extends AbstractCommand implements PrefixableCommandI
{
PrefixHelpers::skipLast($this, $prefix);
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return $this->checkSameHashForKeys(
array_slice(($args = $this->getArguments()), 0, count($args) - 1)
);
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/rpop
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/brpop
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/rpoplpush
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastPushHead extends AbstractCommand implements PrefixableCommandInterface
class ListPopLastPushHead extends Command implements IPrefixable
{
/**
* {@inheritdoc}
@@ -32,4 +32,12 @@ class ListPopLastPushHead extends AbstractCommand implements PrefixableCommandIn
{
PrefixHelpers::all($this, $prefix);
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return $this->checkSameHashForKeys($this->getArguments());
}
}
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/brpoplpush
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ListPopLastPushHeadBlocking extends AbstractCommand implements PrefixableCommandInterface
class ListPopLastPushHeadBlocking extends Command implements IPrefixable
{
/**
* {@inheritdoc}
@@ -32,4 +32,14 @@ class ListPopLastPushHeadBlocking extends AbstractCommand implements PrefixableC
{
PrefixHelpers::skipLast($this, $prefix);
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return $this->checkSameHashForKeys(
array_slice($args = $this->getArguments(), 0, count($args) - 1)
);
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/lpush
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/lpushx
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Helpers;
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/rpushx
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/lrange
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/lrem
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/lset
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/ltrim
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* Class that defines a few helpers method for prefixing keys.
@@ -21,10 +21,10 @@ class PrefixHelpers
/**
* Applies the specified prefix only the first argument.
*
* @param CommandInterface $command Command instance.
* @param ICommand $command Command instance.
* @param string $prefix Prefix string.
*/
public static function first(CommandInterface $command, $prefix)
public static function first(ICommand $command, $prefix)
{
if ($arguments = $command->getArguments()) {
$arguments[0] = "$prefix{$arguments[0]}";
@@ -35,10 +35,10 @@ class PrefixHelpers
/**
* Applies the specified prefix to all the arguments.
*
* @param CommandInterface $command Command instance.
* @param ICommand $command Command instance.
* @param string $prefix Prefix string.
*/
public static function all(CommandInterface $command, $prefix)
public static function all(ICommand $command, $prefix)
{
$arguments = $command->getArguments();
@@ -52,10 +52,10 @@ class PrefixHelpers
/**
* Applies the specified prefix only to even arguments in the list.
*
* @param CommandInterface $command Command instance.
* @param ICommand $command Command instance.
* @param string $prefix Prefix string.
*/
public static function interleaved(CommandInterface $command, $prefix)
public static function interleaved(ICommand $command, $prefix)
{
$arguments = $command->getArguments();
$length = count($arguments);
@@ -70,10 +70,10 @@ class PrefixHelpers
/**
* Applies the specified prefix to all the arguments but the first one.
*
* @param CommandInterface $command Command instance.
* @param ICommand $command Command instance.
* @param string $prefix Prefix string.
*/
public static function skipFirst(CommandInterface $command, $prefix)
public static function skipFirst(ICommand $command, $prefix)
{
$arguments = $command->getArguments();
$length = count($arguments);
@@ -88,10 +88,10 @@ class PrefixHelpers
/**
* Applies the specified prefix to all the arguments but the last one.
*
* @param CommandInterface $command Command instance.
* @param ICommand $command Command instance.
* @param string $prefix Prefix string.
*/
public static function skipLast(CommandInterface $command, $prefix)
public static function skipLast(ICommand $command, $prefix)
{
$arguments = $command->getArguments();
$length = count($arguments);
@@ -9,14 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* Base class for Redis commands with prefixable keys.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
abstract class PrefixableCommand extends AbstractCommand implements PrefixableCommandInterface
abstract class PrefixableCommand extends Command implements IPrefixable
{
/**
* {@inheritdoc}
@@ -9,21 +9,21 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Processor;
namespace Predis\Commands\Processors;
use Predis\Command\CommandInterface;
use Predis\Commands\ICommand;
/**
* A command processor processes commands before they are sent to Redis.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface CommandProcessorInterface
interface ICommandProcessor
{
/**
* Processes a Redis command.
*
* @param CommandInterface $command Redis command.
* @param ICommand $command Redis command.
*/
public function process(CommandInterface $command);
public function process(ICommand $command);
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Processor;
namespace Predis\Commands\Processors;
/**
* A command processor chain processes a command using multiple chained command
@@ -17,21 +17,21 @@ namespace Predis\Command\Processor;
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface CommandProcessorChainInterface extends CommandProcessorInterface, \IteratorAggregate, \Countable
interface ICommandProcessorChain extends ICommandProcessor, \IteratorAggregate, \Countable
{
/**
* Adds a command processor.
*
* @param CommandProcessorInterface $processor A command processor.
* @param ICommandProcessor $processor A command processor.
*/
public function add(CommandProcessorInterface $processor);
public function add(ICommandProcessor $processor);
/**
* Removes a command processor from the chain.
*
* @param CommandProcessorInterface $processor A command processor.
* @param ICommandProcessor $processor A command processor.
*/
public function remove(CommandProcessorInterface $processor);
public function remove(ICommandProcessor $processor);
/**
* Returns an ordered list of the command processors in the chain.
@@ -9,26 +9,26 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Processor;
namespace Predis\Commands\Processors;
/**
* Defines an object that can process commands using command processors.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface CommandProcessingInterface
interface IProcessingSupport
{
/**
* Associates a command processor.
*
* @param CommandProcessorInterface $processor The command processor.
* @param ICommandProcessor $processor The command processor.
*/
public function setProcessor(CommandProcessorInterface $processor);
public function setProcessor(ICommandProcessor $processor);
/**
* Returns the associated command processor.
*
* @return CommandProcessorInterface
* @return ICommandProcessor
*/
public function getProcessor();
}
@@ -9,10 +9,10 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Processor;
namespace Predis\Commands\Processors;
use Predis\Command\CommandInterface;
use Predis\Command\PrefixableCommandInterface;
use Predis\Commands\ICommand;
use Predis\Commands\IPrefixable;
/**
* Command processor that is used to prefix the keys contained in the arguments
@@ -20,7 +20,7 @@ use Predis\Command\PrefixableCommandInterface;
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPrefixProcessor implements CommandProcessorInterface
class KeyPrefixProcessor implements ICommandProcessor
{
private $prefix;
@@ -55,9 +55,9 @@ class KeyPrefixProcessor implements CommandProcessorInterface
/**
* {@inheritdoc}
*/
public function process(CommandInterface $command)
public function process(ICommand $command)
{
if ($command instanceof PrefixableCommandInterface) {
if ($command instanceof IPrefixable) {
$command->prefixKeys($this->prefix);
}
}
@@ -9,21 +9,21 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Processor;
namespace Predis\Commands\Processors;
use Predis\Command\CommandInterface;
use Predis\Commands\ICommand;
/**
* Default implementation of a command processors chain.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ProcessorChain implements CommandProcessorChainInterface, \ArrayAccess
class ProcessorChain implements ICommandProcessorChain, \ArrayAccess
{
private $processors = array();
/**
* @param array $processors List of instances of CommandProcessorInterface.
* @param array $processors List of instances of ICommandProcessor.
*/
public function __construct($processors = array())
{
@@ -35,7 +35,7 @@ class ProcessorChain implements CommandProcessorChainInterface, \ArrayAccess
/**
* {@inheritdoc}
*/
public function add(CommandProcessorInterface $processor)
public function add(ICommandProcessor $processor)
{
$this->processors[] = $processor;
}
@@ -43,9 +43,10 @@ class ProcessorChain implements CommandProcessorChainInterface, \ArrayAccess
/**
* {@inheritdoc}
*/
public function remove(CommandProcessorInterface $processor)
public function remove(ICommandProcessor $processor)
{
if (false !== $index = array_search($processor, $this->processors, true)) {
$index = array_search($processor, $this->processors, true);
if ($index !== false) {
unset($this[$index]);
}
}
@@ -53,9 +54,10 @@ class ProcessorChain implements CommandProcessorChainInterface, \ArrayAccess
/**
* {@inheritdoc}
*/
public function process(CommandInterface $command)
public function process(ICommand $command)
{
for ($i = 0; $i < $count = count($this->processors); $i++) {
$count = count($this->processors);
for ($i = 0; $i < $count; $i++) {
$this->processors[$i]->process($command);
}
}
@@ -109,10 +111,10 @@ class ProcessorChain implements CommandProcessorChainInterface, \ArrayAccess
*/
public function offsetSet($index, $processor)
{
if (!$processor instanceof CommandProcessorInterface) {
if (!$processor instanceof ICommandProcessor) {
throw new \InvalidArgumentException(
'A processor chain can hold only instances of classes implementing '.
'the Predis\Command\Processor\CommandProcessorInterface interface'
'the Predis\Commands\Preprocessors\ICommandProcessor interface'
);
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/publish
@@ -24,4 +24,12 @@ class PubSubPublish extends PrefixableCommand
{
return 'PUBLISH';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Helpers;
@@ -17,7 +17,7 @@ use Predis\Helpers;
* @link http://redis.io/commands/subscribe
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PubSubSubscribe extends AbstractCommand implements PrefixableCommandInterface
class PubSubSubscribe extends Command implements IPrefixable
{
/**
* {@inheritdoc}
@@ -42,4 +42,12 @@ class PubSubSubscribe extends AbstractCommand implements PrefixableCommandInterf
{
PrefixHelpers::all($this, $prefix);
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Helpers;
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
use Predis\Helpers;
@@ -17,7 +17,7 @@ use Predis\Helpers;
* @link http://redis.io/commands/unsubscribe
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PubSubUnsubscribe extends AbstractCommand implements PrefixableCommandInterface
class PubSubUnsubscribe extends Command implements IPrefixable
{
/**
* {@inheritdoc}
@@ -42,4 +42,12 @@ class PubSubUnsubscribe extends AbstractCommand implements PrefixableCommandInte
{
PrefixHelpers::all($this, $prefix);
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/punsubscribe
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* Base class used to implement an higher level abstraction for "virtual"
@@ -18,7 +18,7 @@ namespace Predis\Command;
* @link http://redis.io/commands/eval
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
abstract class ScriptedCommand extends ServerEvalSHA
abstract class ScriptedCommand extends ServerEval
{
/**
* Gets the body of a Lua script.
@@ -62,21 +62,11 @@ abstract class ScriptedCommand extends ServerEvalSHA
{
if (false !== $numkeys = $this->getKeysCount()) {
$numkeys = $numkeys >= 0 ? $numkeys : count($arguments) + $numkeys;
} else {
}
else {
$numkeys = count($arguments);
}
return array_merge(array(sha1($this->getScript()), $numkeys), $arguments);
}
/**
* @return array
*/
public function getEvalArguments()
{
$arguments = $this->getArguments();
$arguments[0] = $this->getScript();
return $arguments;
return array_merge(array($this->getScript(), $numkeys), $arguments);
}
}
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/bgrewriteaof
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerBackgroundRewriteAOF extends AbstractCommand
class ServerBackgroundRewriteAOF extends Command
{
/**
* {@inheritdoc}
@@ -25,6 +25,14 @@ class ServerBackgroundRewriteAOF extends AbstractCommand
return 'BGREWRITEAOF';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
/**
* {@inheritdoc}
*/
@@ -9,13 +9,13 @@
* file that was distributed with this source code.
*/
namespace Predis\Command;
namespace Predis\Commands;
/**
* @link http://redis.io/commands/bgsave
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerBackgroundSave extends AbstractCommand
class ServerBackgroundSave extends Command
{
/**
* {@inheritdoc}
@@ -25,11 +25,23 @@ class ServerBackgroundSave extends AbstractCommand
return 'BGSAVE';
}
/**
* {@inheritdoc}
*/
protected function canBeHashed()
{
return false;
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data === 'Background saving started' ? true : $data;
if ($data == 'Background saving started') {
return true;
}
return $data;
}
}

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