Compare commits

..

136 Commits

Author SHA1 Message Date
Daniele Alessandri 9ead747663 Update CHANGELOG and bump VERSION. 2016-05-30 17:25:52 +02:00
Daniele Alessandri 39cf6abf82 [tests] Do not allow failures for HHVM on Travis CI. 2016-05-30 17:00:46 +02:00
Daniele Alessandri dbae384a64 [tests] Troubles with HHVM <= 3.6.6 and float timeouts.
HHVM is still being used by Travis CI but this bug makes the build to
take 14 minutes to complete, which is unacceptable.
2016-05-30 17:00:14 +02:00
Daniele Alessandri d9ea0ce14f Update README of test directory.
[ci skip]
2016-05-27 22:01:45 +02:00
Daniele Alessandri 55f940e400 Update README.
[ci skip]
2016-05-27 21:32:47 +02:00
Daniele Alessandri 648be824d3 Fix fetching slots map from unreachable nodes.
When various nodes in the configuration are unreachable while trying
to send a command, we should attempt to contact a reachable node to
fetch an updated slots map up to $retryLimit times or until there are
no more servers in the pool before giving up.

It is possible that the slots map fetched from Redis contains stale
data and points to a dead server, this happens when the nodes still
have to agree that a master server is down before promoting a slave
to the role of master. In this case no further attempts to execute
the command are performed and an exception is thrown.

This still needs some more testing and will delay v1.0.4 a few days
past its scheduled release.
2016-05-27 14:28:12 +02:00
Daniele Alessandri c4e0f2f037 Run php-cs-fixer. 2016-05-26 09:52:18 +02:00
Daniele Alessandri 62f5ce6baf Update CHANGELOG. 2016-05-25 10:57:01 +02:00
Daniele Alessandri 9cd4ed9e1e Update SPOP's @method signature in phpdoc.
SPOP accepts the optional "count" argument since Redis 3.2.
2016-05-24 23:28:40 +02:00
Daniele Alessandri 8491b1d880 Add new command: GEORADIUSBYMEMBER (Redis 3.2.0). 2016-05-24 23:28:23 +02:00
Daniele Alessandri 8c8261432f Add new command: GEORADIUS (Redis 3.2.0). 2016-05-24 23:28:11 +02:00
Daniele Alessandri 5eccd6ee3c Add new command: GEODIST (Redis 3.2.0). 2016-05-24 23:28:01 +02:00
Daniele Alessandri 3d9588ec1a Add new command: GEOPOS (Redis 3.2.0). 2016-05-24 23:27:50 +02:00
Daniele Alessandri e96f7f748a Add new command: GEOHASH (Redis 3.2.0). 2016-05-24 23:27:33 +02:00
Daniele Alessandri dd0edf761a Add new command: GEOADD (Redis 3.2.0). 2016-05-24 23:27:10 +02:00
Daniele Alessandri 8cbbd073cb Add new command: BITFIELD (Redis 3.2.0). 2016-05-24 23:26:48 +02:00
Daniele Alessandri 1e521b56cd [tests] Test count argument for SPOP in Redis 3.2. 2016-05-24 23:25:46 +02:00
Daniele Alessandri fcacd65576 Add new server profile for Redis 3.2 (new stable). 2016-05-24 23:25:31 +02:00
Daniele Alessandri d5488f81f7 Check that STORE key in SORT ends up in same slot. 2016-05-24 22:09:13 +02:00
Daniele Alessandri 188dc6bf62 Improve detection of STORE in SORT command arguments. 2016-05-24 21:39:26 +02:00
Daniele Alessandri 0bd57ab744 [tests] Add missing test for key prefix in HSTRLEN. 2016-05-24 15:28:39 +02:00
Daniele Alessandri b996b005cf Fix cluster strategy to handle variadic EXISTS (Redis >= 3.0.3). 2016-05-24 15:26:35 +02:00
Daniele Alessandri 82116b0366 Fix prefix processor to handle variadic EXISTS (Redis >= 3.0.3).
Backported from master.

Conflicts:
	src/Command/Processor/KeyPrefixProcessor.php
2016-05-24 15:26:01 +02:00
Daniele Alessandri 5c1ddc9cfa Add missing @method tag for HSTRLEN in phpdocs.
[ci skip]
2016-05-24 12:26:50 +02:00
Daniele Alessandri ea2fd9700d [tests] Missing @requiresRedisVersion for HSTRLEN. 2016-05-24 11:50:15 +02:00
Daniele Alessandri b2ec264ff9 Update description in package.ini. 2016-05-24 10:27:37 +02:00
Daniele Alessandri bf7a70d6f0 Update phpunit.xml.* 2016-05-22 20:21:52 +02:00
Daniele Alessandri 4ecc44e1ee Update description in composer.json. 2016-05-22 20:05:31 +02:00
Daniele Alessandri 52d5b23db7 Fix README.
[ci skip]
2016-05-22 15:32:08 +02:00
Daniele Alessandri d31a4c71ba Update CHANGELOG.
[ci skip]
2016-05-22 15:24:39 +02:00
Daniele Alessandri c92e305852 Update README.
Blame copy&paste practices for the noise (or just blame me).

[ci skip]
2016-05-22 15:21:09 +02:00
Daniele Alessandri b075564278 Update and improve README.
[ci skip]
2016-05-22 15:12:14 +02:00
Daniele Alessandri f9e817f14c Run php-cs-fixer. 2016-05-21 15:49:25 +02:00
Daniele Alessandri 9e568396c6 Remove old references about HHVM being unstable. 2016-05-20 20:35:58 +02:00
Daniele Alessandri 2a91ebaba9 [tests] Rename old branch for exclusion.
[ci skip]
2016-05-20 20:15:12 +02:00
Daniele Alessandri ce15bd22b9 Update badges in CHANGELOG.
[ci skip]
2016-05-20 18:33:28 +02:00
Daniele Alessandri b0439e503e Update CHANGELOG. 2016-05-20 16:28:19 +02:00
Daniele Alessandri ffc1c25baa Try again on connection failure to node in cluster.
When the connection to a node in the cluster fails in the attempt to
execute a command, Predis now removes the failed connection from the
cluster pool and contacts a random node to ask for a fresh slots map
and tries to execute the command once again.

When the cluster is configured to have each master replicated to one
or more slaves, one the slaves is automatically promoted to the role
of master by redis-cluster with this change being reflected in the
output of CLUSTER SLOTS, so the next execution should run just fine.

Our current approach is relatively naive as CLUSTER SLOTS is executed
against a random master node, meaning that the client must open a new
connection and execute one more roundtrip only to fetch the new slots
map. For now it is enough, it is still better than having the client
fail when you actually have somes slaves in your redis-cluster setup,
but one improvement could consist in caching the list of slaves for
each master returned in the response of CLUSTER SLOTS so that when a
connection fails the client can try to guess which connection should
use for the next attempt.

This commit closes #173, closes #215, and closes #314.
2016-05-20 13:18:25 +02:00
Daniele Alessandri ac165194f5 Evict connection from slots cache when removed. 2016-05-20 11:43:15 +02:00
Daniele Alessandri 18ae294864 [tests] Fix assertion.
PHPUnit_Util_Type::export() has been removed a while ago...
2016-05-19 11:46:56 +02:00
Daniele Alessandri 47de667aea Bump min. version of PHPUnit (require-dev). 2016-05-19 11:46:52 +02:00
Daniele Alessandri 588940c5cb Update CHANGELOG. 2016-05-18 21:27:37 +02:00
Daniele Alessandri a1fe4013e1 Improve handling of slots mapping via parameters.
When using redis-cluster it is now also possible to pass one slot or
non-contiguous ranges of slots via connection parameters in order to
improve the ability to pre-configure the slots map on the client.

Here is an example:

  $parameters = [
    'tcp://10.0.0.1:6379?slots=0-5460,5500-5600,11000',
    'tcp://10.0.0.2:6379?slots=5461-5499,5600-10921',
    'tcp://10.0.0.3:6379?slots=10922-10999,11001-16383',
  ];

This commit fixes #312 (props to @kenotr0n for the original PR).
2016-05-18 21:16:42 +02:00
Daniele Alessandri 3328ad76be [tests] Fix wrong method in mock. 2016-05-16 15:38:42 +02:00
Daniele Alessandri 17b717a8f5 Fix CHANGELOG. 2016-05-15 21:42:00 +02:00
Daniele Alessandri 95dbc2bb34 Bump year in LICENSE. 2016-05-15 21:40:46 +02:00
Daniele Alessandri 9bb5a1c71f Apply minor styling fix. 2016-05-15 21:40:14 +02:00
Daniele Alessandri 8eb4804a5c Move member variable initialization. 2016-05-15 21:39:51 +02:00
Daniele Alessandri 5d99914939 Prevent warnings picking slave from empty pool. 2016-05-15 21:39:34 +02:00
Ante Lucic c45bb15767 add docblocks to MasterSlaveReplication 2016-05-13 17:00:27 +02:00
Ante Braovic e0329d6c74 added zrevrangebylex in the list of available methods 2016-05-13 16:43:50 +02:00
Daniele Alessandri 2175d87a96 [tests] Fix test executed on current Redis unstable. 2016-05-10 10:59:40 +02:00
Daniele Alessandri ce85fdadd1 [tests] Fix test executed on current Redis unstable. 2016-05-10 10:59:24 +02:00
Daniele Alessandri b15c0a02de [tests] Fix wrong @group annotation 2016-05-09 17:50:22 +02:00
Daniele Alessandri d2fbcc6b8c Prevent failures serializing commands with "holes" in arguments array.
This could be triggered when passing an array with "holes" to variadic commands.
Connection classes based on the protocol serialized exposed by phpiredis were
not affected by this bug.

Fixes #316.
2016-05-08 18:35:06 +02:00
Daniele Alessandri 6596460a57 Merge branch 'issue-tests' into v1.0 2016-05-08 16:50:28 +02:00
Daniele Alessandri 9ab0e31a75 Merge branch 'v1.0-fix-phpDoc-in-ClientInterface' into v1.0 2016-05-08 16:45:49 +02:00
John Maguire 11adccdaeb Update useClusterNodes() docs to indicate default 2016-05-08 15:21:01 +02:00
Daniele Alessandri f465792b27 Update CHANGELOG. 2016-05-08 15:15:48 +02:00
Daniele Alessandri 1399d3863c Fix bug in HSCAN-based iterator when hash have integer fields.
When iterating a hash containing integer fields our iterator abstraction
based on HSCAN was always returning "0" as a field name after the first
$field => $value pair due to a wrong assumption on how the PHP function
array_shift() (which is used internally to advance to the next pair in
our buffered response to HSCAN) works.

The ZSCAN-based iterator had this very same bug which was already fixed
in 24e19a9 so I am not sure how this one went unnoticed until now.
2016-05-08 15:10:09 +02:00
Daniele Alessandri 1f1c9cf1a4 Run php-cs-fixer. 2016-05-08 14:54:29 +02:00
Daniele Alessandri dae2df0ae9 Merge remote-tracking branch 'marvin255/fix_namespace_typo' into v1.0 2016-05-08 14:49:06 +02:00
postbox433@gmail.com 9338eaf573 Fix namespace typo in StreamConnection.php 2016-04-12 15:20:26 +05:00
Remi Collet f378faaf22 fix tests, list of allowed commands have changed (redis 3.0.6) 2015-12-28 10:34:24 +01:00
Alexander Cheprasov 8b150fcbf5 Update ClientInterface.php 2015-10-27 16:37:58 +00:00
Daniele Alessandri 84060b9034 Update CHANGELOG and bump VERSION.
Hotfix release targeting a severe regression on HHVM, see commit d4a7bd5.
2015-07-30 20:34:15 +02:00
Daniele Alessandri d4a7bd52be Fix severe regression on HHVM.
Apparently HHVM is more strict than PHP in stream_socket_client() and does not
like at all IPv4 addresses and hostnames eclosed in square brackets. Note that
it is not that weird as square brackets are mandatory only when IPv6 addresses
are embedded in URI strings, so it is more like a weird incompatiblity of HHVM
with the behaviour of the standard PHP interpreter. The connect() attempt fails
but not due to the server being unavailable or some connectivity issue.

The important lesson is: never rely on undocumented behaviours especially when
targeting different runtimes, and do not forget to run the test suite on every
platform right before release like I unfortunately did.

This commit fixes #269.
2015-07-30 20:23:45 +02:00
Daniele Alessandri 16a9d4e041 Back to development.
[ci skip]
2015-07-30 19:41:27 +02:00
Daniele Alessandri 0b92e8d156 Update CHANGELOG and bump VERSION. 2015-07-30 11:20:39 +02:00
Daniele Alessandri 1f7b534072 Preserve remainder of path in URI after database (redis scheme). 2015-07-30 11:13:42 +02:00
Daniele Alessandri 0c0cbbafff Use PHP_VERSION_ID constant. 2015-07-30 10:56:54 +02:00
Daniele Alessandri 8aec51b34f Implement full support for IPv6.
Using IPv6 with Predis was basically impossible due to various inconsistencies
and bugs through the library, now it is supported by all the connection classes.

Following the standard for IPv6 literal addresses in URI strings, the IP literal
must be enclosed within square brackets when passing the parameters as a string:

  $parameters = 'tcp://[2001:db8:0:f101::1]:6379';

See https://tools.ietf.org/html/rfc3986#section-3.2.2 for further details.

This commit also fixes #239 making redis-cluster usable with nodes using IPv6.
2015-07-29 23:09:52 +02:00
Daniele Alessandri eeb1e8b7a7 Reduce unneeded code duplication in URI parsing. 2015-07-29 17:04:34 +02:00
Daniele Alessandri c3a44b8e2e Strip brackets from host when parsing embedded IPv6 address.
I don't know why PHP's parse_url() does not do that, it does not make
sense when the IP is by itself so maybe it is a bug?
2015-07-29 17:01:19 +02:00
Daniele Alessandri 255beac137 Use static:: for invoking static methods in key prefix processor.
This trivial change makes it possible to use overridden static methods
when extending Predis\Command\Processor\KeyPrefixProcessor. PHP always
invokes the static methods of a parent class when using self:: even if
the extended classes override them.
2015-07-29 14:16:52 +02:00
Daniele Alessandri d473328a22 Update CHANGELOG. 2015-07-29 11:54:08 +02:00
Daniele Alessandri 0d25e7b0bb Add new command: HSTRLEN (Redis 3.2.0).
Also bump the unstable profile version to 3.2.
2015-07-29 11:52:55 +02:00
Daniele Alessandri be3b84ca14 Rephrase latest entries in CHANGELOG by using intelligible English.
[ci skip]
2015-07-26 22:27:57 +02:00
Daniele Alessandri 7d0597c62c Rephrase some parts of the README.
[ci skip]
2015-07-26 22:27:49 +02:00
Daniele Alessandri 833e46b79d [tests] Fix typo. 2015-07-25 21:59:08 +02:00
Daniele Alessandri a22858438d [tests] Require pcntl extension to run blocking PubSub\Consumer test. 2015-07-25 21:58:01 +02:00
Daniele Alessandri f221d0c81e Run php-cs-fixer. 2015-07-25 21:27:05 +02:00
Daniele Alessandri 0b5bfdfe33 [tests] Improve code-reuse in tests for the Predis\Connection namespace. 2015-07-25 21:27:02 +02:00
Daniele Alessandri 3700d48771 [tests] Share common test among connection classes. 2015-07-25 20:54:01 +02:00
Daniele Alessandri 653d3f08ab [tests] Use @requires annotation. 2015-07-25 20:52:01 +02:00
Daniele Alessandri 2d2de7837b [tests] Fix some tests methods names. 2015-07-25 10:07:58 +02:00
Daniele Alessandri fc18e6456a [tests] Apply small changes to commands tests with expirations. 2015-07-25 10:07:55 +02:00
Daniele Alessandri 6f2064ed90 [tests] Cover SET modifiers EX, PX, NX|XX (Redis >= 2.6.12). 2015-07-25 09:11:16 +02:00
Daniele Alessandri e2e645a8bd Update .gitattributes file.
[ci skip]
2015-07-24 23:31:04 +02:00
Daniele Alessandri 2219964b7e Restore alignment for a few equals symbols. 2015-07-24 23:21:23 +02:00
Daniele Alessandri 381e44b6e6 Run php-cs-fixer with new configuration. 2015-07-24 23:21:07 +02:00
Daniele Alessandri 808693bfbd Add .php_cs configuration file for php-cs-fixer. 2015-07-24 23:19:23 +02:00
Daniele Alessandri 33eab38314 Remove unneeded "use" imports. 2015-07-24 21:25:48 +02:00
Daniele Alessandri 82e404660e Run php-cs-fixer against codebase in src/ and tests/. 2015-07-24 17:58:56 +02:00
Daniele Alessandri 6d60a42400 [tests] Fix me being stupid here. 2015-07-24 17:35:53 +02:00
Daniele Alessandri 95624de5c4 [tests] Apply some fixes and improvements and remove old stuff. 2015-07-24 17:27:41 +02:00
Daniele Alessandri 2ee135099c Remove useless if condition. 2015-07-24 12:40:33 +02:00
Daniele Alessandri e26e3b423a Reverting stupid oversight after a merge from master branch.
The culprit is commit 3500371.
2015-07-24 10:49:14 +02:00
Daniele Alessandri 946fac4454 No need to check the scheme here.
Bug introduced in v1.0-dev after changes to support redis scheme.

Fixes #268
2015-07-24 10:45:12 +02:00
Daniele Alessandri 4e976a3eb6 Update CHANGELOG.
[ci skip]
2015-07-23 21:05:05 +02:00
Daniele Alessandri baaf26fe5b Add support for the 'redis://' scheme in URI strings.
The URI string will be handled following the rules as described by the
the provisional IANA registration document that can be found on IANA's
website: http://www.iana.org/assignments/uri-schemes/prov/redis.
2015-07-23 19:21:16 +02:00
Daniele Alessandri 6a0348fbea Add missing command: MIGRATE (Redis 2.6.0).
Fixes #209.
2015-07-23 19:21:03 +02:00
Daniele Alessandri 8365a6be77 [tests] Fix minor oversight. 2015-07-23 19:20:52 +02:00
Daniele Alessandri f028e428be Switch to container-based builds on Travis CI.
We do not need "sudo" anyway.
See http://docs.travis-ci.com/user/migrating-from-legacy/ for details.
2015-07-23 12:16:27 +02:00
Daniele Alessandri c13b82557d Update CHANGELOG. 2015-07-23 11:43:47 +02:00
Daniele Alessandri 30d01c40d4 [tests] Improve assert failure messages for replication stategy tests. 2015-07-23 11:40:52 +02:00
Daniele Alessandri 6459546e25 Fix missing BITPOS in replication strategy. 2015-07-23 11:40:39 +02:00
Daniele Alessandri dede828008 Add missing BITPOS command in key prefix processor.
See #265.
2015-07-23 11:40:27 +02:00
Daniele Alessandri 5c0716575e Exclude .php-version file.
[ci skip]
2015-07-19 13:05:40 +02:00
Richard Heelin 90bffb1a5a Emit socket error if ipredis connection has been lost/reset.
The current code is checking for a failure (return false) or an empty buffer string, however
neither of these will be the case if the connection has been reset or has errored. According
to the docs for socket_recv, $buffer will be set to null if the connection is reset or their
is no data. As currently null is not allowed for, we enter an infinite loop, to prevent this
I've added null to the things we check before we emit a socket error. This prevents the
infinite loop and correctly results in an Exception if the connection is lost/reset.

Conflicts:
	src/Connection/PhpiredisSocketConnection.php
2015-07-07 18:51:44 +02:00
Daniele Alessandri 64ee96c312 [tests] Fix base test case class to handle required Redis versions.
This change is needed due to some internal changes in one of the
latest minor releases of PHPUnit 4.x that essentially broke how we
were checking for the required Redis version from method annotations.
2015-07-07 18:05:55 +02:00
Daniele Alessandri bff5a9729e Update CHANGELOG. 2015-07-07 16:19:46 +02:00
Daniele Alessandri 5cd7b774c4 [tests] Adapt to internal encoding changes for lists in Redis 3.0. 2015-07-07 16:11:49 +02:00
Daniele Alessandri 354d1ef921 Support ZADD modifiers when using simplified command signature.
The NX|XX, CH and INCR modifiers for ZADD are available in Redis since
version 3.0.2. See http://redis.io/commands/ZADD for additional info.
2015-07-07 16:11:43 +02:00
Daniele Alessandri 9e3a8da94d Update CHANGELOG. 2015-07-07 15:01:00 +02:00
Daniele Alessandri 35003713a2 Merge remote-tracking branch 'github/pr/235' into v1.0
Conflicts:
	.travis.yml
	CHANGELOG.md
	VERSION
	package.ini
	src/Client.php
2015-07-07 14:52:09 +02:00
Daniele Alessandri cd991e6370 [tests] Remove hhvm-nightly from Travis CI configuration.
See https://github.com/travis-ci/travis-ci/issues/3788 for details.
2015-07-07 14:44:28 +02:00
Daniele Alessandri c59853afb8 [tests] It's 7.0 and not 5.7! 2015-07-07 14:44:08 +02:00
Daniele Alessandri d4982b6c43 [tests] Add PHP 5.7 to Travis CI configuration. 2015-07-07 13:22:59 +02:00
Daniele Alessandri 96f4af816d Minor tweak in .editorconfig file.
[ci skip]
2015-07-06 18:42:10 +02:00
Daniele Alessandri 22ff3ff992 Commit .editorconfig file.
See http://editorconfig.org for more info about EditorConfig

[ci skip]
2015-07-06 18:38:30 +02:00
Daniele Alessandri 77b75e0738 Fix wrong phpdoc.
Thanks @Aliance for spotting this oversight.
2015-07-03 12:11:45 +02:00
Daniele Alessandri eeea28b275 Update CHANGELOG. 2015-07-03 12:10:44 +02:00
Daniele Alessandri aa067d8fd9 [tests] Implement test for #257 to guard against regressions. 2015-07-03 12:03:15 +02:00
Dominic Scheirlinck ce17c5383a Don't check for __invoke on non-objects
`method_exists` will cause autoloading for strings. Fixes #257
2015-07-03 11:11:53 +02:00
Daniele Alessandri c4c36cae7a Back to development. 2015-01-02 13:54:29 +01:00
Daniele Alessandri 7a170b3d81 Update CHANGELOG and bump VERSION. 2015-01-02 13:51:34 +01:00
Daniele Alessandri 17e7bce22e Bump year in LICENSE.
[ci skip]
2015-01-02 13:45:19 +01:00
Daniele Alessandri 7a5d6bac3e Update CHANGELOG.
[ci skip]
2015-01-02 13:42:50 +01:00
Daniele Alessandri 9d6c031ead Merge remote-tracking branch 'srhnsn/v1.0' into v1.0 2015-01-02 13:33:10 +01:00
Daniele Alessandri 692718afb4 Merge remote-tracking branch 'blocktrail/hhvm-pubsub-blockingread-test-v1.0' into v1.0 2015-01-02 13:00:25 +01:00
Daniele Alessandri 0776377412 Temporarily allow failures for HHVM on Travis-CI.
[ci skip]
2015-01-02 12:56:48 +01:00
Daniele Alessandri e4b0512d42 No need to access a private field used by PHPUnit.
Fixes #207.
2015-01-02 12:20:00 +01:00
Daniele Alessandri 75ca2f8111 Add minimum required Redis version for BITPOS integration tests. 2015-01-02 12:14:05 +01:00
Daniele Alessandri 37b1c88115 Apply minor CS fixes and trim spurious spaces. 2015-01-02 12:13:58 +01:00
Daniele Alessandri a4e6130568 Merge remote-tracking branch 'nicchap/v1.0' into v1.0 2015-01-02 11:58:30 +01:00
556 changed files with 16313 additions and 21741 deletions
+3 -2
View File
@@ -1,9 +1,10 @@
* text=auto
/tests export-ignore
/.github export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.php_cs.dist export-ignore
/.php_cs export-ignore
/.travis.yml export-ignore
/phpunit.xml.dist export-ignore
/phpunit.xml.travisci export-ignore
-6
View File
@@ -1,6 +0,0 @@
The Predis code of conduct is derived from the Ruby code of conduct. Any violations of the code of conduct may be reported to [Till Krüss](https://till.im):
- Participants will be tolerant of opposing views.
- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
- When interpreting the words and actions of others, participants should always assume good intentions.
- Behavior that can be reasonably considered harassment will not be tolerated.
-2
View File
@@ -1,2 +0,0 @@
github: tillkruss
custom: "https://www.paypal.me/tillkruss"
-31
View File
@@ -1,31 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Run command '...'
2. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Versions (please complete the following information):**
- Predis: [e.g. 1.1.2]
- PHP [e.g. 8.0.0]
- Redis Server [e.g. 6.0.0]
- OS [e.g. Ubuntu 20.10]
**Code sample**
If applicable, a small snippet of code that reproduces the issue.
**Additional context**
Add any other context about the problem here.
-20
View File
@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
-52
View File
@@ -1,52 +0,0 @@
name: Tests
on:
- push
- pull_request
jobs:
predis:
name: PHP ${{ matrix.php-versions }} (Redis ${{ matrix.redis-versions }})
runs-on: ubuntu-latest
services:
redis:
image: redis:${{ matrix.redis-versions }}
ports:
- 6379:6379
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1']
redis-versions: ['3', '4', '5', '6', '7']
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP with Composer and extensions
with:
php-version: ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
- name: Get Composer cache directory
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache Composer dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
env:
PHP_VERSION: ${{ matrix.php-versions }}
run: composer install --no-progress --prefer-dist --optimize-autoloader $(if [ "$PHP_VERSION" == "8.0" ]; then echo "--ignore-platform-reqs"; fi;)
- name: Test with PHPUnit
run: vendor/bin/phpunit
+6 -3
View File
@@ -1,6 +1,9 @@
/vendor
*.tgz
*.phar
.php-version
.php_cs.cache
.phpunit.result.cache
composer.lock
phpunit.xml
package.xml
composer.lock
experiments/
vendor/
+34
View File
@@ -0,0 +1,34 @@
<?php
$PREDIS_HEADER = <<<EOS
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.
EOS;
Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($PREDIS_HEADER);
return Symfony\CS\Config\Config::create()
->setUsingCache(true)
->level(Symfony\CS\FixerInterface::SYMFONY_LEVEL)
->fixers(array(
// Symfony
'-unalign_equals',
'-unalign_double_arrow',
// Contribs
'header_comment',
'ordered_use',
'phpdoc_order',
'long_array_syntax',
))
->finder(
Symfony\CS\Finder\DefaultFinder::create()
->in(__DIR__.'/bin')
->in(__DIR__.'/src')
->in(__DIR__.'/tests')
->in(__DIR__.'/examples')
);
-33
View File
@@ -1,33 +0,0 @@
<?php
$PREDIS_HEADER = <<<EOS
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.
EOS;
return PhpCsFixer\Config::create()
->setUsingCache(true)
->setRules(array(
'@Symfony' => true,
'header_comment' => array(
'header' => $PREDIS_HEADER,
),
'ordered_imports' => true,
'phpdoc_order' => true,
'binary_operator_spaces' => array(
'align_double_arrow' => false,
'align_equals' => false,
),
'array_syntax' => array('syntax' => 'long'),
))
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__.'/bin')
->in(__DIR__.'/src')
->in(__DIR__.'/tests')
->in(__DIR__.'/examples')
);
+23
View File
@@ -0,0 +1,23 @@
language: php
sudo: false
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
branches:
except:
- v0.5
- v0.6
- v0.6-PHP_5.2
- documentation
services: redis-server
before_script:
- composer self-update
- composer install --no-interaction --prefer-source --dev
script:
- vendor/bin/phpunit -c phpunit.xml.travisci
matrix:
fast_finish: true
+863 -100
View File
@@ -1,129 +1,892 @@
## Changelog
v1.0.4 (2016-05-30)
================================================================================
## v2.0.0 (2022-06-08)
- Added new profile for Redis 3.2 with its new commands: `HSTRLEN`, `BITFIELD`,
`GEOADD`, `GEOHASH`, `GEOPOS`, `GEODIST`, `GEORADIUS`, `GEORADIUSBYMEMBER`.
The default server profile for Predis is still the one for Redis 3.0 you must
set the `profile` client option to `3.2` when initializing the client in order
to be able to use them when connecting to Redis 3.2.
- Dropped support for PHP 7.1 and older
- Various improvements in the handling of redis-cluster:
- Accepted values for some client options have changed, this is the new list of
accepted values:
- If the connection to a specific node fails when executing a command, the
client tries to connect to another node in order to refresh the slots map
and perform a new attempt to execute the command.
- `aggregate`: callable returning an aggregate connection.
- `cluster`: string value (`predis`, `redis`), callable returning an aggregate
connection.
- `replication`: string value (`predis`, `sentinel`), callable returning an
aggregate connection.
- `commands`: command factory, named array mapping command IDs to PHP classes,
callable returning a command factory or a named array.
- `connections`: connection factory, callable object returning a connection
factory, named array mapping URI schemes to PHP classes, string identifying
a supported combination of configurations for the connection factory.
- `prefix`: string value, command processor, callable.
- `exceptions`: boolean.
- Connections to nodes can be preassigned to non-contiguous slot ranges via
the `slots` parameter using a comma separator. This is how it looks like
in practice: `tcp://127.0.0.1:6379?slots=0-5460,5500-5600,11000`.
Note that both the `cluster` and `replication` options now return a closure
acting as initializer instead of an aggregate connection instance.
- __FIX__: broken values returned by `Predis\Collection\Iterator\HashKey` when
iterating hash keys containing integer fields (PR #330, ISSUE #331).
- The `connections` client option now accepts certain string values identifying
certain combinations of configurations for the connection factory. Currenlty
this is used to provide a short way to configure Predis to load our phpiredis
based connection backends simply, accepted values are:
- __FIX__: prevent failures when `Predis\Connection\StreamConnection` serializes
commands with holes in their arguments (e.g. `[0 => 'key:0', 2 => 'key:2']`).
The same fix has been applied to `Predis\Protocol\Text\RequestSerializer`.
(ISSUE #316).
- `phpiredis-stream` maps `Phpiredis\Connection\PhpiredisStreamConnection` to
`tcp`, `redis`, `unix` URI schemes.
- `phpiredis-socket` maps `Phpiredis\Connection\PhpiredisSocketConnection` to
`tcp`, `redis`, `unix` URI schemes.
- `phpiredis-stream` is simply an alias of `phpiredis-stream`.
- Added the new `Predis\Cluster\Hash\PhpiredisCRC16` class using ext-phpiredis
to speed-up the generation of the CRC16 hash of keys for redis-cluster. Predis
automatically uses this class when ext-phpiredis is loaded, but it is possible
to configure the hash generator using the new `crc16` client option (accepted
values `predis`, `phpiredis` or an hash generator instance).
v1.0.3 (2015-07-30)
================================================================================
- Replication backends now use the `role` parameter instead of `alias` in order
to distinguish the role of a connection. Accepted values are `master`, `slave`
and, for redis-sentinel, `sentinel`. This led to a redesign of how connections
can be retrieved from replication backends: the method getConnectionById() now
retrieves a connection only by its ID (ip:port pair), to get a connection by
its alias there is the new method getConnectionByAlias(). This method is not
supported by the redis-sentinel backend due to its dynamic nature (connections
are retrieved and initialized at runtime from sentinels) but it is possible to
get a single connection from the pool by using its ID. It is also possible to
retrive a connection by role using the method getConnectionByRole().
- __FIX__: the previous release introduced a severe regression on HHVM that made
the library unable to connect to Redis when using IPv4 addresses. Code running
on the standard PHP interpreter is not affected.
- The concept of connection ID (ip:port pair) and connection alias (the `alias`
parameter) in `Predis\Connection\Cluster\PredisCluster` has been separated.
This change does not affect distribution and it is safe for existing clusters.
- Client option classes now live in the `Predis\Configuration\Option` namespace.
v1.0.2 (2015-07-30)
================================================================================
- Classes for Redis commands have been moved into the new `Predis\Command\Redis`
namespace and each class name mirrors the respective Redis command ID.
- IPv6 is now fully supported.
- The concept of server profiles is gone, the library now uses a single command
factory to create instances of commands classes. The `profile` option has been
replaced by the `commands` option accepting `Predis\Command\FactoryInterface`
to customize the underlying command factory. The default command factory class
used by Predis is `Predis\Command\RedisFactory` and it still allows developers
to define or override commands with their own implementations. In addition to
that, `Predis\Command\RedisFactory` relies on a convention-over-configuration
approach by looking for a suitable class with the same name as the command ID
in the `Predis\Command\Redis` when the internal class map does not contain a
class associated.
- Added `redis` as an accepted scheme for connection parameters. When using this
scheme, the rules used to parse URI strings match the provisional registration
[published by IANA](http://www.iana.org/assignments/uri-schemes/prov/redis).
- The method `Predis\Client::getClientFor($connectionID)` has been replaced by
`getClientBy($selector, $value, $callable = null)` which is more flexible as
it is not limited to picking a connection from the underlying replication or
cluster backend by ID, but allows users to specify a `$selector` that can be
either `id` (the old behavior), `key`, `slot` or `command`. The client uses
duck-typing instead of type-checking to verify that the underlying connection
implements a method that matches the specified selector which means that some
selectors may not be available to all kinds of connection backends.
- Added new or missing commands: `HSTRLEN` (>= 3.2), `ZREVRANGEBYLEX` (>= 2.8)
and `MIGRATE` (>= 2.6).
- The method `Predis\Client::getConnectionById($connectionID)` has been removed.
- Implemented support for the `ZADD` modifiers `NX|XX`, `CH`, `INCR` (Redis >=
3.0.2) using the simplified signature where scores and members are passed as
a named array.
- Changed the signature for the constructor of `Predis\Command\RawCommand`.
- __FIX__: `Predis\Configuration\Options` must not trigger the autoloader when
option values are strings (ISSUE #257).
- The `Predis\Connection\Aggregate` namespace has been split into two separate
namespaces for cluster backends (`Predis\Connection\Cluster`) and replication
backends (`Predis\Connection\Replication`).
- __FIX__: `BITPOS` was not defined in the key-prefix processor (ISSUE #265) and
in the replication strategy.
- The method `Predis\Connection\AggregateConnectionInterface::getConnection()`
has been renamed to `getConnectionByCommand()`.
- The methods `switchToMaster()` and `switchToSlave()` have been promoted to be
part of `Predis\Connection\Replication\ReplicationInterface` while the method
`switchTo($connection)` has been removed from it.
v1.0.1 (2015-01-02)
================================================================================
- The method `Predis\Connection\Cluster\PredisCluster::executeCommandOnNodes()`
has been removed as it is possible to achieve the same by iterating over the
connection or, even better, over the client instance in order to execute the
same command against all of the registered connections.
- Added `BITPOS` to the server profile for Redis 2.8.
- The class `Predis\CommunicationException` now uses the correct default types
for the `$message` (string) and `$code` (integer) parameters.
- Connection timeout for read/write operations can now be set for UNIX sockets
where the underlying connection uses PHP's stream.
- The method `onConnectionError()` in `Predis\Connection\AbstractConnection`
class now passes the second argument as an integer value `0` as its default
value instead of `null`.
- __FIX__: broken values returned by `Predis\Collection\Iterator\SortedSetKey`
when iterating sorted set containing integer members (ISSUE #216).
- Support Pub/Sub and Pipelines when using replication
- __FIX__: applied a minor workaround for a bug in old versions of PHP < 5.3.9
affecting inheritance.
- The class `Predis\Transaction\AbortedMultiExecException` now uses the correct
default types for the `$code` (integer) parameter.
- __FIX__: prevent E_NOTICE warnings when using INFO [section] returns an empty
response due to an unsupported specific set of information requested to Redis.
- __FIX__: using `strval` in `getScanOptions()` method, part of
`Predis\Collection\Iterator\CursorBasedIterator` to make sure we retrieve the
string value of `$this->match` and not passing `null` to `strlen()` function.
- __FIX__: the value returned from `getArgument()` in `isReadOperation()` method,
part of `Predis\Replication\ReplicationStrategy` class, is checked to not pass
`null` to `sha1` function.
v1.0.0 (2014-08-01)
================================================================================
- __FIX__: the value returned from `getArgument()` in `parseResponse()`method,
part of `Predis\Command\Redis\SENTINEL` class, is checked to not pass `null`
to `strtolower()` function.
- Switched to PSR-4 for autoloading.
## v2.0.0-beta.1 (2022-05-26)
- The default server profile for Redis is `3.0`.
Same as v2.0.0
- Removed server profile for Redis 1.2.
- Added `SENTINEL` to the profile for Redis 2.6 and `PUBSUB` to the profile for
Redis 2.8.
- `Predis\Client` can now send raw commands using `Predis\Client::executeRaw()`.
- Status responses are returned as instances of `Predis\Response\Status`, for
example +OK is not returned as boolean TRUE anymore which is a breaking change
for those using strict comparisons. Status responses can be casted to string
values carrying the original payload, so one can do `$response == 'OK'` which
is also more akin to how Redis replies to clients.
- Commands `ZRANGE`, `ZRANGEBYSCORE`, `ZREVRANGE` and `ZREVRANGEBYSCORE` using
`WITHSCORE` return a named array of member => score instead of using an array
of [member, score] elements. Insertion order is preserved anyway due to how
PHP works internally.
- The command `ZSCAN` returns a named array of member => score instead of using
an array of [member, score] elements. Insertion order is preserved anyway due
to how PHP works internally.
- The rules for redis-cluster are now leveraged for empty key tags when using
client-side sharding, which means that when one or the first occurrence of {}
is found in a key it will most likely produce a different hash than previous
versions of Predis thus leading to a different partitioning in these cases.
- Invoking `Predis\Client::connect()` when the underlying connection has been
already established does not throw any exception anymore, now the connection
simply does not attempt to perform any operation.
- Added the `aggregate` client option, useful to fully customize how the client
should aggregate multiple connections when an array of connection parameters
is passed to `Predis\Client::__construct()`.
- Dropped support for streamable multibulk responses. Actually we still ship the
iterator response classes just in case anyone would want to build custom stuff
at a level lower than the client abstraction (our standard and composable text
protocol processors still handle them and can be used as an example).
- Simplified the implementation of connection parameters by removing method used
to cast to int / bool / float certain parameters supplied by users. Casting
values, if deemed necessary, should be done by the consumer or you can just
subclass `Predis\Connection\Parameters` and override the `filter()` method.
- Changed a couple of options for our transaction abstraction:
- `exceptions`: overrides the value of the client option with the same name.
Please note that it does not affect all the transaction control commands
such as `MULTI`, `EXEC`, `DISCARD`, `WATCH` and `UNWATCH`.
- `on_retry`: this option has been removed.
- Removed pipeline executors, now command pipelines can be easily customized by
extending the standard `Predis\Pipeline\Pipeline` class. Accepted options when
creating a pipeline using `Predis\Client::pipeline()` are:
- `atomic`: returns a pipeline wrapped in a MULTI / EXEC transaction
(class: `Predis\Pipeline\Atomic`).
- `fire-and-forget`: returns a pipeline that does not read back responses
(class: `Predis\Pipeline\FireAndForget`).
- Renamed the two base abstract command classes:
- `Predis\Command\AbstractCommand` is now `Predis\Command\Command`
- `Predis\Command\ScriptedCommand` is now `Predis\Command\ScriptCommand`
- Dropped `Predis\Command\Command::__toString()` (see issue #151).
- The key prefixing logic has been moved from command classes to the key prefix
processor. Developers can define or override handlers used to prefix keys, but
they can also define the needed logic in their command classes by implementing
`Predis\Command\PrefixableCommandInterface` just like before.
- `Predis\PubSub\DispatcherLoop` now takes a `Predis\PubSub\Consumer` instance
as the sole argument of its constructor instead of `Predis\ClientInterface`.
- All of the interfaces and classes related to translated Redis response types
have been moved in the new `Predis\Response` namespace and most of them have
been renamed to make their fully-qualified name less redundant. Now the base
response interface is `Predis\Response\ResponseInterface`.
- Renamed interface `Predis\Command\Processor\CommandProcessorInterface` to a
shorter `Predis\Command\Processor\ProcessorInterface`. Also removed interface
for chain processors since it is basically useless.
- Renamed `Predis\ExecutableContextInterface` to `Predis\ClientContextInterface`
and augmented it with a couple of required methods since this interface is no
more comparable to a basic client as it could be misleading.
- The `Predis\Option` namespace is now known as `Predis\Configuration` and have
a fully-reworked `Options` class with the ability to lazily initialize values
using objects that responds to `__invoke()` (not all the kinds of callables)
even for custom options defined by the user.
- Renamed `Predis\Connection\ConnectionInterface::writeCommand()` into
`writeRequest()` for consistency with its counterpart, `readResponse()`.
- Renamed `Predis\Connection\SingleConnectionInterface::pushInitCommand()` into
`addConnectCommand()` which is more obvious.
- Renamed the connection class based on both ext-phpiredis and ext-socket into
`Predis\Connection\PhpiredisSocketConnection`. The one based on PHP's streams
is still named `Predis\Connection\PhpiredisStreamConnection`.
- Renamed the connection factory class to `Predis\Connection\Factory`. Now its
constructor does not require anymore a profile instance to create `AUTH` and
`SELECT` commands when parameters contain both `password` and `database`. Raw
commands will be used instead.
- Renamed the connection parameters class to `Predis\Connection\Parameters`. Now
its constructor accepts only named arrays, but instances can still be created
using both URIs or arrays using the static method `Parameters::create()`.
- The profile factory code has been extracted from the abstract Redis profile
class and now lives in `Predis\Profile\Factory`.
- The `Predis\Connection` namespace has been completely reorganized by renaming
a few classes and interfaces and adding some sub-namespaces.
- Most classes and interfaces in the `Predis\Protocol` namespace have been moved
or renamed while rationalizing the whole API for external protocol processors.
v0.8.7 (2014-08-01)
================================================================================
- Added `3.0` in the server profiles aliases list for Redis 3.0. `2.8` is still
the default server profile and `dev` still targets Redis 3.0.
- Added `COMMAND` to the server profile for Redis 2.8.
- Switched internally to the `CLUSTER SLOTS` command instead of `CLUSTER NODES`
to fetch the updated slots map from redis-cluster. This change requires users
to upgrade Redis nodes to >= 3.0.0b7.
- The updated slots map is now fetched automatically from redis-cluster upon the
first `-MOVED` response by default. This change makes it possible to feed the
client constructor with only a few nodes of the actual cluster composition,
without needing a more complex configuration.
- Implemented support for `PING` in PUB/SUB loop for Redis >= 3.0.0b8.
- The default client-side sharding strategy and the one for redis-cluster now
share the same implementations as they follow the same rules. One difference,
aside from the different hashing function used to calculate distribution, is
in how empty hash tags like {} are treated by redis-cluster.
- __FIX__: the patch applied to fix #180 introduced a regression affecting read/
write timeouts in `Predis\Connection\PhpiredisStreamConnection`. Unfortunately
the only possible solution requires PHP 5.4+. On PHP 5.3, read/write timeouts
will be ignored from now on.
v0.8.6 (2014-07-15)
================================================================================
- Redis 2.8 is now 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.
- Implemented PHP iterators for incremental iterations over Redis collections:
- keyspace (cursor-based iterator using `SCAN`)
- sets (cursor-based iterator using `SSCAN`)
- sorted sets (cursor-based iterator using `ZSCAN`)
- hashes (cursor-based iterator using `HSCAN`)
- lists (plain iterator using `LRANGE`)
- 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.
- Connection exceptions now report basic host details in their messages.
- Allow `Predis\Connection\PhpiredisConnection` to use a random IP when a host
actually has several IPs (ISSUE #116).
- __FIX__: allow `HMSET` when using a cluster of Redis nodes with client-side
sharding or redis-cluster (ISSUE #106).
- __FIX__: set `WITHSCORES` modifer for `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`
and `ZREVRANGEBYSCORE` only when the options array passed to these commands
has `WITHSCORES` set to `true` (ISSUE #107).
- __FIX__: scripted commands falling back from `EVALSHA` to `EVAL` resulted in
PHP errors when using a prefixed client (ISSUE #109).
- __FIX__: `Predis\PubSub\DispatcherLoop` now works properly when using key
prefixing (ISSUE #114).
v0.8.3 (2013-02-18)
================================================================================
- Added `CLIENT SETNAME` and `CLIENT GETNAME` (ISSUE #102).
- Implemented the `Predis\Connection\PhpiredisStreamConnection` class using the
`phpiredis` extension like `Predis\Connection\PhpiredisStreamConnection`, but
without requiring the `socket` extension since it relies on PHP's streams.
- Added support for the TCP_NODELAY flag via the `tcp_nodelay` parameter for
stream-based connections, namely `Predis\Connection\StreamConnection` and
`Predis\Connection\PhpiredisStreamConnection` (requires PHP >= 5.4.0).
- Updated the aggregated connection class for redis-cluster to work with 16384
hash slots instead of 4096 to reflect the recent change from redis unstable
([see this commit](https://github.com/antirez/redis/commit/ebd666d)).
- The constructor of `Predis\Client` now accepts a callable as first argument
returning `Predis\Connection\ConnectionInterface`. Users can create their
own self-contained strategies to create and set up the underlying connection.
- Users should return `0` from `Predis\Command\ScriptedCommand::getKeysCount()`
instead of `FALSE` to indicate that all of the arguments of a Lua script must
be used to populate `ARGV[]`. This does not represent a breaking change.
- The `Predis\Helpers` class has been deprecated and it will be removed in
future releases.
v0.8.2 (2013-02-03)
================================================================================
- Added `Predis\Session\SessionHandler` to make it easy to store PHP sessions
on Redis using Predis. Please note that this class needs either PHP >= 5.4.0
or a polyfill for PHP's `SessionHandlerInterface`.
- Added the ability to get the default value of a client option directly from
`Predis\Option\ClientOption` using the `getDefault()` method by passing the
option name or its instance.
- __FIX__: the standard pipeline executor was not using the response parser
methods associated to commands to process raw responses (ISSUE #101).
v0.8.1 (2013-01-19)
================================================================================
- The `connections` client option can now accept a callable object returning
an instance of `Predis\Connection\ConnectionFactoryInterface`.
- Client options accepting callable objects as factories now pass their actual
instance to the callable as the second argument.
- `Predis\Command\Processor\KeyPrefixProcessor` can now be directly casted to
string to obtain the current prefix, useful with string interpolation.
- Added an optional callable argument to `Predis\Cluster\Distribution\HashRing`
and `Predis\Cluster\Distribution\KetamaPureRing` constructor that can be used
to customize how the distributor should extract the connection hash when
initializing the nodes distribution (ISSUE #36).
- Correctly handle `TTL` and `PTTL` returning -2 on non existing keys starting
with Redis 2.8.
- __FIX__: a missing use directive in `Predis\Transaction\MultiExecContext`
caused PHP errors when Redis did not return `+QUEUED` replies to commands
when inside a MULTI / EXEC context.
- __FIX__: the `parseResponse()` method implemented for a scripted command was
ignored when retrying to execute a Lua script by falling back to `EVAL` after
a `-NOSCRIPT` error (ISSUE #94).
- __FIX__: when subclassing `Predis\Client` the `getClientFor()` method returns
a new instance of the subclass instead of a new instance of `Predis\Client`.
v0.8.0 (2012-10-23)
================================================================================
- The default server profile for Redis is now `2.6`.
- Certain connection parameters have been renamed:
- `connection_async` is now `async_connect`
- `connection_timeout` is now `timeout`
- `connection_persistent` is now `persistent`
- The `throw_errors` connection parameter has been removed and replaced by the
new `exceptions` client option since exceptions on `-ERR` replies returned by
Redis are not generated by connection classes anymore but instead are thrown
by the client class and other abstractions such as pipeline contexts.
- Added smart support for redis-cluster (Redis v3.0) in addition to the usual
cluster implementation that uses client-side sharding.
- Various namespaces and classes have been renamed to follow rules inspired by
the Symfony2 naming conventions.
- The second argument of the constructor of `Predis\Client` does not accept
strings or instances of `Predis\Profile\ServerProfileInterface` anymore.
To specify a server profile you must explicitly set `profile` in the array
of client options.
- `Predis\Command\ScriptedCommand` internally relies on `EVALSHA` instead of
`EVAL` thus avoiding to send Lua scripts bodies on each request. The client
automatically resends the command falling back to `EVAL` when Redis returns a
`-NOSCRIPT` error. Automatic fallback to `EVAL` does not work with pipelines,
inside a `MULTI / EXEC` context or with plain `EVALSHA` commands.
- Complex responses are no more parsed by connection classes as they must be
processed by consumer classes using the handler associated to the issued
command. This means that executing commands directly on connections only
returns simple Redis types, but nothing changes when using `Predis\Client`
or the provided abstractions for pipelines and transactions.
- Iterators for multi-bulk replies now skip the response parsing method of the
command that generated the response and are passed directly to user code.
Pipeline and transaction objects still consume automatically iterators.
- Cluster and replication connections now extend a new common interface,
`Predis\Connection\AggregatedConnectionInterface`.
- `Predis\Connection\MasterSlaveReplication` now uses an external strategy
class to handle the logic for checking readable / writable commands and Lua
scripts.
- Command pipelines have been optimized for both speed and code cleanness, but
at the cost of bringing a breaking change in the signature of the interface
for pipeline executors.
- Added a new pipeline executor that sends commands wrapped in a MULTI / EXEC
context to make the execution atomic: if a pipeline fails at a certain point
then the whole pipeline is discarded.
- The key-hashing mechanism for commands is now handled externally and is no
more a competence of each command class. This change is neeeded to support
both client-side sharding and Redis cluster.
- `Predis\Options\Option` is now abstract, see `Predis\Option\AbstractOption`.
v0.7.3 (2012-06-01)
================================================================================
- New commands available in the Redis v2.6 profile (dev): `BITOP`, `BITCOUNT`.
- When the number of keys `Predis\Commands\ScriptedCommand` is negative, Predis
will count from the end of the arguments list to calculate the actual number
of keys that will be interpreted as elements for `KEYS` by the underlying
`EVAL` command.
- __FIX__: `examples\CustomDistributionStrategy.php` had a mistyped constructor
call and produced a bad distribution due to an error as pointed in ISSUE #63.
This bug is limited to the above mentioned example and does not affect the
classes implemented in the `Predis\Distribution` namespace.
- __FIX__: `Predis\Commands\ServerEvalSHA::getScriptHash()` was calculating the
hash while it just needs to return the first argument of the command.
- __FIX__: `Predis\Autoloader` has been modified to allow cascading autoloaders
for the `Predis` namespace.
v0.7.2 (2012-04-01)
================================================================================
- Added `2.6` in the server profiles aliases list for the upcoming Redis 2.6.
`2.4` is still the default server profile. `dev` now targets Redis 2.8.
- Connection instances can be serialized and unserialized using `serialize()`
and `unserialize()`. This is handy in certain scenarios such as client-side
clustering or replication to lower the overhead of initializing a connection
object with many sub-connections since unserializing them can be up to 5x
times faster.
- Reworked the default autoloader to make it faster. It is also possible to
prepend it in PHP's autoload stack.
- __FIX__: fixed parsing of the payload returned by `MONITOR` with Redis 2.6.
v0.7.1 (2011-12-27)
================================================================================
- The PEAR channel on PearHub has been deprecated in favour of `pear.nrk.io`.
- Miscellaneous minor fixes.
- Added transparent support for master / slave replication configurations where
write operations are performed on the master server and read operations are
routed to one of the slaves. Please refer to ISSUE #21 for a bit of history
and more details about replication support in Predis.
- The `profile` client option now accepts a callable object used to initialize
a new instance of `Predis\Profiles\IServerProfile`.
- Exposed a method for MULTI / EXEC contexts that adds the ability to execute
instances of Redis commands against transaction objects.
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
for each class). You can use any PSR-0 compatible autoloader to load Predis
or just leverage the default one shipped with the library by requiring the
`Predis/Autoloader.php` and call `Predis\Autoloader::register()`.
- The default server profile for Redis is now 2.4. The `dev` profile supports
all the features of Redis 2.6 (currently unstable) such as Lua scripting.
- Support for long aliases (method names) for Redis commands has been dropped.
- Redis 1.0 is no more supported. From now on Predis will use only the unified
protocol to serialize commands.
- It is possible to prefix keys transparently on a client-level basis with the
new `prefix` client option.
- An external connection factory is used to initialize new connection instances
and developers can now register their own connection classes using the new
`connections` client option.
- It is possible to connect locally to Redis using UNIX domain sockets. Just
use `unix:///path/to/redis.sock` or a named array just like in the following
example: `array('scheme' => 'unix', 'path' => '/path/to/redis.sock');`.
- If the `phpiredis` extension is loaded by PHP, it is now possible to use an
alternative connection class that leverages it to make Predis faster on many
cases, especially when dealing with big multibulk replies, with the the only
downside that persistent connections are not supported. Please refer to the
documentation to see how to activate this class using the new `connections`
client option.
- Predis is capable to talk with Webdis, albeit with some limitations such as
the lack of pipelining and transactions, just by using the `http` scheme in
in the connection parameters. All is needed is PHP with the `curl` and the
`phpiredis` extensions loaded.
- Way too many changes in the public API to make a list here, we just tried to
make all the Redis commands compatible with previous releases of v0.6 so that
you do not have to worry if you are simply using Predis as a client. Probably
the only breaking changes that should be mentioned here are:
- `throw_on_error` has been renamed to `throw_errors` and it is a connection
parameter instead of a client option, along with `iterable_multibulk`.
- `key_distribution` has been removed from the client options. To customize
the distribution strategy you must provide a callable object to the new
`cluster` client option to configure and then return a new instance of
`Predis\Network\IConnectionCluster`.
- `Predis\Client::create()` has been removed. Just use the constructor to set
up a new instance of `Predis\Client`.
- `Predis\Client::pipelineSafe()` was deprecated in Predis v0.6.1 and now has
finally removed. Use `Predis\Client::pipeline(array('safe' => true))`.
- `Predis\Client::rawCommand()` has been removed due to inconsistencies with
the underlying connection abstractions. You can still get the raw resource
out of a connection with `Predis\Network\IConnectionSingle::getResource()`
so that you can talk directly with Redis.
- The `Predis\MultiBulkCommand` class has been merged into `Predis\Command` and
thus removed. Serialization of commands is now a competence of connections.
- The `Predis\IConnection` interface has been splitted into two new interfaces:
`Predis\Network\IConnectionSingle` and `Predis\Network\IConnectionCluster`.
- The constructor of `Predis\Client` now accepts more type of arguments such as
instances of `Predis\IConnectionParameters` and `Predis\Network\IConnection`.
v0.6.6 (2011-04-01)
================================================================================
- Switched to Redis 2.2 as the default server profile (there are no changes
that would break compatibility with previous releases). Long command names
are no more supported by default but if you need them you can still require
`Predis_Compatibility.php` to avoid breaking compatibility.
- Added a `VERSION` constant to `Predis\Client`.
- Some performance improvements for multibulk replies (parsing them is about
16% faster than the previous version). A few core classes have been heavily
optimized to reduce overhead when creating new instances.
- Predis now uses by default a new protocol reader, more lightweight and
faster than the default handler-based one. Users can revert to the old
protocol reader with the `reader` client option set to `composable`.
This client option can also accept custom reader classes implementing the
new `Predis\IResponseReader` interface.
- Added support for connecting to Redis using UNIX domain sockets (ISSUE #25).
- The `read_write_timeout` connection parameter can now be set to 0 or false
to disable read and write timeouts on connections. The old behaviour of -1
is still intact.
- `ZUNIONSTORE` and `ZINTERSTORE` can accept an array to specify a list of the
source keys to be used to populate the destination key.
- `MGET`, `SINTER`, `SUNION` and `SDIFF` can accept an array to specify a list
of keys. `SINTERSTORE`, `SUNIONSTORE` and `SDIFFSTORE` can also accept an
array to specify the list of source keys.
- `SUBSCRIBE` and `PSUBSCRIBE` can accept a list of channels for subscription.
- __FIX__: some client-side clean-ups for `MULTI/EXEC` were handled incorrectly
in a couple of corner cases (ISSUE #27).
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
desynchronization errors (ISSUE #20).
v0.6.4 (2011-02-12)
================================================================================
- Various performance improvements (15% ~ 25%) especially when dealing with
long multibulk replies or when using clustered connections.
- Added the `on_retry` option to `Predis\MultiExecBlock` that can be used to
specify an external callback (or any callable object) that gets invoked
whenever a transaction is aborted by the server.
- Added inline (p)subscribtion via options when initializing an instance of
`Predis\PubSubContext`.
v0.6.3 (2011-01-01)
================================================================================
- New commands available in the Redis v2.2 profile (dev):
- Strings: `SETRANGE`, `GETRANGE`, `SETBIT`, `GETBIT`
- Lists : `BRPOPLPUSH`
- The abstraction for `MULTI/EXEC` transactions has been dramatically improved
by providing support for check-and-set (CAS) operations when using Redis >=
2.2. Aborted transactions can also be optionally replayed in automatic up
to a user-defined number of times, after which a `Predis\AbortedMultiExec`
exception is thrown.
v0.6.2 (2010-11-28)
================================================================================
- Minor internal improvements and clean ups.
- New commands available in the Redis v2.2 profile (dev):
- Strings: `STRLEN`
- Lists : `LINSERT`, `RPUSHX`, `LPUSHX`
- ZSets : `ZREVRANGEBYSCORE`
- Misc. : `PERSIST`
- WATCH also accepts a single array parameter with the keys that should be
monitored during a transaction.
- Improved the behaviour of `Predis\MultiExecBlock` in certain corner cases.
- Improved parameters checking for the SORT command.
- __FIX__: the `STORE` parameter for the `SORT` command didn't work correctly
when using `0` as the target key (ISSUE #13).
- __FIX__: the methods for `UNWATCH` and `DISCARD` do not break anymore method
chaining with `Predis\MultiExecBlock`.
v0.6.1 (2010-07-11)
================================================================================
- Minor internal improvements and clean ups.
- New commands available in the Redis v2.2 profile (dev):
- Misc. : `WATCH`, `UNWATCH`
- Optional modifiers for `ZRANGE`, `ZREVRANGE` and `ZRANGEBYSCORE` queries are
supported using an associative array passed as the last argument of their
respective methods.
- The `LIMIT` modifier for `ZRANGEBYSCORE` can be specified using either:
- an indexed array: `array($offset, $count)`
- an associative array: `array('offset' => $offset, 'count' => $count)`
- The method `Predis\Client::__construct()` now accepts also instances of
`Predis\ConnectionParameters`.
- `Predis\MultiExecBlock` and `Predis\PubSubContext` now throw an exception
when trying to create their instances using a profile that does not
support the required Redis commands or when the client is connected to
a cluster of connections.
- Various improvements to `Predis\MultiExecBlock`:
- fixes and more consistent behaviour across various usage cases.
- support for `WATCH` and `UNWATCH` when using the current development
profile (Redis v2.2) and aborted transactions.
- New signature for `Predis\Client::multiExec()` which is now able to accept
an array of options for the underlying instance of `Predis\MultiExecBlock`.
Backwards compatibility with previous releases of Predis is ensured.
- New signature for `Predis\Client::pipeline()` which is now able to accept
an array of options for the underlying instance of Predis\CommandPipeline.
Backwards compatibility with previous releases of Predis is ensured.
The method `Predis\Client::pipelineSafe()` is to be considered deprecated.
- __FIX__: The `WEIGHT` modifier for `ZUNIONSTORE` and `ZINTERSTORE` was
handled incorrectly with more than two weights specified.
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
deprecated as they will be removed in future releases of Redis.
- The default server profile is `2.0` (targeting Redis 2.0.x). If you are
using older versions of Redis, it is highly recommended that you specify
which server profile the client should use (e.g. `1.2` when connecting
to instances of Redis 1.2.x).
- Support for Redis 1.0 is now optional and it is provided by requiring
'Predis_Compatibility.php' before creating an instance of `Predis\Client`.
- New commands added to the Redis 2.0 profile since Predis 0.5.1:
- Strings: `SETEX`, `APPEND`, `SUBSTR`
- ZSets : `ZCOUNT`, `ZRANK`, `ZUNIONSTORE`, `ZINTERSTORE`, `ZREMBYRANK`,
`ZREVRANK`
- Hashes : `HSET`, `HSETNX`, `HMSET`, `HINCRBY`, `HGET`, `HMGET`, `HDEL`,
`HEXISTS`, `HLEN`, `HKEYS`, `HVALS`, `HGETALL`
- PubSub : `PUBLISH`, `SUBSCRIBE`, `UNSUBSCRIBE`
- Misc. : `DISCARD`, `CONFIG`
- Introduced client-level options with the new `Predis\ClientOptions` class.
Options can be passed to the constructor of `Predis\Client` in its second
argument as an array or an instance of `Predis\ClientOptions`. For brevity's
sake and compatibility with older versions, the constructor still accepts
an instance of `Predis\RedisServerProfile` in its second argument. The
currently supported client options are:
- `profile` [default: `2.0` as of Predis 0.6.0]: specifies which server
profile to use when connecting to Redis. This option accepts an instance
of `Predis\RedisServerProfile` or a string that indicates the version.
- `key_distribution` [default: `Predis\Distribution\HashRing`]: specifies
which key distribution strategy to use to distribute keys among the
servers that compose a cluster. This option accepts an instance of
`Predis\Distribution\IDistributionStrategy` so that users can implement
their own key distribution strategy. `Predis\Distribution\KetamaPureRing`
is an alternative distribution strategy providing a pure-PHP implementation
of the same algorithm used by libketama.
- `throw_on_error` [default: `TRUE`]: server errors can optionally be handled
"silently": instead of throwing an exception, the client returns an error
response type.
- `iterable_multibulk` [EXPERIMENTAL - default: `FALSE`]: in addition to the
classic way of fetching a whole multibulk reply into an array, the client
can now optionally stream a multibulk reply down to the user code by using
PHP iterators. It is just a little bit slower, but it can save a lot of
memory in certain scenarios.
- New parameters for connections:
- `alias` [default: not set]: every connection can now be identified by an
alias that is useful to get a specific connections when connected to a
cluster of Redis servers.
- `weight` [default: not set]: allows to balance keys asymmetrically across
multiple servers. This is useful when you have servers with different
amounts of memory to distribute the load of your keys accordingly.
- `connection_async` [default: `FALSE`]: estabilish connections to servers
in a non-blocking way, so that the client is not blocked while the socket
resource performs the actual connection.
- `connection_persistent` [default: `FALSE`]: the underlying socket resource
is left open when a script ends its lifecycle. Persistent connections can
lead to unpredictable or strange behaviours, so they should be used with
extreme care.
- Introduced the `Predis\Pipeline\IPipelineExecutor` interface. Classes that
implements this interface are used internally by the `Predis\CommandPipeline`
class to change the behaviour of the pipeline when writing/reading commands
from one or multiple servers. Here is the list of the default executors:
- `Predis\Pipeline\StandardExecutor`: exceptions generated by server errors
might be thrown depending on the options passed to the client (see the
`throw_on_error` client option). Instead, protocol or network errors always
throw exceptions. This is the default executor for single and clustered
connections and shares the same behaviour of Predis 0.5.x.
- `Predis\Pipeline\SafeExecutor`: exceptions generated by server, protocol
or network errors are not thrown but returned in the response array as
instances of `Predis\ResponseError` or `Predis\CommunicationException`.
- `Predis\Pipeline\SafeClusterExecutor`: this executor shares the same
behaviour of `Predis\Pipeline\SafeExecutor` but it is geared towards
clustered connections.
- Support for PUB/SUB is handled by the new `Predis\PubSubContext` class, which
could also be used to build a callback dispatcher for PUB/SUB scenarios.
- When connected to a cluster of connections, it is now possible to get a
new `Predis\Client` instance for a single connection of the cluster by
passing its alias/index to the new `Predis\Client::getClientFor()` method.
- `Predis\CommandPipeline` and `Predis\MultiExecBlock` return their instances
when invokink commands, thus allowing method chaining in pipelines and
multi-exec blocks.
- `Predis\MultiExecBlock` can handle the new `DISCARD` command.
- Connections now support float values for the `connection_timeout` parameter
to express timeouts with a microsecond resolution.
- __FIX__: TCP connections now respect the read/write timeout parameter when
reading the payload of server responses. Previously, `stream_get_contents()`
was being used internally to read data from a connection but it looks like
PHP does not honour the specified timeout for socket streams when inside
this function.
- __FIX__: The `GET` parameter for the `SORT` command now accepts also multiple
key patterns by passing an array of strings. (ISSUE #1).
* __FIX__: Replies to the `DEL` command return the number of elements deleted
by the server and not 0 or 1 interpreted as a boolean response. (ISSUE #4).
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
is still available via the `ListPopLastPushHeadBulk` class so that you can
override the server profile if you need the old (and uncorrect) behaviour
when connecting to a Redis 1.2.0 instance.
* Added missing support for `BGREWRITEAOF` for Redis >= 1.2.0.
* Implemented a factory method for the `RedisServerProfile` class to ease the
creation of new server profile instances based on a version string.
v0.5.0 (2010-01-09)
================================================================================
* First versioned release of Predis
+1 -1
View File
@@ -1,6 +1,6 @@
## Filing bug reports ##
Bugs or feature requests can be posted on the [GitHub issues](http://github.com/predis/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
+1 -9
View File
@@ -18,14 +18,6 @@ least to some degree).
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 SSL-encrypted connections? ###
Yes. Encrypted connections are mostly useful when connecting to Redis instances exposed by various
cloud hosting providers without the need to configure an SSL proxy, but you should also take into
account the general performances degradation especially during the connect() operation when the TLS
handshake must be performed to secure the connection. Persistent SSL-encrypted connections may help
in that respect, but they are supported only when running on PHP >= 7.0.0.
### Does Predis support transparent (de)serialization of values? ###
No and it will not ever do that by default. The reason behind this decision is that serialization is
@@ -33,7 +25,7 @@ usually something that developers prefer to customize depending on their needs a
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/predis/predis/issues/29#issuecomment-1202624).
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? ###
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2009-2021 Daniele Alessandri
Copyright (c) 2009-2016 Daniele Alessandri
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
+73 -131
View File
@@ -1,39 +1,46 @@
# Predis #
[![Software license][ico-license]](LICENSE)
[![Latest stable][ico-version-stable]][link-packagist]
[![Latest development][ico-version-dev]][link-packagist]
[![Software license][ico-license]](LICENSE)
[![Monthly installs][ico-downloads-monthly]][link-downloads]
[![Build status][ico-build]][link-actions]
[![Build status][ico-travis]][link-travis]
[![HHVM support][ico-hhvm]][link-hhvm]
A flexible and feature-complete [Redis](http://redis.io) client for PHP 7.2 and newer.
Flexible and feature-complete [Redis](http://redis.io) client for PHP >= 5.3 and HHVM >= 2.3.0.
Predis does not require any additional C extension by default, but it can be optionally paired with
[phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of the serialization and parsing
of the [Redis RESP Protocol](http://redis.io/topics/protocol). For an __experimental__ asynchronous
implementation of the client you can refer to [Predis\Async](https://github.com/nrk/predis-async).
More details about this project can be found on the [frequently asked questions](FAQ.md).
## Main features ##
- Support for Redis from __3.0__ to __7.0__.
- Support for different versions of Redis (from __2.0__ to __3.2__) using profiles.
- Support for clustering using client-side sharding and pluggable keyspace distributors.
- Support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
- Support for master-slave replication setups and [redis-sentinel](http://redis.io/topics/sentinel).
- Support for standalone master-slave replication setups.
- Transparent key prefixing of keys using a customizable prefix strategy.
- Command pipelining on both single nodes and clusters (client-side sharding only).
- Abstraction for Redis transactions (Redis >= 2.0) and CAS operations (Redis >= 2.2).
- Abstraction for Lua scripting (Redis >= 2.6) and automatic switching between `EVALSHA` or `EVAL`.
- Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators.
- Connections are established lazily by the client upon the first command and can be persisted.
- Connections can be established via TCP/IP (also TLS/SSL-encrypted) or UNIX domain sockets.
- Connections can be established via TCP/IP or UNIX domain sockets.
- Support for [Webdis](http://webd.is) (requires both `ext-curl` and `ext-phpiredis`).
- Support for custom connection classes for providing different network or protocol backends.
- Flexible system for defining custom commands and override the default ones.
- Flexible system for defining custom commands and server profiles.
## How to _install_ and use Predis ##
This library can be found on [Packagist](http://packagist.org/packages/predis/predis) for an easier
management of projects dependencies using [Composer](http://packagist.org/about-composer).
Compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
management of projects dependencies using [Composer](http://packagist.org/about-composer) or on our
[own PEAR channel](http://pear.nrk.io) for a more traditional installation using PEAR. Ultimately,
compressed archives of each release are [available on GitHub](https://github.com/nrk/predis/tags).
### Loading the library ###
@@ -50,6 +57,10 @@ require 'Predis/Autoloader.php';
Predis\Autoloader::register();
```
It is also possible to create a [phar](http://www.php.net/manual/en/intro.phar.php) archive directly
from the repository by launching the `bin/create-phar` script. The generated phar already contains a
stub defining its own autoloader, so you just need to `require()` it to start using the library.
### Connecting to Redis ###
@@ -78,62 +89,34 @@ $client = new Predis\Client([
$client = new Predis\Client('tcp://10.0.0.1:6379');
```
Password protected servers can be accessed by adding `password` to the parameters set. When ACLs are
enabled on Redis >= 6.0, both `username` and `password` are required for user authentication.
It is also possible to connect to local instances of Redis using UNIX domain sockets, in this case
the parameters must use the `unix` scheme and specify a path for the socket file:
```php
$client = new Predis\Client(['scheme' => 'unix', 'path' => '/path/to/redis.sock']);
$client = new Predis\Client('unix:/path/to/redis.sock');
```
The client can leverage TLS/SSL encryption to connect to secured remote Redis instances without the
need to configure an SSL proxy like stunnel. This can be useful when connecting to nodes running on
various cloud hosting providers. Encryption can be enabled with using the `tls` scheme and an array
of suitable [options](http://php.net/manual/context.ssl.php) passed via the `ssl` parameter:
```php
// Named array of connection parameters:
$client = new Predis\Client([
'scheme' => 'tls',
'ssl' => ['cafile' => 'private.pem', 'verify_peer' => true],
]);
// Same set of parameters, but using an URI string:
$client = new Predis\Client('tls://127.0.0.1?ssl[cafile]=private.pem&ssl[verify_peer]=1');
$client = new Predis\Client('unix:///path/to/redis.sock');
```
The connection schemes [`redis`](http://www.iana.org/assignments/uri-schemes/prov/redis) (alias of
`tcp`) and [`rediss`](http://www.iana.org/assignments/uri-schemes/prov/rediss) (alias of `tls`) are
also supported, with the difference that URI strings containing these schemes are parsed following
the rules described on their respective IANA provisional registration documents.
`tcp`) is also supported, with the difference that URI strings containing these schemes are parsed
following the rules described on the IANA provisional registration.
The actual list of supported connection parameters can vary depending on each connection backend so
it is recommended to refer to their specific documentation or implementation for details.
Predis can aggregate multiple connections when providing an array of connection parameters and the
appropriate option to instruct the client about how to aggregate them (clustering, replication or a
custom aggregation logic). Named arrays and URI strings can be mixed when providing configurations
When an array of connection parameters is provided, Predis automatically works in cluster mode using
client-side sharding. Both named arrays and URI strings can be mixed when providing configurations
for each node:
```php
$client = new Predis\Client([
'tcp://10.0.0.1?alias=first-node', ['host' => '10.0.0.2', 'alias' => 'second-node'],
], [
'cluster' => 'predis',
'tcp://10.0.0.1?alias=first-node',
['host' => '10.0.0.2', 'alias' => 'second-node'],
]);
```
See the [aggregate connections](#aggregate-connections) section of this document for more details.
Connections to Redis are lazy meaning that the client connects to a server only if and when needed.
While it is recommended to let the client do its own stuff under the hood, there may be times when
it is still desired to have control of when the connection is opened or closed: this can easily be
achieved by invoking `$client->connect()` and `$client->disconnect()`. Please note that the effect
of these methods on aggregate connections may differ depending on each specific implementation.
### Client configuration ###
@@ -141,20 +124,19 @@ Many aspects and behaviors of the client can be configured by passing specific c
second argument of `Predis\Client::__construct()`:
```php
$client = new Predis\Client($parameters, ['prefix' => 'sample:']);
$client = new Predis\Client($parameters, ['profile' => '2.8', 'prefix' => 'sample:']);
```
Options are managed using a mini DI-alike container and their values can be lazily initialized only
when needed. The client options supported by default in Predis are:
- `prefix`: prefix string applied to every key found in commands.
- `profile`: specifies the profile to use to match a specific version of Redis.
- `prefix`: prefix string automatically applied to keys found in commands.
- `exceptions`: whether the client should throw or return responses upon Redis errors.
- `connections`: list of connection backends or a connection factory instance.
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable).
- `replication`: specifies a replication backend (`predis`, `sentinel` or callable).
- `aggregate`: configures the client with a custom aggregate connection (callable).
- `parameters`: list of default connection parameters for aggregate connections.
- `commands`: specifies a command factory instance to use through the library.
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable object).
- `replication`: specifies a replication backend (`TRUE` or callable object).
- `aggregate`: overrides `cluster` and `replication` to provide a custom connections aggregator.
Users can also provide custom options with values or callable objects (for lazy initialization) that
are stored in the options container for later use through the library.
@@ -165,18 +147,18 @@ are stored in the options container for later use through the library.
Aggregate connections are the foundation upon which Predis implements clustering and replication and
they are used to group multiple connections to single Redis nodes and hide the specific logic needed
to handle them properly depending on the context. Aggregate connections usually require an array of
connection parameters along with the appropriate client option when creating a new client instance.
connection parameters when creating a new client instance.
#### Cluster ####
Predis can be configured to work in clustering mode with a traditional client-side sharding approach
to create a cluster of independent nodes and distribute the keyspace among them. This approach needs
some sort of external health monitoring of nodes and requires the keyspace to be rebalanced manually
when nodes are added or removed:
By default, when no specific client options are set and an array of connection parameters is passed
to the client's constructor, Predis configures itself to work in clustering mode using a traditional
client-side sharding approach to create a cluster of independent nodes and distribute the keyspace
among them. This approach needs some form of external health monitoring of nodes and requires manual
operations to rebalance the keyspace when changing its configuration by adding or removing nodes:
```php
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['cluster' => 'predis'];
$client = new Predis\Client($parameters);
```
@@ -206,45 +188,16 @@ the value of a key. Instead of raising a connection error when a slave fails, th
fall back to a different slave among the ones provided in the configuration.
The basic configuration needed to use the client in replication mode requires one Redis server to be
identified as the master (this can be done via connection parameters by setting the `role` parameter
to `master`) and one or more slaves (in this case setting `role` to `slave` for slaves is optional):
identified as the master (this can be done via connection parameters using the `alias` parameter set
to `master`) and one or more servers acting as slaves:
```php
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => 'predis'];
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => true];
$client = new Predis\Client($parameters, $options);
```
The above configuration has a static list of servers and relies entirely on the client's logic, but
it is possible to rely on [`redis-sentinel`](http://redis.io/topics/sentinel) for a more robust HA
environment with sentinel servers acting as a source of authority for clients for service discovery.
The minimum configuration required by the client to work with redis-sentinel is a list of connection
parameters pointing to a bunch of sentinel instances, the `replication` option set to `sentinel` and
the `service` option set to the name of the service:
```php
$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => 'sentinel', 'service' => 'mymaster'];
$client = new Predis\Client($sentinels, $options);
```
If the master and slave nodes are configured to require an authentication from clients, a password
must be provided via the global `parameters` client option. This option can also be used to specify
a different database index. The client options array would then look like this:
```php
$options = [
'replication' => 'sentinel',
'service' => 'mymaster',
'parameters' => [
'password' => $secretpassword,
'database' => 10,
],
];
```
While Predis is able to distinguish commands performing write and read-only operations, `EVAL` and
`EVALSHA` represent a corner case in which the client switches to the master node because it cannot
tell when a Lua script is safe to be executed on slaves. While this is indeed the default behavior,
@@ -252,13 +205,13 @@ when certain Lua scripts do not perform write operations it is possible to provi
the client to stick with slaves for their execution:
```php
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['replication' => function () {
// Set scripts that won't trigger a switch from a slave to the master node.
$strategy = new Predis\Replication\ReplicationStrategy();
$strategy->setScriptReadOnly($LUA_SCRIPT);
return new Predis\Connection\Replication\MasterSlaveReplication($strategy);
return new Predis\Connection\Aggregate\MasterSlaveReplication($strategy);
}];
$client = new Predis\Client($parameters, $options);
@@ -317,7 +270,7 @@ of a transaction using CAS you can see [the following example](examples/transact
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 old 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 default command factory used by the client:
command classes to define or override commands in the default server profiles used by the client:
```php
// Define a new command by extending Predis\Command\Command:
@@ -329,12 +282,9 @@ class BrandNewRedisCommand extends Predis\Command\Command
}
}
// Inject your command in the current command factory:
$client = new Predis\Client($parameters, [
'commands' => [
'newcmd' => 'BrandNewRedisCommand',
],
]);
// Inject your command in the current profile:
$client = new Predis\Client();
$client->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand');
$response = $client->newcmd();
```
@@ -353,7 +303,7 @@ $response = $client->executeRaw(['SET', 'foo', 'bar']);
While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using
directly [`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha),
Predis offers script commands as an higher level abstraction built upon them to make things simple.
Script commands can be registered in the command factory used by the client and are accessible as if
Script 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 Lua scripts that get transmitted to the server for
remote execution. Internally they use [`EVALSHA`](http://redis.io/commands/evalsha) by default and
identify a script by its SHA1 hash to save bandwidth, but [`EVAL`](http://redis.io/commands/eval)
@@ -379,12 +329,9 @@ LUA;
}
}
// Inject the script command in the current command factory:
$client = new Predis\Client($parameters, [
'commands' => [
'lpushrand' => 'ListPushRandomValue',
],
]);
// Inject the script command in the current profile:
$client = new Predis\Client();
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');
$response = $client->lpushrand('random_values', $seed = mt_rand());
```
@@ -406,17 +353,6 @@ $client = new Predis\Client('tcp://127.0.0.1', [
]);
```
The client can also be configured to rely on a [phpiredis](https://github.com/nrk/phpiredis)-backend
by specifying a descriptive string for the `connections` client option. Supported string values are:
- `phpiredis-stream` maps `tcp`, `redis` and `unix` to `Predis\Connection\PhpiredisStreamConnection`
- `phpiredis-socket` maps `tcp`, `redis` and `unix` to `Predis\Connection\PhpiredisSocketConnection`
- `phpiredis` is simply an alias of `phpiredis-stream`
```php
$client = new Predis\Client('tcp://127.0.0.1', ['connections' => 'phpiredis']);
```
Developers can create their own connection classes to support whole new network backends, extend
existing classes or provide completely different implementations. Connection classes must implement
`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
@@ -453,14 +389,18 @@ stay consistent while working on the project.
__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 and that can optionally
perform integration tests against a running instance of Redis (required >= 2.4.0 in order to verify
the correct behavior of the implementation of each command. Integration tests for unsupported Redis
commands are automatically skipped. If you do not have Redis up and running, integration tests can
be disabled. See [the tests README](tests/README.md) for more details about testing this library.
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
behavior 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 GitHub Actions for continuous integration and the history for past and current builds can be
found [on its actions page](https://github.com/predis/predis/actions).
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 ##
@@ -468,27 +408,29 @@ found [on its actions page](https://github.com/predis/predis/actions).
### Project related links ###
- [Source code](https://github.com/predis/predis)
- [Wiki](https://github.com/predis/predis/wiki)
- [Issue tracker](https://github.com/predis/predis/issues)
- [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)
### Author ###
- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN))
- [Till Krüss](https://till.im) ([Twitter](http://twitter.com/tillkruss))
### License ###
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
[ico-license]: https://img.shields.io/github/license/predis/predis.svg?style=flat-square
[ico-license]: https://img.shields.io/github/license/nrk/predis.svg?style=flat-square
[ico-version-stable]: https://img.shields.io/packagist/v/predis/predis.svg?style=flat-square
[ico-version-dev]: https://img.shields.io/packagist/vpre/predis/predis.svg?style=flat-square
[ico-downloads-monthly]: https://img.shields.io/packagist/dm/predis/predis.svg?style=flat-square
[ico-build]: https://img.shields.io/github/workflow/status/predis/predis/Tests/main?style=flat-square
[ico-travis]: https://img.shields.io/travis/nrk/predis.svg?style=flat-square
[ico-hhvm]: https://img.shields.io/hhvm/predis/predis.svg?style=flat-square
[link-packagist]: https://packagist.org/packages/predis/predis
[link-actions]: https://github.com/predis/predis/actions
[link-travis]: https://travis-ci.org/nrk/predis
[link-downloads]: https://packagist.org/packages/predis/predis/stats
[link-hhvm]: http://hhvm.h4cc.de/package/predis/predis
+1 -1
View File
@@ -1 +1 @@
2.0.0
1.0.4
+21 -24
View File
@@ -15,7 +15,7 @@
// of a test case to test a Redis command by specifying the name of the class
// in the Predis\Command namespace (only classes in this namespace are valid).
// For example, to generate a test case for SET (which is represented by the
// Predis\Command\Redis\StringSet class):
// Predis\Command\StringSet class):
//
// $ ./bin/generate-command-test --class=StringSet
//
@@ -45,11 +45,6 @@ class CommandTestCaseGenerator
if (!isset($options['class'])) {
throw new RuntimeException("Missing 'class' option.");
}
if (!isset($options['realm'])) {
throw new RuntimeException("Missing 'realm' option.");
}
$this->options = $options;
}
@@ -97,12 +92,8 @@ class CommandTestCaseGenerator
throw new RuntimeException("Missing 'class' option.");
}
if (!isset($options['realm'])) {
throw new RuntimeException("Missing 'realm' option.");
}
$options['fqn'] = "Predis\\Command\\Redis\\{$options['class']}";
$options['path'] = "Command/Redis/{$options['class']}.php";
$options['fqn'] = "Predis\\Command\\{$options['class']}";
$options['path'] = "Command/{$options['class']}.php";
$source = __DIR__.'/../src/'.$options['path'];
if (!file_exists($source)) {
@@ -110,7 +101,7 @@ class CommandTestCaseGenerator
}
if (!isset($options['output'])) {
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', '_Test.php', $options['path']));
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', 'Test.php', $options['path']));
}
return new self($options);
@@ -118,11 +109,18 @@ class CommandTestCaseGenerator
protected function getTestRealm()
{
if (empty($this->options['realm'])) {
throw new RuntimeException('Invalid value for realm has been sepcified (empty).');
if (isset($this->options['realm'])) {
if (!$this->options['realm']) {
throw new RuntimeException('Invalid value for realm has been sepcified (empty).');
}
return $this->options['realm'];
}
return $this->options['realm'];
$fqnParts = explode('\\', $this->options['fqn']);
$class = array_pop($fqnParts);
list($realm,) = preg_split('/([[:upper:]][[:lower:]]+)/', $class, 2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
return strtolower($realm);
}
public function generate()
@@ -132,7 +130,6 @@ class CommandTestCaseGenerator
if (!$reflection->isInstantiable()) {
throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed.");
}
if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) {
throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface.");
}
@@ -176,7 +173,7 @@ class CommandTestCaseGenerator
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
namespace Predis\Command;
/**
* @group commands
@@ -187,7 +184,7 @@ class $class extends PredisCommandTestCase
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
protected function getExpectedCommand()
{
return '$fqn';
}
@@ -195,7 +192,7 @@ class $class extends PredisCommandTestCase
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
protected function getExpectedId()
{
return '$id';
}
@@ -203,7 +200,7 @@ class $class extends PredisCommandTestCase
/**
* @group disconnected
*/
public function testFilterArguments(): void
public function testFilterArguments()
{
\$this->markTestIncomplete('This test has not been implemented yet.');
@@ -219,7 +216,7 @@ class $class extends PredisCommandTestCase
/**
* @group disconnected
*/
public function testParseResponse(): void
public function testParseResponse()
{
\$this->markTestIncomplete('This test has not been implemented yet.');
@@ -239,7 +236,7 @@ PHP;
/**
* @group disconnected
*/
public function testPrefixKeys(): void
public function testPrefixKeys()
{
\$this->markTestIncomplete('This test has not been implemented yet.');
@@ -255,7 +252,7 @@ PHP;
/**
* @group disconnected
*/
public function testPrefixKeysIgnoredOnEmptyArguments(): void
public function testPrefixKeysIgnoredOnEmptyArguments()
{
\$command = \$this->getCommand();
\$command->prefixKeys('prefix:');
+233
View File
@@ -0,0 +1,233 @@
#!/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($_,$name,$user,$email) = $regs;
$author['name'] = $name;
$author['user'] = $user;
$author['email'] = $email;
} elseif (count($regs) == 3) {
list($_,$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],
);
}
return null;
}
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 rewritePackageInstallAs($pkg)
{
foreach ($pkg->phprelease->filelist->install as $file) {
if (preg_match('/^src\//', $file['name'])) {
$file['as'] = "Predis/{$file['as']}";
}
}
}
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');
rewritePackageInstallAs($pkg);
savePackageXml($pkg);
buildPackage();
});
+71
View File
@@ -0,0 +1,71 @@
#!/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 Phar archive of Predis,
// the Phar module must be loaded and the "phar.readonly" directive php.ini must
// be set to "off". You can change the values in the $options array to customize
// the creation of the Phar archive to better suit your needs.
// -------------------------------------------------------------------------- //
$options = array(
'name' => 'predis',
'project_path' => __DIR__ . '/../src',
'compression' => Phar::NONE,
'append_version' => true,
);
function getPharFilename($options)
{
$filename = $options['name'];
// NOTE: do not consider "append_version" with Phar compression do to a bug in
// Phar::compress() when renaming phar archives containing dots in their name.
if ($options['append_version'] && $options['compression'] === Phar::NONE) {
$versionFile = @fopen(__DIR__ . '/../VERSION', 'r');
if ($versionFile === false) {
throw new Exception("Could not locate the VERSION file.");
}
$version = trim(fgets($versionFile));
fclose($versionFile);
$filename .= "_$version";
}
return "$filename.phar";
}
function getPharStub($options)
{
return <<<EOSTUB
<?php
Phar::mapPhar('predis.phar');
spl_autoload_register(function (\$class) {
if (strpos(\$class, 'Predis\\\\') === 0) {
\$file = 'phar://predis.phar/'.strtr(substr(\$class, 7), '\\\', '/').'.php';
if (file_exists(\$file)) {
require \$file;
return true;
}
}
});
__HALT_COMPILER();
EOSTUB;
}
// -------------------------------------------------------------------------- //
$phar = new Phar(getPharFilename($options));
$phar->compress($options['compression']);
$phar->setStub(getPharStub($options));
$phar->buildFromDirectory($options['project_path']);
+662
View File
@@ -0,0 +1,662 @@
#!/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.
*/
// -------------------------------------------------------------------------- //
// This script can be used to automatically glue all the .php files of Predis
// into a single monolithic script file that can be used without an autoloader,
// just like the other previous versions of the library.
//
// Much of its complexity is due to the fact that we cannot simply join PHP
// files, but namespaces and classes definitions must follow a precise order
// when dealing with subclassing and inheritance.
//
// The current implementation is pretty naïve, but it should do for now.
// -------------------------------------------------------------------------- //
class CommandLine
{
public static function getOptions()
{
$parameters = array(
's:' => 'source:',
'o:' => 'output:',
'e:' => 'exclude:',
'E:' => 'exclude-classes:',
);
$getops = getopt(implode(array_keys($parameters)), $parameters);
$options = array(
'source' => __DIR__ . "/../src",
'output' => PredisFile::NS_ROOT . '.php',
'exclude' => array(),
);
foreach ($getops as $option => $value) {
switch ($option) {
case 's':
case 'source':
$options['source'] = $value;
break;
case 'o':
case 'output':
$options['output'] = $value;
break;
case 'E':
case 'exclude-classes':
$options['exclude'] = @file($value, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: $value;
break;
case 'e':
case 'exclude':
$options['exclude'] = is_array($value) ? $value : array($value);
break;
}
}
return $options;
}
}
class PredisFile
{
const NS_ROOT = 'Predis';
private $namespaces;
public function __construct()
{
$this->namespaces = array();
}
public static function from($libraryPath, array $exclude = array())
{
$predisFile = new PredisFile();
$libIterator = new RecursiveDirectoryIterator($libraryPath);
foreach (new RecursiveIteratorIterator($libIterator) as $classFile)
{
if (!$classFile->isFile()) {
continue;
}
$namespace = self::NS_ROOT.strtr(str_replace($libraryPath, '', $classFile->getPath()), '/', '\\');
if (in_array(sprintf('%s\\%s', $namespace, $classFile->getBasename('.php')), $exclude)) {
continue;
}
$phpNamespace = $predisFile->getNamespace($namespace);
if ($phpNamespace === false) {
$phpNamespace = new PhpNamespace($namespace);
$predisFile->addNamespace($phpNamespace);
}
$phpClass = new PhpClass($phpNamespace, $classFile);
}
return $predisFile;
}
public function addNamespace(PhpNamespace $namespace)
{
if (isset($this->namespaces[(string)$namespace])) {
throw new InvalidArgumentException("Duplicated namespace");
}
$this->namespaces[(string)$namespace] = $namespace;
}
public function getNamespaces()
{
return $this->namespaces;
}
public function getNamespace($namespace)
{
if (!isset($this->namespaces[$namespace])) {
return false;
}
return $this->namespaces[$namespace];
}
public function getClassByFQN($classFqn)
{
if (($nsLastPos = strrpos($classFqn, '\\')) !== false) {
$namespace = $this->getNamespace(substr($classFqn, 0, $nsLastPos));
if ($namespace === false) {
return null;
}
$className = substr($classFqn, $nsLastPos + 1);
return $namespace->getClass($className);
}
return null;
}
private function calculateDependencyScores(&$classes, $fqn)
{
if (!isset($classes[$fqn])) {
$classes[$fqn] = 0;
}
$classes[$fqn] += 1;
if (($phpClass = $this->getClassByFQN($fqn)) === null) {
throw new RuntimeException(
"Cannot found the class $fqn which is required by other subclasses. Are you missing a file?"
);
}
foreach ($phpClass->getDependencies() as $fqn) {
$this->calculateDependencyScores($classes, $fqn);
}
}
private function getDependencyScores()
{
$classes = array();
foreach ($this->getNamespaces() as $phpNamespace) {
foreach ($phpNamespace->getClasses() as $phpClass) {
$this->calculateDependencyScores($classes, $phpClass->getFQN());
}
}
return $classes;
}
private function getOrderedNamespaces($dependencyScores)
{
$namespaces = array_fill_keys(array_unique(
array_map(
function ($fqn) { return PhpNamespace::extractName($fqn); },
array_keys($dependencyScores)
)
), 0);
foreach ($dependencyScores as $classFqn => $score) {
$namespaces[PhpNamespace::extractName($classFqn)] += $score;
}
arsort($namespaces);
return array_keys($namespaces);
}
private function getOrderedClasses(PhpNamespace $phpNamespace, $classes)
{
$nsClassesFQNs = array_map(function ($cl) { return $cl->getFQN(); }, $phpNamespace->getClasses());
$nsOrderedClasses = array();
foreach ($nsClassesFQNs as $nsClassFQN) {
$nsOrderedClasses[$nsClassFQN] = $classes[$nsClassFQN];
}
arsort($nsOrderedClasses);
return array_keys($nsOrderedClasses);
}
public function getPhpCode()
{
$buffer = array("<?php\n\n", PhpClass::LICENSE_HEADER, "\n\n");
$classes = $this->getDependencyScores();
$namespaces = $this->getOrderedNamespaces($classes);
foreach ($namespaces as $namespace) {
$phpNamespace = $this->getNamespace($namespace);
// generate namespace directive
$buffer[] = $phpNamespace->getPhpCode();
$buffer[] = "\n";
// generate use directives
$useDirectives = $phpNamespace->getUseDirectives();
if (count($useDirectives) > 0) {
$buffer[] = $useDirectives->getPhpCode();
$buffer[] = "\n";
}
// generate classes bodies
$nsClasses = $this->getOrderedClasses($phpNamespace, $classes);
foreach ($nsClasses as $classFQN) {
$buffer[] = $this->getClassByFQN($classFQN)->getPhpCode();
$buffer[] = "\n\n";
}
$buffer[] = "/* " . str_repeat("-", 75) . " */";
$buffer[] = "\n\n";
}
return implode($buffer);
}
public function saveTo($outputFile)
{
// TODO: add more sanity checks
if ($outputFile === null || $outputFile === '') {
throw new InvalidArgumentException('You must specify a valid output file');
}
file_put_contents($outputFile, $this->getPhpCode());
}
}
class PhpNamespace implements IteratorAggregate
{
private $namespace;
private $classes;
public function __construct($namespace)
{
$this->namespace = $namespace;
$this->classes = array();
$this->useDirectives = new PhpUseDirectives($this);
}
public static function extractName($fqn)
{
$nsSepLast = strrpos($fqn, '\\');
if ($nsSepLast === false) {
return $fqn;
}
$ns = substr($fqn, 0, $nsSepLast);
return $ns !== '' ? $ns : null;
}
public function addClass(PhpClass $class)
{
$this->classes[$class->getName()] = $class;
}
public function getClass($className)
{
if (isset($this->classes[$className])) {
return $this->classes[$className];
}
}
public function getClasses()
{
return array_values($this->classes);
}
public function getIterator()
{
return new \ArrayIterator($this->getClasses());
}
public function getUseDirectives()
{
return $this->useDirectives;
}
public function getPhpCode()
{
return "namespace $this->namespace;\n";
}
public function __toString()
{
return $this->namespace;
}
}
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->reverseAliases = array();
}
public function add($use, $as = null)
{
if (in_array($use, $this->use)) {
return;
}
$rename = null;
$this->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()
{
return $this->use;
}
public function getIterator()
{
return new \ArrayIterator($this->getList());
}
public function getPhpCode()
{
$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, '');
}
public function getNamespace()
{
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) {
if (isset($this->aliases[$className])) {
return $this->aliases[$className];
}
return (string)$this->getNamespace() . "\\$className";
}
if ($nsSepFirst != 0) {
throw new InvalidArgumentException("Partially qualified names are not supported");
}
return $className;
}
public function count()
{
return count($this->use);
}
}
class PhpClass
{
const LICENSE_HEADER = <<<LICENSE
/*
* 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.
*/
LICENSE;
private $namespace;
private $file;
private $body;
private $implements;
private $extends;
private $name;
public function __construct(PhpNamespace $namespace, SplFileInfo $classFile)
{
$this->namespace = $namespace;
$this->file = $classFile;
$this->implements = array();
$this->extends = array();
$this->extractData();
$namespace->addClass($this);
}
public static function extractName($fqn)
{
$nsSepLast = strrpos($fqn, '\\');
if ($nsSepLast === false) {
return $fqn;
}
return substr($fqn, $nsSepLast + 1);
}
private function extractData()
{
$renames = array();
$useDirectives = $this->getNamespace()->getUseDirectives();
$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'));
$classBuffer = str_replace(self::LICENSE_HEADER, '', $classBuffer);
$classBuffer = preg_replace('/<\?php\s?\\n\s?/', '', $classBuffer);
$classBuffer = preg_replace('/\s?\?>\n?/ms', '', $classBuffer);
$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();
}
private function extractHierarchy()
{
$implements = array();
$extends = array();
$extractor = function ($iterator, $callback) {
$className = '';
$iterator->seek($iterator->key() + 1);
while ($iterator->valid()) {
$token = $iterator->current();
if (is_string($token)) {
if (preg_match('/\s?,\s?/', $token)) {
$callback(trim($className));
$className = '';
} else if ($token == '{') {
$callback(trim($className));
return;
}
}
switch ($token[0]) {
case T_NS_SEPARATOR:
$className .= '\\';
break;
case T_STRING:
$className .= $token[1];
break;
case T_IMPLEMENTS:
case T_EXTENDS:
$callback(trim($className));
$iterator->seek($iterator->key() - 1);
return;
}
$iterator->next();
}
};
$tokens = token_get_all("<?php\n" . trim($this->getPhpCode()));
$iterator = new ArrayIterator($tokens);
while ($iterator->valid()) {
$token = $iterator->current();
if (is_string($token)) {
$iterator->next();
continue;
}
switch ($token[0]) {
case T_CLASS:
case T_INTERFACE:
$iterator->seek($iterator->key() + 2);
$tk = $iterator->current();
$this->name = $tk[1];
break;
case T_IMPLEMENTS:
$extractor($iterator, function ($fqn) use (&$implements) {
$implements[] = $fqn;
});
break;
case T_EXTENDS:
$extractor($iterator, function ($fqn) use (&$extends) {
$extends[] = $fqn;
});
break;
}
$iterator->next();
}
$this->implements = $this->guessFQN($implements);
$this->extends = $this->guessFQN($extends);
}
public function guessFQN($classes)
{
$useDirectives = $this->getNamespace()->getUseDirectives();
return array_map(array($useDirectives, 'getFQN'), $classes);
}
public function getImplementedInterfaces($all = false)
{
if ($all) {
return $this->implements;
}
return array_filter(
$this->implements,
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
);
}
public function getExtendedClasses($all = false)
{
if ($all) {
return $this->extemds;
}
return array_filter(
$this->extends,
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
);
}
public function getDependencies($all = false)
{
return array_merge(
$this->getImplementedInterfaces($all),
$this->getExtendedClasses($all)
);
}
public function getNamespace()
{
return $this->namespace;
}
public function getFile()
{
return $this->file;
}
public function getName()
{
return $this->name;
}
public function getFQN()
{
return (string)$this->getNamespace() . '\\' . $this->name;
}
public function getPhpCode()
{
return $this->body;
}
public function __toString()
{
return "class " . $this->getName() . '{ ... }';
}
}
/* -------------------------------------------------------------------------- */
$options = CommandLine::getOptions();
$predisFile = PredisFile::from($options['source'], $options['exclude']);
$predisFile->saveTo($options['output']);
+7 -26
View File
@@ -1,50 +1,31 @@
{
"name": "predis/predis",
"type": "library",
"description": "A flexible and feature-complete Redis client for PHP.",
"description": "Flexible and feature-complete Redis client for PHP and HHVM",
"keywords": ["nosql", "redis", "predis"],
"homepage": "http://github.com/predis/predis",
"homepage": "http://github.com/nrk/predis",
"license": "MIT",
"support": {
"issues": "https://github.com/predis/predis/issues"
"issues": "https://github.com/nrk/predis/issues"
},
"authors": [
{
"name": "Daniele Alessandri",
"email": "suppakilla@gmail.com",
"homepage": "http://clorophilla.net",
"role": "Creator & Maintainer"
},
{
"name": "Till Krüss",
"homepage": "https://till.im",
"role": "Maintainer"
}
],
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/tillkruss"
"homepage": "http://clorophilla.net"
}
],
"require": {
"php": "^7.2 || ^8.0"
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "^8.0 || ~9.4.4"
"phpunit/phpunit": "~4.8"
},
"suggest": {
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
"ext-curl": "Allows access to Webdis when paired with phpiredis"
},
"autoload": {
"psr-4": {
"Predis\\": "src/"
}
},
"extra": {
"branch-alias": {
"dev-main": "2.0-dev"
}
"psr-4": {"Predis\\": "src/"}
}
}
+3 -3
View File
@@ -18,7 +18,7 @@ require __DIR__.'/shared.php';
use Predis\Cluster\Distributor\DistributorInterface;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\Cluster\PredisStrategy;
use Predis\Connection\Cluster\PredisCluster;
use Predis\Connection\Aggregate\PredisCluster;
class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
{
@@ -104,8 +104,8 @@ for ($i = 0; $i < 100; ++$i) {
$client->get("key:$i");
}
$server1 = $client->getClientBy('alias', 'first')->info();
$server2 = $client->getClientBy('alias', 'second')->info();
$server1 = $client->getClientFor('first')->info();
$server2 = $client->getClientFor('second')->info();
if (isset($server1['Keyspace'], $server2['Keyspace'])) {
$server1 = $server1['Keyspace'];
+1 -1
View File
@@ -36,7 +36,7 @@ class SimpleDebuggableConnection extends StreamConnection
$firtsArg = $command->getArgument(0);
$timestamp = round(microtime(true) - $this->tstart, 4);
$debug = $command->getId();
$debug = $command->getId();
$debug .= isset($firtsArg) ? " $firtsArg " : ' ';
$debug .= "$direction $this";
$debug .= " [{$timestamp}s]";
+2 -2
View File
@@ -51,7 +51,7 @@ class EventsListener implements Countable
return $this->events;
}
public function __invoke($payload, $dispatcher)
public function __invoke($payload)
{
$this->events[] = $payload;
}
@@ -61,7 +61,7 @@ class EventsListener implements Countable
$dispatcher->attachCallback('events', ($events = new EventsListener()));
// Attach a function to control the dispatcher loop termination with a message.
$dispatcher->attachCallback('control', function ($payload, $dispatcher) {
$dispatcher->attachCallback('control', function ($payload) use ($dispatcher) {
if ($payload === 'terminate_dispatcher') {
$dispatcher->stop();
}
+11 -7
View File
@@ -13,10 +13,11 @@ require __DIR__.'/shared.php';
// This example will not work with versions of Redis < 2.6.
//
// Additionally to the EVAL command, the Predis\Command\ScriptCommand class can
// be used to leverage an higher level abstraction for Lua scripting that makes
// scripts appear just like any other command on the client-side. This is basic
// example on how a script-based INCREX command can be defined:
// Additionally to the EVAL command defined in the current development profile,
// the Predis\Command\ScriptCommand class can be used to build an higher level
// abstraction for "scriptable" commands so that they will appear just like any
// other command on the client-side. This is a quick example used to implement
// INCREX.
use Predis\Command\ScriptCommand;
@@ -49,9 +50,12 @@ LUA;
}
$client = new Predis\Client($single_server, array(
'commands' => array(
'increxby' => 'IncrementExistingKeysBy',
),
'profile' => function ($options) {
$profile = $options->getDefault('profile');
$profile->defineCommand('increxby', 'IncrementExistingKeysBy');
return $profile;
},
));
$client->mset('foo', 10, 'foobar', 100);
+3 -2
View File
@@ -13,7 +13,7 @@ require __DIR__.'/shared.php';
use Predis\Collection\Iterator;
// Starting with Redis 2.8, clients can iterate incrementally over collections
// Starting from Redis 2.8, clients can iterate incrementally over collections
// without blocking the server like it happens when a command such as KEYS is
// executed on a Redis instance storing millions of keys. These commands are:
//
@@ -29,7 +29,8 @@ use Predis\Collection\Iterator;
// See http://redis.io/commands/scan for more details.
//
$client = new Predis\Client($single_server);
// Create a client using `2.8` as a server profile (needs Redis 2.8!)
$client = new Predis\Client($single_server, array('profile' => '2.8'));
// Prepare some keys for our example
$client->del('predis:set', 'predis:zset', 'predis:hash');
+13 -10
View File
@@ -14,12 +14,12 @@ require __DIR__.'/shared.php';
// Predis allows to set Lua scripts as read-only operations for replication.
// This works for both EVAL and EVALSHA and also for the client-side abstraction
// built upon them (Predis\Command\ScriptCommand). This example shows a slightly
// more complex configuration that injects a new script command in the command
// factory used by the client and marks it as a read-only operation so that it
// will be executed on slaves.
// more complex configuration that injects a new script command in the server
// profile used by the new client instance and marks it marks it as a read-only
// operation for replication so that it will be executed on slaves.
use Predis\Command\ScriptCommand;
use Predis\Connection\Replication\MasterSlaveReplication;
use Predis\Connection\Aggregate\MasterSlaveReplication;
use Predis\Replication\ReplicationStrategy;
// ------------------------------------------------------------------------- //
@@ -47,14 +47,17 @@ LUA;
// ------------------------------------------------------------------------- //
$parameters = array(
'tcp://127.0.0.1:6381?role=master&database=15',
'tcp://127.0.0.1:6382?role=slave&alias=slave-01&database=15',
'tcp://127.0.0.1:6379/?alias=master',
'tcp://127.0.0.1:6380/?alias=slave',
);
$options = array(
'commands' => array(
'hmgetall' => 'HashMultipleGetAll',
),
'profile' => function ($options, $option) {
$profile = $options->getDefault($option);
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
return $profile;
},
'replication' => function () {
$strategy = new ReplicationStrategy();
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
@@ -76,7 +79,7 @@ $client = new Predis\Client($parameters, $options);
$hashes = $client->hmgetall('metavars', 'servers');
$replication = $client->getConnection();
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionByAlias('slave-01');
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave');
echo 'Is still on slave? ', $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL;
var_export($hashes);
-58
View File
@@ -1,58 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
require __DIR__.'/shared.php';
// Predis supports redis-sentinel to provide high availability in master / slave
// scenarios. The only but relevant difference with a basic replication scenario
// is that sentinel servers can manage the master server and its slaves based on
// their state, which means that they are able to provide an authoritative and
// updated configuration to clients thus avoiding static configurations for the
// replication servers and their roles.
// Instead of connection parameters pointing to redis nodes, we provide a list
// of instances of redis-sentinel. Users should always provide a timeout value
// low enough to not hinder operations just in case a sentinel is unreachable
// but Predis uses a default value of 100 milliseconds for sentinel parameters
// without an explicit timeout value.
//
// NOTE: in real-world scenarios sentinels should be running on different hosts!
$sentinels = array(
'tcp://127.0.0.1:5380?timeout=0.100',
'tcp://127.0.0.1:5381?timeout=0.100',
'tcp://127.0.0.1:5382?timeout=0.100',
);
$client = new Predis\Client($sentinels, array(
'replication' => 'sentinel',
'service' => 'mymaster',
));
// Read operation.
$exists = $client->exists('foo') ? 'yes' : 'no';
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Does 'foo' exist on {$current->role}? $exists.", PHP_EOL;
// Write operation.
$client->set('foo', 'bar');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Now 'foo' has been set to 'bar' on {$current->role}!", PHP_EOL;
// Read operation.
$bar = $client->get('foo');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "We fetched 'foo' from {$current->role} and its value is '$bar'.", PHP_EOL;
/* OUTPUT:
Does 'foo' exist on slave-127.0.0.1:6381? yes.
Now 'foo' has been set to 'bar' on master!
We fetched 'foo' from master and its value is 'bar'.
*/
+6 -6
View File
@@ -22,28 +22,28 @@ require __DIR__.'/shared.php';
//
$parameters = array(
'tcp://127.0.0.1:6381?role=master&database=15',
'tcp://127.0.0.1:6382?role=slave&database=15',
'tcp://127.0.0.1:6379?database=15&alias=master',
'tcp://127.0.0.1:6380?database=15&alias=slave',
);
$options = array('replication' => 'predis');
$options = array('replication' => true);
$client = new Predis\Client($parameters, $options);
// Read operation.
$exists = $client->exists('foo') ? 'yes' : 'no';
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Does 'foo' exist on {$current->role}? $exists.", PHP_EOL;
echo "Does 'foo' exist on {$current->alias}? $exists.", PHP_EOL;
// Write operation.
$client->set('foo', 'bar');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "Now 'foo' has been set to 'bar' on {$current->role}!", PHP_EOL;
echo "Now 'foo' has been set to 'bar' on {$current->alias}!", PHP_EOL;
// Read operation.
$bar = $client->get('foo');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "We fetched 'foo' from {$current->role} and its value is '$bar'.", PHP_EOL;
echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL;
/* OUTPUT:
Does 'foo' exist on slave? yes.
+8 -12
View File
@@ -9,10 +9,6 @@
* file that was distributed with this source code.
*/
if (PHP_SAPI !== 'cli') {
die("Example scripts are meant to be executed locally via CLI.");
}
require __DIR__.'/../autoload.php';
function redis_version($info)
@@ -27,22 +23,22 @@ function redis_version($info)
}
$single_server = array(
'host' => '127.0.0.1',
'port' => 6379,
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15,
);
$multiple_servers = array(
array(
'host' => '127.0.0.1',
'port' => 6379,
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15,
'alias' => 'first',
'alias' => 'first',
),
array(
'host' => '127.0.0.1',
'port' => 6380,
'host' => '127.0.0.1',
'port' => 6380,
'database' => 15,
'alias' => 'second',
'alias' => 'second',
),
);
+1 -1
View File
@@ -28,7 +28,7 @@ function zpop($client, $key)
{
$element = null;
$options = array(
'cas' => true, // Initialize with support for CAS operations
'cas' => true, // Initialize with support for CAS operations
'watch' => $key, // Key that needs to be WATCHed to detect changes
'retry' => 3, // Number of retries on aborted transactions, after
// which the client bails out with an exception.
+36
View File
@@ -0,0 +1,36 @@
; This file is meant to be used with Onion http://c9s.github.com/Onion/
; For instructions on how to build a PEAR package of Predis please follow
; the instructions at this URL:
;
; https://github.com/c9s/Onion#a-quick-tutorial-for-building-pear-package
;
[package]
name = "Predis"
desc = "Flexible and feature-complete Redis client for PHP and HHVM"
homepage = "http://github.com/nrk/predis"
license = "MIT"
version = "1.0.4"
stability = "stable"
channel = "pear.nrk.io"
author = "Daniele Alessandri \"nrk\" <suppakilla@gmail.com>"
[require]
php = ">= 5.3.2"
pearinstaller = "1.4.1"
[roles]
*.xml.dist = test
*.md = doc
LICENSE = doc
[optional phpiredis]
hint = "Add support for faster protocol handling with phpiredis"
extensions[] = socket
extensions[] = phpiredis
[optional webdis]
hint = "Add support for Webdis"
extensions[] = curl
extensions[] = phpiredis
+3
View File
@@ -8,6 +8,8 @@
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
syntaxCheck="true"
beStrictAboutTestSize="true"
beStrictAboutTestsThatDoNotTestAnything="true">
<testsuites>
@@ -36,6 +38,7 @@
<php>
<!-- Redis -->
<const name="REDIS_SERVER_VERSION" value="2.8" />
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
<const name="REDIS_SERVER_PORT" value="6379" />
<const name="REDIS_SERVER_DBNUM" value="15" />
+54
View File
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
syntaxCheck="true"
beStrictAboutTestSize="true"
beStrictAboutTestsThatDoNotTestAnything="true">
<testsuites>
<testsuite name="Predis Test Suite">
<directory>tests/Predis/</directory>
</testsuite>
</testsuites>
<groups>
<exclude>
<group>ext-phpiredis</group>
<group>ext-curl</group>
<group>realm-webdis</group>
<!-- <group>connected</group> -->
<!-- <group>disconnected</group> -->
<!-- <group>commands</group> -->
<!-- <group>slow</group> -->
</exclude>
</groups>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-text" target="php://stdout" />
</logging>
<php>
<!-- Redis -->
<const name="REDIS_SERVER_VERSION" value="2.8" />
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
<const name="REDIS_SERVER_PORT" value="6379" />
<const name="REDIS_SERVER_DBNUM" value="15" />
<!-- Webdis -->
<const name="WEBDIS_SERVER_HOST" value="127.0.0.1" />
<const name="WEBDIS_SERVER_PORT" value="7379" />
</php>
</phpunit>
-1
View File
@@ -16,7 +16,6 @@ namespace Predis;
*
* @author Eric Naeseth <eric@thumbtack.com>
* @author Daniele Alessandri <suppakilla@gmail.com>
* @codeCoverageIgnore
*/
class Autoloader
{
+117 -128
View File
@@ -16,8 +16,8 @@ use Predis\Command\RawCommand;
use Predis\Command\ScriptCommand;
use Predis\Configuration\Options;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\Parameters;
use Predis\Connection\ParametersInterface;
use Predis\Monitor\Consumer as MonitorConsumer;
use Predis\Pipeline\Pipeline;
@@ -38,18 +38,13 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class Client implements ClientInterface, \IteratorAggregate
class Client implements ClientInterface
{
const VERSION = '2.0.0';
const VERSION = '1.0.4';
/** @var OptionsInterface */
private $options;
/** @var ConnectionInterface */
private $connection;
/** @var Command\FactoryInterface */
private $commands;
protected $connection;
protected $options;
private $profile;
/**
* @param mixed $parameters Connection parameters for one or more servers.
@@ -57,101 +52,126 @@ class Client implements ClientInterface, \IteratorAggregate
*/
public function __construct($parameters = null, $options = null)
{
$this->options = static::createOptions($options ?? new Options);
$this->connection = static::createConnection($this->options, $parameters ?? new Parameters);
$this->commands = $this->options->commands;
$this->options = $this->createOptions($options ?: array());
$this->connection = $this->createConnection($parameters ?: array());
$this->profile = $this->options->profile;
}
/**
* Creates a new set of client options for the client.
* Creates a new instance of Predis\Configuration\Options from different
* types of arguments or simply returns the passed argument if it is an
* instance of Predis\Configuration\OptionsInterface.
*
* @param array|OptionsInterface $options Set of client options
* @param mixed $options Client options.
*
* @throws \InvalidArgumentException
*
* @return OptionsInterface
*/
protected static function createOptions($options)
protected function createOptions($options)
{
if (is_array($options)) {
return new Options($options);
} elseif ($options instanceof OptionsInterface) {
return $options;
} else {
throw new \InvalidArgumentException('Invalid type for client options');
}
if ($options instanceof OptionsInterface) {
return $options;
}
throw new \InvalidArgumentException('Invalid type for client options.');
}
/**
* Creates single or aggregate connections from supplied arguments.
* Creates single or aggregate connections from different types of arguments
* (string, array) or returns the passed argument if it is an instance of a
* class implementing Predis\Connection\ConnectionInterface.
*
* This method accepts the following types to create a connection instance:
* Accepted types for connection parameters are:
*
* - Array (dictionary: single connection, indexed: aggregate connections)
* - String (URI for a single connection)
* - Callable (connection initializer callback)
* - Instance of Predis\Connection\ParametersInterface (used as-is)
* - Instance of Predis\Connection\ConnectionInterface (returned as-is)
* - Instance of Predis\Connection\ConnectionInterface.
* - Instance of Predis\Connection\ParametersInterface.
* - Array
* - String
* - Callable
*
* When a callable is passed, it receives the original set of client options
* and must return an instance of Predis\Connection\ConnectionInterface.
*
* Connections are created using the connection factory (in case of single
* connections) or a specialized aggregate connection initializer (in case
* of cluster and replication) retrieved from the supplied client options.
*
* @param OptionsInterface $options Client options container
* @param mixed $parameters Connection parameters
* @param mixed $parameters Connection parameters or connection instance.
*
* @throws \InvalidArgumentException
*
* @return ConnectionInterface
*/
protected static function createConnection(OptionsInterface $options, $parameters)
protected function createConnection($parameters)
{
if ($parameters instanceof ConnectionInterface) {
return $parameters;
}
if ($parameters instanceof ParametersInterface || is_string($parameters)) {
return $options->connections->create($parameters);
return $this->options->connections->create($parameters);
}
if (is_array($parameters)) {
if (!isset($parameters[0])) {
return $options->connections->create($parameters);
} elseif ($options->defined('cluster') && $initializer = $options->cluster) {
return $initializer($parameters, true);
} elseif ($options->defined('replication') && $initializer = $options->replication) {
return $initializer($parameters, true);
} elseif ($options->defined('aggregate') && $initializer = $options->aggregate) {
return $initializer($parameters, false);
} else {
throw new \InvalidArgumentException(
'Array of connection parameters requires `cluster`, `replication` or `aggregate` client option'
);
return $this->options->connections->create($parameters);
}
}
if (is_callable($parameters)) {
$connection = call_user_func($parameters, $options);
$options = $this->options;
if (!$connection instanceof ConnectionInterface) {
throw new \InvalidArgumentException('Callable parameters must return a valid connection');
if ($options->defined('aggregate')) {
$initializer = $this->getConnectionInitializerWrapper($options->aggregate);
$connection = $initializer($parameters, $options);
} else {
if ($options->defined('replication') && $replication = $options->replication) {
$connection = $replication;
} else {
$connection = $options->cluster;
}
$options->connections->aggregate($connection, $parameters);
}
return $connection;
}
throw new \InvalidArgumentException('Invalid type for connection parameters');
if (is_callable($parameters)) {
$initializer = $this->getConnectionInitializerWrapper($parameters);
$connection = $initializer($this->options);
return $connection;
}
throw new \InvalidArgumentException('Invalid type for connection parameters.');
}
/**
* Wraps a callable to make sure that its returned value represents a valid
* connection type.
*
* @param mixed $callable
*
* @return \Closure
*/
protected function getConnectionInitializerWrapper($callable)
{
return function () use ($callable) {
$connection = call_user_func_array($callable, func_get_args());
if (!$connection instanceof ConnectionInterface) {
throw new \UnexpectedValueException(
'The callable connection initializer returned an invalid type.'
);
}
return $connection;
};
}
/**
* {@inheritdoc}
*/
public function getCommandFactory()
public function getProfile()
{
return $this->commands;
return $this->profile;
}
/**
@@ -163,53 +183,23 @@ class Client implements ClientInterface, \IteratorAggregate
}
/**
* Creates a new client using a specific underlying connection.
* Creates a new client instance for the specified connection ID or alias,
* only when working with an aggregate connection (cluster, replication).
* The new client instances uses the same options of the original one.
*
* This method allows to create a new client instance by picking a specific
* connection out of an aggregate one, with the same options of the original
* client instance.
* @param string $connectionID Identifier of a connection.
*
* The specified selector defines which logic to use to look for a suitable
* connection by the specified value. Supported selectors are:
* @throws \InvalidArgumentException
*
* - `id`
* - `key`
* - `slot`
* - `command`
* - `alias`
* - `role`
*
* Internally the client relies on duck-typing and follows this convention:
*
* $selector string => getConnectionBy$selector($value) method
*
* This means that support for specific selectors may vary depending on the
* actual logic implemented by connection classes and there is no interface
* binding a connection class to implement any of these.
*
* @param string $selector Type of selector.
* @param mixed $value Value to be used by the selector.
*
* @return ClientInterface
* @return Client
*/
public function getClientBy($selector, $value)
public function getClientFor($connectionID)
{
$selector = strtolower($selector);
if (!in_array($selector, array('id', 'key', 'slot', 'role', 'alias', 'command'))) {
throw new \InvalidArgumentException("Invalid selector type: `$selector`");
if (!$connection = $this->getConnectionById($connectionID)) {
throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
}
if (!method_exists($this->connection, $method = "getConnectionBy$selector")) {
$class = get_class($this->connection);
throw new \InvalidArgumentException("Selecting connection by $selector is not supported by $class");
}
if (!$connection = $this->connection->$method($value)) {
throw new \InvalidArgumentException("Cannot find a connection by $selector matching `$value`");
}
return new static($connection, $this->getOptions());
return new static($connection, $this->options);
}
/**
@@ -257,12 +247,33 @@ class Client implements ClientInterface, \IteratorAggregate
return $this->connection;
}
/**
* Retrieves the specified connection from the aggregate connection when the
* client is in cluster or replication mode.
*
* @param string $connectionID Index or alias of the single connection.
*
* @throws NotSupportedException
*
* @return Connection\NodeConnectionInterface
*/
public function getConnectionById($connectionID)
{
if (!$this->connection instanceof AggregateConnectionInterface) {
throw new NotSupportedException(
'Retrieving connections by ID is supported only by aggregate connections.'
);
}
return $this->connection->getConnectionById($connectionID);
}
/**
* Executes a command without filtering its arguments, parsing the response,
* applying any prefix to keys or throwing exceptions on Redis errors even
* regardless of client options.
*
* It is possible to identify Redis error responses from normal responses
* It is possibile to indentify Redis error responses from normal responses
* using the second optional argument which is populated by reference.
*
* @param array $arguments Command arguments as defined by the command signature.
@@ -273,10 +284,9 @@ class Client implements ClientInterface, \IteratorAggregate
public function executeRaw(array $arguments, &$error = null)
{
$error = false;
$commandID = array_shift($arguments);
$response = $this->connection->executeCommand(
new RawCommand($commandID, $arguments)
new RawCommand($arguments)
);
if ($response instanceof ResponseInterface) {
@@ -305,7 +315,7 @@ class Client implements ClientInterface, \IteratorAggregate
*/
public function createCommand($commandID, $arguments = array())
{
return $this->commands->create($commandID, $arguments);
return $this->profile->createCommand($commandID, $arguments);
}
/**
@@ -339,7 +349,10 @@ class Client implements ClientInterface, \IteratorAggregate
protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
{
if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
$response = $this->executeCommand($command->getEvalCommand());
$eval = $this->createCommand('EVAL');
$eval->setRawArguments($command->getEvalArguments());
$response = $this->executeCommand($eval);
if (!$response instanceof ResponseInterface) {
$response = $command->parseResponse($response);
@@ -382,11 +395,9 @@ class Client implements ClientInterface, \IteratorAggregate
return $this->$initializer($arg0, $arg1);
// @codeCoverageIgnoreStart
default:
return $this->$initializer($this, $argv);
}
// @codeCoverageIgnoreEnd
}
/**
@@ -465,7 +476,7 @@ class Client implements ClientInterface, \IteratorAggregate
}
/**
* Creates a new publish/subscribe context and returns it, or starts its loop
* Creates a new publis/subscribe context and returns it, or starts its loop
* inside the optionally provided callable object.
*
* @param mixed ... Array of options, a callable for execution, or both.
@@ -509,26 +520,4 @@ class Client implements ClientInterface, \IteratorAggregate
{
return new MonitorConsumer($this);
}
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
$clients = array();
$connection = $this->getConnection();
if (!$connection instanceof \Traversable) {
return new \ArrayIterator(array(
(string) $connection => new static($connection, $this->getOptions())
));
}
foreach ($connection as $node) {
$clients[(string) $node] = new static($node, $this->getOptions());
}
return new \ArrayIterator($clients);
}
}
+17 -18
View File
@@ -16,7 +16,7 @@ use Predis\Command\CommandInterface;
/**
* Interface defining a client-side context such as a pipeline or transaction.
*
* @method $this del(array|string $keys)
* @method $this del(array $keys)
* @method $this dump($key)
* @method $this exists($key)
* @method $this expire($key, $seconds)
@@ -38,8 +38,7 @@ use Predis\Command\CommandInterface;
* @method $this append($key, $value)
* @method $this bitcount($key, $start = null, $end = null)
* @method $this bitop($operation, $destkey, $key)
* @method $this bitfield($key, $subcommand, ...$subcommandArg)
* @method $this bitpos($key, $bit, $start = null, $end = null)
* @method $this bitfield($key, ...)
* @method $this decr($key)
* @method $this decrby($key, $decrement)
* @method $this get($key)
@@ -74,15 +73,15 @@ use Predis\Command\CommandInterface;
* @method $this hsetnx($key, $field, $value)
* @method $this hvals($key)
* @method $this hstrlen($key, $field)
* @method $this blpop(array|string $keys, $timeout)
* @method $this brpop(array|string $keys, $timeout)
* @method $this blpop(array $keys, $timeout)
* @method $this brpop(array $keys, $timeout)
* @method $this brpoplpush($source, $destination, $timeout)
* @method $this lindex($key, $index)
* @method $this linsert($key, $whence, $pivot, $value)
* @method $this llen($key)
* @method $this lpop($key)
* @method $this lpush($key, array $values)
* @method $this lpushx($key, array $values)
* @method $this lpushx($key, $value)
* @method $this lrange($key, $start, $stop)
* @method $this lrem($key, $count, $value)
* @method $this lset($key, $index, $value)
@@ -90,13 +89,13 @@ use Predis\Command\CommandInterface;
* @method $this rpop($key)
* @method $this rpoplpush($source, $destination)
* @method $this rpush($key, array $values)
* @method $this rpushx($key, array $values)
* @method $this rpushx($key, $value)
* @method $this sadd($key, array $members)
* @method $this scard($key)
* @method $this sdiff(array|string $keys)
* @method $this sdiffstore($destination, array|string $keys)
* @method $this sinter(array|string $keys)
* @method $this sinterstore($destination, array|string $keys)
* @method $this sdiff(array $keys)
* @method $this sdiffstore($destination, array $keys)
* @method $this sinter(array $keys)
* @method $this sinterstore($destination, array $keys)
* @method $this sismember($key, $member)
* @method $this smembers($key)
* @method $this smove($source, $destination, $member)
@@ -104,13 +103,13 @@ use Predis\Command\CommandInterface;
* @method $this srandmember($key, $count = null)
* @method $this srem($key, $member)
* @method $this sscan($key, $cursor, array $options = null)
* @method $this sunion(array|string $keys)
* @method $this sunionstore($destination, array|string $keys)
* @method $this sunion(array $keys)
* @method $this sunionstore($destination, array $keys)
* @method $this zadd($key, array $membersAndScoresDictionary)
* @method $this zcard($key)
* @method $this zcount($key, $min, $max)
* @method $this zincrby($key, $increment, $member)
* @method $this zinterstore($destination, array|string $keys, array $options = null)
* @method $this zinterstore($destination, array $keys, array $options = null)
* @method $this zrange($key, $start, $stop, array $options = null)
* @method $this zrangebyscore($key, $min, $max, array $options = null)
* @method $this zrank($key, $member)
@@ -118,9 +117,9 @@ use Predis\Command\CommandInterface;
* @method $this zremrangebyrank($key, $start, $stop)
* @method $this zremrangebyscore($key, $min, $max)
* @method $this zrevrange($key, $start, $stop, array $options = null)
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
* @method $this zrevrangebyscore($key, $min, $max, array $options = null)
* @method $this zrevrank($key, $member)
* @method $this zunionstore($destination, array|string $keys, array $options = null)
* @method $this zunionstore($destination, array $keys, array $options = null)
* @method $this zscore($key, $member)
* @method $this zscan($key, $cursor, array $options = null)
* @method $this zrangebylex($key, $start, $stop, array $options = null)
@@ -128,8 +127,8 @@ use Predis\Command\CommandInterface;
* @method $this zremrangebylex($key, $min, $max)
* @method $this zlexcount($key, $min, $max)
* @method $this pfadd($key, array $elements)
* @method $this pfmerge($destinationKey, array|string $sourceKeys)
* @method $this pfcount(array|string $keys)
* @method $this pfmerge($destinationKey, array $sourceKeys)
* @method $this pfcount(array $keys)
* @method $this pubsub($subcommand, $argument)
* @method $this publish($channel, $message)
* @method $this discard()
+151 -156
View File
@@ -12,10 +12,9 @@
namespace Predis;
use Predis\Command\CommandInterface;
use Predis\Command\FactoryInterface;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Response\Status;
use Predis\Profile\ProfileInterface;
/**
* Interface defining a client able to execute commands against Redis.
@@ -25,168 +24,164 @@ use Predis\Response\Status;
* and more friendly interface to ease programming which is described in the
* following list of methods:
*
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
* @method string|null dump(string $key)
* @method int exists(string $key)
* @method int expire(string $key, int $seconds)
* @method int expireat(string $key, int $timestamp)
* @method array keys(string $pattern)
* @method int move(string $key, int $db)
* @method mixed object($subcommand, string $key)
* @method int persist(string $key)
* @method int pexpire(string $key, int $milliseconds)
* @method int pexpireat(string $key, int $timestamp)
* @method int pttl(string $key)
* @method string|null randomkey()
* @method mixed rename(string $key, string $target)
* @method int renamenx(string $key, string $target)
* @method array scan($cursor, array $options = null)
* @method array sort(string $key, array $options = null)
* @method int ttl(string $key)
* @method mixed type(string $key)
* @method int append(string $key, $value)
* @method int bitcount(string $key, $start = null, $end = null)
* @method int bitop($operation, $destkey, $key)
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
* @method int bitpos(string $key, $bit, $start = null, $end = null)
* @method int decr(string $key)
* @method int decrby(string $key, int $decrement)
* @method string|null get(string $key)
* @method int getbit(string $key, $offset)
* @method string getrange(string $key, $start, $end)
* @method string|null getset(string $key, $value)
* @method int incr(string $key)
* @method int incrby(string $key, int $increment)
* @method string incrbyfloat(string $key, int|float $increment)
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method Status psetex(string $key, $milliseconds, $value)
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit(string $key, $offset, $value)
* @method Status setex(string $key, $seconds, $value)
* @method int setnx(string $key, $value)
* @method int setrange(string $key, $offset, $value)
* @method int strlen(string $key)
* @method int hdel(string $key, array $fields)
* @method int hexists(string $key, string $field)
* @method string|null hget(string $key, string $field)
* @method array hgetall(string $key)
* @method int hincrby(string $key, string $field, int $increment)
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
* @method array hkeys(string $key)
* @method int hlen(string $key)
* @method array hmget(string $key, array $fields)
* @method mixed hmset(string $key, array $dictionary)
* @method array hscan(string $key, $cursor, array $options = null)
* @method int hset(string $key, string $field, string $value)
* @method int hsetnx(string $key, string $field, string $value)
* @method array hvals(string $key)
* @method int hstrlen(string $key, string $field)
* @method array|null blpop(array|string $keys, int|float $timeout)
* @method array|null brpop(array|string $keys, int|float $timeout)
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
* @method string|null lindex(string $key, int $index)
* @method int linsert(string $key, $whence, $pivot, $value)
* @method int llen(string $key)
* @method string|null lpop(string $key)
* @method int lpush(string $key, array $values)
* @method int lpushx(string $key, array $values)
* @method string[] lrange(string $key, int $start, int $stop)
* @method int lrem(string $key, int $count, string $value)
* @method mixed lset(string $key, int $index, string $value)
* @method mixed ltrim(string $key, int $start, int $stop)
* @method string|null rpop(string $key)
* @method string|null rpoplpush(string $source, string $destination)
* @method int rpush(string $key, array $values)
* @method int rpushx(string $key, array $values)
* @method int sadd(string $key, array $members)
* @method int scard(string $key)
* @method string[] sdiff(array|string $keys)
* @method int sdiffstore(string $destination, array|string $keys)
* @method string[] sinter(array|string $keys)
* @method int sinterstore(string $destination, array|string $keys)
* @method int sismember(string $key, string $member)
* @method string[] smembers(string $key)
* @method int smove(string $source, string $destination, string $member)
* @method string|null spop(string $key, int $count = null)
* @method string|null srandmember(string $key, int $count = null)
* @method int srem(string $key, string $member)
* @method array sscan(string $key, int $cursor, array $options = null)
* @method string[] sunion(array|string $keys)
* @method int sunionstore(string $destination, array|string $keys)
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
* @method int zadd(string $key, array $membersAndScoresDictionary)
* @method int zcard(string $key)
* @method string zcount(string $key, int|string $min, int|string $max)
* @method string zincrby(string $key, int $increment, string $member)
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
* @method array zpopmin(string $key, int $count = 1)
* @method array zpopmax(string $key, int $count = 1)
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
* @method int|null zrank(string $key, string $member)
* @method int zrem(string $key, string ...$member)
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
* @method int|null zrevrank(string $key, string $member)
* @method int zunionstore(string $destination, array|string $keys, array $options = null)
* @method string|null zscore(string $key, string $member)
* @method array zscan(string $key, int $cursor, array $options = null)
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
* @method int zremrangebylex(string $key, string $min, string $max)
* @method int zlexcount(string $key, string $min, string $max)
* @method int pfadd(string $key, array $elements)
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed pubsub($subcommand, $argument)
* @method int publish($channel, $message)
* @method mixed discard()
* @method array|null exec()
* @method mixed multi()
* @method mixed unwatch()
* @method mixed watch(string $key)
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed script($subcommand, $argument = null)
* @method mixed auth(string $password)
* @method string echo(string $message)
* @method mixed ping(string $message = null)
* @method mixed select(int $database)
* @method mixed bgrewriteaof()
* @method mixed bgsave()
* @method mixed client($subcommand, $argument = null)
* @method mixed config($subcommand, $argument = null)
* @method int dbsize()
* @method mixed flushall()
* @method mixed flushdb()
* @method array info($section = null)
* @method int lastsave()
* @method mixed save()
* @method mixed slaveof(string $host, int $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method array time()
* @method array command()
* @method int geoadd(string $key, $longitude, $latitude, $member)
* @method array geohash(string $key, array $members)
* @method array geopos(string $key, array $members)
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
* @method int del(array $keys)
* @method string dump($key)
* @method int exists($key)
* @method int expire($key, $seconds)
* @method int expireat($key, $timestamp)
* @method array keys($pattern)
* @method int move($key, $db)
* @method mixed object($subcommand, $key)
* @method int persist($key)
* @method int pexpire($key, $milliseconds)
* @method int pexpireat($key, $timestamp)
* @method int pttl($key)
* @method string randomkey()
* @method mixed rename($key, $target)
* @method int renamenx($key, $target)
* @method array scan($cursor, array $options = null)
* @method array sort($key, array $options = null)
* @method int ttl($key)
* @method mixed type($key)
* @method int append($key, $value)
* @method int bitcount($key, $start = null, $end = null)
* @method int bitop($operation, $destkey, $key)
* @method array bitfield($key, ...)
* @method int decr($key)
* @method int decrby($key, $decrement)
* @method string get($key)
* @method int getbit($key, $offset)
* @method string getrange($key, $start, $end)
* @method string getset($key, $value)
* @method int incr($key)
* @method int incrby($key, $increment)
* @method string incrbyfloat($key, $increment)
* @method array mget(array $keys)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method mixed psetex($key, $milliseconds, $value)
* @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit($key, $offset, $value)
* @method int setex($key, $seconds, $value)
* @method int setnx($key, $value)
* @method int setrange($key, $offset, $value)
* @method int strlen($key)
* @method int hdel($key, array $fields)
* @method int hexists($key, $field)
* @method string hget($key, $field)
* @method array hgetall($key)
* @method int hincrby($key, $field, $increment)
* @method string hincrbyfloat($key, $field, $increment)
* @method array hkeys($key)
* @method int hlen($key)
* @method array hmget($key, array $fields)
* @method mixed hmset($key, array $dictionary)
* @method array hscan($key, $cursor, array $options = null)
* @method int hset($key, $field, $value)
* @method int hsetnx($key, $field, $value)
* @method array hvals($key)
* @method int hstrlen($key, $field)
* @method array blpop(array $keys, $timeout)
* @method array brpop(array $keys, $timeout)
* @method array brpoplpush($source, $destination, $timeout)
* @method string lindex($key, $index)
* @method int linsert($key, $whence, $pivot, $value)
* @method int llen($key)
* @method string lpop($key)
* @method int lpush($key, array $values)
* @method int lpushx($key, $value)
* @method array lrange($key, $start, $stop)
* @method int lrem($key, $count, $value)
* @method mixed lset($key, $index, $value)
* @method mixed ltrim($key, $start, $stop)
* @method string rpop($key)
* @method string rpoplpush($source, $destination)
* @method int rpush($key, array $values)
* @method int rpushx($key, $value)
* @method int sadd($key, array $members)
* @method int scard($key)
* @method array sdiff(array $keys)
* @method int sdiffstore($destination, array $keys)
* @method array sinter(array $keys)
* @method int sinterstore($destination, array $keys)
* @method int sismember($key, $member)
* @method array smembers($key)
* @method int smove($source, $destination, $member)
* @method string spop($key, $count = null)
* @method string srandmember($key, $count = null)
* @method int srem($key, $member)
* @method array sscan($key, $cursor, array $options = null)
* @method array sunion(array $keys)
* @method int sunionstore($destination, array $keys)
* @method int zadd($key, array $membersAndScoresDictionary)
* @method int zcard($key)
* @method string zcount($key, $min, $max)
* @method string zincrby($key, $increment, $member)
* @method int zinterstore($destination, array $keys, array $options = null)
* @method array zrange($key, $start, $stop, array $options = null)
* @method array zrangebyscore($key, $min, $max, array $options = null)
* @method int zrank($key, $member)
* @method int zrem($key, $member)
* @method int zremrangebyrank($key, $start, $stop)
* @method int zremrangebyscore($key, $min, $max)
* @method array zrevrange($key, $start, $stop, array $options = null)
* @method array zrevrangebyscore($key, $max, $min, array $options = null)
* @method int zrevrank($key, $member)
* @method int zunionstore($destination, array $keys, array $options = null)
* @method string zscore($key, $member)
* @method array zscan($key, $cursor, array $options = null)
* @method array zrangebylex($key, $start, $stop, array $options = null)
* @method array zrevrangebylex($key, $start, $stop, array $options = null)
* @method int zremrangebylex($key, $min, $max)
* @method int zlexcount($key, $min, $max)
* @method int pfadd($key, array $elements)
* @method mixed pfmerge($destinationKey, array $sourceKeys)
* @method int pfcount(array $keys)
* @method mixed pubsub($subcommand, $argument)
* @method int publish($channel, $message)
* @method mixed discard()
* @method array exec()
* @method mixed multi()
* @method mixed unwatch()
* @method mixed watch($key)
* @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method mixed script($subcommand, $argument = null)
* @method mixed auth($password)
* @method string echo($message)
* @method mixed ping($message = null)
* @method mixed select($database)
* @method mixed bgrewriteaof()
* @method mixed bgsave()
* @method mixed client($subcommand, $argument = null)
* @method mixed config($subcommand, $argument = null)
* @method int dbsize()
* @method mixed flushall()
* @method mixed flushdb()
* @method array info($section = null)
* @method int lastsave()
* @method mixed save()
* @method mixed slaveof($host, $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method array time()
* @method array command()
* @method int geoadd($key, $longitude, $latitude, $member)
* @method array geohash($key, array $members)
* @method array geopos($key, array $members)
* @method string geodist($key, $member1, $member2, $unit = null)
* @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method array georadiusbymember($key, $member, $radius, $unit, array $options = null)
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface ClientInterface
{
/**
* Returns the command factory used by the client.
* Returns the server profile used by the client.
*
* @return FactoryInterface
* @return ProfileInterface
*/
public function getCommandFactory();
public function getProfile();
/**
* Returns the client options specified upon initialization.
+3 -2
View File
@@ -161,7 +161,7 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
$replicas = (int) round($weightRatio * $totalNodes * $replicas);
for ($i = 0; $i < $replicas; ++$i) {
$key = $this->hash("$nodeHash:$i");
$key = crc32("$nodeHash:$i");
$ring[$key] = $nodeObject;
}
}
@@ -239,8 +239,9 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
public function get($value)
{
$hash = $this->hash($value);
$node = $this->getByHash($hash);
return $this->getByHash($hash);
return $node;
}
/**
-2
View File
@@ -61,8 +61,6 @@ class CRC16 implements HashGeneratorInterface
// CRC-CCITT-16 algorithm
$crc = 0;
$CCITT_16 = self::$CCITT_16;
$value = (string) $value;
$strlen = strlen($value);
for ($i = 0; $i < $strlen; ++$i) {
-44
View File
@@ -1,44 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster\Hash;
use Predis\NotSupportedException;
/**
* Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PhpiredisCRC16 implements HashGeneratorInterface
{
/**
*
*/
public function __construct()
{
if (!function_exists('phpiredis_utils_crc16')) {
// @codeCoverageIgnoreStart
throw new NotSupportedException(
'This hash generator requires a compatible version of ext-phpiredis'
);
// @codeCoverageIgnoreEnd
}
}
/**
* {@inheritdoc}
*/
public function hash($value)
{
return phpiredis_utils_crc16($value);
}
}
+2 -1
View File
@@ -40,8 +40,9 @@ class PredisStrategy extends ClusterStrategy
{
$key = $this->extractKeyTag($key);
$hash = $this->distributor->hash($key);
$slot = $this->distributor->getSlot($hash);
return $this->distributor->getSlot($hash);
return $slot;
}
/**
+5 -3
View File
@@ -41,8 +41,9 @@ class RedisStrategy extends ClusterStrategy
public function getSlotByKey($key)
{
$key = $this->extractKeyTag($key);
$slot = $this->hashGenerator->hash($key) & 0x3FFF;
return $this->hashGenerator->hash($key) & 0x3FFF;
return $slot;
}
/**
@@ -50,7 +51,8 @@ class RedisStrategy extends ClusterStrategy
*/
public function getDistributor()
{
$class = get_class($this);
throw new NotSupportedException("$class does not provide an external distributor");
throw new NotSupportedException(
'This cluster strategy does not provide an external distributor'
);
}
}
-201
View File
@@ -1,201 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Cluster;
/**
* Slot map for redis-cluster.
*/
class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
{
private $slots = array();
/**
* Checks if the given slot is valid.
*
* @param int $first Slot index.
*
* @return bool
*/
public static function isValid($slot)
{
return $slot >= 0x0000 && $slot <= 0x3FFF;
}
/**
* Checks if the given slot range is valid.
*
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
*
* @return bool
*/
public static function isValidRange($first, $last)
{
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
}
/**
* Resets the slot map.
*/
public function reset()
{
$this->slots = array();
}
/**
* Checks if the slot map is empty.
*
* @return bool
*/
public function isEmpty()
{
return empty($this->slots);
}
/**
* Returns the current slot map as a dictionary of $slot => $node.
*
* The order of the slots in the dictionary is not guaranteed.
*
* @return array
*/
public function toArray()
{
return $this->slots;
}
/**
* Returns the list of unique nodes in the slot map.
*
* @return array
*/
public function getNodes()
{
return array_keys(array_flip($this->slots));
}
/**
* Assigns the specified slot range to a node.
*
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
* @param NodeConnectionInterface|string $connection ID or connection instance.
*
* @throws \OutOfBoundsException
*/
public function setSlots($first, $last, $connection)
{
if (!static::isValidRange($first, $last)) {
throw new \OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
}
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
}
/**
* Returns the specified slot range.
*
* @param int $first Initial slot of the range.
* @param int $last Last slot of the range.
*
* @return array
*/
public function getSlots($first, $last)
{
if (!static::isValidRange($first, $last)) {
throw new \OutOfBoundsException("Invalid slot range $first-$last");
}
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
}
/**
* Checks if the specified slot is assigned.
*
* @param int $slot Slot index.
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($slot)
{
return isset($this->slots[$slot]);
}
/**
* Returns the node assigned to the specified slot.
*
* @param int $slot Slot index.
*
* @return string
*/
#[\ReturnTypeWillChange]
public function offsetGet($slot)
{
if (isset($this->slots[$slot])) {
return $this->slots[$slot];
}
}
/**
* Assigns the specified slot to a node.
*
* @param int $slot Slot index.
* @param NodeConnectionInterface|string $connection ID or connection instance.
*
* @return string
*/
#[\ReturnTypeWillChange]
public function offsetSet($slot, $connection)
{
if (!static::isValid($slot)) {
throw new \OutOfBoundsException("Invalid slot $slot for `$connection`");
}
$this->slots[(int) $slot] = (string) $connection;
}
/**
* Returns the node assigned to the specified slot.
*
* @param int $slot Slot index.
*
* @return string
*/
#[\ReturnTypeWillChange]
public function offsetUnset($slot)
{
unset($this->slots[$slot]);
}
/**
* Returns the current number of assigned slots.
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return count($this->slots);
}
/**
* Returns an iterator over the slot map.
*
* @return \ArrayIterator
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->slots);
}
}
@@ -65,8 +65,8 @@ abstract class CursorBasedIterator implements \Iterator
*/
protected function requiredCommand(ClientInterface $client, $commandID)
{
if (!$client->getCommandFactory()->supports($commandID)) {
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
if (!$client->getProfile()->supportsCommand($commandID)) {
throw new NotSupportedException("The current profile does not support '$commandID'.");
}
}
@@ -92,7 +92,7 @@ abstract class CursorBasedIterator implements \Iterator
{
$options = array();
if (strlen(strval($this->match)) > 0) {
if (strlen($this->match) > 0) {
$options['MATCH'] = $this->match;
}
@@ -139,7 +139,6 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -149,7 +148,6 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -158,7 +156,6 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -167,7 +164,6 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
tryFetch: {
@@ -188,7 +184,6 @@ abstract class CursorBasedIterator implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+5 -3
View File
@@ -50,9 +50,11 @@ class HashKey extends CursorBasedIterator
*/
protected function extractNext()
{
$this->position = key($this->elements);
$this->current = current($this->elements);
if ($kv = each($this->elements)) {
$this->position = $kv[0];
$this->current = $kv[1];
unset($this->elements[$this->position]);
unset($this->elements[$this->position]);
}
}
}
+2 -7
View File
@@ -73,8 +73,8 @@ class ListKey implements \Iterator
*/
protected function requiredCommand(ClientInterface $client, $commandID)
{
if (!$client->getCommandFactory()->supports($commandID)) {
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
if (!$client->getProfile()->supportsCommand($commandID)) {
throw new NotSupportedException("The current profile does not support '$commandID'.");
}
}
@@ -128,7 +128,6 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -138,7 +137,6 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -147,7 +145,6 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -156,7 +153,6 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function next()
{
if (!$this->elements && $this->fetchmore) {
@@ -173,7 +169,6 @@ class ListKey implements \Iterator
/**
* {@inheritdoc}
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+5 -3
View File
@@ -50,9 +50,11 @@ class SortedSetKey extends CursorBasedIterator
*/
protected function extractNext()
{
$this->position = key($this->elements);
$this->current = current($this->elements);
if ($kv = each($this->elements)) {
$this->position = $kv[0];
$this->current = $kv[1];
unset($this->elements[$this->position]);
unset($this->elements[$this->position]);
}
}
}
+14 -2
View File
@@ -21,12 +21,24 @@ abstract class Command implements CommandInterface
private $slot;
private $arguments = array();
/**
* Returns a filtered array of the arguments.
*
* @param array $arguments List of arguments.
*
* @return array
*/
protected function filterArguments(array $arguments)
{
return $arguments;
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
$this->arguments = $this->filterArguments($arguments);
unset($this->slot);
}
@@ -92,7 +104,7 @@ abstract class Command implements CommandInterface
*/
public static function normalizeArguments(array $arguments)
{
if (count($arguments) === 1 && isset($arguments[0]) && is_array($arguments[0])) {
if (count($arguments) === 1 && is_array($arguments[0])) {
return $arguments[0];
}
+1 -1
View File
@@ -73,7 +73,7 @@ interface CommandInterface
/**
* Parses a raw response and returns a PHP object.
*
* @param string|array|null $data Binary string containing the whole response.
* @param string $data Binary string containing the whole response.
*
* @return mixed
*/
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/auth
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class AUTH extends RedisCommand
class ConnectionAuth extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/echo
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ECHO_ extends RedisCommand
class ConnectionEcho extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/ping
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PING extends RedisCommand
class ConnectionPing extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/quit
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class QUIT extends RedisCommand
class ConnectionQuit extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/select
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class SELECT extends RedisCommand
class ConnectionSelect extends Command
{
/**
* {@inheritdoc}
-145
View File
@@ -1,145 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
use Predis\ClientException;
use Predis\Command\Processor\ProcessorInterface;
/**
* Base command factory class.
*
* This class provides all of the common functionalities required for a command
* factory to create new instances of Redis commands objects. It also allows to
* define or undefine command handler classes for each command ID.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
abstract class Factory implements FactoryInterface
{
protected $commands = [];
protected $processor;
/**
* {@inheritdoc}
*/
public function supports(string ...$commandIDs): bool
{
foreach ($commandIDs as $commandID) {
if ($this->getCommandClass($commandID) === null) {
return false;
}
}
return true;
}
/**
* Returns the FQCN of a class that represents the specified command ID.
*
* @codeCoverageIgnore
*
* @param string $commandID Command ID
*
* @return ?string
*/
public function getCommandClass(string $commandID): ?string
{
if (isset($this->commands[$commandID = strtoupper($commandID)])) {
return $this->commands[$commandID];
}
}
/**
* {@inheritdoc}
*/
public function create(string $commandID, array $arguments = []): CommandInterface
{
if (!$commandClass = $this->getCommandClass($commandID)) {
$commandID = strtoupper($commandID);
throw new ClientException("Command `$commandID` is not a registered Redis command.");
}
$command = new $commandClass();
$command->setArguments($arguments);
if (isset($this->processor)) {
$this->processor->process($command);
}
return $command;
}
/**
* Defines a command in the factory.
*
* Only classes implementing Predis\Command\CommandInterface are allowed to
* handle a command. If the command specified by its ID is already handled
* by the factory, the underlying command class is replaced by the new one.
*
* @param string $commandID Command ID
* @param string $commandClass FQCN of a class implementing Predis\Command\CommandInterface
*
* @throws \InvalidArgumentException
*/
public function define(string $commandID, string $commandClass): void
{
if (!is_a($commandClass, 'Predis\Command\CommandInterface', true)) {
throw new \InvalidArgumentException(
"Class $commandClass must implement Predis\Command\CommandInterface"
);
}
$this->commands[strtoupper($commandID)] = $commandClass;
}
/**
* Undefines a command in the factory.
*
* When the factory already has a class handler associated to the specified
* command ID it is removed from the map of known commands. Nothing happens
* when the command is not handled by the factory.
*
* @param string $commandID Command ID
*/
public function undefine(string $commandID): void
{
unset($this->commands[strtoupper($commandID)]);
}
/**
* Sets a command processor for processing command arguments.
*
* Command processors are used to process and transform arguments of Redis
* commands before their newly created instances are returned to the caller
* of "create()".
*
* A NULL value can be used to effectively unset any processor if previously
* set for the command factory.
*
* @param ProcessorInterface|null $processor Command processor or NULL value.
*/
public function setProcessor(?ProcessorInterface $processor): void
{
$this->processor = $processor;
}
/**
* Returns the current command processor.
*
* @return ?ProcessorInterface
*/
public function getProcessor(): ?ProcessorInterface
{
return $this->processor;
}
}
-43
View File
@@ -1,43 +0,0 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* Command factory interface.
*
* A command factory is used through the library to create instances of commands
* classes implementing Predis\Command\CommandInterface mapped to Redis commands
* by their command ID string (SET, GET, etc...).
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface FactoryInterface
{
/**
* Checks if the command factory supports the specified list of commands.
*
* @param array $commandIDs List of command IDs
*
* @return bool
*/
public function supports(string ...$commandIDs): bool;
/**
* Creates a new command instance.
*
* @param string $commandID Command ID
* @param array $arguments Arguments for the command
*
* @return CommandInterface
*/
public function create(string $commandID, array $arguments = array()): CommandInterface;
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/geoadd
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GEOADD extends RedisCommand
class GeospatialGeoAdd extends Command
{
/**
* {@inheritdoc}
@@ -31,7 +29,7 @@ class GEOADD extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
foreach (array_pop($arguments) as $item) {
@@ -39,6 +37,6 @@ class GEOADD extends RedisCommand
}
}
parent::setArguments($arguments);
return $arguments;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/geodist
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GEODIST extends RedisCommand
class GeospatialGeoDist extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/geohash
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GEOHASH extends RedisCommand
class GeospatialGeoHash extends Command
{
/**
* {@inheritdoc}
@@ -31,13 +29,13 @@ class GEOHASH extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$members = array_pop($arguments);
$arguments = array_merge($arguments, $members);
}
parent::setArguments($arguments);
return $arguments;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/geopos
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GEOPOS extends RedisCommand
class GeospatialGeoPos extends Command
{
/**
* {@inheritdoc}
@@ -31,13 +29,13 @@ class GEOPOS extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$members = array_pop($arguments);
$arguments = array_merge($arguments, $members);
}
parent::setArguments($arguments);
return $arguments;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/georadius
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GEORADIUS extends RedisCommand
class GeospatialGeoRadius extends Command
{
/**
* {@inheritdoc}
@@ -31,7 +29,7 @@ class GEORADIUS extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
if ($arguments && is_array(end($arguments))) {
$options = array_change_key_case(array_pop($arguments), CASE_UPPER);
@@ -68,6 +66,6 @@ class GEORADIUS extends RedisCommand
}
}
parent::setArguments($arguments);
return $arguments;
}
}
@@ -9,14 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
namespace Predis\Command;
/**
* @link http://redis.io/commands/georadiusbymember
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GEORADIUSBYMEMBER extends GEORADIUS
class GeospatialGeoRadiusByMember extends GeospatialGeoRadius
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hdel
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HDEL extends RedisCommand
class HashDelete extends Command
{
/**
* {@inheritdoc}
@@ -31,10 +29,8 @@ class HDEL extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);
parent::setArguments($arguments);
return self::normalizeVariadic($arguments);
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hexists
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HEXISTS extends RedisCommand
class HashExists extends Command
{
/**
* {@inheritdoc}
@@ -27,4 +25,12 @@ class HEXISTS extends RedisCommand
{
return 'HEXISTS';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hget
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HGET extends RedisCommand
class HashGet extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hgetall
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HGETALL extends RedisCommand
class HashGetAll extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hmget
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HMGET extends RedisCommand
class HashGetMultiple extends Command
{
/**
* {@inheritdoc}
@@ -31,10 +29,8 @@ class HMGET extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);
parent::setArguments($arguments);
return self::normalizeVariadic($arguments);
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hincrby
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HINCRBY extends RedisCommand
class HashIncrementBy extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hincrbyfloat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HINCRBYFLOAT extends RedisCommand
class HashIncrementByFloat extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hkeys
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HKEYS extends RedisCommand
class HashKeys extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HLEN extends RedisCommand
class HashLength extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hscan
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HSCAN extends RedisCommand
class HashScan extends Command
{
/**
* {@inheritdoc}
@@ -31,14 +29,14 @@ class HSCAN extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
if (count($arguments) === 3 && is_array($arguments[2])) {
$options = $this->prepareOptions(array_pop($arguments));
$arguments = array_merge($arguments, $options);
}
parent::setArguments($arguments);
return $arguments;
}
/**
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HSET extends RedisCommand
class HashSet extends Command
{
/**
* {@inheritdoc}
@@ -27,4 +25,12 @@ class HSET extends RedisCommand
{
return 'HSET';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hmset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HMSET extends RedisCommand
class HashSetMultiple extends Command
{
/**
* {@inheritdoc}
@@ -31,7 +29,7 @@ class HMSET extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$flattenedKVs = array($arguments[0]);
@@ -42,9 +40,9 @@ class HMSET extends RedisCommand
$flattenedKVs[] = $v;
}
$arguments = $flattenedKVs;
return $flattenedKVs;
}
parent::setArguments($arguments);
return $arguments;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hsetnx
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HSETNX extends RedisCommand
class HashSetPreserve extends Command
{
/**
* {@inheritdoc}
@@ -27,4 +25,12 @@ class HSETNX extends RedisCommand
{
return 'HSETNX';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hstrlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HSTRLEN extends RedisCommand
class HashStringLength extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/hvals
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HVALS extends RedisCommand
class HashValues extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/pfadd
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PFADD extends RedisCommand
class HyperLogLogAdd extends Command
{
/**
* {@inheritdoc}
@@ -31,10 +29,16 @@ class PFADD extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);
return self::normalizeVariadic($arguments);
}
parent::setArguments($arguments);
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/pfcount
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PFCOUNT extends RedisCommand
class HyperLogLogCount extends Command
{
/**
* {@inheritdoc}
@@ -31,10 +29,8 @@ class PFCOUNT extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);
parent::setArguments($arguments);
return self::normalizeArguments($arguments);
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/pfmerge
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PFMERGE extends RedisCommand
class HyperLogLogMerge extends Command
{
/**
* {@inheritdoc}
@@ -31,10 +29,8 @@ class PFMERGE extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);
parent::setArguments($arguments);
return self::normalizeArguments($arguments);
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/del
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class DEL extends RedisCommand
class KeyDelete extends Command
{
/**
* {@inheritdoc}
@@ -31,10 +29,8 @@ class DEL extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
$arguments = self::normalizeArguments($arguments);
parent::setArguments($arguments);
return self::normalizeArguments($arguments);
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/dump
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class DUMP extends RedisCommand
class KeyDump extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/exists
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class EXISTS extends RedisCommand
class KeyExists extends Command
{
/**
* {@inheritdoc}
@@ -27,4 +25,12 @@ class EXISTS extends RedisCommand
{
return 'EXISTS';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/expire
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class EXPIRE extends RedisCommand
class KeyExpire extends Command
{
/**
* {@inheritdoc}
@@ -27,4 +25,12 @@ class EXPIRE extends RedisCommand
{
return 'EXPIRE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/expireat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class EXPIREAT extends RedisCommand
class KeyExpireAt extends Command
{
/**
* {@inheritdoc}
@@ -27,4 +25,12 @@ class EXPIREAT extends RedisCommand
{
return 'EXPIREAT';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/keys
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KEYS extends RedisCommand
class KeyKeys extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/migrate
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class MIGRATE extends RedisCommand
class KeyMigrate extends Command
{
/**
* {@inheritdoc}
@@ -31,7 +29,7 @@ class MIGRATE extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
if (is_array(end($arguments))) {
foreach (array_pop($arguments) as $modifier => $value) {
@@ -47,6 +45,6 @@ class MIGRATE extends RedisCommand
}
}
parent::setArguments($arguments);
return $arguments;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/move
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class MOVE extends RedisCommand
class KeyMove extends Command
{
/**
* {@inheritdoc}
@@ -27,4 +25,12 @@ class MOVE extends RedisCommand
{
return 'MOVE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/persist
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PERSIST extends RedisCommand
class KeyPersist extends Command
{
/**
* {@inheritdoc}
@@ -27,4 +25,12 @@ class PERSIST extends RedisCommand
{
return 'PERSIST';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/pexpire
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PEXPIRE extends RedisCommand
class KeyPreciseExpire extends KeyExpire
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/pexpireat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PEXPIREAT extends RedisCommand
class KeyPreciseExpireAt extends KeyExpireAt
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/pttl
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class PTTL extends RedisCommand
class KeyPreciseTimeToLive extends KeyTimeToLive
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/randomkey
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RANDOMKEY extends RedisCommand
class KeyRandom extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/rename
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RENAME extends RedisCommand
class KeyRename extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/renamenx
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RENAMENX extends RedisCommand
class KeyRenamePreserve extends KeyRename
{
/**
* {@inheritdoc}
@@ -27,4 +25,12 @@ class RENAMENX extends RedisCommand
{
return 'RENAMENX';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/restore
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class RESTORE extends RedisCommand
class KeyRestore extends Command
{
/**
* {@inheritdoc}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/scan
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class SCAN extends RedisCommand
class KeyScan extends Command
{
/**
* {@inheritdoc}
@@ -31,14 +29,14 @@ class SCAN extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$options = $this->prepareOptions(array_pop($arguments));
$arguments = array_merge($arguments, $options);
}
parent::setArguments($arguments);
return $arguments;
}
/**
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/sort
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class SORT extends RedisCommand
class KeySort extends Command
{
/**
* {@inheritdoc}
@@ -31,12 +29,10 @@ class SORT extends RedisCommand
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
protected function filterArguments(array $arguments)
{
if (count($arguments) === 1) {
parent::setArguments($arguments);
return;
return $arguments;
}
$query = array($arguments[0]);
@@ -82,6 +78,6 @@ class SORT extends RedisCommand
$query[] = $sortParams['STORE'];
}
parent::setArguments($query);
return $query;
}
}
@@ -9,16 +9,14 @@
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
namespace Predis\Command;
/**
* @link http://redis.io/commands/ttl
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class TTL extends RedisCommand
class KeyTimeToLive extends Command
{
/**
* {@inheritdoc}

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