mirror of
https://github.com/predis/predis.git
synced 2026-08-20 18:42:18 +00:00
Compare commits
164 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 495e3c6f8a | |||
| 3465bc7c1f | |||
| 8bf896f81f | |||
| 0085bc08ae | |||
| f7232b9ddc | |||
| 95cc528904 | |||
| 4a16cb9417 | |||
| 94758d5f58 | |||
| acc0f04bdb | |||
| 09de7be7cb | |||
| 70b279bbdc | |||
| f8cbbc5b8f | |||
| 83720075f3 | |||
| 9901233fa1 | |||
| dcb4093046 | |||
| 1e8af648cb | |||
| ff5e3515c3 | |||
| 59813cd74e | |||
| bfd96b15dc | |||
| 51b466267d | |||
| 78f3596566 | |||
| ac01c1a17f | |||
| 46920c9c77 | |||
| b26d6103a6 | |||
| a4760d65cd | |||
| 49aa6f0aca | |||
| 292f9939b2 | |||
| daa36a980d | |||
| 0b09d2c359 | |||
| 0a622aee31 | |||
| 040dfebd9c | |||
| 0e4604a9bb | |||
| deb64c295a | |||
| aac06a4cd8 | |||
| dfb5cf2c1b | |||
| a1024bba53 | |||
| f357cafb14 | |||
| 2b4cf80d90 | |||
| 6cd1f03834 | |||
| 732d7f96d1 | |||
| b4e1ab106c | |||
| 5a2c61c02b | |||
| 1b9e10bdd8 | |||
| 37e2b546b0 | |||
| f163e1221b | |||
| 1bb2ab95ef | |||
| ea18afc820 | |||
| baa91da35b | |||
| 6d85a81da4 | |||
| f913e81b9f | |||
| 003b3d180d | |||
| a6e1ac11d0 | |||
| be46ab813c | |||
| 6918de6e4b | |||
| cfa97dd9f5 | |||
| 1e8f790ee4 | |||
| c655affe5f | |||
| 457fd6076d | |||
| 282287765b | |||
| 00d6160549 | |||
| 576163724c | |||
| 9df9dbdedc | |||
| 8d7f5099c4 | |||
| e8daf45269 | |||
| 8567b05459 | |||
| 6e76ea6c7e | |||
| e774a301c4 | |||
| 2b85824d74 | |||
| 81422b9992 | |||
| c230b6e831 | |||
| 69d4c013f7 | |||
| 1ac9cc9c71 | |||
| b2f59a0c63 | |||
| 93971bcb45 | |||
| e54bc31212 | |||
| b9eddae96f | |||
| 7683f97ff2 | |||
| f4f9d02551 | |||
| bacdb7ff62 | |||
| e0c6ad58be | |||
| febd99421f | |||
| 8559c2cb30 | |||
| 2ba54a0e1c | |||
| 024a31d571 | |||
| d0572b0efc | |||
| 0f80cb55bb | |||
| 55a3fd65a2 | |||
| 6c732c737b | |||
| 559853bdd5 | |||
| 28e1e7ad25 | |||
| 98112fdb39 | |||
| 0e8d978348 | |||
| 469f3cc68b | |||
| 36f8210864 | |||
| 81be513eaa | |||
| 4b73afc316 | |||
| fd6a14d80b | |||
| f2d92f1e29 | |||
| c3cafaa9a6 | |||
| 89d6c0e9b6 | |||
| 9f51263136 | |||
| a75e52cf5a | |||
| b865cee1f8 | |||
| 1f0be85a3d | |||
| 2578e154d4 | |||
| 81da6d2553 | |||
| 188eaf035b | |||
| 6638776d85 | |||
| 511fe0bbab | |||
| 56a3e327fc | |||
| 63cf5048a7 | |||
| 177d7e1c6a | |||
| 8a84f8b6cf | |||
| 09f15a9cb9 | |||
| 2b4885abc3 | |||
| 649e71c0ab | |||
| 14ac381aad | |||
| 78360ea27b | |||
| 214b7d2dc9 | |||
| 2d0b30dac5 | |||
| 75afe831b0 | |||
| c36e6fd900 | |||
| 743ccc39ae | |||
| 41c29bed4e | |||
| 90f37f8698 | |||
| dc889be2a2 | |||
| ca7bd8149f | |||
| 9426d78b90 | |||
| 6b6c91cd15 | |||
| b511911ddb | |||
| eb450ae0b4 | |||
| ca033d15a9 | |||
| 9443da1948 | |||
| 298dc87613 | |||
| 92697aa075 | |||
| 7c393cbc09 | |||
| 5b6e4dc3dd | |||
| b37d072b68 | |||
| 608c48146f | |||
| bcba9e1009 | |||
| 64a84d9020 | |||
| a3b4c2244a | |||
| 4805a048ed | |||
| c394458b80 | |||
| 51d0b58cd3 | |||
| 890ace8565 | |||
| 8ba432e8f7 | |||
| d61bb3af07 | |||
| da381fffa9 | |||
| 7b01bc0644 | |||
| 7893fd1faa | |||
| 785670dcf7 | |||
| ec723fd4a9 | |||
| b67af29eb6 | |||
| dc14c29676 | |||
| ce625ca902 | |||
| 46d293a716 | |||
| fcd41a4787 | |||
| 8dc9450f39 | |||
| 6ad522f492 | |||
| 317fb6c398 | |||
| 93163bdfb3 | |||
| 626520b836 | |||
| aaea628c55 |
@@ -2,4 +2,6 @@
|
||||
*.phar
|
||||
phpunit.xml
|
||||
package.xml
|
||||
composer.lock
|
||||
experiments/
|
||||
vendor/
|
||||
|
||||
+7
-3
@@ -2,8 +2,12 @@ language: php
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- v0.7
|
||||
except:
|
||||
- v0.5
|
||||
- v0.6
|
||||
- php5.2_backport
|
||||
- documentation
|
||||
services: redis-server
|
||||
script: phpunit -c phpunit.xml.travisci
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# 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`
|
||||
+141
-4
@@ -1,3 +1,140 @@
|
||||
v0.8.1 (2013-01-19)
|
||||
===============================================================================
|
||||
|
||||
- The `connections` client option can now accept a callable object returning
|
||||
an instance of `Predis\Connection\ConnectionFactoryInterface`.
|
||||
|
||||
- Client options accepting callable objects as factories now pass their actual
|
||||
instance to the callable as the second argument.
|
||||
|
||||
- `Predis\Command\Processor\KeyPrefixProcessor` can now be directly casted to
|
||||
string to obtain the current prefix, useful with string interpolation.
|
||||
|
||||
- Added an optional callable argument to `Predis\Cluster\Distribution\HashRing`
|
||||
and `Predis\Cluster\Distribution\KetamaPureRing` constructor that can be used
|
||||
to customize how the distributor should extract the connection hash when
|
||||
initializing the nodes distribution (ISSUE #36).
|
||||
|
||||
- Correctly handle `TTL` and `PTTL` returning -2 on non existing keys starting
|
||||
with Redis 2.8.
|
||||
|
||||
- __FIX__: a missing use directive in `Predis\Transaction\MultiExecContext`
|
||||
caused PHP errors when Redis did not return `+QUEUED` replies to commands
|
||||
when inside a MULTI / EXEC context.
|
||||
|
||||
- __FIX__: the `parseResponse()` method implemented for a scripted command was
|
||||
ignored when retrying to execute a Lua script by falling back to `EVAL` after
|
||||
a `-NOSCRIPT` error (ISSUE #94).
|
||||
|
||||
- __FIX__: when subclassing `Predis\Client` the `getClientFor()` method returns
|
||||
a new instance of the subclass instead of a new instance of `Predis\Client`.
|
||||
|
||||
|
||||
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)
|
||||
===============================================================================
|
||||
|
||||
- New commands available in the Redis v2.6 profile (dev): `BITOP`, `BITCOUNT`.
|
||||
|
||||
- When the number of keys `Predis\Commands\ScriptedCommand` is negative, 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.
|
||||
|
||||
- __FIX__: `examples\CustomDistributionStrategy.php` had a mistyped constructor
|
||||
call and produced a bad distribution due to an error as pointed in ISSUE #63.
|
||||
This bug is limited to the above mentioned example and does not affect the
|
||||
classes implemented in the `Predis\Distribution` namespace.
|
||||
|
||||
- __FIX__: `Predis\Commands\ServerEvalSHA::getScriptHash()` was calculating the
|
||||
hash while it just needs to return the first argument of the command.
|
||||
|
||||
- __FIX__: `Predis\Autoloader` has been modified to allow cascading autoloaders
|
||||
for the `Predis` namespace.
|
||||
|
||||
|
||||
v0.7.2 (2012-04-01)
|
||||
===============================================================================
|
||||
|
||||
- Added `2.6` in the server profiles aliases list for the upcoming Redis 2.6.
|
||||
`2.4` is still the default server profile. `dev` now targets Redis 2.8.
|
||||
|
||||
- Connection instances can be serialized and unserialized using `serialize()`
|
||||
and `unserialize()`. This is handy in certain scenarios such as client-side
|
||||
clustering or replication to lower the overhead of initializing a connection
|
||||
object with many sub-connections since unserializing them can be up to 5x
|
||||
times faster.
|
||||
|
||||
- Reworked the default autoloader to make it faster. It is also possible to
|
||||
prepend it in PHP's autoload stack.
|
||||
|
||||
- __FIX__: fixed parsing of the payload returned by `MONITOR` with Redis 2.6.
|
||||
|
||||
|
||||
v0.7.1 (2011-12-27)
|
||||
===============================================================================
|
||||
|
||||
@@ -97,7 +234,7 @@ v0.6.6 (2011-04-01)
|
||||
- Switched to Redis 2.2 as the default server profile (there are no changes
|
||||
that would break compatibility with previous releases). Long command names
|
||||
are no more supported by default but if you need them you can still require
|
||||
Predis_Compatibility.php to avoid breaking compatibility.
|
||||
`Predis_Compatibility.php` to avoid breaking compatibility.
|
||||
|
||||
- Added a `VERSION` constant to `Predis\Client`.
|
||||
|
||||
@@ -107,7 +244,7 @@ v0.6.6 (2011-04-01)
|
||||
|
||||
- Predis now uses by default a new protocol reader, more lightweight and
|
||||
faster than the default handler-based one. Users can revert to the old
|
||||
protocol reader with the 'reader' client option set to `composable`.
|
||||
protocol reader with the `reader` client option set to `composable`.
|
||||
This client option can also accept custom reader classes implementing the
|
||||
new `Predis\IResponseReader` interface.
|
||||
|
||||
@@ -246,7 +383,7 @@ v0.6.0 (2010-05-24)
|
||||
to instances of Redis 1.2.x).
|
||||
|
||||
- Support for Redis 1.0 is now optional and it is provided by requiring
|
||||
'Predis_Compatibility.php' before creating an instance of Predis\Client.
|
||||
'Predis_Compatibility.php' before creating an instance of `Predis\Client`.
|
||||
|
||||
- New commands added to the Redis 2.0 profile since Predis 0.5.1:
|
||||
- Strings: `SETEX`, `APPEND`, `SUBSTR`
|
||||
@@ -259,7 +396,7 @@ v0.6.0 (2010-05-24)
|
||||
|
||||
- Introduced client-level options with the new `Predis\ClientOptions` class.
|
||||
Options can be passed to the constructor of `Predis\Client` in its second
|
||||
argument as an array or an instance of Predis\ClientOptions. For brevity's
|
||||
argument as an array or an instance of `Predis\ClientOptions`. For brevity's
|
||||
sake and compatibility with older versions, the constructor still accepts
|
||||
an instance of `Predis\RedisServerProfile` in its second argument. The
|
||||
currently supported client options are:
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
## 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 (check the `VERSION` file or the `Predis\Client::VERSION` constant).
|
||||
2. version of Redis (check the `redis_version` field returned by [`INFO`](http://redis.io/commands/info)).
|
||||
3. version of PHP.
|
||||
4. name and version of the operating system.
|
||||
5. 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.
|
||||
@@ -1,79 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,5 +1,5 @@
|
||||
# Some frequently asked questions about Predis #
|
||||
____________________________________________
|
||||
_________________________________________________
|
||||
|
||||
|
||||
### What is the point of Predis? ###
|
||||
@@ -14,34 +14,6 @@ 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
|
||||
@@ -56,3 +28,128 @@ generalized when using Redis because of the many possible access patterns for th
|
||||
mean that it is impossible to have such a feature, you can leverage Predis' extensibility to define your
|
||||
own serialization-aware commands. See [here](http://github.com/nrk/predis/issues/29#issuecomment-1202624)
|
||||
for more details on how to implement such a feature with a practical example.
|
||||
|
||||
|
||||
### How can I force Predis to connect to Redis before sending any command? ###
|
||||
|
||||
Explicitly connecting to Redis is usually not needed since the client library relies on lazily initialized
|
||||
connections to the server, but this behavior can be inconvenient in certain scenarios when you absolutely
|
||||
need to do an upfront check to detect if the server is up and running and eventually catch exceptions on
|
||||
failures. In this case developers can use `Predis\Client::connect()` to explicitly connect to the server:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client();
|
||||
|
||||
try {
|
||||
$client->connect();
|
||||
} catch (Predis\Connection\ConnectionException $exception) {
|
||||
// We could not connect to Redis! Your handling code goes here.
|
||||
}
|
||||
|
||||
$client->info();
|
||||
```
|
||||
|
||||
|
||||
### How Predis implements abstraction of Redis commands? ###
|
||||
|
||||
The approach used in Predis to implement the abstraction of Redis commands is quite simple. By default
|
||||
every command in the library follows exactly the same argument list as defined in the great online
|
||||
[Redis documentation](http://redis.io/commands) which makes things pretty easy if you already know how
|
||||
Redis works or if you need to look up how to use certain commands. Alternatively, variadic commands can
|
||||
accept an array for keys or values (depending on the command) instead of a list of arguments. See for
|
||||
example how [RPUSH](http://redis.io/commands/rpush) or [HMSET](http://redis.io/commands/hmset) work:
|
||||
|
||||
```php
|
||||
$client->rpush('my:list', 'value1', 'value2', 'value3'); // values as arguments
|
||||
$client->rpush('my:list', array('value1', 'value2', 'value3')); // values as single argument array
|
||||
|
||||
$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // values as arguments
|
||||
$client->hmset('my:hash', array('field1'=>'value1', 'field2'=>'value2'); // values as single named array
|
||||
```
|
||||
|
||||
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.4.7 (custom build) and Redis 2.2
|
||||
(localhost) under Ubuntu 12.04.1 (running on a Intel Q6600):
|
||||
|
||||
21500 SET/sec using 12 bytes for both key and value
|
||||
21000 GET/sec while retrieving the very same values
|
||||
0.130 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)?
|
||||
|
||||
30100 SET/sec using 12 bytes for both key and value
|
||||
29400 GET/sec while retrieving the very same values
|
||||
0.035 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:
|
||||
3200 SET/sec using 12 bytes for both key and value
|
||||
3200 GET/sec while retrieving the very same values
|
||||
0.132 seconds to fetch 30000 keys using "KEYS *".
|
||||
|
||||
Using phpredis:
|
||||
3500 SET/sec using 12 bytes for both key and value
|
||||
3500 GET/sec while retrieving the very same values
|
||||
0.045 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/nrk/phpiredis)__ (note the additional _i_ in the name)
|
||||
and let Predis using it. __phpiredis__ is a C-based extension that wraps __hiredis__ (the official
|
||||
Redis C client library) with a thin layer that exposes its features to PHP. You will now get the
|
||||
benefits of a faster protocol parser just by adding a single line of code in your application:
|
||||
|
||||
```php
|
||||
$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.035 seconds from a local Redis instance
|
||||
0.047 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2011 Daniele Alessandri
|
||||
Copyright (c) 2009-2013 Daniele Alessandri
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -2,24 +2,29 @@
|
||||
|
||||
Predis is a flexible and feature-complete PHP (>= 5.3) client library for the Redis key-value store.
|
||||
|
||||
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,
|
||||
The library does not require any additional extension loaded in PHP but it can be optionally paired
|
||||
with the [phpiredis](https://github.com/nrk/phpiredis) C-based 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.
|
||||
|
||||
|
||||
## Main features ##
|
||||
|
||||
- Complete support for Redis from __1.2__ to __2.4__ and the current development versions using different
|
||||
server profiles.
|
||||
- Client-side sharding with support for consistent hashing or custom distribution strategies.
|
||||
- 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.
|
||||
- Support for master / slave replication configurations (write on master, read from slaves).
|
||||
- Command pipelining on single and aggregated connections.
|
||||
- Transparent key prefixing strategy capable of handling any command known that has keys in its arguments.
|
||||
- Command pipelining on single and aggregated connections.
|
||||
- Abstraction for Redis transactions (Redis >= 2.0) with support for CAS operations (Redis >= 2.2).
|
||||
- Connections to Redis instances are automatically and lazily estabilished upon the first call to a command.
|
||||
- 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.
|
||||
- Ability to connect to Redis using TCP/IP or UNIX domain sockets with support for persistent connections.
|
||||
- Ability to use alternative connection classes to use different types of network or protocol backends.
|
||||
- Ability to specify 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.
|
||||
|
||||
|
||||
@@ -35,13 +40,13 @@ by browsing the list of [tagged releases](http://github.com/nrk/predis/tags).
|
||||
|
||||
### Loading the library ###
|
||||
|
||||
To automatically load all of its files, Predis relies on the autoloading features of PHP 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:
|
||||
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:
|
||||
|
||||
``` php
|
||||
```php
|
||||
<?php
|
||||
// prepend a base path if Predis is not present in your "include_path".
|
||||
require 'Predis/Autoloader.php';
|
||||
@@ -49,32 +54,29 @@ require 'Predis/Autoloader.php';
|
||||
Predis\Autoloader::register();
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
||||
### Connecting to a local instance of Redis ###
|
||||
### Connecting to Redis ###
|
||||
|
||||
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:
|
||||
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:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
```php
|
||||
$redis = new Predis\Client();
|
||||
$redis->set('foo', 'bar');
|
||||
$value = $redis->get('foo');
|
||||
```
|
||||
|
||||
However you can use an URI string or a named array to specify the needed connection parameters:
|
||||
It is possible to specify the various connection parameters using URI strings or named arrays:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
```php
|
||||
$redis = new Predis\Client('tcp://10.0.0.1:6379');
|
||||
|
||||
// is equivalent to:
|
||||
@@ -87,21 +89,20 @@ $redis = new Predis\Client(array(
|
||||
```
|
||||
|
||||
|
||||
### Pipelining multiple commands to multiple instances of Redis with client-side sharding ###
|
||||
### Pipelining 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
|
||||
supports client-side sharding using consistent-hashing on keys while clustered connections are supported
|
||||
natively by the client class.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
```php
|
||||
$redis = new Predis\Client(array(
|
||||
array('host' => '10.0.0.1', 'port' => 6379),
|
||||
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");
|
||||
@@ -110,15 +111,24 @@ $replies = $redis->pipeline(function($pipe) {
|
||||
```
|
||||
|
||||
|
||||
### Overriding standard connection classes with custom ones ###
|
||||
### Multiple and customizable connection backends ###
|
||||
|
||||
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.
|
||||
Predis can optionally use different connection backends to connect to Redis. One of them leverages
|
||||
the [phpiredis](http://github.com/nrk/phpiredis) C-based extension resulting in a major speed bump
|
||||
especially when dealing with long multibulk replies (the `socket` extension is also required):
|
||||
|
||||
``` php
|
||||
<?php
|
||||
class MyConnectionClass implements Predis\Network\IConnectionSingle
|
||||
```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
|
||||
class MyConnectionClass implements Predis\Connection\SingleConnectionInterface
|
||||
{
|
||||
// implementation goes here
|
||||
}
|
||||
@@ -129,8 +139,8 @@ $client = new Predis\Client('tcp://127.0.0.1', array(
|
||||
));
|
||||
```
|
||||
|
||||
The classes contained in the `Predis\Network` namespace give you a better insight with actual code on
|
||||
how to create new connection classes.
|
||||
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.
|
||||
|
||||
|
||||
### Defining and registering new commands on the client at runtime ###
|
||||
@@ -141,9 +151,8 @@ code or waiting for it to find its way into a stable Predis release, then you ca
|
||||
a new class that matches the command type and its behaviour and then bind it to a client instance at
|
||||
runtime. Actually, it is easier done than said:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
class BrandNewRedisCommand extends Predis\Commands\Command
|
||||
```php
|
||||
class BrandNewRedisCommand extends Predis\Command\AbstractCommand
|
||||
{
|
||||
public function getId()
|
||||
{
|
||||
@@ -157,6 +166,42 @@ $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
|
||||
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
|
||||
@@ -175,19 +220,6 @@ 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
|
||||
@@ -216,7 +248,7 @@ other Git hosting provider of your preference.
|
||||
- [Lorenzo Castelli](http://github.com/lcastelli)
|
||||
- [Jordi Boggiano](http://github.com/Seldaek) ([twitter](http://twitter.com/seldaek))
|
||||
- [Sebastian Waisbrot](http://github.com/seppo0010) ([twitter](http://twitter.com/seppo0010))
|
||||
for his work on extending [phpiredis](http://github.com/seppo0010/phpiredis) for Predis
|
||||
for his past work on extending [phpiredis](http://github.com/nrk/phpiredis) for Predis.
|
||||
|
||||
## License ##
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
* Documentation! The README is obviously not enought to show how to use Predis
|
||||
as it does not cover all of its features.
|
||||
|
||||
* [v0.8] Implement smart and transparent support for redis-cluster.
|
||||
+1
-1
@@ -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)) {
|
||||
|
||||
@@ -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,7 +352,9 @@ 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, '');
|
||||
}
|
||||
@@ -430,7 +432,7 @@ LICENSE;
|
||||
{
|
||||
$useDirectives = $this->getNamespace()->getUseDirectives();
|
||||
|
||||
$useExtractor = function($m) use($useDirectives) {
|
||||
$useExtractor = function ($m) use ($useDirectives) {
|
||||
$useDirectives->add(($namespacedPath = $m[1]));
|
||||
};
|
||||
|
||||
@@ -453,7 +455,7 @@ LICENSE;
|
||||
$implements = array();
|
||||
$extends = array();
|
||||
|
||||
$extractor = function($iterator, $callback) {
|
||||
$extractor = function ($iterator, $callback) {
|
||||
$className = '';
|
||||
$iterator->seek($iterator->key() + 1);
|
||||
|
||||
@@ -464,8 +466,7 @@ LICENSE;
|
||||
if (preg_match('/\s?,\s?/', $token)) {
|
||||
$callback(trim($className));
|
||||
$className = '';
|
||||
}
|
||||
else if ($token == '{') {
|
||||
} else if ($token == '{') {
|
||||
$callback(trim($className));
|
||||
return;
|
||||
}
|
||||
@@ -510,13 +511,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;
|
||||
|
||||
@@ -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\Commands namespace (only classes in this namespace are valid).
|
||||
// in the Predis\Command namespace (only classes in this namespace are valid).
|
||||
// For example, to generate a test case for SET (which is represented by the
|
||||
// Predis\Commands\StringSet class):
|
||||
// Predis\Command\StringSet class):
|
||||
//
|
||||
// $ ./bin/generate-command-test.php --class=StringSet
|
||||
//
|
||||
@@ -33,8 +33,8 @@
|
||||
// is explicitly specified.
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
use Predis\Commands\ICommand;
|
||||
use Predis\Commands\IPrefixable;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\PrefixableCommandInterface;
|
||||
|
||||
class CommandTestCaseGenerator
|
||||
{
|
||||
@@ -92,8 +92,8 @@ class CommandTestCaseGenerator
|
||||
throw new RuntimeException("Missing 'class' option.");
|
||||
}
|
||||
|
||||
$options['fqn'] = "Predis\\Commands\\{$options['class']}";
|
||||
$options['path'] = "Predis/Commands/{$options['class']}.php";
|
||||
$options['fqn'] = "Predis\\Command\\{$options['class']}";
|
||||
$options['path'] = "Predis/Command/{$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\Commands\ICommand')) {
|
||||
throw new RuntimeException("Class $class must implement the Predis\Commands\ICommand interface.");
|
||||
if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) {
|
||||
throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface.");
|
||||
}
|
||||
|
||||
$instance = $reflection->newInstance();
|
||||
@@ -148,7 +148,7 @@ class CommandTestCaseGenerator
|
||||
file_put_contents($options['output'], $this->generate());
|
||||
}
|
||||
|
||||
protected function getTestCaseBuffer(ICommand $instance)
|
||||
protected function getTestCaseBuffer(CommandInterface $instance)
|
||||
{
|
||||
$id = $instance->getId();
|
||||
$fqn = get_class($instance);
|
||||
@@ -167,7 +167,7 @@ class CommandTestCaseGenerator
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
use \PHPUnit_Framework_TestCase as StandardTestCase;
|
||||
|
||||
@@ -226,7 +226,7 @@ class $class extends CommandTestCase
|
||||
|
||||
PHP;
|
||||
|
||||
if ($instance instanceof IPrefixable) {
|
||||
if ($instance instanceof PrefixableCommandInterface) {
|
||||
$buffer .=<<<PHP
|
||||
|
||||
/**
|
||||
|
||||
+5
-1
@@ -13,7 +13,11 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
"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"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"Predis": "lib/"}
|
||||
|
||||
@@ -13,17 +13,18 @@ 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 the Predis\Distribution\IDistributionStrategy interface.
|
||||
// that implements Predis\Distribution\DistributionStrategyInterface.
|
||||
|
||||
use Predis\Distribution\IDistributionStrategy;
|
||||
use Predis\Network\PredisCluster;
|
||||
use Predis\Connection\PredisCluster;
|
||||
use Predis\Cluster\Distribution\DistributionStrategyInterface;
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
|
||||
class NaiveDistributionStrategy implements IDistributionStrategy
|
||||
class NaiveDistributionStrategy implements DistributionStrategyInterface, HashGeneratorInterface
|
||||
{
|
||||
private $nodes;
|
||||
private $nodesCount;
|
||||
|
||||
public function __constructor()
|
||||
public function __construct()
|
||||
{
|
||||
$this->nodes = array();
|
||||
$this->nodesCount = 0;
|
||||
@@ -37,7 +38,7 @@ class NaiveDistributionStrategy implements IDistributionStrategy
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
@@ -46,24 +47,30 @@ class NaiveDistributionStrategy implements IDistributionStrategy
|
||||
|
||||
public function get($key)
|
||||
{
|
||||
$count = $this->nodesCount;
|
||||
if ($count === 0) {
|
||||
if (0 === $count = $this->nodesCount) {
|
||||
throw new RuntimeException('No connections');
|
||||
}
|
||||
|
||||
return $this->nodes[$count > 1 ? abs(crc32($key) % $count) : 0];
|
||||
return $this->nodes[$count > 1 ? abs($key % $count) : 0];
|
||||
}
|
||||
|
||||
public function generateKey($value)
|
||||
public function hash($value)
|
||||
{
|
||||
return crc32($value);
|
||||
}
|
||||
|
||||
public function getHashGenerator()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
$options = array(
|
||||
'cluster' => function() {
|
||||
'cluster' => function () {
|
||||
$distributor = new NaiveDistributionStrategy();
|
||||
return new PredisCluster($distributor);
|
||||
$cluster = new PredisCluster($distributor);
|
||||
|
||||
return $cluster;
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -13,14 +13,15 @@ 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\Commands\ScriptedCommand). This example
|
||||
// abstraction built upon them (Predis\Command\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\Profiles\ServerProfile;
|
||||
use Predis\Commands\ScriptedCommand;
|
||||
use Predis\Network\MasterSlaveReplication;
|
||||
use Predis\Command\ScriptedCommand;
|
||||
use Predis\Connection\MasterSlaveReplication;
|
||||
use Predis\Profile\ServerProfile;
|
||||
use Predis\Replication\ReplicationStrategy;
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
@@ -50,15 +51,17 @@ $parameters = array(
|
||||
);
|
||||
|
||||
$options = array(
|
||||
'profile' => function($options) {
|
||||
$profile = ServerProfile::get('dev');
|
||||
'profile' => function ($options) {
|
||||
$profile = ServerProfile::get('2.6');
|
||||
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
|
||||
|
||||
return $profile;
|
||||
},
|
||||
'replication' => function($options) {
|
||||
$replication = new MasterSlaveReplication();
|
||||
$replication->setScriptReadOnly(HashMultipleGetAll::BODY);
|
||||
'replication' => function ($options) {
|
||||
$strategy = new ReplicationStrategy();
|
||||
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
|
||||
|
||||
$replication = new MasterSlaveReplication($strategy);
|
||||
|
||||
return $replication;
|
||||
},
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?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
|
||||
*/
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -38,12 +38,10 @@ 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";
|
||||
}
|
||||
|
||||
@@ -14,36 +14,53 @@ 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\Commands\ScriptedCommand base class can be used to build an higher abstraction
|
||||
// Predis\Command\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\Commands\ScriptedCommand;
|
||||
use Predis\Command\ScriptedCommand;
|
||||
|
||||
class IncrementExistingKey extends ScriptedCommand
|
||||
class IncrementExistingKeysBy extends ScriptedCommand
|
||||
{
|
||||
public function getKeysCount()
|
||||
{
|
||||
return 1;
|
||||
// Tell Predis to use all the arguments but the last one as arguments
|
||||
// for KEYS. The last one will be used to populate ARGV.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function getScript()
|
||||
{
|
||||
return
|
||||
<<<LUA
|
||||
local cmd = redis.call
|
||||
if cmd('exists', KEYS[1]) == 1 then
|
||||
return cmd('incr', KEYS[1])
|
||||
end
|
||||
local cmd, insert = redis.call, table.insert
|
||||
local increment, results = ARGV[1], { }
|
||||
|
||||
for idx, key in ipairs(KEYS) do
|
||||
if cmd('exists', key) == 1 then
|
||||
insert(results, idx, cmd('incrby', key, increment))
|
||||
else
|
||||
insert(results, idx, false)
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
LUA;
|
||||
}
|
||||
}
|
||||
|
||||
$client = new Predis\Client($single_server, 'dev');
|
||||
$client = new Predis\Client($single_server);
|
||||
|
||||
$client->getProfile()->defineCommand('increx', 'IncrementExistingKey');
|
||||
$client->getProfile()->defineCommand('increxby', 'IncrementExistingKeysBy');
|
||||
|
||||
$client->set('foo', 10);
|
||||
$client->mset('foo', 10, 'foobar', 100);
|
||||
|
||||
var_dump($client->increx('foo')); // int(11)
|
||||
var_dump($client->increx('bar')); // NULL
|
||||
var_export($client->increxby('foo', 'foofoo', 'foobar', 50));
|
||||
|
||||
/*
|
||||
array (
|
||||
0 => 60,
|
||||
1 => NULL,
|
||||
2 => 150,
|
||||
)
|
||||
*/
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
|
||||
require 'SharedConfigurations.php';
|
||||
|
||||
use Predis\ConnectionParameters;
|
||||
use Predis\Commands\ICommand;
|
||||
use Predis\Network\StreamConnection;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Connection\StreamConnection;
|
||||
|
||||
class SimpleDebuggableConnection extends StreamConnection
|
||||
{
|
||||
@@ -27,7 +26,7 @@ class SimpleDebuggableConnection extends StreamConnection
|
||||
parent::connect();
|
||||
}
|
||||
|
||||
private function storeDebug(ICommand $command, $direction)
|
||||
private function storeDebug(CommandInterface $command, $direction)
|
||||
{
|
||||
$firtsArg = $command->getArgument(0);
|
||||
$timestamp = round(microtime(true) - $this->tstart, 4);
|
||||
@@ -40,14 +39,14 @@ class SimpleDebuggableConnection extends StreamConnection
|
||||
$this->debugBuffer[] = $debug;
|
||||
}
|
||||
|
||||
public function writeCommand(ICommand $command)
|
||||
public function writeCommand(CommandInterface $command)
|
||||
{
|
||||
parent::writeCommand($command);
|
||||
|
||||
$this->storeDebug($command, '->');
|
||||
}
|
||||
|
||||
public function readResponse(ICommand $command)
|
||||
public function readResponse(CommandInterface $command)
|
||||
{
|
||||
$reply = parent::readResponse($command);
|
||||
$this->storeDebug($command, '<-');
|
||||
|
||||
+17
-12
@@ -15,27 +15,32 @@ namespace Predis;
|
||||
* Implements a lightweight PSR-0 compliant autoloader.
|
||||
*
|
||||
* @author Eric Naeseth <eric@thumbtack.com>
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class Autoloader
|
||||
{
|
||||
private $baseDir;
|
||||
private $directory;
|
||||
private $prefix;
|
||||
private $prefixLength;
|
||||
|
||||
/**
|
||||
* @param string $baseDirectory Base directory where the source files are located.
|
||||
*/
|
||||
public function __construct($baseDirectory = null)
|
||||
public function __construct($baseDirectory = __DIR__)
|
||||
{
|
||||
$this->baseDir = $baseDirectory ?: dirname(__FILE__);
|
||||
$this->directory = $baseDirectory;
|
||||
$this->prefix = __NAMESPACE__ . '\\';
|
||||
$this->prefixLength = strlen($this->prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the autoloader class with the PHP SPL autoloader.
|
||||
*
|
||||
* @param boolean $prepend Prepend the autoloader on the stack instead of appending it.
|
||||
*/
|
||||
public static function register()
|
||||
public static function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array(new self, 'autoload'));
|
||||
spl_autoload_register(array(new self, 'autoload'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,13 +50,13 @@ class Autoloader
|
||||
*/
|
||||
public function autoload($className)
|
||||
{
|
||||
if (0 !== strpos($className, $this->prefix)) {
|
||||
return;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
$relativeClassName = substr($className, strlen($this->prefix));
|
||||
$classNameParts = explode('\\', $relativeClassName);
|
||||
|
||||
require_once $this->baseDir.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $classNameParts).'.php';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?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);
|
||||
}
|
||||
+106
-78
@@ -11,15 +11,19 @@
|
||||
|
||||
namespace Predis;
|
||||
|
||||
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\Command\CommandInterface;
|
||||
use Predis\Command\ScriptedCommand;
|
||||
use Predis\Connection\AggregatedConnectionInterface;
|
||||
use Predis\Connection\ConnectionInterface;
|
||||
use Predis\Connection\ConnectionFactory;
|
||||
use Predis\Connection\ConnectionFactoryInterface;
|
||||
use Predis\Monitor\MonitorContext;
|
||||
use Predis\Option\ClientOptions;
|
||||
use Predis\Option\ClientOptionsInterface;
|
||||
use Predis\Pipeline\PipelineContext;
|
||||
use Predis\Profile\ServerProfile;
|
||||
use Predis\Profile\ServerProfileInterface;
|
||||
use Predis\PubSub\PubSubContext;
|
||||
use Predis\Transaction\MultiExecContext;
|
||||
|
||||
/**
|
||||
@@ -27,9 +31,9 @@ use Predis\Transaction\MultiExecContext;
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class Client
|
||||
class Client implements ClientInterface
|
||||
{
|
||||
const VERSION = '0.7.1';
|
||||
const VERSION = '0.8.1';
|
||||
|
||||
private $options;
|
||||
private $profile;
|
||||
@@ -44,17 +48,16 @@ class Client
|
||||
*/
|
||||
public function __construct($parameters = null, $options = null)
|
||||
{
|
||||
$options = $this->filterOptions($options);
|
||||
$this->options = $options;
|
||||
$this->profile = $options->profile;
|
||||
$this->connections = $options->connections;
|
||||
$this->options = $this->filterOptions($options);
|
||||
$this->profile = $this->options->profile;
|
||||
$this->connections = $this->options->connections;
|
||||
$this->connection = $this->initializeConnection($parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* @param mixed $options Client options.
|
||||
* @return ClientOptions
|
||||
@@ -64,15 +67,14 @@ class Client
|
||||
if ($options === null) {
|
||||
return new ClientOptions();
|
||||
}
|
||||
|
||||
if (is_array($options)) {
|
||||
return new ClientOptions($options);
|
||||
}
|
||||
if ($options instanceof IClientOptions) {
|
||||
|
||||
if ($options instanceof ClientOptionsInterface) {
|
||||
return $options;
|
||||
}
|
||||
if ($options instanceof IServerProfile || is_string($options)) {
|
||||
return new ClientOptions(array('profile' => $options));
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("Invalid type for client options");
|
||||
}
|
||||
@@ -80,33 +82,29 @@ class Client
|
||||
/**
|
||||
* Initializes one or multiple connection (cluster) objects from various
|
||||
* types of arguments (string, array) or returns the passed object if it
|
||||
* implements the Predis\Network\IConnection interface.
|
||||
* implements Predis\Connection\ConnectionInterface.
|
||||
*
|
||||
* @param mixed $parameters Connection parameters or instance.
|
||||
* @return IConnection
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
protected function initializeConnection($parameters)
|
||||
{
|
||||
if ($parameters instanceof IConnection) {
|
||||
if ($parameters instanceof ConnectionInterface) {
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
if (is_array($parameters) && isset($parameters[0])) {
|
||||
$replication = isset($this->options->replication) && $this->options->replication;
|
||||
|
||||
$connection = $this->options->{$replication ? 'replication' : 'cluster'};
|
||||
$initializer = $replication ? 'createReplication' : 'createCluster';
|
||||
|
||||
return $this->connections->$initializer($connection, $parameters, $this->profile);
|
||||
return $this->connections->createAggregated($connection, $parameters);
|
||||
}
|
||||
|
||||
return $this->connections->create($parameters, $this->profile);
|
||||
return $this->connections->create($parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server profile used by the client.
|
||||
*
|
||||
* @return IServerProfile
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getProfile()
|
||||
{
|
||||
@@ -114,9 +112,7 @@ class Client
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client options specified upon initialization.
|
||||
*
|
||||
* @return ClientOptions
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
@@ -126,7 +122,7 @@ class Client
|
||||
/**
|
||||
* Returns the connection factory object used by the client.
|
||||
*
|
||||
* @return IConnectionFactory
|
||||
* @return ConnectionFactoryInterface
|
||||
*/
|
||||
public function getConnectionFactory()
|
||||
{
|
||||
@@ -140,13 +136,13 @@ class Client
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function getClientFor($connectionAlias)
|
||||
public function getClientFor($connectionID)
|
||||
{
|
||||
if (($connection = $this->getConnection($connectionAlias)) === null) {
|
||||
throw new \InvalidArgumentException("Invalid connection alias: '$connectionAlias'");
|
||||
if (($connection = $this->getConnectionById($connectionID)) === null) {
|
||||
throw new \InvalidArgumentException("Invalid connection ID: '$connectionID'");
|
||||
}
|
||||
|
||||
return new Client($connection, $this->options);
|
||||
return new static($connection, $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,27 +183,30 @@ class Client
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConnection($id = null)
|
||||
public function getConnection()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dinamically invokes a Redis command with the specified arguments.
|
||||
* 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.
|
||||
*
|
||||
* @param string $method The name of a Redis command.
|
||||
* @param array $arguments The arguments for the command.
|
||||
@@ -216,15 +215,21 @@ class Client
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
$command = $this->profile->createCommand($method, $arguments);
|
||||
return $this->connection->executeCommand($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createCommand($method, $arguments = array())
|
||||
{
|
||||
@@ -232,35 +237,50 @@ class Client
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the specified Redis command.
|
||||
*
|
||||
* @param ICommand $command A Redis command.
|
||||
* @return mixed
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function executeCommand(ICommand $command)
|
||||
public function executeCommand(CommandInterface $command)
|
||||
{
|
||||
return $this->connection->executeCommand($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the specified Redis command on all the nodes of a cluster.
|
||||
* Handles -ERR responses returned by Redis.
|
||||
*
|
||||
* @param ICommand $command A Redis command.
|
||||
* @return array
|
||||
* @param CommandInterface $command The command that generated the error.
|
||||
* @param ResponseErrorInterface $response The error response instance.
|
||||
* @return mixed
|
||||
*/
|
||||
public function executeCommandOnShards(ICommand $command)
|
||||
protected function onResponseError(CommandInterface $command, ResponseErrorInterface $response)
|
||||
{
|
||||
if (Helpers::isCluster($this->connection)) {
|
||||
$replies = array();
|
||||
if ($command instanceof ScriptedCommand && $response->getErrorType() === 'NOSCRIPT') {
|
||||
$eval = $this->createCommand('eval');
|
||||
$eval->setRawArguments($command->getEvalArguments());
|
||||
|
||||
foreach ($this->connection as $connection) {
|
||||
$replies[] = $connection->executeCommand($command);
|
||||
$response = $this->executeCommand($eval);
|
||||
|
||||
if (false === $response instanceof ResponseObjectInterface) {
|
||||
$response = $command->parseResponse($response);
|
||||
}
|
||||
|
||||
return $replies;
|
||||
return $response;
|
||||
}
|
||||
|
||||
return array($this->connection->executeCommand($command));
|
||||
if ($this->options->exceptions === true) {
|
||||
throw new ServerException($response->getMessage());
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -312,8 +332,16 @@ class Client
|
||||
*/
|
||||
protected function initPipeline(Array $options = null, $callable = null)
|
||||
{
|
||||
$pipeline = new PipelineContext($this, $options);
|
||||
return $this->pipelineExecute($pipeline, $callable);
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?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());
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?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);
|
||||
}
|
||||
+9
-2
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Distribution;
|
||||
namespace Predis\Cluster\Distribution;
|
||||
|
||||
/**
|
||||
* A distributor implements the logic to automatically distribute
|
||||
@@ -17,7 +17,7 @@ namespace Predis\Distribution;
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface IDistributionStrategy extends INodeKeyGenerator
|
||||
interface DistributionStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Adds a node to the distributor with an optional weight.
|
||||
@@ -40,4 +40,11 @@ interface IDistributionStrategy extends INodeKeyGenerator
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* Returns the underlying hash generator instance.
|
||||
*
|
||||
* @return Predis\Cluster\Hash\HashGeneratorInterface
|
||||
*/
|
||||
public function getHashGenerator();
|
||||
}
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Distribution;
|
||||
namespace Predis\Cluster\Distribution;
|
||||
|
||||
/**
|
||||
* Exception class that identifies empty rings.
|
||||
+29
-13
@@ -9,7 +9,9 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Distribution;
|
||||
namespace Predis\Cluster\Distribution;
|
||||
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
|
||||
/**
|
||||
* This class implements an hashring-based distributor that uses the same
|
||||
@@ -19,24 +21,26 @@ namespace Predis\Distribution;
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
* @author Lorenzo Castelli <lcastelli@gmail.com>
|
||||
*/
|
||||
class HashRing implements IDistributionStrategy
|
||||
class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
|
||||
{
|
||||
const DEFAULT_REPLICAS = 128;
|
||||
const DEFAULT_WEIGHT = 100;
|
||||
|
||||
private $nodes;
|
||||
private $ring;
|
||||
private $ringKeys;
|
||||
private $ringKeysCount;
|
||||
private $replicas;
|
||||
private $nodeHashCallback;
|
||||
private $nodes = array();
|
||||
|
||||
/**
|
||||
* @param int $replicas Number of replicas in the ring.
|
||||
* @param mixed $nodeHashCallback Callback returning the string used to calculate the hash of a node.
|
||||
*/
|
||||
public function __construct($replicas = self::DEFAULT_REPLICAS)
|
||||
public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null)
|
||||
{
|
||||
$this->replicas = $replicas;
|
||||
$this->nodes = array();
|
||||
$this->nodeHashCallback = $nodeHashCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,6 +105,7 @@ class HashRing implements IDistributionStrategy
|
||||
private function computeTotalWeight()
|
||||
{
|
||||
$totalWeight = 0;
|
||||
|
||||
foreach ($this->nodes as $node) {
|
||||
$totalWeight += $node['weight'];
|
||||
}
|
||||
@@ -123,14 +128,14 @@ class HashRing implements IDistributionStrategy
|
||||
|
||||
$this->ring = array();
|
||||
$totalWeight = $this->computeTotalWeight();
|
||||
$nodesCount = count($this->nodes);
|
||||
$nodesCount = count($this->nodes);
|
||||
|
||||
foreach ($this->nodes as $node) {
|
||||
$weightRatio = $node['weight'] / $totalWeight;
|
||||
$this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio);
|
||||
}
|
||||
ksort($this->ring, SORT_NUMERIC);
|
||||
|
||||
ksort($this->ring, SORT_NUMERIC);
|
||||
$this->ringKeys = array_keys($this->ring);
|
||||
$this->ringKeysCount = count($this->ringKeys);
|
||||
}
|
||||
@@ -161,7 +166,11 @@ class HashRing implements IDistributionStrategy
|
||||
*/
|
||||
protected function getNodeHash($nodeObject)
|
||||
{
|
||||
return (string) $nodeObject;
|
||||
if ($this->nodeHashCallback === null) {
|
||||
return (string) $nodeObject;
|
||||
}
|
||||
|
||||
return call_user_func($this->nodeHashCallback, $nodeObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +179,7 @@ class HashRing implements IDistributionStrategy
|
||||
* @param string $value Input value.
|
||||
* @return int
|
||||
*/
|
||||
public function generateKey($value)
|
||||
public function hash($value)
|
||||
{
|
||||
return crc32($value);
|
||||
}
|
||||
@@ -199,13 +208,12 @@ class HashRing implements IDistributionStrategy
|
||||
while ($lower <= $upper) {
|
||||
$index = ($lower + $upper) >> 1;
|
||||
$item = $ringKeys[$index];
|
||||
|
||||
if ($item > $key) {
|
||||
$upper = $index - 1;
|
||||
}
|
||||
else if ($item < $key) {
|
||||
} else if ($item < $key) {
|
||||
$lower = $index + 1;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
@@ -227,4 +235,12 @@ class HashRing implements IDistributionStrategy
|
||||
// equal to the key. If no such item exists, return the last item.
|
||||
return $upper >= 0 ? $upper : $ringKeysCount - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHashGenerator()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Distribution;
|
||||
namespace Predis\Cluster\Distribution;
|
||||
|
||||
/**
|
||||
* This class implements an hashring-based distributor that uses the same
|
||||
@@ -24,11 +24,11 @@ class KetamaPureRing extends HashRing
|
||||
const DEFAULT_REPLICAS = 160;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mixed $nodeHashCallback Callback returning the string used to calculate the hash of a node.
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct($nodeHashCallback = null)
|
||||
{
|
||||
parent::__construct($this::DEFAULT_REPLICAS);
|
||||
parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,6 +42,7 @@ class KetamaPureRing extends HashRing
|
||||
|
||||
for ($i = 0; $i < $replicas; $i++) {
|
||||
$unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));
|
||||
|
||||
foreach ($unpackedDigest as $key) {
|
||||
$ring[$key] = $nodeObject;
|
||||
}
|
||||
@@ -51,7 +52,7 @@ class KetamaPureRing extends HashRing
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function generateKey($value)
|
||||
public function hash($value)
|
||||
{
|
||||
$hash = unpack('V', md5($value, true));
|
||||
return $hash[1];
|
||||
@@ -0,0 +1,72 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Distribution;
|
||||
namespace Predis\Cluster\Hash;
|
||||
|
||||
/**
|
||||
* A generator of node keys implements the logic used to calculate the hash of
|
||||
@@ -17,7 +17,7 @@ namespace Predis\Distribution;
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface INodeKeyGenerator
|
||||
interface HashGeneratorInterface
|
||||
{
|
||||
/**
|
||||
* Generates an hash that is used by the distributor algorithm
|
||||
@@ -25,5 +25,5 @@ interface INodeKeyGenerator
|
||||
* @param string $value Value used to generate the hash.
|
||||
* @return int
|
||||
*/
|
||||
public function generateKey($value);
|
||||
public function hash($value);
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
<?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,17 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
|
||||
use Predis\Helpers;
|
||||
use Predis\Distribution\INodeKeyGenerator;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Base class for Redis commands.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
abstract class Command implements ICommand
|
||||
abstract class AbstractCommand implements CommandInterface
|
||||
{
|
||||
private $hash;
|
||||
private $arguments = array();
|
||||
@@ -76,57 +73,21 @@ abstract class Command implements ICommand
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the command can return an hash for client-side sharding.
|
||||
*
|
||||
* @return Boolean
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function canBeHashed()
|
||||
public function setHash($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;
|
||||
$this->hash = $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHash(INodeKeyGenerator $distributor)
|
||||
public function getHash()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -149,6 +110,7 @@ abstract class Command implements ICommand
|
||||
if (strlen($argument) > 32) {
|
||||
$argument = substr($argument, 0, 32) . '[...]';
|
||||
}
|
||||
|
||||
$accumulator .= " $argument";
|
||||
|
||||
return $accumulator;
|
||||
@@ -9,15 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
|
||||
use Predis\Distribution\INodeKeyGenerator;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Defines an abstraction representing a Redis command.
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface ICommand
|
||||
interface CommandInterface
|
||||
{
|
||||
/**
|
||||
* Gets the ID of a Redis command.
|
||||
@@ -27,21 +25,33 @@ interface ICommand
|
||||
public function getId();
|
||||
|
||||
/**
|
||||
* Returns an hash of the command using the provided algorithm against the
|
||||
* key (used to calculate the distribution of keys with client-side sharding).
|
||||
* Set the hash for the command.
|
||||
*
|
||||
* @param INodeKeyGenerator $distributor Distribution algorithm.
|
||||
* @return int
|
||||
* @param int $hash Calculated hash.
|
||||
*/
|
||||
public function getHash(INodeKeyGenerator $distributor);
|
||||
public function setHash($hash);
|
||||
|
||||
/**
|
||||
* Sets the arguments of the command.
|
||||
* Returns the hash of the command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHash();
|
||||
|
||||
/**
|
||||
* Sets the arguments for 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,19 +9,19 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/evalsha
|
||||
* @link http://redis.io/commands/auth
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ServerEvalSHA extends ServerEval
|
||||
class ConnectionAuth extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'EVALSHA';
|
||||
return 'AUTH';
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/echo
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ConnectionEcho extends Command
|
||||
class ConnectionEcho extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -24,12 +24,4 @@ class ConnectionEcho extends Command
|
||||
{
|
||||
return 'ECHO';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function canBeHashed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/ping
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ConnectionPing extends Command
|
||||
class ConnectionPing extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -25,14 +25,6 @@ class ConnectionPing extends Command
|
||||
return 'PING';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function canBeHashed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/quit
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ConnectionQuit extends Command
|
||||
class ConnectionQuit extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -24,12 +24,4 @@ class ConnectionQuit extends Command
|
||||
{
|
||||
return 'QUIT';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function canBeHashed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/select
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ConnectionSelect extends Command
|
||||
class ConnectionSelect extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -24,12 +24,4 @@ class ConnectionSelect extends Command
|
||||
{
|
||||
return 'SELECT';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function canBeHashed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
use Predis\Helpers;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hexists
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hget
|
||||
@@ -9,9 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
|
||||
use Predis\Iterators\MultiBulkResponseTuple;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hgetall
|
||||
@@ -32,11 +30,8 @@ class HashGetAll extends PrefixableCommand
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
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\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
use Predis\Helpers;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hincrby
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hincrbyfloat
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hkeys
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hlen
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hset
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hmset
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hsetnx
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hvals
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
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 Command implements IPrefixable
|
||||
class KeyDelete extends AbstractCommand implements PrefixableCommandInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -42,17 +42,4 @@ class KeyDelete extends Command implements IPrefixable
|
||||
{
|
||||
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\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/exists
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/expire
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/expireat
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/keys
|
||||
@@ -24,12 +24,4 @@ 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\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/keys
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/move
|
||||
@@ -25,14 +25,6 @@ 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\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/persist
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pexpire
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pexpireat
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pttl
|
||||
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/randomkey
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class KeyRandom extends Command
|
||||
class KeyRandom extends AbstractCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -25,14 +25,6 @@ class KeyRandom extends Command
|
||||
return 'RANDOMKEY';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function canBeHashed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/rename
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class KeyRename extends Command implements IPrefixable
|
||||
class KeyRename extends AbstractCommand implements PrefixableCommandInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -32,12 +32,4 @@ class KeyRename extends Command implements IPrefixable
|
||||
{
|
||||
PrefixHelpers::all($this, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function canBeHashed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/renamenx
|
||||
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/sort
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class KeySort extends Command implements IPrefixable
|
||||
class KeySort extends AbstractCommand implements PrefixableCommandInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -44,20 +44,21 @@ class KeySort extends Command implements IPrefixable
|
||||
|
||||
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\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/ttl
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/type
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/lindex
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/linsert
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/llen
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/lpop
|
||||
+3
-12
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/blpop
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ListPopFirstBlocking extends Command implements IPrefixable
|
||||
class ListPopFirstBlocking extends AbstractCommand implements PrefixableCommandInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -34,6 +34,7 @@ class ListPopFirstBlocking extends Command implements IPrefixable
|
||||
list($arguments, $timeout) = $arguments;
|
||||
array_push($arguments, $timeout);
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
@@ -44,14 +45,4 @@ class ListPopFirstBlocking extends Command implements IPrefixable
|
||||
{
|
||||
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\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/rpop
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/brpop
|
||||
+2
-10
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/rpoplpush
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ListPopLastPushHead extends Command implements IPrefixable
|
||||
class ListPopLastPushHead extends AbstractCommand implements PrefixableCommandInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -32,12 +32,4 @@ class ListPopLastPushHead extends Command implements IPrefixable
|
||||
{
|
||||
PrefixHelpers::all($this, $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function canBeHashed()
|
||||
{
|
||||
return $this->checkSameHashForKeys($this->getArguments());
|
||||
}
|
||||
}
|
||||
+2
-12
@@ -9,13 +9,13 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/brpoplpush
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ListPopLastPushHeadBlocking extends Command implements IPrefixable
|
||||
class ListPopLastPushHeadBlocking extends AbstractCommand implements PrefixableCommandInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -32,14 +32,4 @@ class ListPopLastPushHeadBlocking extends Command implements IPrefixable
|
||||
{
|
||||
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\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/lpush
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/lpushx
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
use Predis\Helpers;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/rpushx
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/lrange
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/lrem
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/lset
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/ltrim
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* 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 ICommand $command Command instance.
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function first(ICommand $command, $prefix)
|
||||
public static function first(CommandInterface $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 ICommand $command Command instance.
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function all(ICommand $command, $prefix)
|
||||
public static function all(CommandInterface $command, $prefix)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
|
||||
@@ -52,10 +52,10 @@ class PrefixHelpers
|
||||
/**
|
||||
* Applies the specified prefix only to even arguments in the list.
|
||||
*
|
||||
* @param ICommand $command Command instance.
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function interleaved(ICommand $command, $prefix)
|
||||
public static function interleaved(CommandInterface $command, $prefix)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$length = count($arguments);
|
||||
@@ -68,12 +68,30 @@ class PrefixHelpers
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to all the arguments but the last one.
|
||||
* Applies the specified prefix to all the arguments but the first one.
|
||||
*
|
||||
* @param ICommand $command Command instance.
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function skipLast(ICommand $command, $prefix)
|
||||
public static function skipFirst(CommandInterface $command, $prefix)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$length = count($arguments);
|
||||
|
||||
for ($i = 1; $i < $length; $i++) {
|
||||
$arguments[$i] = "$prefix{$arguments[$i]}";
|
||||
}
|
||||
|
||||
$command->setRawArguments($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified prefix to all the arguments but the last one.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
* @param string $prefix Prefix string.
|
||||
*/
|
||||
public static function skipLast(CommandInterface $command, $prefix)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$length = count($arguments);
|
||||
+2
-2
@@ -9,14 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Base class for Redis commands with prefixable keys.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
abstract class PrefixableCommand extends Command implements IPrefixable
|
||||
abstract class PrefixableCommand extends AbstractCommand implements PrefixableCommandInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
+2
-4
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
|
||||
use Predis\Distribution\INodeKeyGenerator;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Defines a command whose keys can be prefixed.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface IPrefixable
|
||||
interface PrefixableCommandInterface
|
||||
{
|
||||
/**
|
||||
* Prefixes all the keys found in the arguments of the command.
|
||||
+5
-5
@@ -9,26 +9,26 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands\Processors;
|
||||
namespace Predis\Command\Processor;
|
||||
|
||||
/**
|
||||
* Defines an object that can process commands using command processors.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface IProcessingSupport
|
||||
interface CommandProcessingInterface
|
||||
{
|
||||
/**
|
||||
* Associates a command processor.
|
||||
*
|
||||
* @param ICommandProcessor $processor The command processor.
|
||||
* @param CommandProcessorInterface $processor The command processor.
|
||||
*/
|
||||
public function setProcessor(ICommandProcessor $processor);
|
||||
public function setProcessor(CommandProcessorInterface $processor);
|
||||
|
||||
/**
|
||||
* Returns the associated command processor.
|
||||
*
|
||||
* @return ICommandProcessor
|
||||
* @return CommandProcessorInterface
|
||||
*/
|
||||
public function getProcessor();
|
||||
}
|
||||
+6
-6
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands\Processors;
|
||||
namespace Predis\Command\Processor;
|
||||
|
||||
/**
|
||||
* A command processor chain processes a command using multiple chained command
|
||||
@@ -17,21 +17,21 @@ namespace Predis\Commands\Processors;
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface ICommandProcessorChain extends ICommandProcessor, \IteratorAggregate, \Countable
|
||||
interface CommandProcessorChainInterface extends CommandProcessorInterface, \IteratorAggregate, \Countable
|
||||
{
|
||||
/**
|
||||
* Adds a command processor.
|
||||
*
|
||||
* @param ICommandProcessor $processor A command processor.
|
||||
* @param CommandProcessorInterface $processor A command processor.
|
||||
*/
|
||||
public function add(ICommandProcessor $processor);
|
||||
public function add(CommandProcessorInterface $processor);
|
||||
|
||||
/**
|
||||
* Removes a command processor from the chain.
|
||||
*
|
||||
* @param ICommandProcessor $processor A command processor.
|
||||
* @param CommandProcessorInterface $processor A command processor.
|
||||
*/
|
||||
public function remove(ICommandProcessor $processor);
|
||||
public function remove(CommandProcessorInterface $processor);
|
||||
|
||||
/**
|
||||
* Returns an ordered list of the command processors in the chain.
|
||||
+5
-5
@@ -9,21 +9,21 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands\Processors;
|
||||
namespace Predis\Command\Processor;
|
||||
|
||||
use Predis\Commands\ICommand;
|
||||
use Predis\Command\CommandInterface;
|
||||
|
||||
/**
|
||||
* A command processor processes commands before they are sent to Redis.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface ICommandProcessor
|
||||
interface CommandProcessorInterface
|
||||
{
|
||||
/**
|
||||
* Processes a Redis command.
|
||||
*
|
||||
* @param ICommand $command Redis command.
|
||||
* @param CommandInterface $command Redis command.
|
||||
*/
|
||||
public function process(ICommand $command);
|
||||
public function process(CommandInterface $command);
|
||||
}
|
||||
+14
-6
@@ -9,10 +9,10 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands\Processors;
|
||||
namespace Predis\Command\Processor;
|
||||
|
||||
use Predis\Commands\ICommand;
|
||||
use Predis\Commands\IPrefixable;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\PrefixableCommandInterface;
|
||||
|
||||
/**
|
||||
* Command processor that is used to prefix the keys contained in the arguments
|
||||
@@ -20,7 +20,7 @@ use Predis\Commands\IPrefixable;
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class KeyPrefixProcessor implements ICommandProcessor
|
||||
class KeyPrefixProcessor implements CommandProcessorInterface
|
||||
{
|
||||
private $prefix;
|
||||
|
||||
@@ -55,10 +55,18 @@ class KeyPrefixProcessor implements ICommandProcessor
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ICommand $command)
|
||||
public function process(CommandInterface $command)
|
||||
{
|
||||
if ($command instanceof IPrefixable) {
|
||||
if ($command instanceof PrefixableCommandInterface) {
|
||||
$command->prefixKeys($this->prefix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getPrefix();
|
||||
}
|
||||
}
|
||||
+11
-13
@@ -9,21 +9,21 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands\Processors;
|
||||
namespace Predis\Command\Processor;
|
||||
|
||||
use Predis\Commands\ICommand;
|
||||
use Predis\Command\CommandInterface;
|
||||
|
||||
/**
|
||||
* Default implementation of a command processors chain.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ProcessorChain implements ICommandProcessorChain, \ArrayAccess
|
||||
class ProcessorChain implements CommandProcessorChainInterface, \ArrayAccess
|
||||
{
|
||||
private $processors = array();
|
||||
|
||||
/**
|
||||
* @param array $processors List of instances of ICommandProcessor.
|
||||
* @param array $processors List of instances of CommandProcessorInterface.
|
||||
*/
|
||||
public function __construct($processors = array())
|
||||
{
|
||||
@@ -35,7 +35,7 @@ class ProcessorChain implements ICommandProcessorChain, \ArrayAccess
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add(ICommandProcessor $processor)
|
||||
public function add(CommandProcessorInterface $processor)
|
||||
{
|
||||
$this->processors[] = $processor;
|
||||
}
|
||||
@@ -43,10 +43,9 @@ class ProcessorChain implements ICommandProcessorChain, \ArrayAccess
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function remove(ICommandProcessor $processor)
|
||||
public function remove(CommandProcessorInterface $processor)
|
||||
{
|
||||
$index = array_search($processor, $this->processors, true);
|
||||
if ($index !== false) {
|
||||
if (false !== $index = array_search($processor, $this->processors, true)) {
|
||||
unset($this[$index]);
|
||||
}
|
||||
}
|
||||
@@ -54,10 +53,9 @@ class ProcessorChain implements ICommandProcessorChain, \ArrayAccess
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ICommand $command)
|
||||
public function process(CommandInterface $command)
|
||||
{
|
||||
$count = count($this->processors);
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
for ($i = 0; $i < $count = count($this->processors); $i++) {
|
||||
$this->processors[$i]->process($command);
|
||||
}
|
||||
}
|
||||
@@ -111,10 +109,10 @@ class ProcessorChain implements ICommandProcessorChain, \ArrayAccess
|
||||
*/
|
||||
public function offsetSet($index, $processor)
|
||||
{
|
||||
if (!$processor instanceof ICommandProcessor) {
|
||||
if (!$processor instanceof CommandProcessorInterface) {
|
||||
throw new \InvalidArgumentException(
|
||||
'A processor chain can hold only instances of classes implementing '.
|
||||
'the Predis\Commands\Preprocessors\ICommandProcessor interface'
|
||||
'the Predis\Command\Processor\CommandProcessorInterface interface'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Commands;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/publish
|
||||
@@ -24,12 +24,4 @@ class PubSubPublish extends PrefixableCommand
|
||||
{
|
||||
return 'PUBLISH';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function canBeHashed()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user