Compare commits

...

111 Commits

Author SHA1 Message Date
Daniele Alessandri 239864d9a0 Update CHANGELOG and bump VERSION. 2014-07-15 11:54:33 +02:00
Daniele Alessandri b006d2306c Rewrite pear packager script.
We still use Onion's package.ini for the configuration of the package
to keep things simple, but we might switch to a more compact solution
in the future since we do not really need much of its features.
2014-07-15 11:39:42 +02:00
Daniele Alessandri 34e394cc10 Reword CHANGELOG. 2014-07-15 11:36:13 +02:00
Daniele Alessandri 79539c3bcf Update CHANGELOG. 2014-07-14 16:26:06 +02:00
Daniele Alessandri 74817b5596 Porperly handle -ERR instead of +QUEUED inside MULTI ... EXEC.
Error responses such as -OOM or -ERR on invalid arguments in commands
are returned immediatly instead of +QUEUED when using a transaction,
which is a condition that had not been tested enough. This condition
led to a bug in which Predis was not invalidating the transaction, so
when trying to create a new transaction Redis returned a "-ERR MULTI
calls can not be nested".

This commit fixes #187.
2014-07-14 16:23:18 +02:00
Daniele Alessandri f967ecd66b Merge remote-tracking branch 'github/pr/184' into v0.8 2014-07-07 11:48:01 +02:00
Daniele Alessandri 261d0b824f Update CHANGELOG. 2014-07-07 11:10:54 +02:00
Daniele Alessandri b77da8476c Backport fix for cursor-based iterator abstraction from PR #182. 2014-07-07 10:58:33 +02:00
Ian Babrou 3fbd29d00b prevent uncaught broken pipe error 2014-07-07 11:30:44 +04:00
Daniele Alessandri 8373663486 Use stream_socket_recvfrom() in PhpiredisStreamConnection.
Similarly to the socket-ext based connection using phpiredis, in our
stream based PhpiredisStreamConnection class we should read data from
the stream using stream_socket_recvfrom() instead of fread() because
the latter could block until a timeout is reached when the read buffer
contains less data then the specified length.

IMPORTANT: stream_socket_recvfrom() bypasses stream wrappers which
means that TLS/SSL, as requested by PR #158, won't ever work with
this connection class as the function returns the original encrypted
bytes.

This commit fixes issue #180.
2014-06-23 14:25:30 +02:00
Daniele Alessandri 18853d1dbb Just rename a couple of local variables. 2014-06-10 10:58:02 +02:00
Daniele Alessandri 1da827c0ae [tests] Use annotations to specify expected Redis server version.
This change targets only method annotations and aims to make the body
of tests more readable using the @requiresRedisVersion annotation.

Tests using this annotation requires to be assigned the "connected"
group of tests because they create a connection to the Redis instance
specified in phpunit.xml to fetch its the version.

This is a quick example of how this annotation can be used:

    /**
     * @group connected
     * @requiresRedisVersion >= 2.8.9
     */
    public function testExecutedOnlyWithMatchingRedisVersion()
    {
    }

Future improvements (currently not needed) include:

  * Same annotation working on a class-level (but still applied only
    to test methods with an explicitly assigned @group connected).

  * Ability to specify a version range.
2014-06-09 17:42:33 +02:00
Daniele Alessandri aa8fd5cea4 Update CHANGELOG. 2014-06-09 14:50:09 +02:00
Daniele Alessandri 8d353cc223 New command: ZREMRANGEBYLEX (Redis 2.8.9).
Closes #177.
2014-06-09 14:50:02 +02:00
Daniele Alessandri 0a580c9d1b New command: ZRANGEBYLEX (Redis 2.8.9).
Closes #175.
2014-06-09 14:49:54 +02:00
Daniele Alessandri 58d1cc12dc New command: ZLEXCOUNT (Redis 2.8.9).
Closes #176.
2014-06-09 14:49:27 +02:00
Daniele Alessandri a8862f0fdd Update CHANGELOG.
[ci skip]
2014-06-03 16:57:22 +02:00
Daniele Alessandri e148dc831a Fix parsing of the output of CLUSTER NODES to fetch slots map.
There was an error in presence of slaves in the cluster configuration,
see #165 for reference.
2014-06-03 16:54:00 +02:00
Daniele Alessandri c4b592298c Skip integration tests for HyperLogLog commands on Redis < 2.8.9.
Caught this by running the test suite on TravisCI which still runs an
older version of Redis 2.8. We should find a more decent way to handle
differences in profiles when commands get added between patch releases
of Redis, our custom skip method will do for now.
2014-05-30 12:22:12 +02:00
Daniele Alessandri 0e3b105959 New commands: PFADD, PFCOUNT, PFMERGE (Redis 2.8.9).
Backported from the master branch.

Many thanks to @rubensayshi for his initial commits on this in #163,
but I had to start from scratch for the master branch first.
2014-05-30 12:01:17 +02:00
Daniele Alessandri 749bbfe0e3 Update CHANGELOG.
[ci skip]

Meh
2014-05-30 11:41:20 +02:00
Daniele Alessandri c2ae1c612e Merge remote-tracking branch 'github/pr/159' into v0.8 2014-05-28 12:58:03 +02:00
Daniele Alessandri a85f89e2c7 Merge remote-tracking branch 'github/pr/170' into v0.8 2014-05-28 12:48:53 +02:00
thbourlove dbb802f0f1 use phpunit 4.x instead of 3.x 2014-05-26 22:57:40 +08:00
thbourlove 64b79511bd isntall requirement with a better way 2014-05-21 17:43:32 +08:00
thbourlove 5a9e747b01 add phpunit as dev requirement 2014-05-21 17:21:16 +08:00
Alexey Kupershtokh cb0fbd01b4 Enable INCRBYFLOAT in cluster mode 2014-03-28 22:12:43 +07:00
Daniele Alessandri 83df2ef11c Update CHANGELOG and README. 2014-03-25 15:02:22 +01:00
Daniele Alessandri c94e5d3a10 Merge remote-tracking branch 'thbourlove/v0.8' into v0.8 2014-03-25 14:51:22 +01:00
Daniele Alessandri c90fbfc0ff Failures for PHP 5.6 on Travis CI are not allowed anymore. 2014-03-25 14:31:04 +01:00
Daniele Alessandri 21d6f0e7c6 Update CHANGELOG. 2014-03-25 14:29:05 +01:00
Daniele Alessandri f4a950b275 Merge remote-tracking branch 'thbourlove/server_info' into v0.8 2014-03-25 14:26:10 +01:00
thbourlove 5ec51c899f original array format 2014-03-15 01:16:38 +08:00
thbourlove e1d700dd0c fix #154 2014-03-15 00:59:55 +08:00
Daniele Alessandri 809f76a7b3 Fix implementation for hash tags extraction from keys.
We now fully comply with the specifications defined by Redis.
2014-02-11 19:10:09 +01:00
Daniele Alessandri ab6cbe7e10 Add PHP 5.6 to the build matix of Travis-CI.
Builds are allowed to fail for now as PHP 5.6 is still in alpha stage.
2014-02-11 16:34:40 +01:00
Daniele Alessandri 80bcb9f475 Add HHVM to the build matrix of Travis-CI for v0.8.
Travis-CI recently provisioned virtual machines with HHVM 2.4.0 which
fixed the issues preventing Predis v0.8 from working correctly.
2014-02-11 16:29:44 +01:00
Daniele Alessandri c8e671a42a [HHVM] Work around a bug in class_exists() with non-string values. 2014-02-11 16:24:09 +01:00
Daniele Alessandri 838e6af403 [tests] Adapt for testing with HHVM. 2014-02-11 15:58:38 +01:00
Daniele Alessandri ba2ffb612a Add support for key hash tags in redis-cluster (Redis 3.0.0b1).
Multi-keys operations are not allowed even when keys generate the same
hash but this will probably be supported in later betas of Redis which
means we will basically end up reusing the whole strategy used for
client-side sharding.
2014-02-11 15:33:07 +01:00
Daniele Alessandri fdaf73987b Update CHANGELOG. 2014-02-11 12:44:00 +01:00
Daniele Alessandri 50f8922df2 Update README about HHVM compatibility. 2014-02-11 12:44:00 +01:00
Daniele Alessandri 60733eb5c9 Make Predis v0.8 compatible with HHVM 2.4.0.
Since we cannot rely on the PHP version to detect the availability of
socket_import_stream(), we switched to function_exists(). As an added
bonus, using function_exists() is twice faster.
2014-02-11 12:44:00 +01:00
Daniele Alessandri a13dd4e180 Back to development. 2014-02-11 12:44:00 +01:00
thbourlove 1678e486b1 use 2.8 as the default server version 2014-01-25 10:55:47 +08:00
Daniele Alessandri 5f2eea628e Update CHANGELOG and bump VERSION. 2014-01-16 15:10:29 +01:00
Daniele Alessandri 0f01b89d44 Bump year in LICENSE.
[ci skip]
2014-01-10 17:46:58 +01:00
Daniele Alessandri 9d0201ed9e [bin] Add missing executable in the repository for new script. 2014-01-08 17:38:22 +01:00
Daniele Alessandri 0cead0245d [bin] Add helper script to automate PEAR package creation.
Note that this script takes care of customizing `phpunit.xml.dist` so
that tests can be run as soon as the PEAR package has been installed
as requested in #126.
2014-01-08 17:30:54 +01:00
Daniele Alessandri 0a4eed1dff [tests] Use Predis\Autoload from include_path when not in repository.
This is useful only for running the test suite after installing the
PEAR package, see #126 for reference.
2014-01-08 17:22:15 +01:00
Daniele Alessandri d2debfd43e Apply some more minor phpdoc fixes. 2013-12-22 17:51:18 +01:00
Daniele Alessandri 9a1884d960 Merge branch 'v0.8-backport-fixes' into v0.8 2013-12-22 17:35:36 +01:00
Daniele Alessandri a6dd685f8d Run php-cs against source code. 2013-12-22 17:34:50 +01:00
Daniele Alessandri f499d5695a Explicitly return null value. 2013-12-22 17:34:50 +01:00
Daniele Alessandri 23c4b95a0b Remove useless return statement.
Backported from da8a203 (master).
2013-12-22 17:34:50 +01:00
Daniele Alessandri 80cb31514a [tests] Remove unused "use" directive. 2013-12-22 17:34:50 +01:00
Daniele Alessandri 6306509086 [tests] Fix a couple of innocuous glitches. 2013-12-22 17:34:50 +01:00
Daniele Alessandri 2aab474670 Apply more phpdoc fixes.
Backported from 6a577a0 (master).
2013-12-22 17:34:50 +01:00
Daniele Alessandri d628c0f23b [tests] Fix a couple of glitches in our base PHPUnit classes. 2013-12-22 17:34:50 +01:00
Daniele Alessandri ad92618003 [tests] No need to reassign $exception.
Backported from 728d9dc (master).
2013-12-22 17:34:49 +01:00
Daniele Alessandri 0fb7674762 Fix phpdocs for fluent interfaces.
Backported from a092f6d (master).

[ci skip]
2013-12-22 17:34:49 +01:00
Daniele Alessandri e778baa491 Make Pipeline::executeCommand() return $this for fluent interface.
Backported from 3befbb3 (master).
2013-12-22 17:34:49 +01:00
Daniele Alessandri d88a280977 Make PhpiredisConnection::getAddress() static and protected.
This method is mostly an utility method which is the main reason for
it bein static, but can be useful to have it available when extending
this class.

This change was inspired by b9b899e (master) even though the original
commit is meant to fix an issue not affecting v0.8.
2013-12-22 17:34:49 +01:00
Daniele Alessandri eb6cbef4e7 Remove erroneous duplication of INFO in Redis profiles.
This oversight didn't end up in a blatant bug only because the correct
handler definition for INFO replaced the old class in the profile.

Backported from 5065541 (master).
2013-12-22 17:34:49 +01:00
Daniele Alessandri 2aa771004d Add a note about missing break in switch statement.
[ci skip]
2013-12-22 17:34:49 +01:00
Daniele Alessandri 43bf3096a0 Fix some errors in phpdocs.
Backported from 25cd430 (master).
2013-12-22 17:34:40 +01:00
Daniele Alessandri 317fa42891 Improve handling of -MOVED and -ASK responses.
This change improves code reusing and simplifies the internals of
our redis-cluster connection backend.
2013-12-21 12:00:44 +01:00
Daniele Alessandri b45f87caf2 [tests] Add $message as third parameter for our custom assertions. 2013-12-19 11:51:45 +01:00
Daniele Alessandri b7cba07014 Update README.
[ci skip]
2013-12-18 16:09:01 +01:00
Daniele Alessandri 56627f30f8 Run php-cs against test suite. 2013-12-17 13:00:39 +01:00
Daniele Alessandri 9d4524cae7 Run php-cs against codebase. 2013-12-17 12:59:31 +01:00
Daniele Alessandri fd1c6f244f Apply minor change in command serialization. 2013-12-16 18:52:11 +01:00
Daniele Alessandri aa4d0a05ac Update README.
[ci skip]
2013-12-16 17:05:22 +01:00
Daniele Alessandri d45b5f7ec8 Update FAQ.
[ci skip]
2013-12-16 16:59:23 +01:00
Daniele Alessandri 87ca5a1a49 Update testing README.
[ci skip]
2013-12-16 11:45:50 +01:00
Daniele Alessandri e5ec950a11 [bin] Drop extension from scripts in the bin directory. 2013-12-16 11:26:45 +01:00
Daniele Alessandri 3f2c2b9031 [bin] Handle duplicate class names or aliases in same namespace.
This condition can occur when merging multiple class files (with their
own independent "use" directives) in one big file: when imported class
names or aliases clashes, the script now tries to automatically rename
them by defining a new alias in the "use" directive and renaming the
occurences in the buffer containing the class code.

This fix is naive at best, but seems to work fine so we will just live
with it for now.
2013-12-16 11:26:34 +01:00
Daniele Alessandri 88ea08fe83 [bin] Fix broken rendering of "use ... as ..." in output. 2013-12-16 11:26:21 +01:00
Daniele Alessandri bd48225edc Set the test suite to run against Redis 2.8 on Travis CI.
Please note that the distributed phpunit.xml.dist still targets Redis
2.6 to match the default server profile used by Predis v0.8.
2013-12-14 20:47:23 +01:00
Daniele Alessandri ea9809d4e8 Use path to differentiate persistent TCP streams.
Closes #139.

Backported from master (commit e7f2d28).
2013-12-14 14:21:47 +01:00
Daniele Alessandri 09cb6677e2 Improve URI parsing for connection parameters.
Using PHP's "parse_str()" to parse the query string is slightly more
efficient then our own code especially when the number of fields in
the query string grows, with the additional benefit of supporting
arrays for values when brackets are present in fieldnames.

So after this commit, providing this URI string:

  $string = 'tcp://127.0.0.1?metavars[]=foo&metavars[]=hoge';

Is equivalent to providing the following named array:

  $array = [
    'scheme' => 'tcp',
    'host' => '127.0.0.1',
    'metavars' => ['foo', 'hoge'],
  ];

Other improvements are that parsing does not break when the value of a
field contains one or more "=" and empty or incomplete "key=value"
pairs result in an empty string for "key".
2013-12-11 13:10:05 +01:00
Daniele Alessandri c32f6c7152 Add "path" in phpdoc of connections supporting UNIX domain sockets. 2013-12-11 12:21:09 +01:00
Daniele Alessandri 78fd475747 Apply another minor fix to README.
[ci skip]
2013-12-10 22:16:09 +01:00
Daniele Alessandri 86a4343e59 Fix README.
[ci skip]
2013-12-10 22:01:42 +01:00
Daniele Alessandri ecf1cc07e5 Tweak CONTRIBUTING.md.
[ci skip]
2013-12-09 12:22:42 +01:00
Daniele Alessandri 2fc1ea88cd Last minor tweaks in the README.
Hope I won't need to touch README in v0.8 for a while.

[ci skip]
2013-12-09 12:03:47 +01:00
Daniele Alessandri 72e9187662 More README tweaks.
[ci skip]
2013-12-09 11:53:58 +01:00
Daniele Alessandri 98410bbcaa Pad headers lines in CHANGELOG.
[ci skip]
2013-12-09 11:47:57 +01:00
Daniele Alessandri 5ea1d6fe92 Backport rewrite of README from master (v0.9) with necessary changes.
[ci skip]
2013-12-09 11:42:09 +01:00
Daniele Alessandri 73780da52d Backport improvements for redis-cluster from master (v0.9).
List of changes:

  - The cluster connection sends an ASKING command before retrying
    a command on a different node when Redis returns a -ASK response.
  - The cluster connection can be initialized with a partial list of
    nodes, the full slots map will be fetched from Redis itself using
    the CLUSTER NODES command.
  - The slots map can be optionally retrieved from Redis if the server
    returns a -MOVE response, otherwise only the interested slot will
    be permanently reassigned to the new target node.
  - It is possible to specify a set of common parameters applied to
    connections created on the fly when not part of the current pool
    upon -MOVE and -ASK responses returned by Redis for redirections.
  - $cluster->connect() connects to a random connection in the pool
    instead of forcing the connect operation on all the connections.
2013-12-08 15:57:15 +01:00
Daniele Alessandri 381023ad8f Make sure raw commands IDs are normalized to uppercase. 2013-12-02 11:44:40 +01:00
Daniele Alessandri c3c819cc9c [tests] Minor tweak for timeouts. 2013-12-02 11:44:37 +01:00
Daniele Alessandri 987f27556f Merge branch 'backport/test-suite-enhancements' into v0.8 2013-12-01 15:21:05 +01:00
Daniele Alessandri a9b42924e5 [tests] Improve the basic framework of our test suite.
We now have a base test case class for Predis (namely PredisTestCase)
grouping various commonly used utility methods shared by all of the
tests in the suite, greatly improving reusability.

NOTE: Backported from v0.9-dev.
2013-12-01 15:19:51 +01:00
Daniele Alessandri e4e2238f34 Merge branch 'backport/tests-command-constraint' into v0.8 2013-12-01 14:06:31 +01:00
Daniele Alessandri e1f6c8644f [tests] Normalize casing of command identifiers in constraint.
We do not care much about the casing of command IDs in our constraint,
so it makes no difference for use if it is "SET" or "set".
2013-12-01 14:01:37 +01:00
Daniele Alessandri d16a1c8705 [tests] Add a PHPUnit constraint to verify commands.
Commands are verified by checking their IDs and optionally arguments.
Passing a command instance is only a shortcut to set the expected ID
and arguments, commands are never compared for identity but always for
equivalence.
2013-12-01 13:59:27 +01:00
Daniele Alessandri 32aff28ef5 [tests] Fix stupid change. 2013-11-30 14:58:00 +01:00
Daniele Alessandri eb906bb29c Update CHANGELOG.
[ci skip]
2013-11-30 14:53:08 +01:00
Daniele Alessandri 33ef043b8e [tests] Update tests for Predis\Command\AbstractCommand. 2013-11-30 14:48:26 +01:00
Daniele Alessandri 973c9a7031 Add new command class useful to execute "raw" Redis commands.
By raw we mean that input arguments are not filtered and responses are
not parsed, which means arguments must follow the command signature as
defined by Redis and complex responses are left untouched.

When instantiating an instance of `Predis\Command\RawCommand` you must
pass at least the command ID. You can pass further arguments in the
array or you can just set them later with `RawCommand::setArguments()`
but you cannot modify the command ID once instantiated.

  $command = new Predis\Command\RawCommand(['SET', 'foo', 'bar']);
  $response = $client->executeCommand($command);

While higher level abstractions built upon `Predis\Client` should just
use commands created by the profile in use, inner parts of the library
might use raw commands to provide certain functionalities making sure
that input and output of commands are always consistent, independent
of the profile.

NOTE: Backported from v0.9.
2013-11-30 14:37:15 +01:00
Daniele Alessandri db55fae961 Fix phpdoc.
[ci skip]
2013-11-30 14:17:59 +01:00
Daniele Alessandri 5192005862 [tests] Fix tests on PHP 5.3.
Stupid PHP 5.3.
2013-11-29 23:41:05 +01:00
Daniele Alessandri cdc6075b01 [tests] Improve tests of TTL. 2013-11-29 23:33:17 +01:00
Daniele Alessandri 623d1cc1a2 [tests] Improve base command test case. 2013-11-29 23:28:23 +01:00
Daniele Alessandri d53069f096 [tests] Add missing tests for SETNX.
For some obscure reason Predis/Command/StringSetPreserve had no tests.
2013-11-29 22:53:20 +01:00
Daniele Alessandri 8565a42605 [tests] Add "count" argument to SRANDMEMBER (since Redis >= 2.6). 2013-11-29 22:46:32 +01:00
Daniele Alessandri aac811f897 Fix phpdoc.
[ci skip]
2013-11-29 11:14:14 +01:00
Daniele Alessandri 4e664c6c90 Address #133 by reusing our own methods.
The main reason behind that code duplication was performance related
as we tried to reduce method calls when possible, even at the cost of
falling into the realm of early optimizations. Apparently we just lose
~400 req/sec on a 21000 req/sec basis ("SET foo bar") using PHP 5.5.3
(packaged by Ubuntu 13.10) on an Intel Q6600, so we will most likely
stick with this change for the sake of best practices.

Backported from v0.9-dev.
2013-11-22 15:17:50 +01:00
Daniele Alessandri b1a51b2ef9 Update CHANGELOG. 2013-11-09 20:00:33 +01:00
Daniele Alessandri de02c81398 Deprecate Predis\Client::multiExec().
This method will be replaced by Predis\Client::transaction() in the
next major release of Predis.
2013-11-09 20:00:29 +01:00
337 changed files with 4420 additions and 2155 deletions
+11 -1
View File
@@ -3,6 +3,8 @@ php:
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
branches:
except:
- v0.5
@@ -10,4 +12,12 @@ branches:
- php5.2_backport
- documentation
services: redis-server
script: phpunit -c phpunit.xml.travisci
before_script:
- composer self-update
- composer install --no-interaction --prefer-source --dev
script:
- vendor/bin/phpunit -c phpunit.xml.travisci
matrix:
allow_failures:
- php: hhvm
fast_finish: true
+86 -26
View File
@@ -1,8 +1,43 @@
v0.8.5 (2013-xx-xx)
===============================================================================
v0.8.6 (2014-07-15)
================================================================================
- Added `2.8` in the server profiles aliases list for the upcoming Redis 2.8.
`2.6` is still the default server profile. `dev` now targets Redis 3.0.
- Redis 2.8 is not the default server profile as there are no changes that would
break compatibility with previous releases.
- Added `PFADD`, `PFCOUNT`, `PFMERGE` to the server profile for Redis 2.8 for
handling the HyperLogLog data structure introduced in Redis 2.8.9.
- Added `ZLEXCOUNT`, `ZRANGEBYLEX`, `ZREMRANGEBYLEX` to the server profile for
Redis 2.8 for handling lexicographic operations on members of sorted sets.
- Added support for key hash tags when using redis-cluster (Redis 3.0.0b1).
- __FIX__: minor tweaks to make Predis compatible with HHVM >= 2.4.0.
- __FIX__: responses to `INFO` are now properly parsed and will not break when
redis sentinel is being used (ISSUE #154).
- __FIX__: added missing support for `INCRBYFLOAT` in cluster and replication
configurations (ISSUE #159).
- __FIX__: fix parsing of the output of `CLUSTER NODES` to fetch the slots map
from a node when redis-cluster has slaves in its configuration (ISSUE #165).
- __FIX__: prevent a stack overflow when iterating over large Redis collections
using our abstraction for cursor-based iterators (ISSUE #182).
- __FIX__: properly discards transactions when the server immediately returns an
error response (e.g. -OOM or -ERR on invalid arguments for a command) instead
of a +QUEUED response (ISSUE #187).
- Upgraded to PHPUnit 4.* for the test suite.
v0.8.5 (2014-01-16)
================================================================================
- Added `2.8` in the server profiles aliases list for Redis 2.8. `2.6` is still
the default server profile and `dev` now targets Redis 3.0.
- Added `SCAN`, `SSCAN`, `ZSCAN`, `HSCAN` to the server profile for Redis 2.8.
@@ -14,14 +49,39 @@ v0.8.5 (2013-xx-xx)
- hashes (cursor-based iterator using `HSCAN`)
- lists (plain iterator using `LRANGE`)
- `Predis\Client::pubSubLoop()` should now be used instead of the deprecated
`Predis\Client::pubSub()` (which still works like usual to avoid B/C breaks).
`Predis\Client::pubSub()` will change in the next major version of Predis to
support the new PUBSUB command recently added in Redis 2.8.
- It is now possible to execute "raw commands" using `Predis\Command\RawCommand`
and a variable list of command arguments. Input arguments are not filtered and
responses are not parsed, which means arguments must follow the signature of
the command as defined by Redis and complex responses are left untouched.
- URI parsing for connection parameters has been improved and has slightly less
overhead when the number of fields in the querystring grows. New features are:
- Parsing does not break when value of a field contains one or more "=".
- Repeated fieldnames using [] produce an array of values.
- Empty or incomplete "key=value" pairs result in an empty string for "key".
- Various improvements and fixes to the redis-cluster connection backend:
- __FIX__: the `ASKING` command is sent upon -ASK redirections.
- An updated slots-map can be fetched from nodes using the `CLUSTER NODES`
command. By default this is a manual operation but can be enabled to get
automatically done upon -MOVED redirections.
- It is possible to specify a common set of connection parameters that are
applied to connections created on the fly upon redirections to nodes not
part of the initial pool.
- List of deprecated methods:
- `Predis\Client::multiExec()`: superseded by `Predis\Client::transaction()`
and to be removed in the next major release.
- `Predis\Client::pubSub()`: superseded by `Predis\Client::pubSubLoop()` and
to be removed in the next major release. This change was needed due to the
recently introduced `PUBSUB` command in Redis 2.8.
v0.8.4 (2013-07-27)
===============================================================================
================================================================================
- Added `DUMP` and `RESTORE` to the server profile for Redis 2.6.
@@ -45,7 +105,7 @@ v0.8.4 (2013-07-27)
v0.8.3 (2013-02-18)
===============================================================================
================================================================================
- Added `CLIENT SETNAME` and `CLIENT GETNAME` (ISSUE #102).
@@ -74,7 +134,7 @@ v0.8.3 (2013-02-18)
v0.8.2 (2013-02-03)
===============================================================================
================================================================================
- Added `Predis\Session\SessionHandler` to make it easy to store PHP sessions
on Redis using Predis. Please note that this class needs either PHP >= 5.4.0
@@ -89,7 +149,7 @@ v0.8.2 (2013-02-03)
v0.8.1 (2013-01-19)
===============================================================================
================================================================================
- The `connections` client option can now accept a callable object returning
an instance of `Predis\Connection\ConnectionFactoryInterface`.
@@ -121,7 +181,7 @@ v0.8.1 (2013-01-19)
v0.8.0 (2012-10-23)
===============================================================================
================================================================================
- The default server profile for Redis is now `2.6`.
@@ -186,7 +246,7 @@ v0.8.0 (2012-10-23)
v0.7.3 (2012-06-01)
===============================================================================
================================================================================
- New commands available in the Redis v2.6 profile (dev): `BITOP`, `BITCOUNT`.
@@ -208,7 +268,7 @@ v0.7.3 (2012-06-01)
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.
@@ -226,7 +286,7 @@ v0.7.2 (2012-04-01)
v0.7.1 (2011-12-27)
===============================================================================
================================================================================
- The PEAR channel on PearHub has been deprecated in favour of `pear.nrk.io`.
@@ -245,7 +305,7 @@ v0.7.1 (2011-12-27)
v0.7.0 (2011-12-11)
===============================================================================
================================================================================
- Predis now adheres to the PSR-0 standard which means that there is no more a
single file holding all the classes of the library, but multiple files (one
@@ -319,7 +379,7 @@ v0.7.0 (2011-12-11)
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
@@ -358,7 +418,7 @@ v0.6.6 (2011-04-01)
v0.6.5 (2011-02-12)
===============================================================================
================================================================================
- __FIX__: due to an untested internal change introduced in v0.6.4, a wrong
handling of bulk reads of zero-length values was producing protocol
@@ -366,7 +426,7 @@ v0.6.5 (2011-02-12)
v0.6.4 (2011-02-12)
===============================================================================
================================================================================
- Various performance improvements (15% ~ 25%) especially when dealing with
long multibulk replies or when using clustered connections.
@@ -380,7 +440,7 @@ v0.6.4 (2011-02-12)
v0.6.3 (2011-01-01)
===============================================================================
================================================================================
- New commands available in the Redis v2.2 profile (dev):
- Strings: `SETRANGE`, `GETRANGE`, `SETBIT`, `GETBIT`
@@ -394,7 +454,7 @@ v0.6.3 (2011-01-01)
v0.6.2 (2010-11-28)
===============================================================================
================================================================================
- Minor internal improvements and clean ups.
@@ -419,7 +479,7 @@ v0.6.2 (2010-11-28)
v0.6.1 (2010-07-11)
===============================================================================
================================================================================
- Minor internal improvements and clean ups.
@@ -461,7 +521,7 @@ v0.6.1 (2010-07-11)
v0.6.0 (2010-05-24)
===============================================================================
================================================================================
- Switched to the new multi-bulk request protocol for all of the commands
in the Redis 1.2 and Redis 2.0 profiles. Inline and bulk requests are now
@@ -576,7 +636,7 @@ v0.6.0 (2010-05-24)
v0.5.1 (2010-01-23)
===============================================================================
================================================================================
* `RPOPLPUSH` has been changed from bulk command to inline command in Redis
1.2.1, so `ListPopLastPushHead` now extends `InlineCommand`. The old behavior
@@ -591,5 +651,5 @@ v0.5.1 (2010-01-23)
v0.5.0 (2010-01-09)
===============================================================================
================================================================================
* First versioned release of Predis
+19 -15
View File
@@ -1,18 +1,18 @@
## Filing bug reports ##
Bugs or feature requests can be posted online on the [GitHub issues](http://github.com/nrk/predis/issues)
Bugs or feature requests can be posted 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)).
2. version of Redis (check `redis_version` 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,
__Think about it__: we do not have a crystal ball and cannot predict things or 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
@@ -22,19 +22,23 @@ 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
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 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.
The recommended way to contribute to Predis is to fork the project on GitHub, create topic branches
on your newly created repository to fix bugs or add new features (possibly with tests covering your
modifications) and then open a 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.
We always aim for consistency in our code base so you should follow basic coding rules as defined by
[PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
and [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
and stick with the conventions used in Predis to name classes and interfaces. Indentation should be
done with 4 spaces and code should be wrapped at 100 columns (please try to stay within this limit
even if the above mentioned official coding guidelines set the soft limit to 120 columns).
Please also follow some basic [commit guidelines](http://git-scm.com/book/ch5-2.html#Commit-Guidelines)
before opening pull requests.
Please follow these [commit guidelines](http://git-scm.com/book/ch5-2.html#Commit-Guidelines) when
committing your code to Git and always write a meaningful (not necessarily extended) description of
your changes before opening pull requests.
+108 -99
View File
@@ -1,41 +1,39 @@
# Some frequently asked questions about Predis #
_________________________________________________
________________________________________________
### What is the point of Predis? ###
The main point of Predis is about offering a highly customizable client for Redis that can be easily
extended by developers while still being reasonabily fast. With Predis you can swap almost any class
used internally with your own custom implementation: you can build connection classes, or new
distribution strategies for client-side sharding, or class handlers to replace existing commands or
add new ones. All of this can be achieved without messing with the source code of the library and
directly in your own application. Given the fast pace at which Redis is developed and adds new
features, this can be a great asset that allows you to add new and still missing features or commands,
or change the behaviour of the library without the need to break your dependencies in production code
(well, at least to some degree).
The main point of Predis is about offering a highly customizable and extensible client for Redis,
that can be easily extended by developers while still being reasonabily fast. With Predis you can
swap almost any class with your own custom implementation: you can have custom connection classes,
new distribution strategies for client-side sharding, or handlers to replace or add Redis commands.
All of this can be achieved without messing with the source code of the library and directly in your
own application. Given the fast pace at which Redis is developed and adds new features, this can be
a great asset since it allows developers to add new and still missing features or commands or change
the standard behaviour of the library without the need to break dependencies in production code (at
least to some degree).
### Does Predis support UNIX domain sockets and persistent connections? ###
Yes. Obviously, persistent connections actually work when using PHP configured as a persistent process that
gets recycled between requests (see [PHP-FPM](http://php-fpm.org/)).
Yes. Obviously persistent connections actually work only when using PHP configured as a persistent
process reused by the web server (see [PHP-FPM](http://php-fpm.org)).
### Does Predis support transparent (de)serialization of values? ###
No, and it will not ever do that for you by default. The reason behind this decision is that serialization
is usually something that developers prefer to customize depending on their needs and can not be easily
generalized when using Redis because of the many possible access patterns for the data. This does not
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.
No and it will not ever do that by default. The reason behind this decision is that serialization is
usually something that developers prefer to customize depending on their needs and can not be easily
generalized when using Redis because of the many possible access patterns for your data. This does
not mean that it is impossible to have such a feature since you can leverage the extensibility of
this library to define your own serialization-aware commands. You can find more details about how to
do that [on this issue](http://github.com/nrk/predis/issues/29#issuecomment-1202624).
### 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:
Explicitly connecting to Redis is usually not needed since the client initializes connections lazily
only when they are needed. Admittedly, this behavior can be inconvenient in certain scenarios when
you absolutely need to perform an upfront check to determine if the server is up and running and
eventually catch exceptions on failures. Forcing the client to open the underlying connection can be
done by invoking `Predis\Client::connect()`:
```php
$client = new Predis\Client();
@@ -49,112 +47,123 @@ try {
$client->info();
```
### How Predis abstracts Redis commands? ###
### 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:
The approach used to implement Redis commands is quite simple: by default each command follows the
same signature as defined on the [Redis documentation](http://redis.io/commands) which makes things
pretty easy if you already know how Redis works or 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. Commands such as [`RPUSH`](http://redis.io/commands/rpush) and
[`HMSET`](http://redis.io/commands/hmset) are great examples:
```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->rpush('my:list', 'value1', 'value2', 'value3'); // plain method arguments
$client->rpush('my:list', ['value1', 'value2', 'value3']); // 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
$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // plain method arguments
$client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // 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](tests/Predis/Command/KeySortTest.php#L56-77).
An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which modifiers are passed
[using a named array](tests/Predis/Command/KeySortTest.php#L54-L75).
# Frequently asked questions about performances #
# Speaking 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):
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 with a simple test that uses a single client and is executed by PHP 5.5.6
against a local instance of Redis 2.8 that runs under Ubuntu 13.10 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 *_.
```
21000 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)?
How does it compare with [__phpredis__](http://github.com/nicolasff/phpredis), a nice C extension
providing an efficient client for Redis?
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 *"".
```
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.
Wow __phpredis__ seems 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 using a
for-loop on 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?
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 physical network by connecting to remote Redis
instances?
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 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 *".
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.
There you go, you get almost the same average numbers and the reason is simple: network latency is a
real performance killer and you cannot do (almost) anything about that. As a disclaimer, remember
that we are measuring the overhead of client libraries implementations and the effects of network
round-trip times, so we are not really measuring how fast Redis is. Redis shines 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 responses are still worse ###
### I am convinced, but performances for multi-bulk replies (e.g. _KEYS *_) are still worse ###
Fair enough, but there is an option available if you need even more speed and consists on installing
__[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) and let the
client use it. __phpiredis__ is another C extension that wraps __hiredis__ (the official C client
library for Redis) with a thin layer exposing its features to PHP. You can then choose between two
different connection classes:
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 can choose between
two different connection backend classes: `Predis\Connection\PhpiredisConnection` (it depends on the
`socket` extension) and `Predis\Connection\PhpiredisStreamConnection` (it uses PHP's native streams).
You will now get the benefits of a faster protocol parser just by adding a couple of lines of code:
- `Predis\Connection\PhpiredisStreamConnection` (using native PHP streams).
- `Predis\Connection\PhpiredisConnection` (requires `ext-socket`).
You will now get the benefits of a faster protocol serializer and parser just by adding a couple of
lines of code:
```php
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array(
'tcp' => 'Predis\Connection\PhpiredisConnection',
'unix' => 'Predis\Connection\PhpiredisConnection',
),
'tcp' => 'Predis\Connection\PhpiredisStreamConnection',
'unix' => '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:
Dead simple. Nothing changes in the way you use the library in your application. So how fast is it
our basic benchmark script now? There are not much improvements for inline or short bulk responses
like the ones returned by `SET` and `GET`, but the speed for parsing multi-bulk responses is now on
par with phpredis:
Using Predis with a phpiredis-based connection to fetch 30000 keys using _KEYS *_:
```
Fatching 30000 keys with _KEYS *_ using Predis paired with phpiredis::
0.035 seconds from a local Redis instance
0.047 seconds from a remote Redis instance
0.035 seconds from a local Redis instance
0.047 seconds from a remote Redis instance
```
### If I need an extension to get better performances, why not using phpredis? ###
### 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.
Good question. Generically speaking if you need absolute uber-speed using Redis on the localhost 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.8 and the current development version), then
using __phpredis__ makes absolutely sense. Otherwise, Predis is perfect for the job and by adding
__phpiredis__ you can get a nice speed bump almost for free.
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009-2013 Daniele Alessandri
Copyright (c) 2009-2014 Daniele Alessandri
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
+206 -141
View File
@@ -1,166 +1,229 @@
# Predis #
[![Latest Stable Version](https://poser.pugx.org/predis/predis/v/stable.png)](https://packagist.org/packages/predis/predis)
[![Total Downloads](https://poser.pugx.org/predis/predis/downloads.png)](https://packagist.org/packages/predis/predis)
Predis is a flexible and feature-complete PHP (>= 5.3) client library for the Redis key-value store.
Predis is a flexible and feature-complete [Redis](http://redis.io) client library for PHP >= 5.3.
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.
By default Predis does not require any additional C extension, but it can be optionally paired with
[phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of serializing and parsing the
Redis protocol. An asynchronous implementation of the client, albeit experimental, is also available
through [Predis\Async](https://github.com/nrk/predis-async).
For a list of frequently asked questions about Predis see our [FAQ](FAQ.md).
More details are available on the [official wiki](http://wiki.github.com/nrk/predis) of the project.
Predis can be used with [HHVM](http://www.hhvm.com) >= 2.4.0, but there are no guarantees you will
not run into unexpected issues (especially with the JIT compiler enabled via `Eval.Jit = true`) due
to HHVM being still under heavy development, thus unstable and not yet 100% compatible with PHP.
More details about the project can be found in our [frequently asked questions](FAQ.md) section or
on the online [wiki](https://github.com/nrk/predis/wiki).
## Main features ##
- Wide range of Redis versions supported (from __1.2__ to __2.8__ and unstable) using server profiles.
- Wide range of Redis versions supported (from __1.2__ to __2.8__ and unstable) using profiles.
- Clustering via client-side sharding using consistent hashing or custom distributors.
- Smart support for [redis-cluster](http://redis.io/topics/cluster-spec) (Redis >= 3.0).
- Client-side sharding via consistent hashing or custom distribution strategies.
- Support for master / slave replication configurations (write on master, read from slaves).
- Transparent key prefixing strategy capable of handling any command known that has keys in its arguments.
- Command pipelining on single and aggregated connections.
- Abstraction for Redis transactions (Redis >= 2.0) with support for CAS operations (Redis >= 2.2).
- Abstraction for Lua scripting (Redis >= 2.6) capable of automatically switching between `EVAL` and `EVALSHA`.
- Abstraction based on PHP iterators for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8).
- 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 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.
- Support for master-slave replication configurations (write on master, read from slaves).
- Transparent key prefixing for all Redis commands.
- Command pipelining (works on both single and aggregate connections).
- Abstraction for Redis transactions (Redis >= 2.0) supporting CAS operations (Redis >= 2.2).
- Abstraction for Lua scripting (Redis >= 2.6) with automatic switching between `EVALSHA` or `EVAL`.
- Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators.
- Connections to Redis are established lazily by the client upon the first command.
- Support for both TCP/IP and UNIX domain sockets and persistent connections.
- Support for [Webdis](http://webd.is) (both `ext-curl` and `ext-phpiredis` are needed).
- Support for custom connection classes for providing different network or protocol backends.
- Flexible system for defining and registering custom sets of supported commands or profiles.
## How to use Predis ##
Predis is available on [Packagist](http://packagist.org/packages/predis/predis) for an easy installation
using [Composer](http://packagist.org/about-composer). Composer helps you manage dependencies for your
projects and libraries without much hassle which makes it the preferred way to get up and running with
new applications. Alternatively, the library is available on our [own PEAR channel](http://pear.nrk.io)
for a more traditional installation via PEAR. Zip and tar.gz archives are also downloadable from GitHub
by browsing the list of [tagged releases](http://github.com/nrk/predis/tags).
Predis is available on [Packagist](http://packagist.org/packages/predis/predis) which allows a quick
installation using [Composer](http://packagist.org/about-composer). Alternatively, the library can
be found on our [own PEAR channel](http://pear.nrk.io) for a more traditional installation via PEAR.
Ultimately, archives of each release are [available on GitHub](https://github.com/nrk/predis/tags).
### Loading the library ###
Predis relies on the autoloading features of PHP to load its files when needed and complies with the
[PSR-0 standard](http://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) which makes it
compatible with most of the major frameworks and libraries. Autoloading in your application is handled
automatically when managing the dependencies with Composer, but you can also leverage its own autoloader
class if you are going to use it in a project or script without any PSR-0 compliant autoloading facility:
[PSR-0 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) which makes
it compatible with most PHP frameworks. Autoloading is handled automatically when dependencies are
managed using Composer, but you can also leverage its own autoloader if you are going to use it in a
project or script without any PSR-0 compliant autoloading facility:
```php
<?php
// prepend a base path if Predis is not present in your "include_path".
// Prepend a base path if Predis is not available in your "include_path".
require 'Predis/Autoloader.php';
Predis\Autoloader::register();
```
It is possible to create a single [Phar](http://www.php.net/manual/en/intro.phar.php) archive from the
repository just by launching `bin/create-phar.php`. The generated Phar archive ships with a stub defining
an autoloader function for Predis, so you just need to require the Phar to be able to use the library.
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.
It is possible to easily create a [phar](http://www.php.net/manual/en/intro.phar.php) archive from
the repository just by launching `bin/create-phar`. The generated phar contains a stub defining an
autoloader function for Predis, so you just need to require the phar to start using the library.
Alternatively, it is also possible to generate one single PHP file that holds every class like older
versions of Predis by launching `bin/create-single-file`, but this practice __is not__ encouraged.
### Connecting to Redis ###
By default Predis uses `127.0.0.1` and `6379` as the default host and port when creating a new client
instance without specifying any connection parameter:
When not specifying any connection parameter to create a new client, Predis assumes `127.0.0.1` and
`6379` as the default host and port and uses a connection timeout of 5 seconds:
```php
$redis = new Predis\Client();
$redis->set('foo', 'bar');
$value = $redis->get('foo');
$client = new Predis\Client();
$client->set('foo', 'bar');
$value = $client->get('foo');
```
It is possible to specify the various connection parameters using URI strings or named arrays:
Connection parameters can be supplied either in the form of URI strings or named arrays. While the
latter is the preferred way to supply parameters, URI strings can be useful for quick configurations
or when parameters are read from a non-structured source:
```php
$redis = new Predis\Client('tcp://10.0.0.1:6379');
// is equivalent to:
$redis = new Predis\Client(array(
// Named array of connection parameters:
$client = new Predis\Client([
'scheme' => 'tcp',
'host' => '10.0.0.1',
'port' => 6379,
));
]);
// Same set of parameters, but using an URI string:
$client = new Predis\Client('tcp://10.0.0.1:6379');
```
### 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.
When an array of connections parameters is provided, Predis automatically works in clustering mode
using client-side sharding. Both named arrays and URI strings can be mixed for providing each node
configuration:
```php
$redis = new Predis\Client(array(
array('host' => '10.0.0.1', 'port' => 6379),
array('host' => '10.0.0.2', 'port' => 6379)
));
$client = new Predis\Client([
'tcp://10.0.0.1?alias=first-node',
['host' => '10.0.0.2', 'alias' => 'second-node'],
]);
```
$replies = $redis->pipeline(function ($pipe) {
The actual list of supported connection parameters can vary depending on each connection backend so
it is recommended to refer to their specific documentation for details.
### Client configuration ###
Various aspects of the client can be easily configured by passing options to the second argument of
`Predis\Client::__construct()`. Options are managed using a mini DI-alike container and their values
are usually lazily initialized only when needed. Predis by default supports the following options:
- `profile`: which profile to use in order to match a specific version of Redis.
- `prefix`: a prefix string that is automatically applied to keys found in commands.
- `exceptions`: whether the client should throw or return responses upon Redis errors.
- `connections`: connection backends or a connection factory to be used by the client.
- `cluster`: which backend to use for clustering (predis, redis or custom configuration).
- `replication`: which backend to use for replication (predis or custom configuration).
Users can provide custom option values, they are stored in the options container and can be accessed
later through the library.
### Aggregate connections ###
Predis is able to aggregate multiple connections which is the base for clustering and replication.
By default the client implements clustering using either client-side sharding (default) or a Redis
backed solution using [redis-cluster](http://redis.io/topics/cluster-tutorial). As for replication,
Predis can handle single-master and multiple-slaves setups by executing read operations on slaves
and switching to the master for write operations. The replication behaviour is fully configurable.
### Command pipelines ###
Pipelining can help with performances when many commands need to be sent to a server by reducing the
latency introduced by network round-trip timings. Pipelining also works with aggregate connections.
The client can execute the pipeline inside a callable block or return a pipeline instance with the
ability to chain commands thanks to its fluent interface:
```php
// Executes a pipeline inside a given callable block:
$responses = $client->pipeline(function ($pipe) {
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
$pipe->get("key:$i");
}
});
// Returns a pipeline instance with fluent interface:
$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute();
```
### Multiple and customizable connection backends ###
### Transactions ###
Predis can optionally use different connection backends to connect to Redis. Two of them leverage
the [phpiredis](http://github.com/nrk/phpiredis) C-based extension resulting in a major speed bump
especially when dealing with long multibulk replies, namely `Predis\Connection\PhpiredisConnection`
(the `socket` extension is also required) and `Predis\Connection\StreamPhpiredisConnection` (it
does not require additional extensions since it relies on PHP's native streams). Both of them can
connect to Redis using standard TCP/IP connections or UNIX domain sockets:
The client provides an abstraction for Redis transactions based on `MULTI` and `EXEC` with a similar
interface to command pipelines:
```php
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array(
'tcp' => 'Predis\Connection\PhpiredisConnection',
'unix' => 'Predis\Connection\PhpiredisStreamConnection',
)
));
// Executes a transaction inside a given callable block:
$responses = $client->transaction(function ($tx) {
$tx->set('foo', 'bar');
$tx->get('foo');
});
// Returns a transaction instance with fluent interface:
$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute();
```
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
This abstraction can perform check-and-set operations thanks to `WATCH` and `UNWATCH` and provides
automatic retries of transactions aborted by Redis when `WATCH`ed keys are touched. For an example
of a transaction using CAS you can see [the following example](examples/TransactionWithCAS.php).
__NOTE__: the method `transaction()` is available since `v0.8.5`, older versions used `multiExec()`
for the same purpose but it has been deprecated and will be removed in the next major release.
### Customizable connection backends ###
Predis can use different connection backends to connect to Redis. Two of them leverage a third party
extension such as [phpiredis](https://github.com/nrk/phpiredis) resulting in major performance gains
especially when dealing with big multibulk responses. While one is based on PHP streams, the other
is based on socket resources provided by `ext-socket`. Both support TCP/IP or UNIX domain sockets:
```php
$client = new Predis\Client('tcp://127.0.0.1', [
'connections' => [
'tcp' => 'Predis\Connection\PhpiredisStreamConnection', // PHP streams
'unix' => 'Predis\Connection\PhpiredisConnection', // ext-socket
],
]);
```
Developers can create their own connection classes to add support for new network backends, extend
existing ones or provide completely different implementations. Connection classes must implement
`Predis\Connection\SingleConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
```php
class MyConnectionClass implements Predis\Connection\SingleConnectionInterface
{
// implementation goes here
// Implementation goes here...
}
// Let Predis automatically use your own class to handle connections identified by the tcp scheme.
$client = new Predis\Client('tcp://127.0.0.1', array(
'connections' => array('tcp' => 'MyConnectionClass')
));
// Use MyConnectionClass to handle connections for the `tcp` scheme:
$client = new Predis\Client('tcp://127.0.0.1', [
'connections' => ['tcp' => 'MyConnectionClass'],
]);
```
For a more in-depth insight on how to create new connection backends you can look at the actual
implementation of the classes contained in the `Predis\Connection` namespace.
For a more in-depth insight on how to create new connection backends you can refer to the actual
implementation of the standard connection classes available in the `Predis\Connection` namespace.
### Defining and registering new commands on the client at runtime ###
### Adding support for new commands ###
Let's suppose Redis just added the support for a brand new feature associated with a new command. If
you want to start using the above mentioned new feature right away without messing with Predis source
code or waiting for it to find its way into a stable Predis release, then you can start off by creating
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:
While we try to update Predis to stay up to date with all the commands available in Redis, you might
prefer to stick with an older version of the library or provide a different way to filter arguments
or parse responses for specific commands. To achieve that, Predis provides the ability to implement
new command classes to define or override commands in the server profiles used by the client:
```php
// Define a new command by extending Predis\Command\AbstractCommand:
class BrandNewRedisCommand extends Predis\Command\AbstractCommand
{
public function getId()
@@ -169,22 +232,26 @@ class BrandNewRedisCommand extends Predis\Command\AbstractCommand
}
}
$redis = new Predis\Client();
$redis->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand');
$redis->newcmd();
// Inject your command in the current profile:
$client = new Predis\Client();
$client->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand');
$response = $client->newcmd();
```
### Abstraction for handling Lua scripts as plain Redis commands ###
### Scriptable 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:
A scriptable command is just an abstraction for [Lua scripting](http://redis.io/commands/eval) that
aims to simplify the usage of scripting with Redis >= 2.6. Scriptable commands can be registered in
the server profile used by the client and are accessible as if they were plain Redis commands, but
they define a Lua script that gets transmitted to Redis for remote execution. Internally, scriptable
commands use by default [EVALSHA](http://redis.io/commands/evalsha) and identify a Lua script by its
SHA1 hash to save bandwidth but [EVAL](http://redis.io/commands/eval) is automatically preferred as
a fall back when needed:
```php
// Define a new scriptable command by extending Predis\Command\ScriptedCommand:
class ListPushRandomValue extends Predis\Command\ScriptedCommand
{
public function getKeysCount()
@@ -194,8 +261,7 @@ class ListPushRandomValue extends Predis\Command\ScriptedCommand
public function getScript()
{
return
<<<LUA
return <<<LUA
math.randomseed(ARGV[1])
local rnd = tostring(math.random())
redis.call('lpush', KEYS[1], rnd)
@@ -204,61 +270,60 @@ LUA;
}
}
// Inject your scriptable command in the current profile:
$client = new Predis\Client();
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');
$value = $client->lpushrand('random_values', $seed = mt_rand());
$response = $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
production environments or containing data you are interested in!
Predis has a comprehensive test suite covering every aspect of the library. The suite performs integration
tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct behaviour of the
implementation of each command and automatically skips commands not defined in the selected version of
Redis. If you do not have Redis up and running, integration tests can be disabled. By default, the test
suite is configured to execute integration tests using the server profile for Redis v2.4 (which is the
current stable version of Redis). You can optionally run the suite against a Redis instance built from
the `unstable` branch with the development profile by changing the `REDIS_SERVER_VERSION` to `dev` in
the `phpunit.xml` file. More details on testing Predis can be found in [the tests README](tests/README.md).
Predis uses Travis CI for continuous integration. You can find the results of the test suite and the build
history [on its project page](http://travis-ci.org/nrk/predis).
## Development ##
## Dependencies ##
### Reporting bugs and contributing code ###
- PHP >= 5.3.2
- PHPUnit >= 3.5.0 (needed to run the test suite)
Contributions to Predis are highly appreciated either in the form of pull requests for new features,
bug fixes, or just bug reports. We only ask you to adhere to a [basic set of rules](CONTRIBUTING.md)
before submitting your changes or filing bugs on the issue tracker to make it easier for everyone to
stay consistent while working on the project.
## Links ##
### Project ###
- [Source code](http://github.com/nrk/predis/)
- [Wiki](http://wiki.github.com/nrk/predis/)
- [Issue tracker](http://github.com/nrk/predis/issues)
### Test suite ###
__ATTENTION__: Do not ever run the test suite shipped with Predis against instances of Redis running
in production environments or containing data you are interested in!
Predis has a comprehensive test suite covering every aspect of the library. This test suite performs
integration tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct
behaviour of the implementation of each command and automatically skips commands not defined in the
specified Redis profile. If you do not have Redis up and running, integration tests can be disabled.
By default the test suite is configured to execute integration tests using the profile for Redis 2.8
(which is the current stable version of Redis) but can optionally target a Redis instance built from
the `unstable` branch by modifying `phpunit.xml` and setting `REDIS_SERVER_VERSION` to `dev` so that
the development server profile will be used. You can refer to [the tests README](tests/README.md)
for more detailed information about testing Predis.
Predis uses Travis CI for continuous integration and the history for past and current builds can be
found [on its project page](http://travis-ci.org/nrk/predis).
## Other ##
### Project related links ###
- [Source code](https://github.com/nrk/predis)
- [Wiki](https://wiki.github.com/nrk/predis)
- [Issue tracker](https://github.com/nrk/predis/issues)
- [PEAR channel](http://pear.nrk.io)
### Related ###
- [Redis](http://redis.io/)
- [PHP](http://php.net/)
- [PHPUnit](http://www.phpunit.de/)
- [Git](http://git-scm.com/)
## Author ##
### Author ###
- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN))
## Contributors ##
- [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 past work on extending [phpiredis](http://github.com/nrk/phpiredis) for Predis.
## License ##
### License ###
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
+1 -1
View File
@@ -1 +1 @@
0.8.5-dev
0.8.6
@@ -171,13 +171,11 @@ class CommandTestCaseGenerator
namespace Predis\Command;
use \PHPUnit_Framework_TestCase as StandardTestCase;
/**
* @group commands
* @group realm-$realm
*/
class $class extends CommandTestCase
class $class extends PredisCommandTestCase
{
/**
* {@inheritdoc}
+221
View File
@@ -0,0 +1,221 @@
#!/usr/bin/env php
<?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.
*/
// -------------------------------------------------------------------------- //
// In order to be able to execute this script to create a PEAR package of Predis
// the `pear` binary must be available and executable in your $PATH.
// The parts used to parse author and version strings are taken from Onion (used
// by this library in the past) just to keep on relying on the package.ini file
// to simplify things. We might consider to switch to using the PEAR classes
// directly in the future.
// -------------------------------------------------------------------------- //
function executeWithBackup($file, $callback)
{
$exception = null;
$backup = "$file.backup";
copy($file, $backup);
try {
call_user_func($callback, $file);
} catch (Exception $exception) {
// NOOP
}
unlink($file);
rename($backup, $file);
if ($exception) {
throw $exception;
}
}
function parseAuthor($string)
{
$author = array();
if (preg_match('/^\s*(.+?)\s*(?:"(\S+)"\s*)?<(\S+)>\s*$/x', $string , $regs)) {
if (count($regs) == 4) {
list($orig,$name,$user,$email) = $regs;
$author['name'] = $name;
$author['user'] = $user;
$author['email'] = $email;
} elseif (count($regs) == 3) {
list($orig,$name,$email) = $regs;
$author['name'] = $name;
$author['email'] = $email;
}
} else {
$author['name'] = $string;
}
return $author;
}
function parseVersion($string)
{
$version_pattern = '([0-9.]+)';
if (preg_match("/^\s*$version_pattern\s*\$/x", $string, $regs)) {
return array('min' => $regs[1] ?: '0.0.0');
} elseif (preg_match("/^\s*[>=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
return array('min' => $regs[1] ?: '0.0.0');
} elseif (preg_match("/^\s*[<=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
return array('max' => $regs[1]);
} elseif (preg_match("/^\s*$version_pattern\s*<=>\s*$version_pattern\s*\$/x", $string, $regs)) {
return array(
'min' => $regs[1] ?: '0.0.0',
'max' => $regs[2],
);
}
}
function addRolePath($pkg, $path, $role)
{
if (is_dir($path)) {
$dirRoot = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$dirTree = new RecursiveIteratorIterator($dirRoot, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($dirTree as $fileinfo) {
if ($fileinfo->isFile()) {
addPackageFile($pkg, $fileinfo, $role, $path);
}
}
} else {
foreach (glob($path) as $filename) {
addPackageFile($pkg, new SplFileInfo($filename), $role);
}
}
}
function addPackageFile($pkg, $fileinfo, $role, $baseDir = '')
{
$fileNode = $pkg->contents->dir->addChild('file');
$fileNode->addAttribute('name', $filepath = $fileinfo->getPathname());
$fileNode->addAttribute('role', $role);
$fileNode->addAttribute('md5sum', md5_file($filepath));
$installNode = $pkg->phprelease->filelist->addChild('install');
$installNode->addAttribute('name', $filepath);
$installNode->addAttribute('as', !$baseDir ? basename($filepath) : substr($filepath, strlen($baseDir) + 1));
}
function generatePackageXml($packageINI)
{
$XML = <<<XML
<?xml version="1.0"?>
<package packagerversion="1.4.10" version="2.0"
xmlns="http://pear.php.net/dtd/package-2.0"
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd" />
XML;
$cfg = parse_ini_file($packageINI, true);
$pkg = new SimpleXMLElement($XML);
$pkg->name = $cfg['package']['name'];
$pkg->channel = $cfg['package']['channel'];
$pkg->summary = $cfg['package']['desc'];
$pkg->description = $cfg['package']['desc'];
$author = parseAuthor($cfg['package']['author']);
$pkg->addChild('lead');
$pkg->lead->name = $author['name'];
$pkg->lead->user = $author['user'];
$pkg->lead->email = $author['email'];
$pkg->lead->active = 'yes';
$datetime = new DateTime('now');
$pkg->date = $datetime->format('Y-m-d');
$pkg->time = $datetime->format('H:i:s');
$pkg->addChild('version');
$pkg->version->release = $cfg['package']['version'];
$pkg->version->api = $cfg['package']['version'];
$pkg->addChild('stability');
$pkg->stability->release = $cfg['package']['stability'];
$pkg->stability->api = $cfg['package']['stability'];
$pkg->license = $cfg['package']['license'];
$pkg->notes = '-';
$pkg->addChild('contents')->addChild('dir')->addAttribute('name', '/');
$pkg->addChild('dependencies')->addChild('required');
foreach ($cfg['require'] as $required => $version) {
$version = parseVersion($version);
$pkg->dependencies->required->addChild($required);
if (isset($version['min'])) {
$pkg->dependencies->required->$required->min = $version['min'];
}
if (isset($version['max'])) {
$pkg->dependencies->required->$required->min = $version['max'];
}
}
$pkg->addChild('phprelease')->addChild('filelist');
$pathToRole = array(
'doc' => 'doc', 'docs' => 'doc', 'examples' => 'doc',
'lib' => 'php', 'src' => 'php',
'test' => 'test', 'tests' => 'test',
);
foreach (array_merge($pathToRole, $cfg['roles'] ?: array()) as $path => $role) {
addRolePath($pkg, $path, $role);
}
return $pkg;
}
function savePackageXml($xml)
{
$dom = new DOMDocument("1.0");
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
file_put_contents('package.xml', $dom->saveXML());
}
function buildPackage()
{
passthru('pear -q package && rm package.xml');
}
function modifyPhpunitXml($file)
{
$cfg = new SimpleXMLElement($file, null, true);
$cfg[0]['bootstrap'] = str_replace('tests/', '', $cfg[0]['bootstrap']);
$cfg->testsuites->testsuite->directory = str_replace('tests/', '', $cfg->testsuites->testsuite->directory);
$cfg->saveXml($file);
}
// -------------------------------------------------------------------------- //
executeWithBackup(__DIR__.'/../phpunit.xml.dist', function ($file) {
modifyPhpunitXml($file);
$pkg = generatePackageXml('package.ini');
savePackageXml($pkg);
buildPackage();
});
@@ -321,13 +321,15 @@ class PhpUseDirectives implements Countable, IteratorAggregate
{
private $use;
private $aliases;
private $reverseAliases;
private $namespace;
public function __construct(PhpNamespace $namespace)
{
$this->namespace = $namespace;
$this->use = array();
$this->aliases = array();
$this->namespace = $namespace;
$this->reverseAliases = array();
}
public function add($use, $as = null)
@@ -336,8 +338,35 @@ class PhpUseDirectives implements Countable, IteratorAggregate
return;
}
$rename = null;
$this->use[] = $use;
$this->aliases[$as ?: PhpClass::extractName($use)] = $use;
$aliasedClassName = $as ?: PhpClass::extractName($use);
if (isset($this->aliases[$aliasedClassName])) {
$parentNs = $this->getParentNamespace();
if ($parentNs && false !== $pos = strrpos($parentNs, '\\')) {
$parentNs = substr($parentNs, $pos);
}
$newAlias = "{$parentNs}_{$aliasedClassName}";
$rename = (object) array(
'namespace' => $this->namespace,
'from' => $aliasedClassName,
'to' => $newAlias,
);
$this->aliases[$newAlias] = $use;
$as = $newAlias;
} else {
$this->aliases[$aliasedClassName] = $use;
}
if ($as !== null) {
$this->reverseAliases[$use] = $as;
}
return $rename;
}
public function getList()
@@ -352,8 +381,14 @@ class PhpUseDirectives implements Countable, IteratorAggregate
public function getPhpCode()
{
$reducer = function ($str, $use) {
return $str .= "use $use;\n";
$reverseAliases = $this->reverseAliases;
$reducer = function ($str, $use) use ($reverseAliases) {
if (isset($reverseAliases[$use])) {
return $str .= "use $use as {$reverseAliases[$use]};\n";
} else {
return $str .= "use $use;\n";
}
};
return array_reduce($this->getList(), $reducer, '');
@@ -364,6 +399,15 @@ class PhpUseDirectives implements Countable, IteratorAggregate
return $this->namespace;
}
public function getParentNamespace()
{
if (false !== $pos = strrpos($this->namespace, '\\')) {
return substr($this->namespace, 0, $pos);
}
return '';
}
public function getFQN($className)
{
if (($nsSepFirst = strpos($className, '\\')) === false) {
@@ -430,10 +474,19 @@ LICENSE;
private function extractData()
{
$renames = array();
$useDirectives = $this->getNamespace()->getUseDirectives();
$useExtractor = function ($m) use ($useDirectives) {
$useDirectives->add(($namespacedPath = $m[1]));
$useExtractor = function ($m) use ($useDirectives, &$renames) {
array_shift($m);
if (isset($m[1])) {
$m[1] = str_replace(" as ", '', $m[1]);
}
if ($rename = call_user_func_array(array($useDirectives, 'add'), $m)) {
$renames[] = $rename;
}
};
$classBuffer = stream_get_contents(fopen($this->getFile()->getPathname(), 'r'));
@@ -445,6 +498,10 @@ LICENSE;
$classBuffer = preg_replace('/namespace\s+[\w\d_\\\\]+;\s?/', '', $classBuffer);
$classBuffer = preg_replace_callback('/use\s+([\w\d_\\\\]+)(\s+as\s+.*)?;\s?\n?/', $useExtractor, $classBuffer);
foreach ($renames as $rename) {
$classBuffer = str_replace($rename->from, $rename->to, $classBuffer);
}
$this->body = trim($classBuffer);
$this->extractHierarchy();
+3
View File
@@ -15,6 +15,9 @@
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"suggest": {
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
"ext-curl": "Allows access to Webdis when paired with phpiredis"
@@ -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->transaction($options, function ($tx) use ($key, &$element) {
@list($element) = $tx->zrange($key, 0, 0);
if (isset($element)) {
+1 -1
View File
@@ -36,7 +36,7 @@ class Autoloader
/**
* Registers the autoloader class with the PHP SPL autoloader.
*
* @param boolean $prepend Prepend the autoloader on the stack instead of appending it.
* @param bool $prepend Prepend the autoloader on the stack instead of appending it.
*/
public static function register($prepend = false)
{
+1 -1
View File
@@ -24,7 +24,7 @@ interface BasicClientInterface
/**
* Executes the specified Redis command.
*
* @param CommandInterface $command A Redis command.
* @param CommandInterface $command A Redis command.
* @return mixed
*/
public function executeCommand(CommandInterface $command);
+55 -43
View File
@@ -20,7 +20,6 @@ use Predis\Monitor\MonitorContext;
use Predis\Option\ClientOptions;
use Predis\Option\ClientOptionsInterface;
use Predis\Pipeline\PipelineContext;
use Predis\Profile\ServerProfile;
use Predis\PubSub\PubSubContext;
use Predis\Transaction\MultiExecContext;
@@ -31,7 +30,7 @@ use Predis\Transaction\MultiExecContext;
*/
class Client implements ClientInterface
{
const VERSION = '0.8.5-dev';
const VERSION = '0.8.6';
private $options;
private $profile;
@@ -41,7 +40,7 @@ class Client implements ClientInterface
* Initializes a new client with optional connection parameters and client options.
*
* @param mixed $parameters Connection parameters for one or multiple servers.
* @param mixed $options Options that specify certain behaviours for the client.
* @param mixed $options Options that specify certain behaviours for the client.
*/
public function __construct($parameters = null, $options = null)
{
@@ -55,7 +54,7 @@ class Client implements ClientInterface
* 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.
* @param mixed $options Client options.
* @return ClientOptions
*/
protected function filterOptions($options)
@@ -80,7 +79,7 @@ class Client implements ClientInterface
* types of arguments (string, array) or returns the passed object if it
* implements Predis\Connection\ConnectionInterface.
*
* @param mixed $parameters Connection parameters or instance.
* @param mixed $parameters Connection parameters or instance.
* @return ConnectionInterface
*/
protected function initializeConnection($parameters)
@@ -143,6 +142,7 @@ class Client implements ClientInterface
* client is connected to a cluster. The new instance will use the same
* options of the original client.
*
* @param string $connectionID Identifier for the connection.
* @return Client
*/
public function getClientFor($connectionID)
@@ -183,8 +183,8 @@ class Client implements ClientInterface
/**
* Checks if the underlying connection is connected to Redis.
*
* @return Boolean True means that the connection is open.
* False means that the connection is closed.
* @return bool True means that the connection is open.
* False means that the connection is closed.
*/
public function isConnected()
{
@@ -202,7 +202,7 @@ class Client implements ClientInterface
/**
* Retrieves a single connection out of an aggregated connections instance.
*
* @param string $connectionId Index or alias of the connection.
* @param string $connectionId Index or alias of the single connection.
* @return Connection\SingleConnectionInterface
*/
public function getConnectionById($connectionId)
@@ -215,34 +215,27 @@ class Client implements ClientInterface
}
/**
* Dynamically invokes a Redis command with the specified arguments.
* Creates a Redis command with the specified arguments and sends a request
* to the server.
*
* @param string $method The name of a Redis command.
* @param array $arguments The arguments for the command.
* @param string $commandID Command ID.
* @param array $arguments Arguments for the command.
* @return mixed
*/
public function __call($method, $arguments)
public function __call($commandID, $arguments)
{
$command = $this->profile->createCommand($method, $arguments);
$response = $this->connection->executeCommand($command);
$command = $this->createCommand($commandID, $arguments);
$response = $this->executeCommand($command);
if ($response instanceof ResponseObjectInterface) {
if ($response instanceof ResponseErrorInterface) {
$response = $this->onResponseError($command, $response);
}
return $response;
}
return $command->parseResponse($response);
return $response;
}
/**
* {@inheritdoc}
*/
public function createCommand($method, $arguments = array())
public function createCommand($commandID, $arguments = array())
{
return $this->profile->createCommand($method, $arguments);
return $this->profile->createCommand($commandID, $arguments);
}
/**
@@ -266,8 +259,8 @@ class Client implements ClientInterface
/**
* Handles -ERR responses returned by Redis.
*
* @param CommandInterface $command The command that generated the error.
* @param ResponseErrorInterface $response The error response instance.
* @param CommandInterface $command The command that generated the error.
* @param ResponseErrorInterface $response The error response instance.
* @return mixed
*/
protected function onResponseError(CommandInterface $command, ResponseErrorInterface $response)
@@ -297,8 +290,8 @@ class Client implements ClientInterface
*
* TODO: Invert $argv and $initializer.
*
* @param array $argv Arguments for the initializer.
* @param string $initializer The initializer method.
* @param array $argv Arguments for the initializer.
* @param string $initializer The initializer method.
* @return mixed
*/
private function sharedInitializer($argv, $initializer)
@@ -309,10 +302,12 @@ class Client implements ClientInterface
case 1:
list($arg0) = $argv;
return is_array($arg0) ? $this->$initializer($arg0) : $this->$initializer(null, $arg0);
case 2:
list($arg0, $arg1) = $argv;
return $this->$initializer($arg0, $arg1);
default:
@@ -324,7 +319,7 @@ class Client implements ClientInterface
* Creates a new pipeline context and returns it, or returns the results of
* a pipeline executed inside the optionally provided callable object.
*
* @param mixed $arg,... Options for the context, a callable object, or both.
* @param mixed ... Options for the context, a callable object, or both.
* @return PipelineContext|array
*/
public function pipeline(/* arguments */)
@@ -335,8 +330,8 @@ class Client implements ClientInterface
/**
* Pipeline context initializer.
*
* @param array $options Options for the context.
* @param mixed $callable Optional callable object used to execute the context.
* @param array $options Options for the context.
* @param mixed $callable Optional callable object used to execute the context.
* @return PipelineContext|array
*/
protected function initPipeline(Array $options = null, $callable = null)
@@ -356,8 +351,8 @@ class Client implements ClientInterface
/**
* Executes a pipeline context when a callable object is passed.
*
* @param array $options Options of the context initialization.
* @param mixed $callable Optional callable object used to execute the context.
* @param PipelineContext $pipeline Pipeline context instance.
* @param mixed $callable Optional callable object used to execute the context.
* @return PipelineContext|array
*/
private function pipelineExecute(PipelineContext $pipeline, $callable)
@@ -369,7 +364,11 @@ class Client implements ClientInterface
* Creates a new transaction context and returns it, or returns the results of
* a transaction executed inside the optionally provided callable object.
*
* @param mixed $arg,... Options for the context, a callable object, or both.
* @deprecated You should start using the new Client::transaction() method
* as it will replace Client::multiExec() in the next major
* version of the library.
*
* @param mixed ... Options for the context, a callable object, or both.
* @return MultiExecContext|array
*/
public function multiExec(/* arguments */)
@@ -377,16 +376,29 @@ class Client implements ClientInterface
return $this->sharedInitializer(func_get_args(), 'initMultiExec');
}
/**
* Creates a new transaction context and returns it, or returns the results of
* a transaction executed inside the optionally provided callable object.
*
* @param mixed ... Options for the context, a callable object, or both.
* @return MultiExecContext|array
*/
public function transaction(/* arguments */)
{
return $this->sharedInitializer(func_get_args(), 'initMultiExec');
}
/**
* Transaction context initializer.
*
* @param array $options Options for the context.
* @param mixed $callable Optional callable object used to execute the context.
* @param array $options Options for the context.
* @param mixed $callable Optional callable object used to execute the context.
* @return MultiExecContext|array
*/
protected function initMultiExec(Array $options = null, $callable = null)
{
$transaction = new MultiExecContext($this, $options ?: array());
return isset($callable) ? $transaction->execute($callable) : $transaction;
}
@@ -399,8 +411,8 @@ class Client implements ClientInterface
* Client::pubSubLoop() to create Predis\PubSub\PubSubContext
* instances from now on.
*
* @param mixed $arg,... Options for the context, a callable object, or both.
* @return PubSubExecContext|array
* @param mixed ... Options for the context, a callable object, or both.
* @return PubSubContext|array
*/
public function pubSub(/* arguments */)
{
@@ -411,8 +423,8 @@ class Client implements ClientInterface
* Creates a new Publish / Subscribe context and returns it, or executes it
* inside the optionally provided callable object.
*
* @param mixed $arg,... Options for the context, a callable object, or both.
* @return PubSubExecContext|array
* @param mixed ... Options for the context, a callable object, or both.
* @return PubSubContext|array
*/
public function pubSubLoop(/* arguments */)
{
@@ -422,8 +434,8 @@ class Client implements ClientInterface
/**
* Publish / Subscribe context initializer.
*
* @param array $options Options for the context.
* @param mixed $callable Optional callable object used to execute the context.
* @param array $options Options for the context.
* @param mixed $callable Optional callable object used to execute the context.
* @return PubSubContext
*/
protected function initPubSub(Array $options = null, $callable = null)
+2 -2
View File
@@ -58,8 +58,8 @@ interface ClientInterface extends BasicClientInterface
/**
* 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.
* @param string $method The name of a Redis command.
* @param array $arguments The arguments for the command.
* @return Command\CommandInterface
*/
public function createCommand($method, $arguments = array());
@@ -27,7 +27,7 @@ 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.
* @param CommandInterface $command Command to be hashed.
* @return int
*/
public function getHash(CommandInterface $command);
@@ -35,7 +35,7 @@ interface CommandHashStrategyInterface
/**
* Returns the hash for the given key using the specified algorithm.
*
* @param string $key Key to be hashed.
* @param string $key Key to be hashed.
* @return string
*/
public function getKeyHash($key);
@@ -24,8 +24,8 @@ interface DistributionStrategyInterface
/**
* Adds a node to the distributor with an optional weight.
*
* @param mixed $node Node object.
* @param int $weight Weight for the node.
* @param mixed $node Node object.
* @param int $weight Weight for the node.
*/
public function add($node, $weight = null);
@@ -39,6 +39,7 @@ interface DistributionStrategyInterface
/**
* Gets a node from the distributor using the computed hash of a key.
*
* @param mixed $key
* @return mixed
*/
public function get($key);
+14 -14
View File
@@ -34,7 +34,7 @@ class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
private $nodes = array();
/**
* @param int $replicas Number of replicas in the ring.
* @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, $nodeHashCallback = null)
@@ -46,8 +46,8 @@ class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
/**
* Adds a node to the ring with an optional weight.
*
* @param mixed $node Node object.
* @param int $weight Weight for the node.
* @param mixed $node Node object.
* @param int $weight Weight for the node.
*/
public function add($node, $weight = null)
{
@@ -90,7 +90,7 @@ class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
/**
* Returns the initialization status of the distributor.
*
* @return Boolean
* @return bool
*/
private function isInitialized()
{
@@ -143,10 +143,10 @@ class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
/**
* Implements the logic needed to add a node to the hashring.
*
* @param array $ring Source hashring.
* @param mixed $node Node object to be added.
* @param int $totalNodes Total number of nodes.
* @param int $replicas Number of replicas in the ring.
* @param array $ring Source hashring.
* @param mixed $node Node object to be added.
* @param int $totalNodes Total number of nodes.
* @param int $replicas Number of replicas in the ring.
* @param float $weightRatio Weight ratio for the node.
*/
protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
@@ -176,7 +176,7 @@ class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
/**
* Calculates the hash for the specified value.
*
* @param string $value Input value.
* @param string $value Input value.
* @return int
*/
public function hash($value)
@@ -195,7 +195,7 @@ class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
/**
* Calculates the corrisponding key of a node distributed in the hashring.
*
* @param int $key Computed hash of a key.
* @param int $key Computed hash of a key.
* @return int
*/
private function getNodeKey($key)
@@ -211,7 +211,7 @@ class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
if ($item > $key) {
$upper = $index - 1;
} else if ($item < $key) {
} elseif ($item < $key) {
$lower = $index + 1;
} else {
return $item;
@@ -224,9 +224,9 @@ class HashRing implements DistributionStrategyInterface, HashGeneratorInterface
/**
* Implements a strategy to deal with wrap-around errors during binary searches.
*
* @param int $upper
* @param int $lower
* @param int $ringKeysCount
* @param int $upper
* @param int $lower
* @param int $ringKeysCount
* @return int
*/
protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
@@ -55,6 +55,7 @@ class KetamaPureRing extends HashRing
public function hash($value)
{
$hash = unpack('V', md5($value, true));
return $hash[1];
}
@@ -22,7 +22,7 @@ interface HashGeneratorInterface
/**
* Generates an hash that is used by the distributor algorithm
*
* @param string $value Value used to generate the hash.
* @param string $value Value used to generate the hash.
* @return int
*/
public function hash($value);
+111 -101
View File
@@ -42,117 +42,126 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
*/
protected function getDefaultCommands()
{
$keyIsFirstArgument = array($this, 'getKeyFromFirstArgument');
$keysAreAllArguments = array($this, 'getKeyFromAllArguments');
$getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument');
$getKeyFromAllArguments = 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
'DUMP' => $keyIsFirstArgument,
'RESTORE' => $keyIsFirstArgument,
'EXISTS' => $getKeyFromFirstArgument,
'DEL' => $getKeyFromAllArguments,
'TYPE' => $getKeyFromFirstArgument,
'EXPIRE' => $getKeyFromFirstArgument,
'EXPIREAT' => $getKeyFromFirstArgument,
'PERSIST' => $getKeyFromFirstArgument,
'PEXPIRE' => $getKeyFromFirstArgument,
'PEXPIREAT' => $getKeyFromFirstArgument,
'TTL' => $getKeyFromFirstArgument,
'PTTL' => $getKeyFromFirstArgument,
'SORT' => $getKeyFromFirstArgument, // TODO
'DUMP' => $getKeyFromFirstArgument,
'RESTORE' => $getKeyFromFirstArgument,
/* 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,
'APPEND' => $getKeyFromFirstArgument,
'DECR' => $getKeyFromFirstArgument,
'DECRBY' => $getKeyFromFirstArgument,
'GET' => $getKeyFromFirstArgument,
'GETBIT' => $getKeyFromFirstArgument,
'MGET' => $getKeyFromAllArguments,
'SET' => $getKeyFromFirstArgument,
'GETRANGE' => $getKeyFromFirstArgument,
'GETSET' => $getKeyFromFirstArgument,
'INCR' => $getKeyFromFirstArgument,
'INCRBY' => $getKeyFromFirstArgument,
'INCRBYFLOAT' => $getKeyFromFirstArgument,
'SETBIT' => $getKeyFromFirstArgument,
'SETEX' => $getKeyFromFirstArgument,
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
'SETNX' => $keyIsFirstArgument,
'SETRANGE' => $keyIsFirstArgument,
'STRLEN' => $keyIsFirstArgument,
'SUBSTR' => $keyIsFirstArgument,
'SETNX' => $getKeyFromFirstArgument,
'SETRANGE' => $getKeyFromFirstArgument,
'STRLEN' => $getKeyFromFirstArgument,
'SUBSTR' => $getKeyFromFirstArgument,
'BITOP' => array($this, 'getKeyFromBitOp'),
'BITCOUNT' => $keyIsFirstArgument,
'BITCOUNT' => $getKeyFromFirstArgument,
/* commands operating on lists */
'LINSERT' => $keyIsFirstArgument,
'LINDEX' => $keyIsFirstArgument,
'LLEN' => $keyIsFirstArgument,
'LPOP' => $keyIsFirstArgument,
'RPOP' => $keyIsFirstArgument,
'RPOPLPUSH' => $keysAreAllArguments,
'LINSERT' => $getKeyFromFirstArgument,
'LINDEX' => $getKeyFromFirstArgument,
'LLEN' => $getKeyFromFirstArgument,
'LPOP' => $getKeyFromFirstArgument,
'RPOP' => $getKeyFromFirstArgument,
'RPOPLPUSH' => $getKeyFromAllArguments,
'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,
'LPUSH' => $getKeyFromFirstArgument,
'LPUSHX' => $getKeyFromFirstArgument,
'RPUSH' => $getKeyFromFirstArgument,
'RPUSHX' => $getKeyFromFirstArgument,
'LRANGE' => $getKeyFromFirstArgument,
'LREM' => $getKeyFromFirstArgument,
'LSET' => $getKeyFromFirstArgument,
'LTRIM' => $getKeyFromFirstArgument,
/* commands operating on sets */
'SADD' => $keyIsFirstArgument,
'SCARD' => $keyIsFirstArgument,
'SDIFF' => $keysAreAllArguments,
'SDIFFSTORE' => $keysAreAllArguments,
'SINTER' => $keysAreAllArguments,
'SINTERSTORE' => $keysAreAllArguments,
'SUNION' => $keysAreAllArguments,
'SUNIONSTORE' => $keysAreAllArguments,
'SISMEMBER' => $keyIsFirstArgument,
'SMEMBERS' => $keyIsFirstArgument,
'SSCAN' => $keyIsFirstArgument,
'SPOP' => $keyIsFirstArgument,
'SRANDMEMBER' => $keyIsFirstArgument,
'SREM' => $keyIsFirstArgument,
'SADD' => $getKeyFromFirstArgument,
'SCARD' => $getKeyFromFirstArgument,
'SDIFF' => $getKeyFromAllArguments,
'SDIFFSTORE' => $getKeyFromAllArguments,
'SINTER' => $getKeyFromAllArguments,
'SINTERSTORE' => $getKeyFromAllArguments,
'SUNION' => $getKeyFromAllArguments,
'SUNIONSTORE' => $getKeyFromAllArguments,
'SISMEMBER' => $getKeyFromFirstArgument,
'SMEMBERS' => $getKeyFromFirstArgument,
'SSCAN' => $getKeyFromFirstArgument,
'SPOP' => $getKeyFromFirstArgument,
'SRANDMEMBER' => $getKeyFromFirstArgument,
'SREM' => $getKeyFromFirstArgument,
/* commands operating on sorted sets */
'ZADD' => $keyIsFirstArgument,
'ZCARD' => $keyIsFirstArgument,
'ZCOUNT' => $keyIsFirstArgument,
'ZINCRBY' => $keyIsFirstArgument,
'ZADD' => $getKeyFromFirstArgument,
'ZCARD' => $getKeyFromFirstArgument,
'ZCOUNT' => $getKeyFromFirstArgument,
'ZINCRBY' => $getKeyFromFirstArgument,
'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
'ZRANGE' => $keyIsFirstArgument,
'ZRANGEBYSCORE' => $keyIsFirstArgument,
'ZRANK' => $keyIsFirstArgument,
'ZREM' => $keyIsFirstArgument,
'ZREMRANGEBYRANK' => $keyIsFirstArgument,
'ZREMRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANGE' => $keyIsFirstArgument,
'ZREVRANGEBYSCORE' => $keyIsFirstArgument,
'ZREVRANK' => $keyIsFirstArgument,
'ZSCORE' => $keyIsFirstArgument,
'ZRANGE' => $getKeyFromFirstArgument,
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZRANK' => $getKeyFromFirstArgument,
'ZREM' => $getKeyFromFirstArgument,
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANGE' => $getKeyFromFirstArgument,
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANK' => $getKeyFromFirstArgument,
'ZSCORE' => $getKeyFromFirstArgument,
'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
'ZSCAN' => $keyIsFirstArgument,
'ZSCAN' => $getKeyFromFirstArgument,
'ZLEXCOUNT' => $getKeyFromFirstArgument,
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
/* commands operating on hashes */
'HDEL' => $keyIsFirstArgument,
'HEXISTS' => $keyIsFirstArgument,
'HGET' => $keyIsFirstArgument,
'HGETALL' => $keyIsFirstArgument,
'HMGET' => $keyIsFirstArgument,
'HMSET' => $keyIsFirstArgument,
'HINCRBY' => $keyIsFirstArgument,
'HINCRBYFLOAT' => $keyIsFirstArgument,
'HKEYS' => $keyIsFirstArgument,
'HLEN' => $keyIsFirstArgument,
'HSET' => $keyIsFirstArgument,
'HSETNX' => $keyIsFirstArgument,
'HVALS' => $keyIsFirstArgument,
'HSCAN' => $keyIsFirstArgument,
'HDEL' => $getKeyFromFirstArgument,
'HEXISTS' => $getKeyFromFirstArgument,
'HGET' => $getKeyFromFirstArgument,
'HGETALL' => $getKeyFromFirstArgument,
'HMGET' => $getKeyFromFirstArgument,
'HMSET' => $getKeyFromFirstArgument,
'HINCRBY' => $getKeyFromFirstArgument,
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
'HKEYS' => $getKeyFromFirstArgument,
'HLEN' => $getKeyFromFirstArgument,
'HSET' => $getKeyFromFirstArgument,
'HSETNX' => $getKeyFromFirstArgument,
'HVALS' => $getKeyFromFirstArgument,
'HSCAN' => $getKeyFromFirstArgument,
/* commands operating on HyperLogLog */
'PFADD' => $getKeyFromFirstArgument,
'PFCOUNT' => $getKeyFromAllArguments,
'PFMERGE' => $getKeyFromAllArguments,
/* scripting */
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
@@ -180,7 +189,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
* 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.
* @param mixed $callback A valid callable object or NULL.
*/
public function setCommandHandler($commandId, $callback = null)
{
@@ -188,6 +197,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
if (!isset($callback)) {
unset($this->commands[$commandId]);
return;
}
@@ -201,7 +211,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
/**
* Extracts the key from the first argument of a command instance.
*
* @param CommandInterface $command Command instance.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromFirstArgument(CommandInterface $command)
@@ -213,7 +223,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
* 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.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromAllArguments(CommandInterface $command)
@@ -229,7 +239,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
* 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.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromInterleavedArguments(CommandInterface $command)
@@ -249,7 +259,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
/**
* Extracts the key from BLPOP and BRPOP commands.
*
* @param CommandInterface $command Command instance.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromBlockingListCommands(CommandInterface $command)
@@ -264,7 +274,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
/**
* Extracts the key from BITOP command.
*
* @param CommandInterface $command Command instance.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromBitOp(CommandInterface $command)
@@ -279,7 +289,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
/**
* Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
*
* @param CommandInterface $command Command instance.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
@@ -295,7 +305,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
/**
* Extracts the key from EVAL and EVALSHA commands.
*
* @param CommandInterface $command Command instance.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromScriptingCommands(CommandInterface $command)
@@ -344,8 +354,8 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
/**
* Checks if the specified array of keys will generate the same hash.
*
* @param array $keys Array of keys.
* @return Boolean
* @param array $keys Array of keys.
* @return bool
*/
protected function checkSameHashForKeys(Array $keys)
{
@@ -372,7 +382,7 @@ class PredisClusterHashStrategy implements CommandHashStrategyInterface
* 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.
* @param string $key A key.
* @return string
*/
protected function extractKeyTag($key)
+119 -87
View File
@@ -42,103 +42,113 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
*/
protected function getDefaultCommands()
{
$keyIsFirstArgument = array($this, 'getKeyFromFirstArgument');
$getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument');
$getKeyFromAllArguments = array($this, 'getKeyFromAllArguments');
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
'EXISTS' => $getKeyFromFirstArgument,
'DEL' => $getKeyFromAllArguments,
'TYPE' => $getKeyFromFirstArgument,
'EXPIRE' => $getKeyFromFirstArgument,
'EXPIREAT' => $getKeyFromFirstArgument,
'PERSIST' => $getKeyFromFirstArgument,
'PEXPIRE' => $getKeyFromFirstArgument,
'PEXPIREAT' => $getKeyFromFirstArgument,
'TTL' => $getKeyFromFirstArgument,
'PTTL' => $getKeyFromFirstArgument,
'SORT' => $getKeyFromFirstArgument, // TODO
/* commands operating on string values */
'APPEND' => $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,
'APPEND' => $getKeyFromFirstArgument,
'DECR' => $getKeyFromFirstArgument,
'DECRBY' => $getKeyFromFirstArgument,
'GET' => $getKeyFromFirstArgument,
'GETBIT' => $getKeyFromFirstArgument,
'MGET' => $getKeyFromAllArguments,
'SET' => $getKeyFromFirstArgument,
'GETRANGE' => $getKeyFromFirstArgument,
'GETSET' => $getKeyFromFirstArgument,
'INCR' => $getKeyFromFirstArgument,
'INCRBY' => $getKeyFromFirstArgument,
'INCRBYFLOAT' => $getKeyFromFirstArgument,
'SETBIT' => $getKeyFromFirstArgument,
'SETEX' => $getKeyFromFirstArgument,
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
'SETNX' => $keyIsFirstArgument,
'SETRANGE' => $keyIsFirstArgument,
'STRLEN' => $keyIsFirstArgument,
'SUBSTR' => $keyIsFirstArgument,
'BITCOUNT' => $keyIsFirstArgument,
'SETNX' => $getKeyFromFirstArgument,
'SETRANGE' => $getKeyFromFirstArgument,
'STRLEN' => $getKeyFromFirstArgument,
'SUBSTR' => $getKeyFromFirstArgument,
'BITCOUNT' => $getKeyFromFirstArgument,
/* commands operating on lists */
'LINSERT' => $keyIsFirstArgument,
'LINDEX' => $keyIsFirstArgument,
'LLEN' => $keyIsFirstArgument,
'LPOP' => $keyIsFirstArgument,
'RPOP' => $keyIsFirstArgument,
'LINSERT' => $getKeyFromFirstArgument,
'LINDEX' => $getKeyFromFirstArgument,
'LLEN' => $getKeyFromFirstArgument,
'LPOP' => $getKeyFromFirstArgument,
'RPOP' => $getKeyFromFirstArgument,
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
'LPUSH' => $keyIsFirstArgument,
'LPUSHX' => $keyIsFirstArgument,
'RPUSH' => $keyIsFirstArgument,
'RPUSHX' => $keyIsFirstArgument,
'LRANGE' => $keyIsFirstArgument,
'LREM' => $keyIsFirstArgument,
'LSET' => $keyIsFirstArgument,
'LTRIM' => $keyIsFirstArgument,
'LPUSH' => $getKeyFromFirstArgument,
'LPUSHX' => $getKeyFromFirstArgument,
'RPUSH' => $getKeyFromFirstArgument,
'RPUSHX' => $getKeyFromFirstArgument,
'LRANGE' => $getKeyFromFirstArgument,
'LREM' => $getKeyFromFirstArgument,
'LSET' => $getKeyFromFirstArgument,
'LTRIM' => $getKeyFromFirstArgument,
/* commands operating on sets */
'SADD' => $keyIsFirstArgument,
'SCARD' => $keyIsFirstArgument,
'SISMEMBER' => $keyIsFirstArgument,
'SMEMBERS' => $keyIsFirstArgument,
'SSCAN' => $keyIsFirstArgument,
'SPOP' => $keyIsFirstArgument,
'SRANDMEMBER' => $keyIsFirstArgument,
'SREM' => $keyIsFirstArgument,
'SADD' => $getKeyFromFirstArgument,
'SCARD' => $getKeyFromFirstArgument,
'SISMEMBER' => $getKeyFromFirstArgument,
'SMEMBERS' => $getKeyFromFirstArgument,
'SSCAN' => $getKeyFromFirstArgument,
'SPOP' => $getKeyFromFirstArgument,
'SRANDMEMBER' => $getKeyFromFirstArgument,
'SREM' => $getKeyFromFirstArgument,
/* 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,
'ZSCAN' => $keyIsFirstArgument,
'ZADD' => $getKeyFromFirstArgument,
'ZCARD' => $getKeyFromFirstArgument,
'ZCOUNT' => $getKeyFromFirstArgument,
'ZINCRBY' => $getKeyFromFirstArgument,
'ZRANGE' => $getKeyFromFirstArgument,
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZRANK' => $getKeyFromFirstArgument,
'ZREM' => $getKeyFromFirstArgument,
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANGE' => $getKeyFromFirstArgument,
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANK' => $getKeyFromFirstArgument,
'ZSCORE' => $getKeyFromFirstArgument,
'ZSCAN' => $getKeyFromFirstArgument,
'ZLEXCOUNT' => $getKeyFromFirstArgument,
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
/* commands operating on hashes */
'HDEL' => $keyIsFirstArgument,
'HEXISTS' => $keyIsFirstArgument,
'HGET' => $keyIsFirstArgument,
'HGETALL' => $keyIsFirstArgument,
'HMGET' => $keyIsFirstArgument,
'HMSET' => $keyIsFirstArgument,
'HINCRBY' => $keyIsFirstArgument,
'HINCRBYFLOAT' => $keyIsFirstArgument,
'HKEYS' => $keyIsFirstArgument,
'HLEN' => $keyIsFirstArgument,
'HSET' => $keyIsFirstArgument,
'HSETNX' => $keyIsFirstArgument,
'HVALS' => $keyIsFirstArgument,
'HSCAN' => $keyIsFirstArgument,
'HDEL' => $getKeyFromFirstArgument,
'HEXISTS' => $getKeyFromFirstArgument,
'HGET' => $getKeyFromFirstArgument,
'HGETALL' => $getKeyFromFirstArgument,
'HMGET' => $getKeyFromFirstArgument,
'HMSET' => $getKeyFromFirstArgument,
'HINCRBY' => $getKeyFromFirstArgument,
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
'HKEYS' => $getKeyFromFirstArgument,
'HLEN' => $getKeyFromFirstArgument,
'HSET' => $getKeyFromFirstArgument,
'HSETNX' => $getKeyFromFirstArgument,
'HVALS' => $getKeyFromFirstArgument,
'HSCAN' => $getKeyFromFirstArgument,
/* commands operating on HyperLogLog */
'PFADD' => $getKeyFromFirstArgument,
'PFCOUNT' => $getKeyFromAllArguments,
'PFMERGE' => $getKeyFromAllArguments,
/* scripting */
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
@@ -166,7 +176,7 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
* 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.
* @param mixed $callback A valid callable object or NULL.
*/
public function setCommandHandler($commandId, $callback = null)
{
@@ -174,6 +184,7 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
if (!isset($callback)) {
unset($this->commands[$commandId]);
return;
}
@@ -187,7 +198,7 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
/**
* Extracts the key from the first argument of a command instance.
*
* @param CommandInterface $command Command instance.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromFirstArgument(CommandInterface $command)
@@ -199,7 +210,7 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
* 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.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromAllArguments(CommandInterface $command)
@@ -215,7 +226,7 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
* 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.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromInterleavedArguments(CommandInterface $command)
@@ -231,7 +242,7 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
* 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.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromBlockingListCommands(CommandInterface $command)
@@ -246,7 +257,7 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
/**
* Extracts the key from EVAL and EVALSHA commands.
*
* @param CommandInterface $command Command instance.
* @param CommandInterface $command Command instance.
* @return string
*/
protected function getKeyFromScriptingCommands(CommandInterface $command)
@@ -286,6 +297,27 @@ class RedisClusterHashStrategy implements CommandHashStrategyInterface
*/
public function getKeyHash($key)
{
return $this->hashGenerator->hash($key);
$key = $this->extractKeyTag($key);
$hash = $this->hashGenerator->hash($key);
return $hash;
}
/**
* 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)) && $end !== ++$start) {
$key = substr($key, $start, $end - $start);
}
}
return $key;
}
}
@@ -43,8 +43,8 @@ abstract class CursorBasedIterator implements Iterator
/**
* @param ClientInterface $client Client connected to Redis.
* @param string $match Pattern to match during the server-side iteration.
* @param int $count Hints used by Redis to compute the number of results per iteration.
* @param string $match Pattern to match during the server-side iteration.
* @param int $count Hints used by Redis to compute the number of results per iteration.
*/
public function __construct(ClientInterface $client, $match = null, $count = null)
{
@@ -60,8 +60,8 @@ abstract class CursorBasedIterator implements Iterator
* command required to fetch elements from the server to perform
* the iteration.
*
* @param ClientInterface Client connected to Redis.
* @param string $commandID Command ID.
* @param ClientInterface $client Client connected to Redis.
* @param string $commandID Command ID.
*/
protected function requiredCommand(ClientInterface $client, $commandID)
{
@@ -109,7 +109,7 @@ abstract class CursorBasedIterator implements Iterator
*
* @return array
*/
protected abstract function executeCommand();
abstract protected function executeCommand();
/**
* Populates the local buffer of elements fetched from the
@@ -166,16 +166,18 @@ abstract class CursorBasedIterator implements Iterator
*/
public function next()
{
if (!$this->elements && $this->fetchmore) {
$this->fetch();
}
tryFetch: {
if (!$this->elements && $this->fetchmore) {
$this->fetch();
}
if ($this->elements) {
$this->extractNext();
} else if ($this->cursor) {
$this->next();
} else {
$this->valid = false;
if ($this->elements) {
$this->extractNext();
} elseif ($this->cursor) {
goto tryFetch;
} else {
$this->valid = false;
}
}
}
+4 -4
View File
@@ -43,8 +43,8 @@ class ListKey implements Iterator
/**
* @param ClientInterface $client Client connected to Redis.
* @param string $key Redis list key.
* @param int $count Number of items retrieved on each fetch operation.
* @param string $key Redis list key.
* @param int $count Number of items retrieved on each fetch operation.
*/
public function __construct(ClientInterface $client, $key, $count = 10)
{
@@ -66,8 +66,8 @@ class ListKey implements Iterator
* command required to fetch elements from the server to perform
* the iteration.
*
* @param ClientInterface Client connected to Redis.
* @param string $commandID Command ID.
* @param ClientInterface $client Client connected to Redis.
* @param string $commandID Command ID.
*/
protected function requiredCommand(ClientInterface $client, $commandID)
{
+7 -11
View File
@@ -24,7 +24,7 @@ abstract class AbstractCommand implements CommandInterface
/**
* Returns a filtered array of the arguments.
*
* @param array $arguments List of arguments.
* @param array $arguments List of arguments.
* @return array
*/
protected function filterArguments(Array $arguments)
@@ -42,9 +42,7 @@ abstract class AbstractCommand implements CommandInterface
}
/**
* Sets the arguments array without filtering.
*
* @param array $arguments List of arguments.
* {@inheritdoc}
*/
public function setRawArguments(Array $arguments)
{
@@ -61,9 +59,7 @@ abstract class AbstractCommand implements CommandInterface
}
/**
* Gets the argument from the arguments list at the specified index.
*
* @param array $arguments Position of the argument.
* {@inheritdoc}
*/
public function getArgument($index)
{
@@ -101,8 +97,8 @@ abstract class AbstractCommand implements CommandInterface
/**
* Helper function used to reduce a list of arguments to a string.
*
* @param string $accumulator Temporary string.
* @param string $argument Current argument.
* @param string $accumulator Temporary string.
* @param string $argument Current argument.
* @return string
*/
protected function toStringArgumentReducer($accumulator, $argument)
@@ -133,7 +129,7 @@ abstract class AbstractCommand implements CommandInterface
/**
* Normalizes the arguments array passed to a Redis command.
*
* @param array $arguments Arguments for a command.
* @param array $arguments Arguments for a command.
* @return array
*/
public static function normalizeArguments(Array $arguments)
@@ -148,7 +144,7 @@ abstract class AbstractCommand implements CommandInterface
/**
* Normalizes the arguments array passed to a variadic Redis command.
*
* @param array $arguments Arguments for a command.
* @param array $arguments Arguments for a command.
* @return array
*/
public static function normalizeVariadic(Array $arguments)
+3 -2
View File
@@ -62,14 +62,15 @@ interface CommandInterface
/**
* Gets the argument of the command at the specified index.
*
* @return array
* @param int $index Index of the desired argument.
* @return mixed
*/
public function getArgument($index);
/**
* Parses a reply buffer and returns a PHP object.
*
* @param string $data Binary string containing the whole reply.
* @param string $data Binary string containing the whole reply.
* @return mixed
*/
public function parseResponse($data);
+1 -1
View File
@@ -41,7 +41,7 @@ class HashScan extends PrefixableCommand
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
* @param array $options List of options.
* @return array
*/
protected function prepareOptions($options)
+43
View File
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pfadd
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HyperLogLogAdd extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PFADD';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeVariadic($arguments);
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pfcount
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HyperLogLogCount extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PFCOUNT';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeArguments($arguments);
}
/**
* {@inheritdoc}
*/
public function prefixKeys($prefix)
{
PrefixHelpers::all($this, $prefix);
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/pfmerge
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HyperLogLogMerge extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'PFMERGE';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
return self::normalizeArguments($arguments);
}
/**
* {@inheritdoc}
*/
public function prefixKeys($prefix)
{
PrefixHelpers::all($this, $prefix);
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ class KeyScan extends AbstractCommand
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
* @param array $options List of options.
* @return array
*/
protected function prepareOptions($options)
+5 -5
View File
@@ -22,7 +22,7 @@ class PrefixHelpers
* Applies the specified prefix only the first argument.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
* @param string $prefix Prefix string.
*/
public static function first(CommandInterface $command, $prefix)
{
@@ -36,7 +36,7 @@ class PrefixHelpers
* Applies the specified prefix to all the arguments.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
* @param string $prefix Prefix string.
*/
public static function all(CommandInterface $command, $prefix)
{
@@ -53,7 +53,7 @@ class PrefixHelpers
* Applies the specified prefix only to even arguments in the list.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
* @param string $prefix Prefix string.
*/
public static function interleaved(CommandInterface $command, $prefix)
{
@@ -72,7 +72,7 @@ class PrefixHelpers
* Applies the specified prefix to all the arguments but the first one.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
* @param string $prefix Prefix string.
*/
public static function skipFirst(CommandInterface $command, $prefix)
{
@@ -91,7 +91,7 @@ class PrefixHelpers
* Applies the specified prefix to all the arguments but the last one.
*
* @param CommandInterface $command Command instance.
* @param string $prefix Prefix string.
* @param string $prefix Prefix string.
*/
public static function skipLast(CommandInterface $command, $prefix)
{
+157
View File
@@ -0,0 +1,157 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
use InvalidArgumentException;
/**
* Class for generic "anonymous" Redis commands.
*
* This command class does not filter input arguments or parse responses, but
* can be used to leverage the standard Predis API to execute any command simply
* by providing the needed arguments following the command signature as defined
* by Redis in its documentation.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RawCommand implements CommandInterface
{
private $hash;
private $commandID;
private $arguments;
public function __construct(array $arguments)
{
if (!$arguments) {
throw new InvalidArgumentException("Arguments array is missing the command ID");
}
$this->commandID = strtoupper(array_shift($arguments));
$this->arguments = $arguments;
}
/**
* Creates a new raw command using a variadic method.
*
* @param string $commandID Redis command ID.
* @param string ... Arguments list for the command.
* @return CommandInterface
*/
public static function create($commandID /* [ $arg, ... */)
{
$arguments = func_get_args();
$command = new self($arguments);
return $command;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return $this->commandID;
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
unset($this->hash);
}
/**
* {@inheritdoc}
*/
public function setRawArguments(array $arguments)
{
$this->setArguments($arguments);
}
/**
* {@inheritdoc}
*/
public function getArguments()
{
return $this->arguments;
}
/**
* {@inheritdoc}
*/
public function getArgument($index)
{
if (isset($this->arguments[$index])) {
return $this->arguments[$index];
}
}
/**
* {@inheritdoc}
*/
public function setHash($hash)
{
$this->hash = $hash;
}
/**
* {@inheritdoc}
*/
public function getHash()
{
if (isset($this->hash)) {
return $this->hash;
}
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return $data;
}
/**
* Helper function used to reduce a list of arguments to a string.
*
* @param string $accumulator Temporary string.
* @param string $argument Current argument.
* @return string
*/
protected function toStringArgumentReducer($accumulator, $argument)
{
if (strlen($argument) > 32) {
$argument = substr($argument, 0, 32) . '[...]';
}
$accumulator .= " $argument";
return $accumulator;
}
/**
* Returns a partial string representation of the command with its arguments.
*
* @return string
*/
public function __toString()
{
return array_reduce(
$this->getArguments(),
array($this, 'toStringArgumentReducer'),
$this->getId()
);
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ abstract class ScriptedCommand extends ServerEvalSHA
*
* @return string
*/
public abstract function getScript();
abstract public function getScript();
/**
* Specifies the number of arguments that should be considered as keys.
+5 -2
View File
@@ -12,7 +12,10 @@
namespace Predis\Command;
/**
* @link http://redis.io/commands/client
* @link http://redis.io/commands/client-list
* @link http://redis.io/commands/client-kill
* @link http://redis.io/commands/client-getname
* @link http://redis.io/commands/client-setname
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ServerClient extends AbstractCommand
@@ -47,7 +50,7 @@ class ServerClient extends AbstractCommand
* Parses the reply buffer and returns the list of clients returned by
* the CLIENT LIST command.
*
* @param string $data Reply buffer
* @param string $data Reply buffer
* @return array
*/
protected function parseClientList($data)
+25 -15
View File
@@ -34,31 +34,41 @@ class ServerInfo extends AbstractCommand
$infoLines = preg_split('/\r?\n/', $data);
foreach ($infoLines as $row) {
@list($k, $v) = explode(':', $row);
if ($row === '' || !isset($v)) {
if (strpos($row, ':') === false) {
continue;
}
if (!preg_match('/^db\d+$/', $k)) {
if ($k === 'allocation_stats') {
$info[$k] = $this->parseAllocationStats($v);
continue;
}
$info[$k] = $v;
} else {
$info[$k] = $this->parseDatabaseStats($v);
}
list($k, $v) = $this->parseRow($row);
$info[$k] = $v;
}
return $info;
}
/**
* Parses single row of the reply buffer and returns the key-value pair.
*
* @param string $row Single row of the reply buffer.
* @return array
*/
public function parseRow($row)
{
list($k, $v) = explode(':', $row, 2);
if (!preg_match('/^db\d+$/', $k)) {
if ($k === 'allocation_stats') {
$v = $this->parseAllocationStats($v);
}
} else {
$v = $this->parseDatabaseStats($v);
}
return array($k, $v);
}
/**
* Parses the reply buffer and extracts the statistics of each logical DB.
*
* @param string $str Reply buffer.
* @param string $str Reply buffer.
* @return array
*/
protected function parseDatabaseStats($str)
@@ -76,7 +86,7 @@ class ServerInfo extends AbstractCommand
/**
* Parses the reply buffer and extracts the allocation statistics.
*
* @param string $str Reply buffer.
* @param string $str Reply buffer.
* @return array
*/
protected function parseAllocationStats($str)
+2 -12
View File
@@ -41,18 +41,8 @@ class ServerInfoV26x extends ServerInfo
continue;
}
list($k, $v) = explode(':', $row);
if (!preg_match('/^db\d+$/', $k)) {
if ($k === 'allocation_stats') {
$current[$k] = $this->parseAllocationStats($v);
continue;
}
$current[$k] = $v;
} else {
$current[$k] = $this->parseDatabaseStats($v);
}
list($k, $v) = $this->parseRow($row);
$current[$k] = $v;
}
return $info;
+1 -1
View File
@@ -41,7 +41,7 @@ class SetScan extends PrefixableCommand
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
* @param array $options List of options.
* @return array
*/
protected function prepareOptions($options)
+1 -1
View File
@@ -33,7 +33,7 @@ class ZSetAdd extends PrefixableCommand
if (count($arguments) === 2 && is_array($arguments[1])) {
$flattened = array($arguments[0]);
foreach($arguments[1] as $member => $score) {
foreach ($arguments[1] as $member => $score) {
$flattened[] = $score;
$flattened[] = $member;
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/zlexcount
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ZSetLexCount extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'ZLEXCOUNT';
}
}
+3 -2
View File
@@ -41,6 +41,7 @@ class ZSetRange extends PrefixableCommand
if ($lastType === 'array') {
$options = $this->prepareOptions(array_pop($arguments));
return array_merge($arguments, $options);
}
}
@@ -51,7 +52,7 @@ class ZSetRange extends PrefixableCommand
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
* @param array $options List of options.
* @return array
*/
protected function prepareOptions($options)
@@ -69,7 +70,7 @@ class ZSetRange extends PrefixableCommand
/**
* Checks for the presence of the WITHSCORES modifier.
*
* @return Boolean
* @return bool
*/
protected function withScores()
{
+54
View File
@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/zrangebylex
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ZSetRangeByLex extends ZSetRange
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'ZRANGEBYLEX';
}
/**
* {@inheritdoc}
*/
protected function prepareOptions($options)
{
$opts = array_change_key_case($options, CASE_UPPER);
$finalizedOpts = array();
if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) {
$limit = array_change_key_case($opts['LIMIT'], CASE_UPPER);
$finalizedOpts[] = 'LIMIT';
$finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0];
$finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1];
}
return $finalizedOpts;
}
/**
* {@inheritdoc}
*/
protected function withScores()
{
return false;
}
}
@@ -0,0 +1,27 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/zremrangebylex
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ZSetRemoveRangeByLex extends PrefixableCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'ZREMRANGEBYLEX';
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ class ZSetScan extends PrefixableCommand
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
* @param array $options List of options.
* @return array
*/
protected function prepareOptions($options)
+1 -1
View File
@@ -50,7 +50,7 @@ class ZSetUnionStore extends PrefixableCommand
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
* @param array $options List of options.
* @return array
*/
private function prepareOptions($options)
+5 -5
View File
@@ -23,10 +23,10 @@ abstract class CommunicationException extends PredisException
private $connection;
/**
* @param SingleConnectionInterface $connection Connection that generated the exception.
* @param string $message Error message.
* @param int $code Error code.
* @param \Exception $innerException Inner exception for wrapping the original error.
* @param SingleConnectionInterface $connection Connection that generated the exception.
* @param string $message Error message.
* @param int $code Error code.
* @param \Exception $innerException Inner exception for wrapping the original error.
*/
public function __construct(
SingleConnectionInterface $connection, $message = null, $code = null, \Exception $innerException = null
@@ -48,7 +48,7 @@ abstract class CommunicationException extends PredisException
/**
* Indicates if the receiver should reset the underlying connection.
*
* @return Boolean
* @return bool
*/
public function shouldResetConnection()
{
+7 -5
View File
@@ -50,7 +50,8 @@ abstract class AbstractConnection implements SingleConnectionInterface
/**
* Checks some of the parameters used to initialize the connection.
*
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @param ConnectionParametersInterface $parameters Initialization parameters for the connection.
* @return ConnectionParametersInterface
*/
protected function checkParameters(ConnectionParametersInterface $parameters)
{
@@ -73,7 +74,7 @@ abstract class AbstractConnection implements SingleConnectionInterface
*
* @return mixed
*/
protected abstract function createResource();
abstract protected function createResource();
/**
* {@inheritdoc}
@@ -117,6 +118,7 @@ abstract class AbstractConnection implements SingleConnectionInterface
public function executeCommand(CommandInterface $command)
{
$this->writeCommand($command);
return $this->readResponse($command);
}
@@ -132,7 +134,7 @@ abstract class AbstractConnection implements SingleConnectionInterface
* Helper method to handle connection errors.
*
* @param string $message Error message.
* @param int $code Error code.
* @param int $code Error code.
*/
protected function onConnectionError($message, $code = null)
{
@@ -152,8 +154,8 @@ abstract class AbstractConnection implements SingleConnectionInterface
/**
* Helper method to handle not supported connection parameters.
*
* @param string $option Name of the option.
* @param mixed $parameters Parameters used to initialize the connection.
* @param string $option Name of the option.
* @param mixed $parameters Parameters used to initialize the connection.
*/
protected function onInvalidOption($option, $parameters = null)
{
@@ -31,15 +31,15 @@ interface AggregatedConnectionInterface extends ConnectionInterface
* Removes the specified connection instance from the aggregated
* connection.
*
* @param SingleConnectionInterface $connection Instance of a connection.
* @return Boolean Returns true if the connection was in the pool.
* @param SingleConnectionInterface $connection Instance of a connection.
* @return bool Returns true if the connection was in the pool.
*/
public function remove(SingleConnectionInterface $connection);
/**
* Gets the actual connection instance in charge of the specified command.
*
* @param CommandInterface $command Instance of a Redis command.
* @param CommandInterface $command Instance of a Redis command.
* @return SingleConnectionInterface
*/
public function getConnection(CommandInterface $command);
@@ -48,7 +48,7 @@ interface AggregatedConnectionInterface extends ConnectionInterface
* Retrieves a connection instance from the aggregated connection
* using an alias.
*
* @param string $connectionId Alias of a connection
* @param string $connectionId Alias of a connection
* @return SingleConnectionInterface
*/
public function getConnectionById($connectionId);
@@ -27,7 +27,7 @@ class ComposableStreamConnection extends StreamConnection implements ComposableC
/**
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @param ProtocolInterface $protocol A protocol processor.
* @param ProtocolInterface $protocol A protocol processor.
*/
public function __construct(ConnectionParametersInterface $parameters, ProtocolInterface $protocol = null)
{
+1 -2
View File
@@ -11,7 +11,6 @@
namespace Predis\Connection;
use Predis\Profile\ServerProfile;
use Predis\Profile\ServerProfileInterface;
/**
@@ -55,7 +54,7 @@ class ConnectionFactory implements ConnectionFactoryInterface
* implementing Predis\Connection\SingleConnectionInterface. Optionally,
* callable objects are used for lazy initialization of connection objects.
*
* @param mixed $initializer FQN of a connection class or a callable for lazy initialization.
* @param mixed $initializer FQN of a connection class or a callable for lazy initialization.
* @return mixed
*/
protected function checkInitializer($initializer)
@@ -22,8 +22,8 @@ interface ConnectionFactoryInterface
/**
* Defines or overrides the connection class identified by a scheme prefix.
*
* @param string $scheme URI scheme identifying the connection class.
* @param mixed $initializer FQN of a connection class or a callable object for lazy initialization.
* @param string $scheme URI scheme identifying the connection class.
* @param mixed $initializer FQN of a connection class or a callable object for lazy initialization.
*/
public function define($scheme, $initializer);
@@ -37,7 +37,7 @@ interface ConnectionFactoryInterface
/**
* Creates a new connection object.
*
* @param mixed $parameters Parameters for the connection.
* @param mixed $parameters Parameters for the connection.
* @return SingleConnectionInterface
*/
public function create($parameters);
@@ -45,8 +45,8 @@ interface ConnectionFactoryInterface
/**
* Prepares an aggregation of connection objects.
*
* @param AggregatedConnectionInterface $cluster Instance of an aggregated connection class.
* @param array $parameters List of parameters for each connection object.
* @param AggregatedConnectionInterface $cluster Instance of an aggregated connection class.
* @param array $parameters List of parameters for each connection object.
* @return AggregatedConnectionInterface
*/
public function createAggregated(AggregatedConnectionInterface $cluster, Array $parameters);
@@ -34,7 +34,7 @@ interface ConnectionInterface
/**
* Returns if the connection is open.
*
* @return Boolean
* @return bool
*/
public function isConnected();
@@ -48,7 +48,7 @@ interface ConnectionInterface
/**
* Reads the reply for a Redis command from the connection.
*
* @param CommandInterface $command Instance of a Redis command.
* @param CommandInterface $command Instance of a Redis command.
* @return mixed
*/
public function readResponse(CommandInterface $command);
@@ -56,7 +56,7 @@ interface ConnectionInterface
/**
* Writes a Redis command to the connection and reads back the reply.
*
* @param CommandInterface $command Instance of a Redis command.
* @param CommandInterface $command Instance of a Redis command.
* @return mixed
*/
public function executeCommand(CommandInterface $command);
+10 -12
View File
@@ -30,7 +30,7 @@ class ConnectionParameters implements ConnectionParametersInterface
);
/**
* @param string|array Connection parameters in the form of an URI string or a named array.
* @param string|array $parameters Connection parameters in the form of an URI string or a named array.
*/
public function __construct($parameters = array())
{
@@ -71,8 +71,8 @@ class ConnectionParameters implements ConnectionParametersInterface
/**
* Validates value as boolean.
*
* @param mixed $value Input value.
* @return boolean
* @param mixed $value Input value.
* @return bool
*/
private static function castBoolean($value)
{
@@ -82,7 +82,7 @@ class ConnectionParameters implements ConnectionParametersInterface
/**
* Validates value as float.
*
* @param mixed $value Input value.
* @param mixed $value Input value.
* @return float
*/
private static function castFloat($value)
@@ -93,7 +93,7 @@ class ConnectionParameters implements ConnectionParametersInterface
/**
* Validates value as integer.
*
* @param mixed $value Input value.
* @param mixed $value Input value.
* @return int
*/
private static function castInteger($value)
@@ -104,7 +104,7 @@ class ConnectionParameters implements ConnectionParametersInterface
/**
* Parses an URI string and returns an array of connection parameters.
*
* @param string $uri Connection string.
* @param string $uri Connection string.
* @return array
*/
public static function parseURI($uri)
@@ -119,12 +119,10 @@ class ConnectionParameters implements ConnectionParametersInterface
}
if (isset($parsed['query'])) {
foreach (explode('&', $parsed['query']) as $kv) {
@list($k, $v) = explode('=', $kv);
$parsed[$k] = $v;
}
parse_str($parsed['query'], $queryarray);
unset($parsed['query']);
$parsed = array_merge($parsed, $queryarray);
}
return $parsed;
@@ -133,7 +131,7 @@ class ConnectionParameters implements ConnectionParametersInterface
/**
* Validates and converts each value of the connection parameters array.
*
* @param array $parameters Connection parameters.
* @param array $parameters Connection parameters.
* @return array
*/
private function filter(Array $parameters)
@@ -22,15 +22,15 @@ interface ConnectionParametersInterface
/**
* Checks if the specified parameters is set.
*
* @param string $property Name of the property.
* @return Boolean
* @param string $parameter Name of the parameter.
* @return bool
*/
public function __isset($parameter);
/**
* Returns the value of the specified parameter.
*
* @param string $parameter Name of the parameter.
* @param string $parameter Name of the parameter.
* @return mixed
*/
public function __get($parameter);
+12 -8
View File
@@ -37,6 +37,7 @@ use Predis\Command\CommandInterface;
* - scheme: it can be either 'tcp' or 'unix'.
* - host: hostname or IP address of the server.
* - port: TCP port of the server.
* - path: path of a UNIX domain socket when scheme is 'unix'.
* - timeout: timeout to perform the connection.
* - read_write_timeout: timeout of read / write operations.
*
@@ -136,7 +137,6 @@ class PhpiredisConnection extends AbstractConnection
/**
* Gets the handler used by the protocol reader to handle Redis errors.
*
* @param Boolean $throw_errors Specify if Redis errors throw exceptions.
* @return \Closure
*/
private function getErrorHandler()
@@ -183,7 +183,7 @@ class PhpiredisConnection extends AbstractConnection
/**
* Sets options on the socket resource from the connection parameters.
*
* @param resource $socket Socket resource.
* @param resource $socket Socket resource.
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
*/
private function setSocketOptions($socket, ConnectionParametersInterface $parameters)
@@ -223,10 +223,10 @@ class PhpiredisConnection extends AbstractConnection
/**
* Gets the address from the connection parameters.
*
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @return string
*/
private function getAddress(ConnectionParametersInterface $parameters)
protected static function getAddress(ConnectionParametersInterface $parameters)
{
if ($parameters->scheme === 'unix') {
return $parameters->path;
@@ -235,9 +235,10 @@ class PhpiredisConnection extends AbstractConnection
$host = $parameters->host;
if (ip2long($host) === false) {
if (($addresses = gethostbynamel($host)) === false) {
$this->onConnectionError("Cannot resolve the address of $host");
if (false === $addresses = gethostbynamel($host)) {
return false;
}
return $addresses[array_rand($addresses)];
}
@@ -247,12 +248,15 @@ class PhpiredisConnection extends AbstractConnection
/**
* Opens the actual connection to the server with a timeout.
*
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @return string
*/
private function connectWithTimeout(ConnectionParametersInterface $parameters)
{
$host = self::getAddress($parameters);
if (false === $host = self::getAddress($parameters)) {
$this->onConnectionError("Cannot resolve the address of '$parameters->host'.");
}
$socket = $this->getResource();
socket_set_nonblock($socket);
@@ -37,6 +37,7 @@ use Predis\Command\CommandInterface;
* - scheme: it can be either 'tcp' or 'unix'.
* - host: hostname or IP address of the server.
* - port: TCP port of the server.
* - path: path of a UNIX domain socket when scheme is 'unix'.
* - timeout: timeout to perform the connection.
* - read_write_timeout: timeout of read / write operations.
* - async_connect: performs the connection asynchronously.
@@ -132,7 +133,6 @@ class PhpiredisStreamConnection extends StreamConnection
/**
* Gets the handler used by the protocol reader to handle Redis errors.
*
* @param Boolean $throw_errors Specify if Redis errors throw exceptions.
* @return \Closure
*/
protected function getErrorHandler()
@@ -151,11 +151,10 @@ class PhpiredisStreamConnection extends StreamConnection
$reader = $this->reader;
while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) {
$buffer = fread($socket, 4096);
$buffer = stream_socket_recvfrom($socket, 4096);
if ($buffer === false || $buffer === '') {
$this->onConnectionError('Error while reading bytes from the server');
return;
}
phpiredis_reader_feed($reader, $buffer);
+4 -4
View File
@@ -112,8 +112,8 @@ class PredisCluster implements ClusterConnectionInterface, \IteratorAggregate, \
/**
* Removes a connection instance using its alias or index.
*
* @param string $connectionId Alias or index of a connection.
* @return Boolean Returns true if the connection was in the pool.
* @param string $connectionId Alias or index of a connection.
* @return bool Returns true if the connection was in the pool.
*/
public function removeById($connectionId)
{
@@ -151,7 +151,7 @@ class PredisCluster implements ClusterConnectionInterface, \IteratorAggregate, \
/**
* Retrieves a connection instance from the cluster using a key.
*
* @param string $key Key of a Redis value.
* @param string $key Key of a Redis value.
* @return SingleConnectionInterface
*/
public function getConnectionByKey($key)
@@ -216,7 +216,7 @@ class PredisCluster implements ClusterConnectionInterface, \IteratorAggregate, \
/**
* Executes the specified Redis command on all the nodes of a cluster.
*
* @param CommandInterface $command A Redis command.
* @param CommandInterface $command A Redis command.
* @return array
*/
public function executeCommandOnNodes(CommandInterface $command)
+305 -158
View File
@@ -11,24 +11,46 @@
namespace Predis\Connection;
use Predis\ClientException;
use Predis\Cluster\CommandHashStrategyInterface;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use OutOfBoundsException;
use Predis\NotSupportedException;
use Predis\ResponseErrorInterface;
use Predis\Cluster\CommandHashStrategyInterface;
use Predis\Cluster\RedisClusterHashStrategy;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
/**
* Abstraction for Redis cluster (Redis v3.0).
* Abstraction for a Redis-backed cluster of nodes (Redis >= 3.0.0).
*
* This connection backend offers smart support for redis-cluster by handling
* automatic slots map (re)generation upon -MOVE or -ASK responses returned by
* Redis when redirecting a client to a different node.
*
* The cluster can be pre-initialized using only a subset of the actual nodes in
* the cluster, Predis will do the rest by adjusting the slots map and creating
* the missing underlying connection instances on the fly.
*
* It is possible to pre-associate connections to a slots range with the "slots"
* parameter in the form "$first-$last". This can greatly reduce runtime node
* guessing and redirections.
*
* It is also possible to ask for the full and updated slots map directly to one
* of the nodes and optionally enable such a behaviour upon -MOVED redirections.
* Asking for the cluster configuration to Redis is actually done by issuing a
* CLUSTER NODES command to a random node in the pool.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \Countable
class RedisCluster implements ClusterConnectionInterface, IteratorAggregate, Countable
{
private $pool;
private $slots;
private $askClusterNodes = false;
private $defaultParameters = array();
private $pool = array();
private $slots = array();
private $slotsMap;
private $slotsPerNode;
private $strategy;
private $connections;
@@ -37,8 +59,6 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
*/
public function __construct(ConnectionFactoryInterface $connections = null)
{
$this->pool = array();
$this->slots = array();
$this->strategy = new RedisClusterHashStrategy();
$this->connections = $connections ?: new ConnectionFactory();
}
@@ -62,7 +82,7 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
*/
public function connect()
{
foreach ($this->pool as $connection) {
if ($connection = $this->getRandomConnection()) {
$connection->connect();
}
}
@@ -83,10 +103,7 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
public function add(SingleConnectionInterface $connection)
{
$this->pool[(string) $connection] = $connection;
unset(
$this->slotsMap,
$this->slotsPerNode
);
unset($this->slotsMap);
}
/**
@@ -94,11 +111,10 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
*/
public function remove(SingleConnectionInterface $connection)
{
if (($id = array_search($connection, $this->pool, true)) !== false) {
if (false !== $id = array_search($connection, $this->pool, true)) {
unset(
$this->pool[$id],
$this->slotsMap,
$this->slotsPerNode
$this->slotsMap
);
return true;
@@ -108,18 +124,17 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
}
/**
* Removes a connection instance using its alias or index.
* Removes a connection instance by using its identifier.
*
* @param string $connectionId Alias or index of a connection.
* @return Boolean Returns true if the connection was in the pool.
* @param string $connectionID Connection identifier.
* @return bool True if the connection was in the pool.
*/
public function removeById($connectionId)
public function removeById($connectionID)
{
if (isset($this->pool[$connectionId])) {
if (isset($this->pool[$connectionID])) {
unset(
$this->pool[$connectionId],
$this->slotsMap,
$this->slotsPerNode
$this->pool[$connectionID],
$this->slotsMap
);
return true;
@@ -129,14 +144,17 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
}
/**
* Builds the slots map for the cluster.
* Generates the current slots map by guessing the cluster configuration out
* of the connection parameters of the connections in the pool.
*
* @return array
* Generation is based on the same algorithm used by Redis to generate the
* cluster, so it is most effective when all of the connections supplied on
* initialization have the "slots" parameter properly set accordingly to the
* current cluster configuration.
*/
public function buildSlotsMap()
{
$this->slotsMap = array();
$this->slotsPerNode = (int) (16384 / count($this->pool));
foreach ($this->pool as $connectionID => $connection) {
$parameters = $connection->getParameters();
@@ -145,11 +163,42 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
continue;
}
list($first, $last) = explode('-', $parameters->slots, 2);
$this->setSlots($first, $last, $connectionID);
$slots = explode('-', $parameters->slots, 2);
$this->setSlots($slots[0], $slots[1], $connectionID);
}
}
/**
* Generates the current slots map by fetching the cluster configuration to
* one of the nodes by leveraging the CLUSTER NODES command.
*/
public function askClusterNodes()
{
if (!$connection = $this->getRandomConnection()) {
return array();
}
return $this->slotsMap;
$cmdCluster = RawCommand::create('CLUSTER', 'NODES');
$response = $connection->executeCommand($cmdCluster);
$nodes = explode("\n", $response, -1);
$count = count($nodes);
for ($i = 0; $i < $count; $i++) {
$node = explode(' ', $nodes[$i], 9);
if (false === strpos($node[2], 'master')) {
continue;
}
$slots = explode('-', $node[8], 2);
if ($node[1] === ':0') {
$this->setSlots($slots[0], $slots[1], (string) $connection);
} else {
$this->setSlots($slots[0], $slots[1], $node[1]);
}
}
}
/**
@@ -167,79 +216,34 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
}
/**
* Preassociate a connection to a set of slots to avoid runtime guessing.
* Pre-associates a connection to a slots range to avoid runtime guessing.
*
* @todo Check type or existence of the specified connection.
* @todo Cluster loses the slots assigned with this methods when adding / removing connections.
*
* @param int $first Initial slot.
* @param int $last Last slot.
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
* @param SingleConnectionInterface|string $connection ID or connection instance.
*/
public function setSlots($first, $last, $connection)
{
if ($first < 0x0000 || $first > 0x3FFF || $last < 0x0000 || $last > 0x3FFF || $last < $first) {
throw new \OutOfBoundsException("Invalid slot values for $connection: [$first-$last]");
if ($first < 0x0000 || $first > 0x3FFF ||
$last < 0x0000 || $last > 0x3FFF ||
$last < $first
) {
throw new OutOfBoundsException(
"Invalid slot range for $connection: [$first-$last]"
);
}
$this->slotsMap = $this->getSlotsMap() + array_fill($first, $last - $first + 1, (string) $connection);
$slots = array_fill($first, $last - $first + 1, (string) $connection);
$this->slotsMap = $this->getSlotsMap() + $slots;
}
/**
* {@inheritdoc}
*/
public function getConnection(CommandInterface $command)
{
$hash = $this->strategy->getHash($command);
if (!isset($hash)) {
throw new NotSupportedException("Cannot use {$command->getId()} with redis-cluster");
}
$slot = $hash & 0x3FFF;
if (isset($this->slots[$slot])) {
return $this->slots[$slot];
}
$this->slots[$slot] = $connection = $this->pool[$this->guessNode($slot)];
return $connection;
}
/**
* Returns the connection associated to the specified slot.
* Guesses the correct node associated to a given slot using a precalculated
* slots map, falling back to the same logic used by Redis to initialize a
* cluster (best-effort).
*
* @param int $slot Slot ID.
* @return SingleConnectionInterface
*/
public function getConnectionBySlot($slot)
{
if ($slot < 0x0000 || $slot > 0x3FFF) {
throw new \OutOfBoundsException("Invalid slot value [$slot]");
}
if (isset($this->slots[$slot])) {
return $this->slots[$slot];
}
return $this->pool[$this->guessNode($slot)];
}
/**
* {@inheritdoc}
*/
public function getConnectionById($connectionId)
{
return isset($this->pool[$connectionId]) ? $this->pool[$connectionId] : null;
}
/**
* Tries guessing the correct node associated to the given slot using a precalculated
* slots map or the same logic used by redis-trib to initialize a redis cluster.
*
* @param int $slot Slot ID.
* @return string
* @param int $slot Slot index.
* @return string Connection ID.
*/
protected function guessNode($slot)
{
@@ -251,51 +255,109 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
return $this->slotsMap[$slot];
}
$index = min((int) ($slot / $this->slotsPerNode), count($this->pool) - 1);
$count = count($this->pool);
$index = min((int) ($slot / (int) (16384 / $count)), $count - 1);
$nodes = array_keys($this->pool);
return $nodes[$index];
}
/**
* Handles -MOVED or -ASK replies by re-executing the command on the server
* specified by the Redis reply.
* Creates a new connection instance from the given connection ID.
*
* @param CommandInterface $command Command that generated the -MOVE or -ASK reply.
* @param string $request Type of request (either 'MOVED' or 'ASK').
* @param string $details Parameters of the MOVED/ASK request.
* @return mixed
* @param string $connectionID Identifier for the connection.
* @return SingleConnectionInterface
*/
protected function onMoveRequest(CommandInterface $command, $request, $details)
protected function createConnection($connectionID)
{
list($slot, $host) = explode(' ', $details, 2);
$connection = $this->getConnectionById($host);
$host = explode(':', $connectionID, 2);
if (!isset($connection)) {
$parameters = array('host' => null, 'port' => null);
list($parameters['host'], $parameters['port']) = explode(':', $host, 2);
$connection = $this->connections->create($parameters);
$parameters = array_merge($this->defaultParameters, array(
'host' => $host[0],
'port' => $host[1],
));
$connection = $this->connections->create($parameters);
return $connection;
}
/**
* {@inheritdoc}
*/
public function getConnection(CommandInterface $command)
{
$hash = $this->strategy->getHash($command);
if (!isset($hash)) {
throw new NotSupportedException(
"Cannot use {$command->getId()} with redis-cluster"
);
}
switch ($request) {
case 'MOVED':
$this->move($connection, $slot);
return $this->executeCommand($command);
$slot = $hash & 0x3FFF;
case 'ASK':
return $connection->executeCommand($command);
default:
throw new ClientException("Unexpected request type for a move request: $request");
if (isset($this->slots[$slot])) {
return $this->slots[$slot];
} else {
return $this->getConnectionBySlot($slot);
}
}
/**
* Assign the connection instance to a new slot and adds it to the
* pool if the connection was not already part of the pool.
* Returns the connection currently associated to a given slot.
*
* @param SingleConnectionInterface $connection Connection instance
* @param int $slot Target slot.
* @param int $slot Slot index.
* @return SingleConnectionInterface
*/
public function getConnectionBySlot($slot)
{
if ($slot < 0x0000 || $slot > 0x3FFF) {
throw new OutOfBoundsException("Invalid slot [$slot]");
}
if (isset($this->slots[$slot])) {
return $this->slots[$slot];
}
$connectionID = $this->guessNode($slot);
if (!$connection = $this->getConnectionById($connectionID)) {
$connection = $this->createConnection($connectionID);
$this->pool[$connectionID] = $connection;
}
return $this->slots[$slot] = $connection;
}
/**
* {@inheritdoc}
*/
public function getConnectionById($connectionID)
{
if (isset($this->pool[$connectionID])) {
return $this->pool[$connectionID];
}
}
/**
* Returns a random connection from the pool.
*
* @return SingleConnectionInterface
*/
protected function getRandomConnection()
{
if ($this->pool) {
return $this->pool[array_rand($this->pool)];
}
}
/**
* Permanently associates the connection instance to a new slot.
* The connection is added to the connections pool if not yet included.
*
* @param SingleConnectionInterface $connection Connection instance.
* @param int $slot Target slot index.
*/
protected function move(SingleConnectionInterface $connection, $slot)
{
@@ -304,53 +366,76 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
}
/**
* Returns the underlying command hash strategy used to hash
* commands by their keys.
* Handles -ERR responses returned by Redis.
*
* @return CommandHashStrategyInterface
*/
public function getCommandHashStrategy()
{
return $this->strategy;
}
/**
* {@inheritdoc}
*/
public function count()
{
return count($this->pool);
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new \ArrayIterator(array_values($this->pool));
}
/**
* Handles -ERR replies from Redis.
*
* @param CommandInterface $command Command that generated the -ERR reply.
* @param ResponseErrorInterface $error Redis error reply object.
* @param CommandInterface $command Command that generated the -ERR response.
* @param ResponseErrorInterface $error Redis error response object.
* @return mixed
*/
protected function handleServerError(CommandInterface $command, ResponseErrorInterface $error)
protected function onErrorResponse(CommandInterface $command, ResponseErrorInterface $error)
{
list($type, $details) = explode(' ', $error->getMessage(), 2);
$details = explode(' ', $error->getMessage(), 2);
switch ($type) {
switch ($details[0]) {
case 'MOVED':
return $this->onMovedResponse($command, $details[1]);
case 'ASK':
return $this->onMoveRequest($command, $type, $details);
return $this->onAskResponse($command, $details[1]);
default:
return $error;
}
}
/**
* Handles -MOVED responses by executing again the command against the node
* indicated by the Redis response.
*
* @param CommandInterface $command Command that generated the -MOVED response.
* @param string $details Parameters of the -MOVED response.
* @return mixed
*/
protected function onMovedResponse(CommandInterface $command, $details)
{
list($slot, $connectionID) = explode(' ', $details, 2);
if (!$connection = $this->getConnectionById($connectionID)) {
$connection = $this->createConnection($connectionID);
}
if ($this->askClusterNodes) {
$this->askClusterNodes();
}
$this->move($connection, $slot);
$response = $this->executeCommand($command);
return $response;
}
/**
* Handles -ASK responses by executing again the command against the node
* indicated by the Redis response.
*
* @param CommandInterface $command Command that generated the -ASK response.
* @param string $details Parameters of the -ASK response.
* @return mixed
*/
protected function onAskResponse(CommandInterface $command, $details)
{
list($slot, $connectionID) = explode(' ', $details, 2);
if (!$connection = $this->getConnectionById($connectionID)) {
$connection = $this->createConnection($connectionID);
}
$connection->executeCommand(RawCommand::create('ASKING'));
$response = $connection->executeCommand($command);
return $response;
}
/**
* {@inheritdoc}
*/
@@ -373,12 +458,74 @@ class RedisCluster implements ClusterConnectionInterface, \IteratorAggregate, \C
public function executeCommand(CommandInterface $command)
{
$connection = $this->getConnection($command);
$reply = $connection->executeCommand($command);
$response = $connection->executeCommand($command);
if ($reply instanceof ResponseErrorInterface) {
return $this->handleServerError($command, $reply);
if ($response instanceof ResponseErrorInterface) {
return $this->onErrorResponse($command, $response);
}
return $reply;
return $response;
}
/**
* {@inheritdoc}
*/
public function count()
{
return count($this->pool);
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new ArrayIterator(array_values($this->pool));
}
/**
* Returns the underlying hash strategy used to hash commands by their keys.
*
* @return CommandHashStrategyInterface
*/
public function getCommandHashStrategy()
{
return $this->strategy;
}
/**
* Enables automatic fetching of the current slots map from one of the nodes
* using the CLUSTER NODES command. This option is disabled by default but
* asking the current slots map to Redis upon -MOVE responses may reduce
* overhead by eliminating the trial-and-error nature of the node guessing
* procedure, mostly when targeting many keys that would end up in a lot of
* redirections.
*
* The slots map can still be manually fetched using the askClusterNodes()
* method whether or not this option is enabled.
*
* @param bool $value Enable or disable the use of CLUSTER NODES.
*/
public function enableClusterNodes($value)
{
$this->askClusterNodes = (bool) $value;
}
/**
* Sets a default array of connection parameters to be applied when creating
* new connection instances on the fly when they are not part of the initial
* pool supplied upon cluster initialization.
*
* These parameters are not applied to connections added to the pool using
* the add() method.
*
* @param array $parameters Array of connection parameters.
*/
public function setDefaultParameters(array $parameters)
{
$this->defaultParameters = array_merge(
$this->defaultParameters,
$parameters ?: array()
);
}
}
+14 -11
View File
@@ -23,6 +23,7 @@ use Predis\Iterator\MultiBulkResponseSimple;
* - scheme: it can be either 'tcp' or 'unix'.
* - host: hostname or IP address of the server.
* - port: TCP port of the server.
* - path: path of a UNIX domain socket when scheme is 'unix'.
* - timeout: timeout to perform the connection.
* - read_write_timeout: timeout of read / write operations.
* - async_connect: performs the connection asynchronously.
@@ -72,19 +73,21 @@ class StreamConnection extends AbstractConnection
/**
* Initializes a TCP stream resource.
*
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @return resource
*/
private function tcpStreamInitializer(ConnectionParametersInterface $parameters)
{
$uri = "tcp://{$parameters->host}:{$parameters->port}/";
$uri = "tcp://{$parameters->host}:{$parameters->port}";
$flags = STREAM_CLIENT_CONNECT;
if (isset($parameters->async_connect) && $parameters->async_connect) {
$flags |= STREAM_CLIENT_ASYNC_CONNECT;
}
if (isset($parameters->persistent) && $parameters->persistent) {
$flags |= STREAM_CLIENT_PERSISTENT;
$uri .= strpos($path = $parameters->path, '/') === 0 ? $path : "/$path";
}
$resource = @stream_socket_client($uri, $errno, $errstr, $parameters->timeout, $flags);
@@ -101,7 +104,7 @@ class StreamConnection extends AbstractConnection
stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds);
}
if (isset($parameters->tcp_nodelay) && version_compare(PHP_VERSION, '5.4.0') >= 0) {
if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) {
$socket = socket_import_stream($resource);
socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay);
}
@@ -112,7 +115,7 @@ class StreamConnection extends AbstractConnection
/**
* Initializes a UNIX stream resource.
*
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @return resource
*/
private function unixStreamInitializer(ConnectionParametersInterface $parameters)
@@ -180,7 +183,7 @@ class StreamConnection extends AbstractConnection
$socket = $this->getResource();
while (($length = strlen($buffer)) > 0) {
$written = fwrite($socket, $buffer);
$written = @fwrite($socket, $buffer);
if ($length === $written) {
return;
@@ -209,7 +212,7 @@ class StreamConnection extends AbstractConnection
$payload = substr($chunk, 1, -2);
switch ($prefix) {
case '+': // inline
case '+':
switch ($payload) {
case 'OK':
return true;
@@ -221,7 +224,7 @@ class StreamConnection extends AbstractConnection
return $payload;
}
case '$': // bulk
case '$':
$size = (int) $payload;
if ($size === -1) {
return null;
@@ -243,7 +246,7 @@ class StreamConnection extends AbstractConnection
return substr($bulkData, 0, -2);
case '*': // multi bulk
case '*':
$count = (int) $payload;
if ($count === -1) {
@@ -261,10 +264,10 @@ class StreamConnection extends AbstractConnection
return $multibulk;
case ':': // integer
case ':':
return (int) $payload;
case '-': // error
case '-':
return new ResponseError($payload);
default:
@@ -285,7 +288,7 @@ class StreamConnection extends AbstractConnection
$buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandId}\r\n";
for ($i = 0; $i < $reqlen - 1; $i++) {
for ($i = 0, $reqlen--; $i < $reqlen; $i++) {
$argument = $arguments[$i];
$arglen = strlen($argument);
$buffer .= "\${$arglen}\r\n{$argument}\r\n";
+5 -5
View File
@@ -102,7 +102,7 @@ class WebdisConnection implements SingleConnectionInterface
/**
* Initializes cURL.
*
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @return resource
*/
private function initializeCurl(ConnectionParametersInterface $parameters)
@@ -128,7 +128,7 @@ class WebdisConnection implements SingleConnectionInterface
/**
* Initializes phpiredis' protocol reader.
*
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @param ConnectionParametersInterface $parameters Parameters used to initialize the connection.
* @return resource
*/
private function initializeReader(ConnectionParametersInterface $parameters)
@@ -168,8 +168,8 @@ class WebdisConnection implements SingleConnectionInterface
/**
* Feeds phpredis' reader resource with the data read from the network.
*
* @param resource $resource Reader resource.
* @param string $buffer Buffer with the reply read from the network.
* @param resource $resource Reader resource.
* @param string $buffer Buffer with the reply read from the network.
* @return int
*/
protected function feedReader($resource, $buffer)
@@ -206,7 +206,7 @@ class WebdisConnection implements SingleConnectionInterface
/**
* Checks if the specified command is supported by this connection class.
*
* @param CommandInterface $command The instance of a Redis command.
* @param CommandInterface $command The instance of a Redis command.
* @return string
*/
protected function getCommandId(CommandInterface $command)
+1 -1
View File
@@ -22,7 +22,7 @@ interface ExecutableContextInterface
/**
* Starts the execution of the context.
*
* @param mixed $callable Optional callback for execution.
* @param mixed $callable Optional callback for execution.
* @return array
*/
public function execute($callable = null);
+2 -2
View File
@@ -43,7 +43,7 @@ class Helpers
* Normalizes the arguments array passed to a Redis command.
*
* @deprecated Deprecated since v0.8.3 - moved in Predis\Command\AbstractCommand::normalizeArguments()
* @param array $arguments Arguments for a command.
* @param array $arguments Arguments for a command.
* @return array
*/
public static function filterArrayArguments(Array $arguments)
@@ -59,7 +59,7 @@ class Helpers
* Normalizes the arguments array passed to a variadic Redis command.
*
* @deprecated Deprecated since v0.8.3 - moved in Predis\Command\AbstractCommand::normalizeVariadic()
* @param array $arguments Arguments for a command.
* @param array $arguments Arguments for a command.
* @return array
*/
public static function filterVariadicValues(Array $arguments)
+1 -1
View File
@@ -96,5 +96,5 @@ abstract class MultiBulkResponse implements \Iterator, \Countable, ResponseObjec
/**
* {@inheritdoc}
*/
protected abstract function getValue();
abstract protected function getValue();
}
@@ -24,7 +24,7 @@ class MultiBulkResponseSimple extends MultiBulkResponse
/**
* @param SingleConnectionInterface $connection Connection to Redis.
* @param int $size Number of elements of the multibulk reply.
* @param int $size Number of elements of the multibulk reply.
*/
public function __construct(SingleConnectionInterface $connection, $size)
{
@@ -49,8 +49,8 @@ class MultiBulkResponseSimple extends MultiBulkResponse
* read from the connection by consuming the rest of the multibulk reply,
* or simply by dropping the connection.
*
* @param Boolean $drop True to synchronize the client by dropping the connection.
* False to synchronize the client by consuming the multibulk reply.
* @param bool $drop True to synchronize the client by dropping the connection.
* False to synchronize the client by consuming the multibulk reply.
*/
public function sync($drop = false)
{
+1 -1
View File
@@ -117,7 +117,7 @@ class MonitorContext implements \Iterator
/**
* Checks if the the context is still in a valid state to continue.
*
* @return Boolean
* @return bool
*/
public function valid()
{
+4 -3
View File
@@ -25,7 +25,7 @@ class ClientCluster extends AbstractOption
/**
* Checks if the specified value is a valid instance of ClusterConnectionInterface.
*
* @param ClusterConnectionInterface $cluster Instance of a connection cluster.
* @param ClusterConnectionInterface $cluster Instance of a connection cluster.
* @return ClusterConnectionInterface
*/
protected function checkInstance($cluster)
@@ -54,8 +54,8 @@ class ClientCluster extends AbstractOption
/**
* Returns an initializer for the specified FQN or type.
*
* @param string $fqnOrType Type of cluster or FQN of a class implementing ClusterConnectionInterface.
* @param ClientOptionsInterface $options Instance of the client options.
* @param string $fqnOrType Type of cluster or FQN of a class implementing ClusterConnectionInterface.
* @param ClientOptionsInterface $options Instance of the client options.
* @return \Closure
*/
protected function getInitializer(ClientOptionsInterface $options, $fqnOrType)
@@ -79,6 +79,7 @@ class ClientCluster extends AbstractOption
if (is_string($fqnOrType) && !class_exists($fqnOrType)) {
throw new \InvalidArgumentException("Class $fqnOrType does not exist");
}
return function () use ($fqnOrType) {
return new $fqnOrType();
};
@@ -50,7 +50,7 @@ class ClientConnectionFactory extends AbstractOption
return $factory;
}
if (@class_exists($value)) {
if (is_string($value) && @class_exists($value)) {
$factory = new $value();
if (!$factory instanceof ConnectionFactoryInterface) {
+5 -5
View File
@@ -51,7 +51,7 @@ class ClientOptions implements ClientOptionsInterface
/**
* Initializes client options handlers.
*
* @param array $options List of client options values.
* @param array $options List of client options values.
* @return array
*/
protected function initialize(Array $options)
@@ -75,8 +75,8 @@ class ClientOptions implements ClientOptionsInterface
/**
* Checks if the specified option is set.
*
* @param string $option Name of the option.
* @return Boolean
* @param string $option Name of the option.
* @return bool
*/
public function __isset($option)
{
@@ -86,7 +86,7 @@ class ClientOptions implements ClientOptionsInterface
/**
* Returns the value of the specified option.
*
* @param string $option Name of the option.
* @param string $option Name of the option.
* @return mixed
*/
public function __get($option)
@@ -107,7 +107,7 @@ class ClientOptions implements ClientOptionsInterface
/**
* Returns the default value for the specified option.
*
* @param string|OptionInterface $option Name or instance of the option.
* @param string|OptionInterface $option Name or instance of the option.
* @return mixed
*/
public function getDefault($option)
+1 -1
View File
@@ -24,7 +24,7 @@ class ClientReplication extends AbstractOption
/**
* Checks if the specified value is a valid instance of ReplicationConnectionInterface.
*
* @param ReplicationConnectionInterface $connection Instance of a replication connection.
* @param ReplicationConnectionInterface $connection Instance of a replication connection.
* @return ReplicationConnectionInterface
*/
protected function checkInstance($connection)
+2 -2
View File
@@ -33,8 +33,8 @@ class CustomOption implements OptionInterface
/**
* Checks if the specified value in the options array is a callable object.
*
* @param array $options Array of options
* @param string $key Target option.
* @param array $options Array of options
* @param string $key Target option.
*/
private function ensureCallable($options, $key)
{
+7 -5
View File
@@ -21,7 +21,8 @@ interface OptionInterface
/**
* Filters (and optionally converts) the passed value.
*
* @param mixed $value Input value.
* @param ClientOptionsInterface $options Options container.
* @param mixed $value Input value.
* @return mixed
*/
public function filter(ClientOptionsInterface $options, $value);
@@ -29,16 +30,17 @@ interface OptionInterface
/**
* Returns a default value for the option.
*
* @param mixed $value Input value.
* @param ClientOptionsInterface $options Options container.
* @return mixed
*/
public function getDefault(ClientOptionsInterface $options);
/**
* Filters a value and, if no value is specified, returns
* the default one defined by the option.
* Filters a value and, if no value is specified, returns the default one
* defined by the option.
*
* @param mixed $value Input value.
* @param ClientOptionsInterface $options Options container.
* @param mixed $value Input value.
* @return mixed
*/
public function __invoke(ClientOptionsInterface $options, $value);
+4 -4
View File
@@ -100,8 +100,8 @@ class MultiExecExecutor implements PipelineExecutorInterface
/**
* Consumes an iterator response returned by EXEC.
*
* @param SplQueue $commands Pipelined commands
* @param Iterator $responses Responses returned by EXEC.
* @param SplQueue $commands Pipelined commands
* @param Iterator $responses Responses returned by EXEC.
* @return array
*/
protected function consumeIteratorResponse(SplQueue $commands, Iterator $responses)
@@ -129,8 +129,8 @@ class MultiExecExecutor implements PipelineExecutorInterface
/**
* Consumes an array response returned by EXEC.
*
* @param SplQueue $commands Pipelined commands
* @param Array $responses Responses returned by EXEC.
* @param SplQueue $commands Pipelined commands
* @param Array $responses Responses returned by EXEC.
* @return array
*/
protected function consumeArrayResponse(SplQueue $commands, Array &$responses)
+12 -9
View File
@@ -34,7 +34,7 @@ class PipelineContext implements BasicClientInterface, ExecutableContextInterfac
private $running = false;
/**
* @param ClientInterface $client Client instance used by the context.
* @param ClientInterface $client Client instance used by the context.
* @param PipelineExecutorInterface $executor Pipeline executor instace.
*/
public function __construct(ClientInterface $client, PipelineExecutorInterface $executor = null)
@@ -48,7 +48,7 @@ class PipelineContext implements BasicClientInterface, ExecutableContextInterfac
* Returns a pipeline executor depending on the kind of the underlying
* connection and the passed options.
*
* @param ClientInterface $client Client instance used by the context.
* @param ClientInterface $client Client instance used by the context.
* @return PipelineExecutorInterface
*/
protected function createExecutor(ClientInterface $client)
@@ -65,9 +65,9 @@ class PipelineContext implements BasicClientInterface, ExecutableContextInterfac
/**
* Queues a command into the pipeline buffer.
*
* @param string $method Command ID.
* @param array $arguments Arguments for the command.
* @return PipelineContext
* @param string $method Command ID.
* @param array $arguments Arguments for the command.
* @return $this
*/
public function __call($method, $arguments)
{
@@ -91,16 +91,19 @@ class PipelineContext implements BasicClientInterface, ExecutableContextInterfac
* Queues a command instance into the pipeline buffer.
*
* @param CommandInterface $command Command to queue in the buffer.
* @return $this
*/
public function executeCommand(CommandInterface $command)
{
$this->recordCommand($command);
return $this;
}
/**
* Flushes the buffer that holds the queued commands.
*
* @param Boolean $send Specifies if the commands in the buffer should be sent to Redis.
* @param bool $send Specifies if the commands in the buffer should be sent to Redis.
* @return PipelineContext
*/
public function flushPipeline($send = true)
@@ -119,8 +122,8 @@ class PipelineContext implements BasicClientInterface, ExecutableContextInterfac
/**
* Marks the running status of the pipeline.
*
* @param Boolean $bool True if the pipeline is running.
* False if the pipeline is not running.
* @param bool $bool True if the pipeline is running.
* False if the pipeline is not running.
*/
private function setRunning($bool)
{
@@ -134,7 +137,7 @@ class PipelineContext implements BasicClientInterface, ExecutableContextInterfac
/**
* Handles the actual execution of the whole pipeline.
*
* @param mixed $callable Optional callback for execution.
* @param mixed $callable Optional callback for execution.
* @return array
*/
public function execute($callable = null)
@@ -25,8 +25,8 @@ interface PipelineExecutorInterface
/**
* Writes a list of commands to the network and reads back their replies.
*
* @param ConnectionInterface $connection Connection to Redis.
* @param SplQueue $commands Commands queued for execution.
* @param ConnectionInterface $connection Connection to Redis.
* @param SplQueue $commands Commands queued for execution.
* @return array
*/
public function execute(ConnectionInterface $connection, SplQueue $commands);
+6 -5
View File
@@ -56,9 +56,9 @@ class StandardExecutor implements PipelineExecutorInterface
/**
* Handles a response object.
*
* @param ConnectionInterface $connection
* @param CommandInterface $command
* @param ResponseObjectInterface $response
* @param ConnectionInterface $connection
* @param CommandInterface $command
* @param ResponseObjectInterface $response
* @return mixed
*/
protected function onResponseObject(ConnectionInterface $connection, CommandInterface $command, ResponseObjectInterface $response)
@@ -77,8 +77,9 @@ class StandardExecutor implements PipelineExecutorInterface
/**
* Handles -ERR responses returned by Redis.
*
* @param ConnectionInterface $connection The connection that returned the error.
* @param ResponseErrorInterface $response The error response instance.
* @param ConnectionInterface $connection The connection that returned the error.
* @param ResponseErrorInterface $response The error response instance.
* @return ResponseErrorInterface
*/
protected function onResponseError(ConnectionInterface $connection, ResponseErrorInterface $response)
{
+6 -6
View File
@@ -41,7 +41,7 @@ abstract class ServerProfile implements ServerProfileInterface, CommandProcessin
*
* @return array
*/
protected abstract function getSupportedCommands();
abstract protected function getSupportedCommands();
/**
* Returns the default server profile.
@@ -78,7 +78,7 @@ abstract class ServerProfile implements ServerProfileInterface, CommandProcessin
'2.4' => 'Predis\Profile\ServerVersion24',
'2.6' => 'Predis\Profile\ServerVersion26',
'2.8' => 'Predis\Profile\ServerVersion28',
'default' => 'Predis\Profile\ServerVersion26',
'default' => 'Predis\Profile\ServerVersion28',
'dev' => 'Predis\Profile\ServerVersionNext',
);
}
@@ -86,7 +86,7 @@ abstract class ServerProfile implements ServerProfileInterface, CommandProcessin
/**
* Registers a new server profile.
*
* @param string $alias Profile version or alias.
* @param string $alias Profile version or alias.
* @param string $profileClass FQN of a class implementing Predis\Profile\ServerProfileInterface.
*/
public static function define($alias, $profileClass)
@@ -107,7 +107,7 @@ abstract class ServerProfile implements ServerProfileInterface, CommandProcessin
/**
* Returns the specified server profile.
*
* @param string $version Profile version or alias.
* @param string $version Profile version or alias.
* @return ServerProfileInterface
*/
public static function get($version)
@@ -151,7 +151,7 @@ abstract class ServerProfile implements ServerProfileInterface, CommandProcessin
* Returns the FQN of the class that represent the specified command ID
* registered in the current server profile.
*
* @param string $command Command ID.
* @param string $command Command ID.
* @return string
*/
public function getCommandClass($command)
@@ -186,7 +186,7 @@ abstract class ServerProfile implements ServerProfileInterface, CommandProcessin
/**
* Defines a new commands in the server profile.
*
* @param string $alias Command ID.
* @param string $alias Command ID.
* @param string $command FQN of a class implementing Predis\Command\CommandInterface.
*/
public function defineCommand($alias, $command)
@@ -32,15 +32,15 @@ interface ServerProfileInterface
/**
* Checks if the profile supports the specified command.
*
* @param string $command Command ID.
* @return Boolean
* @param string $command Command ID.
* @return bool
*/
public function supportsCommand($command);
/**
* Checks if the profile supports the specified list of commands.
*
* @param array $commands List of command IDs.
* @param array $commands List of command IDs.
* @return string
*/
public function supportsCommands(Array $commands);
@@ -48,8 +48,8 @@ interface ServerProfileInterface
/**
* Creates a new command instance.
*
* @param string $method Command ID.
* @param array $arguments Arguments for the command.
* @param string $method Command ID.
* @param array $arguments Arguments for the command.
* @return CommandInterface
*/
public function createCommand($method, $arguments = array());
-1
View File
@@ -121,7 +121,6 @@ class ServerVersion20 extends ServerProfile
'shutdown' => 'Predis\Command\ServerShutdown',
'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF',
/* ---------------- Redis 2.0 ---------------- */
/* commands operating on string values */
-2
View File
@@ -121,7 +121,6 @@ class ServerVersion22 extends ServerProfile
'shutdown' => 'Predis\Command\ServerShutdown',
'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF',
/* ---------------- Redis 2.0 ---------------- */
/* commands operating on string values */
@@ -170,7 +169,6 @@ class ServerVersion22 extends ServerProfile
/* remote server control commands */
'config' => 'Predis\Command\ServerConfig',
/* ---------------- Redis 2.2 ---------------- */
/* commands operating on the key space */
-3
View File
@@ -121,7 +121,6 @@ class ServerVersion24 extends ServerProfile
'shutdown' => 'Predis\Command\ServerShutdown',
'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF',
/* ---------------- Redis 2.0 ---------------- */
/* commands operating on string values */
@@ -170,7 +169,6 @@ class ServerVersion24 extends ServerProfile
/* remote server control commands */
'config' => 'Predis\Command\ServerConfig',
/* ---------------- Redis 2.2 ---------------- */
/* commands operating on the key space */
@@ -200,7 +198,6 @@ class ServerVersion24 extends ServerProfile
'object' => 'Predis\Command\ServerObject',
'slowlog' => 'Predis\Command\ServerSlowlog',
/* ---------------- Redis 2.4 ---------------- */
/* remote server control commands */
+1 -6
View File
@@ -111,7 +111,7 @@ class ServerVersion26 extends ServerProfile
'quit' => 'Predis\Command\ConnectionQuit',
/* remote server control commands */
'info' => 'Predis\Command\ServerInfo',
'info' => 'Predis\Command\ServerInfoV26x',
'slaveof' => 'Predis\Command\ServerSlaveOf',
'monitor' => 'Predis\Command\ServerMonitor',
'dbsize' => 'Predis\Command\ServerDatabaseSize',
@@ -123,7 +123,6 @@ class ServerVersion26 extends ServerProfile
'shutdown' => 'Predis\Command\ServerShutdown',
'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF',
/* ---------------- Redis 2.0 ---------------- */
/* commands operating on string values */
@@ -172,7 +171,6 @@ class ServerVersion26 extends ServerProfile
/* remote server control commands */
'config' => 'Predis\Command\ServerConfig',
/* ---------------- Redis 2.2 ---------------- */
/* commands operating on the key space */
@@ -202,13 +200,11 @@ class ServerVersion26 extends ServerProfile
'object' => 'Predis\Command\ServerObject',
'slowlog' => 'Predis\Command\ServerSlowlog',
/* ---------------- Redis 2.4 ---------------- */
/* remote server control commands */
'client' => 'Predis\Command\ServerClient',
/* ---------------- Redis 2.6 ---------------- */
/* commands operating on the key space */
@@ -231,7 +227,6 @@ class ServerVersion26 extends ServerProfile
'script' => 'Predis\Command\ServerScript',
/* remote server control commands */
'info' => 'Predis\Command\ServerInfoV26x',
'time' => 'Predis\Command\ServerTime',
);
}
+9 -7
View File
@@ -111,7 +111,7 @@ class ServerVersion28 extends ServerProfile
'quit' => 'Predis\Command\ConnectionQuit',
/* remote server control commands */
'info' => 'Predis\Command\ServerInfo',
'info' => 'Predis\Command\ServerInfoV26x',
'slaveof' => 'Predis\Command\ServerSlaveOf',
'monitor' => 'Predis\Command\ServerMonitor',
'dbsize' => 'Predis\Command\ServerDatabaseSize',
@@ -123,7 +123,6 @@ class ServerVersion28 extends ServerProfile
'shutdown' => 'Predis\Command\ServerShutdown',
'bgrewriteaof' => 'Predis\Command\ServerBackgroundRewriteAOF',
/* ---------------- Redis 2.0 ---------------- */
/* commands operating on string values */
@@ -172,7 +171,6 @@ class ServerVersion28 extends ServerProfile
/* remote server control commands */
'config' => 'Predis\Command\ServerConfig',
/* ---------------- Redis 2.2 ---------------- */
/* commands operating on the key space */
@@ -202,13 +200,11 @@ class ServerVersion28 extends ServerProfile
'object' => 'Predis\Command\ServerObject',
'slowlog' => 'Predis\Command\ServerSlowlog',
/* ---------------- Redis 2.4 ---------------- */
/* remote server control commands */
'client' => 'Predis\Command\ServerClient',
/* ---------------- Redis 2.6 ---------------- */
/* commands operating on the key space */
@@ -231,10 +227,8 @@ class ServerVersion28 extends ServerProfile
'script' => 'Predis\Command\ServerScript',
/* remote server control commands */
'info' => 'Predis\Command\ServerInfoV26x',
'time' => 'Predis\Command\ServerTime',
/* ---------------- Redis 2.8 ---------------- */
/* commands operating on the key space */
@@ -245,9 +239,17 @@ class ServerVersion28 extends ServerProfile
/* commands operating on sorted sets */
'zscan' => 'Predis\Command\ZSetScan',
'zlexcount' => 'Predis\Command\ZSetLexCount',
'zrangebylex' => 'Predis\Command\ZSetRangeByLex',
'zremrangebylex' => 'Predis\Command\ZSetRemoveRangeByLex',
/* commands operating on hashes */
'hscan' => 'Predis\Command\HashScan',
/* commands operating on HyperLogLog */
'pfadd' => 'Predis\Command\HyperLogLogAdd',
'pfcount' => 'Predis\Command\HyperLogLogCount',
'pfmerge' => 'Predis\Command\HyperLogLogMerge',
);
}
}
@@ -23,7 +23,7 @@ interface CommandSerializerInterface
/**
* Serializes a Redis command.
*
* @param CommandInterface $command Redis command.
* @param CommandInterface $command Redis command.
* @return string
*/
public function serialize(CommandInterface $command);
+2 -2
View File
@@ -26,7 +26,7 @@ interface ProtocolInterface extends ResponseReaderInterface
* Writes a Redis command on the specified connection.
*
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param CommandInterface $command Redis command.
* @param CommandInterface $command Redis command.
*/
public function write(ComposableConnectionInterface $connection, CommandInterface $command);
@@ -34,7 +34,7 @@ interface ProtocolInterface extends ResponseReaderInterface
* Sets the options for the protocol processor.
*
* @param string $option Name of the option.
* @param mixed $value Value of the option.
* @param mixed $value Value of the option.
*/
public function setOption($option, $value);
}
@@ -24,9 +24,9 @@ interface ResponseHandlerInterface
* Parses a type of reply returned by Redis and reads more data from the
* connection if needed.
*
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $payload Initial payload of the reply.
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $payload Initial payload of the reply.
* @return mixed
*/
function handle(ComposableConnectionInterface $connection, $payload);
public function handle(ComposableConnectionInterface $connection, $payload);
}
@@ -24,7 +24,7 @@ interface ResponseReaderInterface
/**
* Reads replies from a connection to Redis and deserializes them.
*
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param ComposableConnectionInterface $connection Connection to Redis.
* @return mixed
*/
public function read(ComposableConnectionInterface $connection);
@@ -28,8 +28,8 @@ class ResponseBulkHandler implements ResponseHandlerInterface
/**
* Handles a bulk reply returned by Redis.
*
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $lengthString Bytes size of the bulk reply.
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $lengthString Bytes size of the bulk reply.
* @return string
*/
public function handle(ComposableConnectionInterface $connection, $lengthString)
@@ -28,8 +28,8 @@ class ResponseIntegerHandler implements ResponseHandlerInterface
/**
* Handles an integer reply returned by Redis.
*
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $number String representation of an integer.
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $number String representation of an integer.
* @return int
*/
public function handle(ComposableConnectionInterface $connection, $number)
@@ -28,8 +28,8 @@ class ResponseMultiBulkHandler implements ResponseHandlerInterface
/**
* Handles a multi-bulk reply returned by Redis.
*
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $lengthString Number of items in the multi-bulk reply.
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $lengthString Number of items in the multi-bulk reply.
* @return array
*/
public function handle(ComposableConnectionInterface $connection, $lengthString)
@@ -29,8 +29,8 @@ class ResponseMultiBulkStreamHandler implements ResponseHandlerInterface
/**
* Handles a multi-bulk reply returned by Redis in a streamable fashion.
*
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $lengthString Number of items in the multi-bulk reply.
* @param ComposableConnectionInterface $connection Connection to Redis.
* @param string $lengthString Number of items in the multi-bulk reply.
* @return MultiBulkResponseSimple
*/
public function handle(ComposableConnectionInterface $connection, $lengthString)
@@ -36,7 +36,7 @@ class TextCommandSerializer implements CommandSerializerInterface
$buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandId}\r\n";
for ($i = 0; $i < $reqlen - 1; $i++) {
for ($i = 0, $reqlen--; $i < $reqlen; $i++) {
$argument = $arguments[$i];
$arglen = strlen($argument);
$buffer .= "\${$arglen}\r\n{$argument}\r\n";
+6 -5
View File
@@ -72,7 +72,7 @@ class TextProtocol implements ProtocolInterface
$payload = substr($chunk, 1);
switch ($prefix) {
case '+': // inline
case '+':
switch ($payload) {
case 'OK':
return true;
@@ -84,14 +84,15 @@ class TextProtocol implements ProtocolInterface
return $payload;
}
case '$': // bulk
case '$':
$size = (int) $payload;
if ($size === -1) {
return null;
}
return substr($connection->readBytes($size + 2), 0, -2);
case '*': // multi bulk
case '*':
$count = (int) $payload;
if ($count === -1) {
@@ -109,10 +110,10 @@ class TextProtocol implements ProtocolInterface
return $multibulk;
case ':': // integer
case ':':
return (int) $payload;
case '-': // error
case '-':
return new ResponseError($payload);
default:
@@ -55,7 +55,7 @@ class TextResponseReader implements ResponseReaderInterface
* Sets a response handler for a certain prefix that identifies a type of
* reply that can be returned by Redis.
*
* @param string $prefix Identifier for a type of reply.
* @param string $prefix Identifier for a type of reply.
* @param ResponseHandlerInterface $handler Response handler for the reply.
*/
public function setHandler($prefix, ResponseHandlerInterface $handler)
@@ -67,7 +67,7 @@ class TextResponseReader implements ResponseReaderInterface
* Returns the response handler associated to a certain type of reply that
* can be returned by Redis.
*
* @param string $prefix Identifier for a type of reply.
* @param string $prefix Identifier for a type of reply.
* @return ResponseHandlerInterface
*/
public function getHandler($prefix)
@@ -104,7 +104,7 @@ class TextResponseReader implements ResponseReaderInterface
* reply from a connection to Redis.
*
* @param ComposableConnectionInterface $connection Connection to Redis that generated the error.
* @param string $message Error message.
* @param string $message Error message.
*/
private function protocolError(ComposableConnectionInterface $connection, $message)
{
+18 -18
View File
@@ -43,8 +43,8 @@ abstract class AbstractPubSubContext implements \Iterator
/**
* Checks if the specified flag is valid in the state of the context.
*
* @param int $value Flag.
* @return Boolean
* @param int $value Flag.
* @return bool
*/
protected function isFlagSet($value)
{
@@ -54,9 +54,9 @@ abstract class AbstractPubSubContext implements \Iterator
/**
* Subscribes to the specified channels.
*
* @param mixed $arg,... One or more channel names.
* @param mixed $channel,... One or more channel names.
*/
public function subscribe(/* arguments */)
public function subscribe($channel /*, ... */)
{
$this->writeCommand(self::SUBSCRIBE, func_get_args());
$this->statusFlags |= self::STATUS_SUBSCRIBED;
@@ -65,9 +65,9 @@ abstract class AbstractPubSubContext implements \Iterator
/**
* Unsubscribes from the specified channels.
*
* @param mixed $arg,... One or more channel names.
* @param string ... One or more channel names.
*/
public function unsubscribe(/* arguments */)
public function unsubscribe(/* ... */)
{
$this->writeCommand(self::UNSUBSCRIBE, func_get_args());
}
@@ -75,9 +75,9 @@ abstract class AbstractPubSubContext implements \Iterator
/**
* Subscribes to the specified channels using a pattern.
*
* @param mixed $arg,... One or more channel name patterns.
* @param mixed $pattern,... One or more channel name patterns.
*/
public function psubscribe(/* arguments */)
public function psubscribe($pattern /* ... */)
{
$this->writeCommand(self::PSUBSCRIBE, func_get_args());
$this->statusFlags |= self::STATUS_PSUBSCRIBED;
@@ -86,9 +86,9 @@ abstract class AbstractPubSubContext implements \Iterator
/**
* Unsubscribes from the specified channels using a pattern.
*
* @param mixed $arg,... One or more channel name patterns.
* @param string ... One or more channel name patterns.
*/
public function punsubscribe(/* arguments */)
public function punsubscribe(/* ... */)
{
$this->writeCommand(self::PUNSUBSCRIBE, func_get_args());
}
@@ -98,8 +98,8 @@ abstract class AbstractPubSubContext implements \Iterator
* Optionally, the context can be forcefully closed by dropping the
* underlying connection.
*
* @param Boolean $force Forcefully close the context by closing the connection.
* @return Boolean Returns false if there are no pending messages.
* @param bool $force Forcefully close the context by closing the connection.
* @return bool Returns false if there are no pending messages.
*/
public function closeContext($force = false)
{
@@ -125,15 +125,15 @@ abstract class AbstractPubSubContext implements \Iterator
/**
* Closes the underlying connection on forced disconnection.
*/
protected abstract function disconnect();
abstract protected function disconnect();
/**
* Writes a Redis command on the underlying connection.
*
* @param string $method ID of the command.
* @param array $arguments List of arguments.
* @param string $method ID of the command.
* @param array $arguments List of arguments.
*/
protected abstract function writeCommand($method, $arguments);
abstract protected function writeCommand($method, $arguments);
/**
* {@inheritdoc}
@@ -177,7 +177,7 @@ abstract class AbstractPubSubContext implements \Iterator
/**
* Checks if the the context is still in a valid state to continue.
*
* @return Boolean
* @return bool
*/
public function valid()
{
@@ -202,5 +202,5 @@ abstract class AbstractPubSubContext implements \Iterator
*
* @return array
*/
protected abstract function getValue();
abstract protected function getValue();
}
+2 -2
View File
@@ -90,7 +90,7 @@ class DispatcherLoop
/**
* Binds a callback to a channel.
*
* @param string $channel Channel name.
* @param string $channel Channel name.
* @param Callable $callback A callback.
*/
public function attachCallback($channel, $callback)
@@ -137,7 +137,7 @@ class DispatcherLoop
if (isset($this->callbacks[$message->channel])) {
$callback = $this->callbacks[$message->channel];
call_user_func($callback, $message->payload);
} else if (isset($this->defaultCallback)) {
} elseif (isset($this->defaultCallback)) {
$callback = $this->defaultCallback;
call_user_func($callback, $message);
}
+4 -2
View File
@@ -28,8 +28,8 @@ class PubSubContext extends AbstractPubSubContext
private $options;
/**
* @param ClientInterface $client Client instance used by the context.
* @param array $options Options for the context initialization.
* @param ClientInterface $client Client instance used by the context.
* @param array $options Options for the context initialization.
*/
public function __construct(ClientInterface $client, Array $options = null)
{
@@ -115,6 +115,8 @@ class PubSubContext extends AbstractPubSubContext
if ($response[2] === 0) {
$this->invalidate();
}
// The missing break here is intentional as we must process
// subscriptions and unsubscriptions as standard messages.
case self::MESSAGE:
return (object) array(
+13 -9
View File
@@ -39,8 +39,8 @@ class ReplicationStrategy
* Returns if the specified command performs a read-only operation
* against a key stored on Redis.
*
* @param CommandInterface $command Instance of Redis command.
* @return Boolean
* @param CommandInterface $command Instance of Redis command.
* @return bool
*/
public function isReadOperation(CommandInterface $command)
{
@@ -75,8 +75,8 @@ class ReplicationStrategy
* Returns if the specified command is disallowed in a master/slave
* replication context.
*
* @param CommandInterface $command Instance of Redis command.
* @return Boolean
* @param CommandInterface $command Instance of Redis command.
* @return bool
*/
public function isDisallowedOperation(CommandInterface $command)
{
@@ -87,12 +87,13 @@ class ReplicationStrategy
* Checks if a SORT command is a readable operation by parsing the arguments
* array of the specified commad instance.
*
* @param CommandInterface $command Instance of Redis command.
* @return Boolean
* @param CommandInterface $command Instance of Redis command.
* @return bool
*/
protected function isSortReadOnly(CommandInterface $command)
{
$arguments = $command->getArguments();
return ($c = count($arguments)) === 1 ? true : $arguments[$c - 2] !== 'STORE';
}
@@ -103,7 +104,7 @@ class ReplicationStrategy
* instance of a command performs write operations or not.
*
* @param string $commandID ID of the command.
* @param mixed $readonly A boolean or a callable object.
* @param mixed $readonly A boolean or a callable object.
*/
public function setCommandReadOnly($commandID, $readonly = true)
{
@@ -123,8 +124,8 @@ class ReplicationStrategy
* if the passed instance of EVAL or EVALSHA performs write operations or
* not.
*
* @param string $script Body of the Lua script.
* @param mixed $readonly A boolean or a callable object.
* @param string $script Body of the Lua script.
* @param mixed $readonly A boolean or a callable object.
*/
public function setScriptReadOnly($script, $readonly = true)
{
@@ -200,6 +201,8 @@ class ReplicationStrategy
'ZRANK' => true,
'ZREVRANK' => true,
'ZSCAN' => true,
'ZLEXCOUNT' => true,
'ZRANGEBYLEX' => true,
'HGET' => true,
'HMGET' => true,
'HEXISTS' => true,
@@ -216,6 +219,7 @@ class ReplicationStrategy
'OBJECT' => true,
'BITCOUNT' => true,
'TIME' => true,
'PFCOUNT' => true,
'SORT' => array($this, 'isSortReadOnly'),
);
}
+1
View File
@@ -43,6 +43,7 @@ class ResponseError implements ResponseErrorInterface
public function getErrorType()
{
list($errorType, ) = explode(' ', $this->getMessage(), 2);
return $errorType;
}

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