Compare commits

...

368 Commits

Author SHA1 Message Date
Nicolas Grekas deee2b6d60 Fix deprecation when using "static" in callables (#1403) 2023-09-19 09:11:21 -07:00
Till Krüss bb8cce7bcf adjust travis config 2023-01-10 08:48:39 -08:00
Stefano Borghi dc14604f3e Check resource before reading (#803) 2022-09-20 09:13:12 -07:00
Stefano Borghi b000013f22 check resource before writing (#799) 2022-09-19 12:27:07 -07:00
angeljqv d6da14daa8 Don't export changeling and contribution notes 2022-04-05 09:13:30 -07:00
Andrii Dembitskyi acb2460f4c PHP 8.1 and symfony/error handler deprecations (#748)
* Be sure that position always have type integer

* Annotate properly return types for SPL interfaces implementation

It is allows avoiding deprecation messages from `symfony/error-handler` like a '[info] User Deprecated: Method "Iterator::rewind()" might add "void" as a native return type declaration in the future. Do the same in implementation "Predis\Collection\Iterator\CursorBasedIterator" now to avoid errors or add an explicit @return annotation to suppress this message.'

Fixed via `symfony/error-handler` patch script: ` SYMFONY_PATCH_TYPE_DECLARATIONS="force=phpdoc&php=5.3" ./vendor/bin/patch-type-declarations` and some additional manual work

Some details about why this is necessary and how checks with trigger deprecations works: https://wouterj.nl/2021/09/symfony-6-native-typing
2022-03-15 10:50:51 -07:00
Karol Hrusza 213f00042f Add clear method to pipeline (#749)
* Add clear method to pipeline

Ability to clean pipeline and responses when reusing pipelines.
Allows pipeline to be executed using chunks without out-of-control
increase in memory usage.

* spacing

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-03-11 08:48:10 -08:00
Julian Haupt ab0c46332c throw exception if connection->read() returns an error object in cons… (#746)
* throw exception if connection->read() returns an error object in consumer

* Add test case for Consumer throwing a ServerException when no subscription could be established

* spacing

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-02-17 10:13:02 -08:00
Ondřej Ešler ed19ec3483 #742 create new context for socket connection (#743)
Co-authored-by: Ondřej Ešler <ondrej.esler@peoplepath.com>
2022-02-04 08:41:14 -08:00
fluffycondor 90496e76b5 Fix wrong return iterator value type: implementation returns \stdClass but it's typehinted as array (#740) 2022-01-26 09:22:03 -08:00
Till Krüss 4231c35e09 back to dev 2022-01-05 09:49:10 -08:00
Till Krüss a2fb02d738 bump version to 1.1.10 stable 2022-01-05 09:46:08 -08:00
Pim Jansen ca1f398f7c Fix for changing return types for PHP8.1 (#730)
Co-authored-by: Pim Jansen <pjansen@senet.nl>
2021-12-18 11:23:39 -08:00
kgasienica 5e07be785a bugfix/cannot-use-object-of-type-error-as-array-722 (#724)
Co-authored-by: Karol Gąsienica-Fronek <karol.gasienica-fronek@lppsa.com>
2021-12-01 10:18:59 -08:00
Till Krüss b3cd02e2ba back to dev 2021-10-05 12:03:21 -07:00
Till Krüss c50c3393bb update changelog 2021-10-05 12:02:38 -07:00
Till Krüss 4aa3014bb0 tag 1.1.9 2021-10-05 11:55:45 -07:00
Dries Vints 92adf32e88 Fix PHP 8.1 strlen breakage (#713)
* Fix PHP 8.1 strlen breakage

This adds a fallback to an empty string if the argument is `null` for some reason.

* Update StreamConnection.php

* use `strval()`

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2021-09-30 09:09:37 -07:00
Till Krüss c4560304cf back to dev version 2021-09-29 10:50:22 -07:00
Till Krüss cf5c118a07 tag 1.1.8
tests pass locally PHP 8.1.0RC4-dev
2021-09-29 10:48:39 -07:00
Till Krüss bb9a670c83 add php 8.1 patches for phpunit 2021-09-29 10:43:13 -07:00
Till Krüss 3e56e880e8 nope; and nightly is php 8.0 🤦‍♂️ 2021-09-29 10:06:50 -07:00
Till Krüss 1c71a4e36e does travis do 8.1 yet?
seemingly no docs on this...
2021-09-29 10:02:37 -07:00
Vadim Borodavko 5004f5d6fc Add PHP 8.1 support (#712)
* Add PHP 8.1 support

* Update AbortedMultiExecException.php

* Update CommunicationException.php

* Update ServerSentinel.php

* Update CursorBasedIterator.php

* Update ReplicationStrategy.php

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2021-09-12 15:07:16 -07:00
John Paul E. Balandan, CPA e616f85679 Add ReturnTypeWillChange attribute to CursorBasedIterator (#708) 2021-08-24 10:25:19 -07:00
Alexander M. Turek 9cd093be24 Add ReturnTypeWillChange (#706) 2021-08-05 11:02:38 -07:00
Nick Doulgeridis 305d167b08 Prevent PHP warning
https://github.com/predis/predis/issues/691
2021-05-12 08:29:35 -07:00
Till Krüss 3271e1edcb back to dev 2021-04-04 12:51:10 -07:00
Till Krüss b240daa106 tag v1.1.7 2021-04-04 12:34:46 -07:00
Till Krüss 8fee6ce8c3 Fix PHP 5.3 test and PHP 8 test runs
For some reason Xdebug causes a seg fault under PHP 8 on Travis CI.
2021-04-04 12:33:59 -07:00
Till Krüss 35dd44b222 Merge pull request #686 from remicollet/issue-redis62
fix test expectation for redis 6.2
2021-04-04 10:36:02 -07:00
Till Krüss 545177f09f Merge pull request #685 from BrianMwit/patch-1
Fix misleading argument name in SentinelReplication
2021-04-04 10:34:53 -07:00
Remi Collet 2f9337cb9b fix test expectation for redis 6.2 2021-03-18 11:08:37 +01:00
Pitchaya Boonsarngsuk 437d80a7a3 Fix misleading argument name in SentinelReplication
$this->retryWait is in milliseconds. It's setter misleadingly document it as seconds.
Fixing by renaming the argument variable name without altering the functionality.
2021-03-17 03:41:03 +00:00
Till Krüss 3a14a39d92 Merge pull request #678 from nicolas-grekas/ret-type
Add some return types
2021-02-09 10:09:19 -08:00
Till Krüss 5c9457cc2e Merge pull request #673 from roborourke/apply-patches-on-update
Apply patches on initial install/update
2021-02-09 09:44:11 -08:00
Nicolas Grekas c551fc21f9 Add some return types 2021-02-04 12:06:26 +01:00
Robert O'Rourke 0d84606723 Switch to old array style syntax to support PHP 5.3 2020-11-30 11:07:54 +00:00
Robert O'Rourke c021e380d6 Apply patches on initial install/update
The patches to get PHPUnit working are causing problems for other projects that use both `cweagans/composer-patch-installers` and `predis/predis` along with PHPUnit.

This change moves the patch application to a simple PHP script run on the `post-update-cmd` composer event and removes the dependency on the patch installers package.
2020-11-27 17:24:00 +00:00
Daniele Alessandri 5f4b87080f Tweak tests. 2020-09-20 17:16:49 +02:00
Daniele Alessandri 5ae4ac6b49 Clear pending responses in reader buffer on disconnect.
When using the phpiredis-based connection backends, failed pipelines led
to spurious responses returned after reconnecting to Redis because the
underlying reader was not properly reset by discarding buffered replies
after disconnecting.

Fixes a couple of issues reported in #363.
2020-09-20 16:58:18 +02:00
Daniele Alessandri 521ac8f0eb Fix PhpiredisSocketConnection for PHP 8.
Removed is_resource() check as socket_create() now returns an object.
2020-09-20 15:27:35 +02:00
Daniele Alessandri fbbef2a0a2 Back to development. 2020-09-11 21:20:11 +02:00
Daniele Alessandri 9930e933c6 Update CHANGELOG and bump VERSION. 2020-09-11 21:18:05 +02:00
Daniele Alessandri 16957f3b39 Revert support for redis-sentinel authentication.
Authentication for sentinels was implemented in v1.1.5 (commit 2e76410)
but ended up being bugged (see ISSUE #658). This is now postponed as it
requires a more thorough investigation.
2020-09-11 21:10:40 +02:00
Daniele Alessandri 9e5371f5ce Back to development.
[ci skip]
2020-09-10 15:40:46 +02:00
Daniele Alessandri 8ca99f91e4 Update CHANGELOG and bump VERSION (v1.1.5) 2020-09-10 15:35:23 +02:00
Qusonann c778843b0d Fix zrevrangebyscore pipeline annotation
[ci skip]
2020-09-09 23:42:03 +02:00
Daniele Alessandri dca201291c Fix CHANGELOG for broken Markdown rendering.
[ci skip]
2020-09-05 16:50:25 +02:00
Daniele Alessandri 2e76410e65 Support password-based authentication for sentinel instances.
Password-based authentication for sentinels has been added in Redis 5.
Predis was actively ignoring any "password" parameter for sentinels when
creating connections to them to avoid issues when this parameter is set
in the default "parameters" array passed via client options, as they are
applied to **every** connection created by Predis (see #346).

We need to find a better way to specify a common password for sentinels
to be handled in a different way than the ones for Redis nodes. For now
each sentinel node protected by password must have an explicit password
set in its parameters list even if this password, by design, is the same
for all sentinels. Since we cannot use default "parameters" as explained
above but we still need to pass a common value for all sentinels an idea
could be using a dedicated client option like we did with "service", but
we will see later.

In this commit we also explicitly reset any "username" parameter as it
would trigger an `AUTH $username $password` but sentinels do not support
ACL authentication.

Fixes #594.
2020-09-05 16:25:06 +02:00
Daniele Alessandri ca468b785c Throw exception on FALSE passed to "replication" option.
While "replication" do accept values evaluating to TRUE, the same cannot
be said for values evaluating to FALSE. TRUE is used to tell the client
that we want replication handled using the default backend for unmanaged
replication setups. For using redis-sentinel the "sentinel" string value
must be passed.

Setting "replication" to FALSE led to a failure (and a PHP warning) on
client initialization because this condition was not handled properly.
Being able to do so would not make sense anyway: when the client does
not need to be set up to rely on replication, users simply have to omit
the option. Furthermore, users must always specify either "replication"
or "cluster" and not both with one of them set to FALSE.

Unfortunately options for aggregate connections in Predis v1.1 are a bit
of a mess, they did not scale well with the addition of new features and
are also quite inconsistent (e.g. "cluster" does not accept TRUE).

This has been largely fixed in Predis v2.0-dev but required implementing
a few breaking changes. It also means that this change does not need to
be ported to the main branch.

Addresses #381 using a different approach.
2020-09-05 14:18:30 +02:00
Daniele Alessandri 4a7fb55058 [tests] Add utility method to skip tests on CI environment.
We still have disabled all PUB/SUB related tests on CI for now, until we
understand why they fail at random.

Backported from main branch (ref. 5133706, f723f67, eb8a89e)
2020-09-04 18:42:38 +02:00
Daniele Alessandri fdf663ebba Fix WebdisConnection with IPv4 hosts.
Apparently something changed since last time in cURL internals and now
an IPv4 wrapped by square brackets (which are used for IPv6 addresses)
returns a malformed request error.

m
2020-09-04 18:06:36 +02:00
Daniele Alessandri 880ad09cad Restore broken change from a few years ago.
After this change there was no check to prevent the user from using SSL
with PhpiredisStreamConnection (it does not work due to the fact that
internally is uses stream_socket_recvfrom()).

Pretty obvious phpiredis was not installed on my system when I made that
change or the test suite would have caught the wrong behaviour (like it
just happened now that I build it from scratch).
2020-09-04 18:05:26 +02:00
Daniele Alessandri 5eccc65d42 Fix handling of username when using "redis://".
The username is now correctly retrieved from the userinfo fragment of
the URI when using the "redis" scheme and a "username:password" pair is
present. Values retrieved from the userinfo fragment always override the
ones specified in `username` and `password` if those fields are present
in the query string.
2020-09-01 11:40:30 +02:00
Daniele Alessandri f225c55a01 Back to development.
[ci skip]
2020-08-31 12:39:52 +02:00
Daniele Alessandri 8be2418f01 Update CHANGELOG and bump VERSION (v1.1.4) 2020-08-30 00:15:08 +02:00
Daniele Alessandri b9ebe8a376 [tests] Fix mistype in TODO comments.
... and yay for copypasting it too!
2020-08-30 23:26:42 +02:00
Daniele Alessandri e2d26926d2 Update README. 2020-08-30 21:33:14 +02:00
Daniele Alessandri 309248042f Update .gitignore
Removing annoying .phpunit.result.cache left around when switching back
and forth branches from main to v1.1 for development.
2020-08-30 10:38:47 +02:00
Daniele Alessandri 87452e62d0 Fix authentication to support ACL with Redis 6.0.
When passing both "username" and "password" to connection parameters the
client now uses the extended AUTH command to support ACL authentication
with Redis 6.0.

The plain old authentication method is still supported like usual simply
by passing only "password" to connection parameters.
2020-08-29 14:11:55 +02:00
Daniele Alessandri 31baa9b241 Do not push AUTH and SELECT on empty "password" and "database".
NULL or zero-length string values passed to "password" and "database" in
the connection parameters list do not trigger spurious AUTH and SELECT
commands anymore when connecting to Redis.

Fixes #436.
2020-08-29 13:39:12 +02:00
Daniele Alessandri 6f942400a1 [tests] Skip integration tests for PUB/SUB consumer on CI.
Changes backported from the main branch (ref. 04d5c10, 5afadb5).

This is just a temporary solution, we will revert this change as soon as
the actual cause for the spurious failures is identified.
2020-08-27 17:44:23 +02:00
Daniele Alessandri 5e220a0b57 Update CHANGELOG. 2020-08-25 15:57:51 +02:00
Daniele Alessandri 0eb2633348 Merge remote-tracking branch 'github/pr/554' into v1.1 2020-08-25 15:42:13 +02:00
Daniele Alessandri cbbd6bba88 Update CHANGELOG.
[ci skip]
2020-08-24 22:33:48 +02:00
Daniele Alessandri 3702f24efc Fix more @method tags in interfaces. 2020-08-24 22:27:21 +02:00
Daniele Alessandri 82fefe057b Merge remote-tracking branch 'github/pr/456' into v1.1-annotations 2020-08-24 21:56:13 +02:00
Daniele Alessandri 42336537dc Merge remote-tracking branch 'github/pr/497' into v1.1 2020-08-24 17:10:28 +02:00
Daniele Alessandri 802abac55e Fix pear builder script to generate UTF-8 encoded package.xml.
Fixes issue #531.

[ci skip]
2020-08-24 16:20:06 +02:00
Daniele Alessandri 61e75816ce Apply few changes in .gitattributes file.
Remove .github from directories exported in our release packages and fix
to ignore .php_cs.dist (the actual file commited) instead of .php_cs.

[ci skip]
2020-08-24 14:04:10 +02:00
Daniele Alessandri 17f0c08cb6 [tests] Fix expected response for COMMAND INFO (Redis >= 6.0).
Starting with Redis 6.0 and the introduction of Access Control Lists,
COMMAND INFO returns an additional array for each specified command in
the request with a list of the ACL categories associated to a command.
2020-08-24 11:45:20 +02:00
Daniele Alessandri 2b8af1eef2 Update CHANGELOG. 2020-08-23 18:38:07 +02:00
Daniele Alessandri c5f2b441b4 Merge branch 'github/pr/545' into v1.1 2020-08-23 18:11:49 +02:00
GITSRC 2440a61cba Fix HashRing to use method defined by HashGeneratorInterface. 2020-08-23 18:09:09 +02:00
Daniele Alessandri 7100884201 Update CHANGELOG. 2020-08-23 17:48:38 +02:00
Daniele Alessandri 2a932d7292 Merge remote-tracking branch 'github/pr/556' into v1.1 2020-08-23 17:47:26 +02:00
Daniele Alessandri 0a6306b73e Back to development. 2020-08-23 17:46:12 +02:00
Peter Gribanov 4363345f7c iterate not iterable connections #552 2020-08-23 15:00:22 +03:00
Till Krüss 2ce537d75e bump version to v1.1.3 2020-08-18 14:00:59 -07:00
Till Krüss 7489690306 include patch files in exports just to be safe
See #642
2020-08-18 13:55:36 -07:00
Till Krüss 7376902b20 update org references 2020-08-14 10:11:49 -07:00
Till Krüss a6bd25412e remove gitter badge 2020-08-12 12:00:08 -07:00
Till Krüss 60cc950a58 remove Predis\Async from readme 2020-08-12 11:53:52 -07:00
Till Krüss db82dd9f23 format psr-4 2020-08-12 11:53:12 -07:00
Till Krüss 1bc08d9bcc add funding to composer.json; update authors 2020-08-12 11:50:57 -07:00
Till Krüss 11e4bb44d0 more reference updates to predishq 2020-08-12 11:50:07 -07:00
Till Krüss cddb849812 remove gitter 2020-08-12 11:49:42 -07:00
Till Krüss f2298dd186 update references to predishq 2020-08-12 11:49:37 -07:00
Till Krüss bceda1d8e6 Merge pull request #625 from AhmedRaafat14/add-php8-support
Support PHP 8.0 in v1.1.x
2020-08-12 08:42:08 -07:00
Till Krüss 9f87c849c5 Update .travis.yml 2020-08-12 08:41:50 -07:00
Ahmed Raafat 1382146b03 Add php 8.0 (nightly) to the lines that is allowed to faoil (allow_failures) 2020-08-12 12:22:42 +02:00
Ahmed Raafat cbc08aa5e7 Add php 8.0 to travis 2020-08-12 11:45:43 +02:00
Till Krüss de9cd905bb Update issue templates 2020-08-11 12:05:47 -07:00
Till Krüss 6dd55d7f96 move cweagans/composer-patches as dev dep 2020-08-11 11:08:18 -07:00
Till Krüss 82eb18c6c3 bump version to 1.1.2 2020-08-11 10:28:15 -07:00
Till Krüss ac6b813ba9 Merge pull request #616 from nrk/support-php-74
Support PHP 7.4 in v1.1.x
2020-08-11 09:48:35 -07:00
Till Krüss 6482956d96 patch tests
Taken from: https://github.com/yiisoft/yii/pull/4306 by @samdark
2020-08-11 09:41:04 -07:00
Till Krüss cf15dd9857 add 7.4 to travis matrix 2020-08-11 08:55:49 -07:00
Till Krüss 597c61a098 fix out of range exception test 2020-08-11 08:43:17 -07:00
Till Krüss 9aa99349b1 Merge pull request #574 from matthewbaggett/v1.1-travis-fixup
V1.1 travis fixup
2020-08-11 08:30:12 -07:00
Till Krüss b6f60f0d72 add funding 2020-08-10 17:02:18 -07:00
Till Krüss 10235d62dc adjust error message regex 2020-08-10 13:48:17 -07:00
Till Krüss ec2b1eacd2 Merge pull request #614 from nrk/revert-611-patch-2
Revert "Bring travis config up to date"
2020-08-10 13:31:37 -07:00
Till Krüss 9c3605ab33 Merge pull request #613 from nrk/revert-610-patch-1
Revert "Add more build targets"
2020-08-10 13:31:20 -07:00
Till Krüss 55e6094954 Revert "Bring travis config up to date" 2020-08-10 13:31:03 -07:00
Till Krüss 15314b726b Revert "Add more build targets" 2020-08-10 13:30:45 -07:00
Till Krüss 292604edbf fix HHVM lowercase issue
Fatal error: Uncaught Error: Keyword __HALT_COMPILER must be written in lowercase
2020-08-10 13:02:49 -07:00
Till Krüss fc8f9f5604 Merge pull request #585 from gladx/v1.1
Fix test error exception for SELECT command
2020-08-10 12:41:25 -07:00
Till Krüss 77132e6c6b Merge pull request #589 from funivan/patch-1
Fix wiki link
2020-08-10 12:40:46 -07:00
Till Krüss a7571ef0c7 Merge pull request #583 from Miljan9602/v1.1
Fixed wrong annotation for spop method.
2020-08-10 12:40:27 -07:00
Till Krüss 4901408b08 Merge pull request #611 from hannesvdvreken/patch-2
Bring travis config up to date
2020-08-10 12:29:13 -07:00
Till Krüss dd3897b755 Merge pull request #610 from hannesvdvreken/patch-1
Add more build targets
2020-08-10 12:28:53 -07:00
Hannes Van De Vreken 6e7dbe05fb Bring travis config up to date 2020-07-29 03:33:50 +02:00
Hannes Van De Vreken f0f9d68a8e Add more build targets 2020-07-29 03:18:56 +02:00
Ivan Shcherbak bb6f1ee0e3 Fix wiki link 2019-11-25 14:23:35 +02:00
Ali Yousefi ffcc4bba8c Fix test error exception for SELECT command 2019-10-06 22:10:17 +03:30
Miljan Rakita 83359dff9a Fixed wrong annotation for spop method.
Spop method has return annotation string, but it returns array.
2019-09-10 19:26:00 +02:00
Matthew Baggett 710b4f901b Apparently 5.4 and 5.5 aren't available outside of trusty? 2019-08-09 11:54:35 +02:00
Matthew Baggett f3e46683b9 Sometimes, timing-related tests (read: KeyExpireAtTest::testCanExpireKeys) can fail but then succeed on a retry. So lets automate that retry. 2019-08-09 11:07:43 +02:00
Matthew Baggett 945e05819b Rejig. 2019-08-09 10:54:47 +02:00
Matthew Baggett a64b7354e1 Travis fixup 2019-08-09 10:37:19 +02:00
Mark Fettig 39ff616e86 update sort command to be a write operation 2019-03-18 12:10:26 -04:00
Geoffrey Hoffman 738ec0c50f Correcting the return type hint for BRPOPLPUSH method 2018-04-10 18:09:42 -07:00
Teoh Han Hui 1828222e27 Add null return value to PHPDoc 2017-08-14 19:36:11 +08:00
Daniele Alessandri 111d100ee3 Make examples scripts executable only via CLI.
This is meant as a basic layer of protection for those cases where the whole
Predis repository is cloned into a path which is publicly exposed by the web
server.

Closes #408
2017-07-12 16:39:17 +02:00
Daniele Alessandri 893215a1d4 Push same changes of #446 to Predis\ClientContextInterface.
This also adds a couple of missing changes into the original PR.
2017-07-12 15:34:57 +02:00
Daniele Alessandri f5d8b9fe2d Merge remote-tracking branch 'github/pr/446' into v1.1 2017-07-12 15:24:15 +02:00
Daniele Alessandri 722b513cea Upgrade .php_cs configuration file for php-cs-fixer 2.x 2017-07-12 15:22:58 +02:00
Daniele Alessandri 62774c6cc5 Merge remote-tracking branch 'github/pr/442' into v1.1 2017-07-12 13:37:06 +02:00
Daniele Alessandri 8aa2ded5b0 Merge remote-tracking branch 'github/pr/445' into v1.1 2017-07-12 13:36:17 +02:00
Daniele Alessandri 795c3e41b0 Merge remote-tracking branch 'github/pr/434' into v1.1 2017-07-12 13:31:24 +02:00
Daniele Alessandri 59e616b124 Remove broken badge.
Looks like hhvm.h4cc.de is dead.

[ci skip]
2017-07-12 13:30:52 +02:00
Daniele Alessandri 157f658f0b Fix iterators abstractions for HSCAN and ZSCAN when using PHP 7.2.
Closes #488.
2017-07-12 11:59:35 +02:00
Daniele Alessandri 343afd312a Fix wrong CRC16 calculated on integer value.
Closes #450.
2017-07-12 11:29:25 +02:00
Daniele Alessandri d77b77bbd9 Merge remote-tracking branch 'github/pr/443' into v1.1 2017-07-12 10:59:41 +02:00
gitomato 34b678192f Add string type to $keys argument
Reveals the ability to use a string for $keys argument.
2017-06-29 10:25:14 +02:00
Jorrit Schippers 4de246cfee Correct link to GitHub releases page 2017-06-27 09:35:15 +02:00
Anatoly Pashin 6fd6933580 Create .travis.yml 2017-06-11 00:28:43 +10:00
Anatoly Pashin c7b38abe71 use trusty dist 2017-06-09 18:05:11 +10:00
Anatoly Pashin 574ef78f86 fix typehint 2017-06-09 17:57:00 +10:00
Andrii Volin 1ebabaf8f8 fix typo in tests/README.md 2017-05-01 17:12:25 +03:00
Daniele Alessandri 98ec0cc27e Merge remote-tracking branch 'github/pr/376' into v1.1
[ci skip]
2016-11-22 15:32:30 +01:00
Daniele Alessandri caf8a46cbb Merge remote-tracking branch 'github/pr/373' into v1.1
[ci skip]
2016-11-22 15:30:42 +01:00
Daniele Alessandri 944a16d7c0 Merge remote-tracking branch 'github/pr/393' into v1.1 2016-11-17 17:55:23 +01:00
Remi Collet d1290dfde0 fix test for ArgumentCountError exception with PHP 7.1 2016-11-17 15:24:54 +01:00
Remi Collet 9489d9e5aa enable 7.1 on travis 2016-11-17 15:12:24 +01:00
Fabien Villepinte 94ccb2cc3b Fix PHPDoc of \Predis\Connection\Aggregate\SentinelReplication::assertConnectionRole() 2016-09-14 17:20:55 +02:00
orvice 6c848db2a0 fix typo 2016-08-19 16:48:00 +08:00
Daniele Alessandri 6ab10b2b70 [tests] Fix preassigned slots in cluster tests. 2016-06-18 21:51:10 +02:00
Daniele Alessandri 2d4377fc81 Back to development. 2016-06-18 21:37:42 +02:00
Daniele Alessandri f0210e3888 Update CHANGELOG and bump VERSION. 2016-06-16 18:22:20 +02:00
Daniele Alessandri 5e88200ed6 Merge branch 'github/pr/344' into v1.1 2016-06-16 10:47:45 +02:00
Daniele Alessandri aed1a7bdf1 Apply same fix of 8437fbc to ClientContextInterface. 2016-06-16 10:47:01 +02:00
Daniele Alessandri 2290042680 Fix GC issues with closures for phpiredis reader handlers.
The following code, despite not being something you would do in real
world scenarios, eventually ended up generating an exception for "Too
many open files".

  for ($i = 0; $i < 10000; $i++) {
    $client = new Predis\Client();
    $client->connect();
  }

The reason was that the destructor for the connection was not invoked
by PHP as expected when the client instance went out of scope, so the
underlying stream resource were not being properly released.
Even without an actual "connect()" operation, the memory usage of PHP
kept growing until exhausting the configured value of max memory.

The source of the issue is related to the use of closures as handlers
for the phpiredis reader resource, to be more precise it seems to be
some kind of circular reference memory leak. Apparently PHP does not
like the fact that a closure, automatically bound to "$this" starting
with PHP 5.4, is stored in the reader resource which in turn is kept
referenced by "$this". This ends up the refcount not going down thus
the GC does not collect those connection objects going out of scope.
This is confirmed by the fact that this issue is not triggered when
using PHP 5.3 where the ZE does not automatically bind closures to
"$this", unless you capture "$this" with the "use()" directive (and
the usual "$that = $this" trick).

Using a static assignement instead of simply returning new closures
in "getStatusHandler()" and "getErrorHandler()" is kind of an hack
that seems to be working fine, the added value of this solution is
that we do not have to break the internal API of the three connection
backends based on ext-phpiredis.

This commit fixes #345.
2016-06-14 21:25:14 +02:00
Daniele Alessandri 295e9175ad Update CHANGELOG.
[ci skip]
2016-06-14 18:21:36 +02:00
Pascal Hofmann d92f31dc66 Don't send AUTH and/or SELECT command after connecting to sentinels 2016-06-14 17:20:08 +02:00
Felix Sandström 8437fbc84f Correcting the PHPDocs 2016-06-14 10:02:47 +02:00
Daniele Alessandri 4d58928efd [tests] Add test for Predis\Cluster\Hash\CRC16. 2016-06-13 18:57:06 +02:00
Daniele Alessandri a523cf7731 Use master for connect() when sentinel reports no slaves.
Fixes #342.
2016-06-13 15:51:23 +02:00
Daniele Alessandri 1ad0effb0a Merge remote-tracking branch 'github/pr/343' 2016-06-13 15:21:26 +02:00
Daniele Alessandri 0607d84d0c Back to development. 2016-06-13 15:20:48 +02:00
orvice a1ee7b68ef fix typos
possibile -> possible
indentify -> identify
publis ->  publish
2016-06-12 16:27:14 +08:00
Daniele Alessandri 0e17edbefb Update CHANGELOG and bump VERSION. 2016-06-02 00:06:21 +02:00
Daniele Alessandri 81c0a8f051 Ensure big ints are not truncated on 32 bits PHP.
We check if the string value is different than the casted int value,
if so it means that the integer is beyond PHP_INT_MAX or PHP_INT_MIN
and we simply return the string value. This is also useful on Windows
builds of PHP since the maximum integer size (prior to PHP 7.0) is 32
bits even for 64 bit builds.
2016-06-01 23:49:23 +02:00
Daniele Alessandri 1065edc8d1 Apply last round of CS fixes. 2016-06-01 22:31:06 +02:00
Daniele Alessandri 843ad23ea7 [tests] Move utility method into base test class. 2016-06-01 22:27:36 +02:00
Daniele Alessandri e386f5c732 Update README.
[ci skip]
2016-06-01 22:20:58 +02:00
Daniele Alessandri a06063d2e6 Update CHANGELOG.
[ci skip]
2016-06-01 21:59:28 +02:00
Daniele Alessandri d58929e5a5 Update README.
Let's try using Gitter...

[ci skip]
2016-06-01 20:59:25 +02:00
Daniele Alessandri ecab7e4642 Implement IteratorAggregate interface for Client.
Now it is possible to iterate over traversable aggregate connections
and get a key/value pair of $connectionId => $clientInstance for each
node.
2016-06-01 12:33:20 +02:00
Daniele Alessandri a22fc17800 Update CHANGELOG.
[ci skip]
2016-05-30 17:45:08 +02:00
Daniele Alessandri 7a50b02c36 [tests] Do not allow failures for HHVM on Travis CI. 2016-05-30 17:09:15 +02:00
Daniele Alessandri f6bf2b5977 [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:09:02 +02:00
Daniele Alessandri 5850029f89 Update README.
[ci skip]
2016-05-28 20:28:18 +02:00
Daniele Alessandri f64bd83f9d Update README.
[ci skip]
2016-05-28 20:27:10 +02:00
Daniele Alessandri 922e56b480 Iterate only over connections mapped in slots map.
Iterating over Predis\Connection\Aggregate\RedisCluster returns all
the connections currently mapped in the slots map instead of just the
ones initialized in the pool.

When the slots map is retrieved from Redis (which by default is done
automatically) this allows to iterate over all of the current master
nodes of the cluster. When the underlying use of "CLUSTER SLOTS" is
disabled the iteration returns only connections with a slots range
associated in their parameters or initialized by `-MOVED` responses
to make the behaviour of the iteration consistent between the two
modes of operation.
2016-05-28 17:58:38 +02:00
Daniele Alessandri 5a0dfc3602 Fix parameters overriding for sentinels.
Different fix than PR #339 but thanks @phofmann-trust for spotting.
2016-05-28 15:53:23 +02:00
Daniele Alessandri 39a6e18d71 Update README of test directory.
[ci skip]
2016-05-27 22:03:32 +02:00
Daniele Alessandri a19de6356c Update README.
[ci skip]
2016-05-27 21:31:54 +02:00
Daniele Alessandri 2d01a27e17 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:30:07 +02:00
Daniele Alessandri ad7b8b08cb Run php-cs-fixer. 2016-05-26 09:54:09 +02:00
Daniele Alessandri 0ebfc0e7d2 Update README.
[ci skip]
2016-05-25 21:33:07 +02:00
Daniele Alessandri 9303029c13 Discard slave even when flag is "o_down". 2016-05-25 16:03:12 +02:00
Daniele Alessandri 0477499418 Fix ROLE expectation for read commands with no slaves.
This commit fixes #337.
2016-05-25 15:56:33 +02:00
Daniele Alessandri 9398a793a5 Bump default server profile to Redis 3.2. 2016-05-25 11:25:12 +02:00
Daniele Alessandri 0dff761a61 Merge branch 'v1.1-commands-redis-3.2' 2016-05-24 23:17:33 +02:00
Daniele Alessandri 02ed5a2f4e Update SPOP's @method signature in phpdoc.
SPOP accepts the optional "count" argument since Redis 3.2.
2016-05-24 23:16:00 +02:00
Daniele Alessandri 51932d82e8 Add new command: GEORADIUSBYMEMBER (Redis 3.2.0). 2016-05-24 22:35:27 +02:00
Daniele Alessandri 00000fde4e Add new command: GEORADIUS (Redis 3.2.0). 2016-05-24 22:30:06 +02:00
Daniele Alessandri aeb9b7ccae Check that STORE key in SORT ends up in same slot. 2016-05-24 22:08:41 +02:00
Daniele Alessandri 4273a2a8b6 Improve detection of STORE in SORT command arguments. 2016-05-24 21:38:16 +02:00
Daniele Alessandri b2284d015f Add new command: GEODIST (Redis 3.2.0). 2016-05-24 17:09:35 +02:00
Daniele Alessandri c4e0044269 Add new command: GEOPOS (Redis 3.2.0). 2016-05-24 17:09:33 +02:00
Daniele Alessandri 0b6b3b2558 Add new command: GEOHASH (Redis 3.2.0). 2016-05-24 16:00:08 +02:00
Daniele Alessandri 7ef9619f5c Add new command: GEOADD (Redis 3.2.0). 2016-05-24 16:00:06 +02:00
Daniele Alessandri 3362e474b6 Add new command: BITFIELD (Redis 3.2.0). 2016-05-24 16:00:04 +02:00
Daniele Alessandri 7b3f6e1db5 [tests] Add missing test for key prefix in HSTRLEN. 2016-05-24 15:11:49 +02:00
Daniele Alessandri 1536b2aa82 Add missing @method tag for HSTRLEN in phpdocs.
[ci skip]
2016-05-24 12:27:30 +02:00
Daniele Alessandri 1ab65da368 [tests] Missing @requiresRedisVersion for HSTRLEN. 2016-05-24 11:51:04 +02:00
Daniele Alessandri b11fb84282 Update description in package.ini. 2016-05-24 10:27:57 +02:00
Daniele Alessandri da009e59dd [tests] Test count argument for SPOP in Redis 3.2. 2016-05-23 15:53:30 +02:00
Daniele Alessandri 763acd232d Add new server profile for Redis 3.2 (new stable). 2016-05-23 12:43:00 +02:00
Daniele Alessandri 75a8253ee4 Apply minor grammar fix in comment.
[ci skip]
2016-05-23 12:09:43 +02:00
Daniele Alessandri 9368d98ac3 Update phpunit.xml.* 2016-05-22 20:21:58 +02:00
Daniele Alessandri 8f514fe893 Update description in composer.json. 2016-05-22 20:05:58 +02:00
Daniele Alessandri 6964f060b3 Fix README.
[ci skip]
2016-05-22 15:33:21 +02:00
Daniele Alessandri c125c218ae Apply fix to README for grammar slip.
[ci skip]
2016-05-22 15:10:37 +02:00
Daniele Alessandri 1f3a010681 Update CHANGELOG.
[ci skip]
2016-05-22 15:07:01 +02:00
Daniele Alessandri 49f6b46942 Update and improve README.
[ci skip]
2016-05-22 15:06:56 +02:00
Daniele Alessandri c1795de1f3 Fix README.
[ci skip]
2016-05-21 17:12:20 +02:00
Daniele Alessandri 528cd090d8 Update README.
Improved the section about replication by rewriting some parts of it
and adding details about redis-sentinel.

[ci skip]
2016-05-21 17:05:14 +02:00
Daniele Alessandri 5b3a5bbef9 Run php-cs-fixer. 2016-05-21 15:46:58 +02:00
Daniele Alessandri 349a70a08a Merge branch 'v1.1-sentinel'
This merge resolves #131.
2016-05-21 15:30:22 +02:00
Daniele Alessandri c1de65c4ee Swap params order in redis-sentinel constructor. 2016-05-21 15:23:24 +02:00
Daniele Alessandri ecbdaa1951 Update CHANGELOG.
[ci skip]
2016-05-20 22:33:29 +02:00
Daniele Alessandri 05209e6e7d Switch to next slave on -LOADING error response.
This prevents an early failure of the command execution on the client
when one slave gets back online but is still loading the dataset from
disk (when this happens, Redis returns the -LOADING error response).

This commit fixes #280.
2016-05-20 21:59:40 +02:00
Daniele Alessandri 803a5cfd5b Remove old references about HHVM being unstable. 2016-05-20 20:34:03 +02:00
Daniele Alessandri faaf853cbe [tests] Rename old branch for exclusion.
[ci skip]
2016-05-20 20:14:44 +02:00
Daniele Alessandri 257791edcf Update badges in CHANGELOG.
[ci skip]
2016-05-20 18:34:54 +02:00
Daniele Alessandri d0cc7a5947 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:17:06 +02:00
Daniele Alessandri 5adafb352c Evict connection from slots cache when removed. 2016-05-20 11:43:06 +02:00
Daniele Alessandri d6d307696a [tests] Add tests for redis-sentinel connection. 2016-05-19 22:15:55 +02:00
Daniele Alessandri f3999660fe Fix bug during assertion of connection role.
ROLE was not being sent to master when still disconnected and with an
empty slaves pool preventing the client from checking the actual role
of the server upon connect().
2016-05-19 19:06:49 +02:00
Daniele Alessandri 18e846c2ad Fix bug trying to switch to an unknown connection. 2016-05-19 17:42:51 +02:00
Daniele Alessandri 7e4800010a Add minor comment for clarity. 2016-05-19 15:59:21 +02:00
Daniele Alessandri 6b5181b030 Do not wipe server list when removing connection. 2016-05-19 12:58:07 +02:00
Daniele Alessandri 01483234c7 [tests] Fix assertion.
PHPUnit_Util_Type::export() has been removed a while ago...
2016-05-19 11:43:39 +02:00
Daniele Alessandri cdadb796ad Bump min. version of PHPUnit (require-dev). 2016-05-19 11:43:29 +02:00
Daniele Alessandri 354e5e26da 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 15:04:37 +02:00
Daniele Alessandri d55826f35c [tests] Fix tests from previous commit.
They did work, but I am not exactly sure why. Also added a missing
test for Predis\Connection\CompositeStreamConnection.
2016-05-17 20:06:30 +02:00
Daniele Alessandri 26d7147646 Update CHANGELOG. 2016-05-17 19:47:25 +02:00
Daniele Alessandri 973e8592e3 Throw when command sent in connect() returns error.
Common failures are the use of SELECT with a database index outside
the bound of the configured number of databases in redis.conf or the
use of a wrong password for authentication with AUTH.

This resolves #322.
2016-05-17 19:15:21 +02:00
Daniele Alessandri f696ed125e Update CHANGELOG. 2016-05-17 15:31:59 +02:00
Daniele Alessandri 9f6759ca1c Implement discovery in basic replication.
Now the client can discover the whole replication configuration by
asking to one of the servers (master has the precedence) using the
INFO REPLICATION command. This is obviously a best-effort fallback
and there is no strong guarantee about reliability and efficiency.

By enabling auto-discovery, the client automates this process when
the execution of a command fails because one of the target servers
is unreachable. The replication connection requires an instance of
connection factory associated to it in order to be able to create
new connections on the fly.

It is possible to enable the auto-discovery procedure easily via
client options:

  $client = new Predis\Client($servers, [
    'replication' => true,
    'autodiscovery' => true,
  ]);
2016-05-17 14:24:52 +02:00
Daniele Alessandri 89a1e236ce Use custom ID when adding connection with no alias. 2016-05-16 17:28:13 +02:00
Daniele Alessandri 97dfba1e98 Remove useless use directive. 2016-05-16 16:57:24 +02:00
Daniele Alessandri be8ac3e205 Handle error responses returned by redis-sentinel.
Fixes #289.
2016-05-16 16:50:28 +02:00
Daniele Alessandri a933a13087 [tests] Fix wrong method in mock. 2016-05-16 15:38:29 +02:00
Daniele Alessandri ce3d42b9cf Fix CHANGELOG.
[ci skip]
2016-05-15 22:03:34 +02:00
Daniele Alessandri 629329ac76 Fix failing test.
This was exactly what I meant with the @todo annotation, too bad I
forgot to temporarily adjust the test accordingly.
2016-05-15 21:54:00 +02:00
Daniele Alessandri a95860ce00 Bump year in LICENSE. 2016-05-15 21:38:50 +02:00
Daniele Alessandri 51e8d6c46e Update CHANGELOG. 2016-05-15 21:38:21 +02:00
Daniele Alessandri 126998631b Merge branch 'replication-improvements' 2016-05-15 21:37:35 +02:00
Daniele Alessandri d7f3b8c9d2 Update CHANGELOG. 2016-05-15 21:36:31 +02:00
Daniele Alessandri c9366212d0 Add methods to switch to master or random slave. 2016-05-15 21:27:43 +02:00
Daniele Alessandri 5b76b41fda Use master for connect() on empty slaves pool.
Internally the replication class uses this order to pick which server
it should connect to: current connection, one of the slaves, master.

If there is at least 1 slave, connect() will not fail even if master
is undefined. If there are no slaves, connect() will pick master. If
there are no connections registered for replication, connect() will
fail immediatly.
2016-05-15 20:53:09 +02:00
Daniele Alessandri b491dff126 Send read-only commands on next slave on failure.
If no other slave is available try again on master as last resort
before giving up and throwing an exception.
2016-05-15 20:53:06 +02:00
Daniele Alessandri 774b4014d9 Use master for read requests on empty slaves pool.
This is the last resort in case all of the slaves are unreachable.
2016-05-15 17:20:58 +02:00
Daniele Alessandri 00c5d7de19 Apply minor styling fix. 2016-05-15 17:09:43 +02:00
Daniele Alessandri e3412c4a1b Move member variable initialization. 2016-05-15 16:56:50 +02:00
Daniele Alessandri 44ebf5a5e4 Prevent warnings picking slave from empty pool. 2016-05-15 16:54:57 +02:00
Daniele Alessandri 0c99f9ef28 Disconnect before eventually throwing exception. 2016-05-15 15:56:16 +02:00
Daniele Alessandri 6e0743a29a Change method signature. 2016-05-15 15:53:14 +02:00
Daniele Alessandri b047e8b6a1 Rewrite the connection class for redis-sentinel.
Now we do not extend Predis\Connection\Aggregate\MasterSlaveReplication
anymore in order to obtain a more coherent implementation with the logic
of redis-sentinel and apply more optimizations by avoiding useless round
trips with sentinel servers.
2016-05-14 10:39:39 +02:00
Daniele Alessandri 7be83a1fd6 Merge remote-tracking branch 'github/pr/332' 2016-05-13 16:59:33 +02:00
Daniele Alessandri 367ed2c11c [tests] Remove deprecated phpunit directive. 2016-05-13 16:35:10 +02:00
Daniele Alessandri f0b3014cb4 Merge remote-tracking branch 'github/pr/333' 2016-05-13 16:32:48 +02:00
Daniele Alessandri a816adf6e7 Set default parameters via client options.
This is mostly useful when configuring the client to use redis-cluster
or redis-sentinel in order to set a common password for authentication
or database. In these kind of configurations it is impossible to pass
them via connection parameters as connections are created dinamically
by the client depending on the server response.
2016-05-13 14:56:40 +02:00
Ante Braovic 7cb20f5a0c updated .gitignore 2016-05-12 16:38:46 +02:00
Ante Braovic 62c547601c added zrevrangebylex in the list of available methods 2016-05-12 16:27:17 +02:00
Daniele Alessandri f8b3f18abf Implement ROLE for proper redis-sentinel support.
Once the client discovers the address of the master or a slave instance,
it must connect to that node and issue a ROLE command to verify that its
role still matches what the client got from the sentinel server.
2016-05-11 10:52:09 +02:00
Daniele Alessandri 0ec9f3351f Cap the number of retries on connection failure.
I think it is better to have a default limit to the number of attempts
when trying to send a command after a connection failure, I am just not
sure if 20 is a good value but we can adjust it later.
2016-05-10 17:36:30 +02:00
Daniele Alessandri 622e6c6ba2 Apply fixes on error responses from sentinel.
Fixes manually picked from @djagya's fork, thanks for spotting!
2016-05-10 17:09:37 +02:00
Daniele Alessandri bc212ad2b0 Update phpdoc. 2016-05-10 15:29:17 +02:00
Daniele Alessandri e896d89fa8 Merge remote-tracking branch 'github/pr/306' into v1.1-sentinel 2016-05-10 15:19:21 +02:00
Daniele Alessandri e851aaa21d Automatically assign aliases to slave connections. 2016-05-10 14:55:30 +02:00
Daniele Alessandri 7664f1f29b Improve client configuration for redis-sentinel.
Predis\Client now requires a list of connection parameters pointing to
sentinel instances and mandatory options "replication" and "service" set
respectively to "sentinel" and the chosen name for the master instance.

  $sentinels = ['tcp://127.0.0.1:5381', 'tcp://127.0.0.1:5382'];
  $options   = ['replication' => 'sentinel', 'service' => 'mymaster'];
  $client    = new Predis\Client($sentinels, $options);

Despite being nice and clean on the outside I am not really fond of the
code being used internally to make this kind of configuration possible.
Improvements in this respect would require a few breaking changes (not
even an option for a minor release) so things will change for the good
with Predis 2.0.
2016-05-10 14:19:05 +02:00
Daniele Alessandri 104e42cfe0 [tests] Fix test executed on current Redis unstable. 2016-05-10 10:58:46 +02:00
Daniele Alessandri b6a14cda04 [tests] Fix test executed on current Redis unstable. 2016-05-10 10:51:43 +02:00
Daniele Alessandri e61cb4e4f2 [tests] Fix wrong @group annotation 2016-05-09 17:49:12 +02:00
Daniele Alessandri 1349666494 Update CHANGELOG.
[ci skip]
2016-05-08 18:56:25 +02:00
Daniele Alessandri a0487ec5f4 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:43:50 +02:00
Remi Collet 57a7d5d3e6 fix tests, list of allowed commands have changed (redis 3.0.6) 2016-05-08 16:52:05 +02:00
Alexander Cheprasov 8235228acd Update ClientInterface.php 2016-05-08 16:46:27 +02:00
John Maguire 3ceaa39a3c Update useClusterNodes() docs to indicate default 2016-05-08 15:21:11 +02:00
Daniele Alessandri 0baad16064 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:16:05 +02:00
Ante Lucic 939c0821dd add docblocks to MasterSlaveReplication 2016-05-05 14:22:32 +02:00
Chris Butler eb154cd43d Replace automatic retry yes/no with a retry limit 2016-01-27 12:35:46 +00:00
Daniele Alessandri d8da74adac Update documents. 2015-08-16 20:25:42 +02:00
Daniele Alessandri fda293b573 Add example of replication configuration using redis-sentinel. 2015-08-16 20:25:42 +02:00
Daniele Alessandri 24fb72a732 Implement ability to fetch an updated list of sentinels from active sentinel.
This can be optionally done automatically but is disabled by default, just use
SentinelReplication::setUpdateSentinels() accordingly to enable the automatic
fetching of an updated list of sentinels.
2015-08-16 20:25:42 +02:00
Daniele Alessandri 8d15503d6c Implement transparent auto-retry of commands upon server failure.
By default, when the current server dies while executing a command Predis asks
for a new configuration to one of the sentinels and re-issues the same command.

This behavior can be disabled calling SentinelReplication::setAutomaticRetry().
2015-08-16 20:25:39 +02:00
Daniele Alessandri 587fbbc446 Specify a timeout for the connect() operation to sentinel servers.
This value should be reasonably low so that the client can fallback to the next
sentinel if the connect() operation is taking too much and slowing things down.

When the connection parameters of sentinels contain a "timeout" parameter, its
value takes the precedence over the default sentinels timeout.
2015-08-16 16:28:21 +02:00
Daniele Alessandri 370d4e72c2 Add initial support for redis-sentinel.
This is a first implementation that is based on the work of @vmattila but some
more changes and missing bits are required in order to be considered complete.

To leverage redis-sentinel the client must be configured using the "aggregate"
option instead of the usual "replication" option, thought this may change for
the release of Predis v1.1.0 (it __will__ change for Predis v2.0.0 but this is
a whole different matter). This is a configuration example:

  use Predis\Connection\Aggregate\SentinelReplication;

  $sentinels = [
    'tcp://127.0.0.1:5381',
    'tcp://127.0.0.1:5382',
    'tcp://127.0.0.1:5383',
  ];

  $client = new Predis\Client($sentinels, [
    'service' => 'nrk-master',
    'aggregate' => function() {
      return function ($sentinels, $options) {
        $service = $option->service;
        $connections = $options->connections;

        return new SentinelReplication($sentinels, $service, $connections);
      };
    },
  ]);

The missing bits right now are:

  - A more solid handling of failures when querying sentinels.
  - When the connection fails while executing a command on one of the servers,
    we should query again a sentinel and then re-issue the command accordingly.
2015-08-16 16:28:13 +02:00
Daniele Alessandri 6bc1a38123 Fix wrong link in phpdoc.
Fixes #270.

[ci skip]
2015-07-30 20:51:15 +02:00
Daniele Alessandri f7d7cd59c4 Update CHANGELOG with release details of v1.0.3.
[ci skip]
2015-07-30 20:38:21 +02:00
Daniele Alessandri d46de81d91 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.
2015-07-30 20:22:37 +02:00
Daniele Alessandri f4b6c578be Update CHANGELOG with release details of v1.0.2.
[ci skip]
2015-07-30 11:21:36 +02:00
Daniele Alessandri 2fc0e56a09 Preserve remainder of path in URI after database (redis scheme). 2015-07-30 11:11:30 +02:00
Daniele Alessandri 8106c8b00a Use PHP_VERSION_ID constant. 2015-07-30 10:57:20 +02:00
Daniele Alessandri b8bfd1405d Update README.
[ci skip]
2015-07-30 10:50:14 +02:00
Daniele Alessandri a7ee80702c 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:01:55 +02:00
Daniele Alessandri 0eaa1d929d Reduce unneeded code duplication in URI parsing. 2015-07-29 17:00:12 +02:00
Daniele Alessandri a26390915d 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 16:44:11 +02:00
Daniele Alessandri 24c0f846c7 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 12:28:44 +02:00
Daniele Alessandri f43433baf1 Add new command: HSTRLEN (Redis 3.2.0).
Also bump the unstable profile version to 3.2.
2015-07-29 11:50:20 +02:00
Daniele Alessandri 3dfe62a5b5 Change format required for URI strings when using "unix" scheme.
Instead of using "unix://" you should just use "unix:":

  $old = 'unix:///path/to/redis.sock';
  $new = 'unix:/path/to/redis.sock';

The old format should be considered obsolete and will not be supported
starting from the next major release of Predis.
Meh
2015-07-28 11:23:33 +02:00
Daniele Alessandri ebb72377bb Implement TLS/SSL-encrypted connections.
This is handy for accessing remote Redis instances over a secure SSL connection
which is currently a popular option or even requirement with many cloud hosting
environments.

In order to configure the client to use an SSL-encrypted connection the scheme
in the connection parameters must be either "tsl" or "rediss" and a set of SSL
options (see http://php.net/manual/en/context.ssl.php) must be provided via the
"ssl" parameter as a named array.

The following example (which does not necessarily represent an example of good
practices!) illustrates how to set the "ssl" parameter using a named array and
the equivalent URI string:

  // Parameters as named array
  $parameters = [
    'scheme' => 'tls',
    'host'   => '127.0.0.1',
    'ssl'    => [
        'cafile'            => '/home/adaniele/redis.pem',
        'verify_peer_name'  => false,
    ],
  ];

  // Parameters as URI string
  $parameters = 'tls://127.0.0.1?ssl[cafile]=redis.pem&ssl[verify_peer_name]=1';

Support for SSL is currently limited to the Predis\Connection\StreamConnection
backend but we intend to investigate if it is possible to extend this feature
to Predis\Connection\PhpiredisStreamConnection in the future.

Be aware that using encrypted connections may lead to a performance degradation
especially in the connect() operation due to the overhead of the TLS handshake.
Unfortunately there is no real way to reuse SSL sessions from userland, aside
from enabling persistent connections, but this will work only on PHP >= 7.0.0
because previous versions of PHP do not provide enough info about a stream from
get_stream_meta_data().

NOTE: Redis does not have built-in support for SSL-encrypted connections, but if
you want to expose it to public networks you may want to rely on "stunnel".
2015-07-27 19:16:20 +02:00
Daniele Alessandri cb91ad1aee Rephrase latest entries of the CHANGELOG.
[ci skip]
2015-07-26 22:34:20 +02:00
Daniele Alessandri 5172555009 Rephrase some parts of the README.
[ci skip]
2015-07-26 22:28:58 +02:00
Daniele Alessandri 4dea992cd2 [tests] Fix typo. 2015-07-25 21:58:52 +02:00
Daniele Alessandri 80af1af459 [tests] Require pcntl extension to run blocking PubSub\Consumer test. 2015-07-25 21:56:36 +02:00
Daniele Alessandri abf2ce0bd1 [tests] Exlude persistent connections tests under PHP 5.3.
The get_resource_type() function does not differentiate between normal streams
and persistent streams, so we cannot really test this case.
2015-07-25 21:52:55 +02:00
Daniele Alessandri c7cae66a97 [tests] Improve code-reuse in tests for the Predis\Connection namespace. 2015-07-25 21:32:36 +02:00
Daniele Alessandri 4f30ac6370 Run php-cs-fixer. 2015-07-25 19:13:05 +02:00
Daniele Alessandri 1c8eb7ff6b [tests] Share common test among connection classes. 2015-07-25 19:13:01 +02:00
Daniele Alessandri cb09a7a2b5 [tests] Use @requires annotation. 2015-07-25 18:37:11 +02:00
Daniele Alessandri d69d6c726d Remove Connection\Aggregate\RedisCluster::setDefaultParameters().
The redis-cluster connection relies on a client-initialized connection factory,
so use Connection\Factory::setDefaultParameters() to set the default parameters
that must be applied to new nodes discovered through -MOVED or -ASK responses.
2015-07-25 18:37:11 +02:00
Daniele Alessandri 2b0c8fbb26 Remove "timeout" as a default parameter in Connection\Parameters.
Falling back to a default timeout values should be done by the connection class
as it is an implementation detail that may vary depending on the backend.
2015-07-25 18:37:11 +02:00
Daniele Alessandri 55aab86800 Add support for default connection parameters in Connection\Factory.
These parameters augment the set of user-supplied parameters when creating a new
connection, but they do not override specific parameters when already defined.

An example of self-contained configuration using client options:

  $client = new Predis\Client('tcp://127.0.0.1', [
    'parameters' => [
      'timeout' => 10,
    ],
    'connections' => function ($options) {
      $factory = $options->getDefault('connections');
      $factory->setDefaultParameters($options->parameters);

      return $factory;
    },
  ]);

This change will be useful for both redis-cluster and redis-sentinel as it makes
it easy to apply shared parameters such as a common password for authentication
when the server returns one ore more new nodes from response (think of -MOVED).
2015-07-25 18:37:11 +02:00
Daniele Alessandri 6cce9eb35c Run php-cs-fixer. 2015-07-25 18:37:10 +02:00
Daniele Alessandri 8277afc7a8 Use "persistent" with non-bool strings to open different persistent connections.
stream_socket_client() has the undocumented ability to open different persistent
streams by providing a path in the $address string. Previously we supported this
behaviour with a combination of "persistent" and "path" (see #139) but this can
be confusing, especially now that we support the redis:// scheme which uses the
path part of an URI string to specify a database number.

After this change, instead of using an URI string such as:

  $parameters = 'tcp://127.0.0.1/first?persistent=1&database=5';

You should use the following ones:

  $parameters = 'tcp://127.0.0.1?persistent=first&database=5';
  $parameters = 'redis://127.0.0.1/5?persistent=first';

Avoiding "path" makes even more sense when using array connection parameters:

  $parameters = [
    'host'       => '127.0.0.1',
    'database'   => 5,
    'persistent' => 'first',
  ]

This feature is not supported when using UNIX domain sockets because the path
trick of stream_socket_client() does not play well with the actual path of the
socket file. The client will throw an InvalidArgumentException exception to
notify the user.

NOTE: unfortunately we have to disable the tests for persistent connections when
running under HHVM due to a bug in their implementation of get_resource_type()
preventing us to recognize a persistent stream from userland code.
2015-07-25 18:34:51 +02:00
Daniele Alessandri b012af3247 Reorganize stream resource creation for stream-based connections. 2015-07-25 14:39:05 +02:00
Daniele Alessandri 7fa3c55f51 Move assertion for connection parameters out of abstract connection class.
Each connection class should implement its own checks for connection parameters,
even at the cost of some code duplication (inheritance is not just about code
reuse after all).
2015-07-25 14:37:22 +02:00
Daniele Alessandri 73c9b9d5be [tests] Fix some tests methods names. 2015-07-25 10:04:27 +02:00
Daniele Alessandri 59798e1f9e [tests] Apply small changes to commands tests with expirations. 2015-07-25 10:04:24 +02:00
Daniele Alessandri d0012e67ff [tests] Cover SET modifiers EX, PX, NX|XX (Redis >= 2.6.12). 2015-07-25 09:10:07 +02:00
Daniele Alessandri 65727ca07d Update .gitattributes file.
[ci skip]
2015-07-24 23:30:21 +02:00
Daniele Alessandri 4ac1a81aa4 Restore alignment for a few equals symbols. 2015-07-24 23:17:05 +02:00
Daniele Alessandri 8dd9893a2f Run php-cs-fixer with new configuration. 2015-07-24 23:17:02 +02:00
Daniele Alessandri 35e97967fd Add .php_cs configuration file for php-cs-fixer. 2015-07-24 23:16:19 +02:00
Daniele Alessandri 7282ca2b52 Remove unneeded "use" imports. 2015-07-24 21:25:40 +02:00
Daniele Alessandri c436e01353 Fix cluster strategy to handle variadic EXISTS (Redis >= 3.0.3). 2015-07-24 18:30:00 +02:00
Daniele Alessandri df2e9f4e71 Fix prefix processor to handle variadic EXISTS (Redis >= 3.0.3). 2015-07-24 18:29:51 +02:00
Daniele Alessandri 6590c44a27 Run php-cs-fixer against codebase in src/ and tests/. 2015-07-24 18:04:42 +02:00
Daniele Alessandri bb89cf67e2 [tests] Fix me being stupid here. 2015-07-24 17:35:27 +02:00
Daniele Alessandri 7c1d324f30 [tests] Apply some fixes and improvements and remove old stuff. 2015-07-24 17:26:06 +02:00
Daniele Alessandri bb6287f22d [tests] Fix obviously wrong annotation. 2015-07-24 16:20:50 +02:00
Daniele Alessandri f315404649 Update CHANGELOG. 2015-07-24 16:06:31 +02:00
Daniele Alessandri 8c76bd6761 Do not parse response to SETNX into boolean value. 2015-07-24 15:52:59 +02:00
Daniele Alessandri 38fafb22ba Do not parse response to MSETNX into boolean value. 2015-07-24 15:52:53 +02:00
Daniele Alessandri 5ce683be09 Do not parse response to SMOVE into boolean value. 2015-07-24 15:52:50 +02:00
Daniele Alessandri e531d39f53 Do not parse response to SISMEMBER into boolean value. 2015-07-24 15:52:45 +02:00
Daniele Alessandri fda9f022dd Do not parse response to RENAMENX into boolean value. 2015-07-24 15:52:27 +02:00
Daniele Alessandri 44e92d8a2f Do not parse response to PERSIST into boolean value. 2015-07-24 15:50:43 +02:00
Daniele Alessandri e75b69f6fd Do not parse response to MOVE into boolean value. 2015-07-24 15:50:38 +02:00
Daniele Alessandri 082c51146a Do not parse response to EXPIREAT into boolean value. 2015-07-24 15:50:34 +02:00
Daniele Alessandri b02e3f4911 Do not parse response to EXPIRE into boolean value. 2015-07-24 15:50:21 +02:00
Daniele Alessandri 87921f7a05 Do not parse response to PFADD into boolean value. 2015-07-24 15:50:10 +02:00
Daniele Alessandri 3fdfc50683 Do not parse response to HSETNX into boolean value. 2015-07-24 15:50:05 +02:00
Daniele Alessandri ae26ecc8bf Do not parse response to HSET into boolean value. 2015-07-24 15:50:01 +02:00
Daniele Alessandri 231a3440f7 Do not parse response to HEXISTS into boolean value. 2015-07-24 15:49:55 +02:00
Daniele Alessandri ab20c52115 Do not parse response to EXISTS into boolean value.
Starting with Redis 3.0.3 the EXISTS command is variadic so that it is
possible to check for the existence of multiple keys in one request,
with the server returning the number of keys found.

This change could break codebases relying on strict comparison (===)
against a boolean value, but just doing $redis->exists('key') == TRUE
is totally fine.
2015-07-24 15:49:40 +02:00
Daniele Alessandri 2eaf3d8595 Remove useless if condition. 2015-07-24 12:40:08 +02:00
Daniele Alessandri bb909c573d 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:39:06 +02:00
Daniele Alessandri 35f135be30 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:16:05 +02:00
Daniele Alessandri 474f3ddbe7 Add missing command: MIGRATE (Redis 2.6.0).
Fixes #209.
2015-07-23 17:22:53 +02:00
Daniele Alessandri ae054fc7e1 [tests] Fix minor oversight. 2015-07-23 16:46:07 +02:00
Daniele Alessandri 8168def08f 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:15:14 +02:00
Daniele Alessandri 15f3b6030c [tests] Improve assert failure messages for replication stategy tests. 2015-07-23 11:38:49 +02:00
Daniele Alessandri c0d1a74447 Fix missing BITPOS in replication strategy. 2015-07-23 11:30:32 +02:00
Daniele Alessandri 6882d08373 Add missing BITPOS command in key prefix processor.
See #265.
2015-07-23 11:06:57 +02:00
Daniele Alessandri 871d526300 Exclude .php-version file.
[ci skip]
2015-07-19 09:35:52 +02:00
Richard Heelin d84bca131b 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:00 +02:00
Daniele Alessandri 5cd4f41424 [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 17:43:49 +02:00
Daniele Alessandri b3bcf466f5 [tests] Adapt to internal encoding changes for lists in Redis 3.0. 2015-07-07 16:09:20 +02:00
Daniele Alessandri 24d8f68e7f 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:05:56 +02:00
Daniele Alessandri 0a8754f9b6 Merge remote-tracking branch 'github/pr/235' 2015-07-07 14:48:41 +02:00
Daniele Alessandri 614049cd12 [tests] Remove hhvm-nightly from Travis CI configuration.
See https://github.com/travis-ci/travis-ci/issues/3788 for details.
2015-07-07 14:42:10 +02:00
Daniele Alessandri 5911145d9f [tests] It's 7.0 and not 5.7! 2015-07-07 14:40:34 +02:00
Daniele Alessandri 8029faf72a [tests] Add PHP 5.7 to Travis CI configuration. 2015-07-07 13:23:09 +02:00
Daniele Alessandri e2c08cfe88 Minor tweak in .editorconfig file.
[ci skip]
2015-07-06 18:42:41 +02:00
Daniele Alessandri 3b31b91ad8 Commit .editorconfig file.
See http://editorconfig.org for more info about EditorConfig

[ci skip]
2015-07-06 18:39:28 +02:00
Daniele Alessandri 7a984e7b76 Fix wrong phpdoc.
Thanks @Aliance for spotting this oversight.
2015-07-03 12:12:39 +02:00
Daniele Alessandri 33e6ee2e48 [tests] Implement test for #257 to guard against regressions. 2015-07-03 12:01:47 +02:00
Daniele Alessandri c577c20481 Merge remote-tracking branch 'github/pr/258' 2015-07-03 11:05:42 +02:00
Dominic Scheirlinck 6fbe2d5fa3 Don't check for __invoke on non-objects
`method_exists` will cause autoloading for strings. Fixes #257
2015-06-16 15:55:20 +12:00
417 changed files with 12710 additions and 4294 deletions
+17
View File
@@ -0,0 +1,17 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
[*.php]
trim_trailing_whitespace = true
max_line_length = 120
[*.{md,yml}]
indent_size = 2
+8 -1
View File
@@ -1,8 +1,15 @@
* text=auto
/tests export-ignore
/tests/PHPUnit export-ignore
/tests/Predis export-ignore
/tests/bootstrap.php export-ignore
/.github export-ignore
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.php_cs.dist export-ignore
/.travis.yml export-ignore
/phpunit.xml.dist export-ignore
/phpunit.xml.travisci export-ignore
/CHANGELOG.md export-ignore
/CONTRIBUTING.md export-ignore
+2
View File
@@ -0,0 +1,2 @@
github: tillkruss
custom: "https://www.paypal.me/tillkruss"
+31
View File
@@ -0,0 +1,31 @@
---
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
@@ -0,0 +1,20 @@
---
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.
+3
View File
@@ -1,5 +1,8 @@
*.tgz
*.phar
.php-version
.php_cs.cache
.phpunit.result.cache
phpunit.xml
package.xml
composer.lock
+33
View File
@@ -0,0 +1,33 @@
<?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')
);
+27 -16
View File
@@ -1,25 +1,36 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
- hhvm-nightly
branches:
except:
- v0.5
- v0.6
- php5.2_backport
- documentation
services: redis-server
- main
- v2.0
before_install:
- docker run -d --rm -p 127.0.0.1:6379:6379 redis:3
before_script:
- phpenv config-rm xdebug.ini
- composer self-update
- composer install --no-interaction --prefer-source --dev
script:
- vendor/bin/phpunit -c phpunit.xml.travisci
- travis_retry vendor/bin/phpunit -c phpunit.xml.travisci
matrix:
allow_failures:
- php: hhvm
- php: hhvm-nightly
fast_finish: true
include:
- php: 5.3
dist: precise
services: redis-server
before_install: skip
- php: 5.4
dist: trusty
- php: 5.5
dist: trusty
- php: 5.6
- php: 7.0
- php: 7.1
- php: 7.2
- php: 7.3
- php: 7.4
- php: 8.0
+276
View File
@@ -1,3 +1,279 @@
v1.1.10 (2022-01-05)
================================================================================
- __FIX__: Avoid PHP 8.1 deprecation notices in `Session/Handler`
- __FIX__: Fixed "Cannot use object of type Predis\Response\Error as array"
error in `Connection/Aggregate/SentinelReplication`
v1.1.9 (2021-10-05)
================================================================================
- __FIX__: Fixed PHP 8.1 compatibility in `StreamConnection`
v1.1.8 (2021-09-29)
================================================================================
- __FIX__: Ensure compatibility with PHP 8.1.
v1.1.7 (2021-04-04)
================================================================================
- __FIX__: with the phpiredis-based connection backends, failed pipelines led to
spurious responses returned after reconnecting to Redis because the underlying
reader was not properly reset by discarding buffered replies after disconnecting
(ISSUE #363).
- __FIX__: removed `cweagans/composer-patches` dev dependency and apply patches
using `post-update-cmd` script.
v1.1.6 (2020-09-11)
================================================================================
- __FIX__: reverted support for sentinels authentication implemented in v1.1.5
as it was bugged (see ISSUE #658), sorry for the trouble. This is now postponed
as it requires a more thorough investigation.
v1.1.5 (2020-09-10)
================================================================================
- __FIX__:~~authentication for sentinels is now supported, previously it was not
possible to specify a `password` for sentinels as its value was stripped during
initialization because sentinels did not support authentication until Redis 5.
**Please note** that with the current implementation each sentinel must have
its own `password` parameter set in the parameters list despite this password is
the same for all sentinels (read how `requirepass` works on the Redis docs). In
this case you should avoid using the global `parameters` client option used to
set default parameters for every connection created by Predis as this would end
up using the same password even when connecting to actual Redis nodes.~~
- __FIX__: the username is now correctly retrieved from the userinfo fragment of
the URI when using the "redis" scheme and a "username:password" pair is present.
Values retrieved from the userinfo fragment always override the ones specified
in `username` and `password` if those fields are present in the query string.
- __FIX__: `Predis\Connection\WebdisConnection` was unable to connect to Webdis
when using an IPv4 address in the URL and this is probably due to some change in
cURL internals since the last time we tested it.
- __FIX__: an exception is thrown whe passing `FALSE` or any value evaluating to
`FALSE` to the `replication` client option. This was supposed to be unsupported,
in fact it actually breaks client initialization and raises a PHP warning. Now
the user is alerted with an `InvalidArgumentException` and a proper message.
(PR #381).
v1.1.4 (2020-08-31)
================================================================================
- Improved @method annotations for methods responding to Redis commands defined
by `Predis\ClientInterface` and `Predis\ClientContextInterface`. (PR #456 and
PR #497, other fixes applied after further analysys).
- __FIX__: the client can now handle ACL authentication when connecting to Redis
6.x simply by passing both `username` and `password` to connection parameters.
See [the Redis docs](https://redis.io/topics/acl) for details on this topic.
- __FIX__: NULL or zero-length string values passed to `password` and `database`
in the connection parameters list do not trigger spurious `AUTH` and `SELECT`
commands anymore when connecting to Redis (ISSUE #436).
- __FIX__: initializing an iteration over a client instance when it is connected
to a standalone Redis server will not throw an exception anymore, instead it
will return an iterator that will run for just one loop returning a new client
instance using the underlying single-node connection (ISSUE #552, PR #556).
- __FIX__: `Predis\Cluster\Distributor\HashRingaddNodeToRing()` was calculating
the hash required for distribution by using `crc32()` directly instead of the
method `Predis\Cluster\Hash\HashGeneratorInterface::hash()` implemented by the
class itself. This bug fix does not have any impact on existing clusters that
use client-side sharding based on this distributor simply because it does not
take any external hash generators so distribution is not going to be affected.
- __FIX__: `SORT` now always trigger a switch to the master node in replication
configurations instead of just when the `STORE` modifier is specified, this is
because `SORT` is always considered to be a write operation and actually fails
with a `-READONLY` error response when executed against a replica node. (ISSUE
#554).
v1.1.3 (2020-08-18)
================================================================================
- Ensure compatibility with PHP 8.
- Moved repository from `github.com/nrk/predis` to `github.com/predis/predis`.
- __FIX__: Moved `cweagans/composer-patches` dependency to `require-dev`.
- __FIX__: Include PHPUnit `.patch` files in exports.
v1.1.2 (2020-08-11)
================================================================================
- __FIX__: pure CRC16 implementation failed to calculate the correct hash when
the input value passed to the `hash()` method is an integer (PR #450).
- __FIX__: make PHP iterator abstractions for `ZSCAN` and `HSCAN` working with
PHP 7.2 due to a breaking change, namely the removal of `each()` (PR #448).
v1.1.1 (2016-06-16)
================================================================================
- __FIX__: `password` and `database` from the global `parameters` client option
were still being applied to sentinels connections making them fail (sentinels
do not understand the `AUTH` and `SELECT` commands) (PR #346).
- __FIX__: when a sentinel instance reports no sentinel for a service, invoking
`connect()` on the redis-sentinel connection backend should fall back to the
master connection instead of failing (ISSUE #342).
- __FIX__: the two connection backends based on ext-phpiredis has some kind of
issues with the GC and the internal use of closures as reader callbacks that
prevented connections going out of scope from being properly collected and the
underlying stream or socket resources from being closed and freed. This should
not have had any actual effect in real-world scenarios due to the lifecycle of
PHP scripts, but we fixed it anyway (ISSUE #345).
v1.1.0 (2016-06-02)
================================================================================
- The default server profile for the client now targets Redis 3.2.
- Responses to the following commands are not casted into booleans anymore, the
original integer value is returned: `SETNX`, `MSETNX`, `SMOVE`, `SISMEMBER`,
`HSET`, `HSETNX`, `HEXISTS`, `PFADD`, `EXISTS`, `MOVE`, `PERSIST`, `EXPIRE`,
`EXPIREAT`, `RENAMENX`. This change does not have a significant impact unless
when using strict comparisons (=== and !==) the returned value.
- Non-boolean string values passed to the `persistent` connection parameter can
be used to create different persistent connections. Note that this feature was
already present in Predis but required both `persistent` and `path` to be set
as illustrated by [#139](https://github.com/nrk/predis/pull/139). This change
is needed to prevent confusion with how `path` is used to select a database
when using the `redis` scheme.
- The client throws exceptions when Redis returns any kind of error response to
initialization commands (the ones being automatically sent when a connection
is established, such as `SELECT` and `AUTH` when database and password are set
in connection parameters) regardless of the value of the exception option.
- Using `unix:///path/to/socket` in URI strings to specify a UNIX domain socket
file is now deprecated in favor of the format `unix:/path/to/socket` (note the
lack of the double slash after the scheme) and will not be supported starting
with the next major release.
- Implemented full support for redis-sentinel.
- Implemented the ability to specify default connection parameters for aggregate
connections with the new `parameters` client option. These parameters augment
the usual user-supplied connection parameters (but do not take the precedence
over them) when creating new connections and they are mostly useful when the
client is using aggregate connections such as redis-cluster and redis-sentinel
as these backends can create new connections on the fly based on responses and
redirections from Redis.
- Redis servers protected by SSL-encrypted connections can be accessed by using
the `tls` or `rediss` scheme in connection parameters along with SSL-specific
options in the `ssl` parameter (see http://php.net/manual/context.ssl.php).
- `Predis\Client` implements `IteratorAggregate` making it possible to iterate
over traversable aggregate connections and get a new client instance for each
Redis node.
- Iterating over an instance of `Predis\Connection\Aggregate\RedisCluster` will
return all the connections mapped in the slots map instead of just the ones in
the pool. This change makes it possible, when the slots map is retrieved from
Redis, to iterate over all of the master nodes in the cluster. When the use of
`CLUSTER SLOTS` is disabled via the `useClusterSlots()` method, the iteration
returns only the connections with slots ranges associated in their parameters
or the ones initialized by `-MOVED` responses in order to make the behaviour
of the iteration consistent between the two modes of operation.
- Various improvements to `Predis\Connection\Aggregate\MasterSlaveReplication`
(the "basic" replication backend, not the new one based on redis-sentinel):
- When the client is not able to send a read-only command to a slave because
the current connection fails or the slave is resyncing (`-LOADING` response
returned by Redis), the backend discards the failed connection and performs
a new attempt on the next slave. When no other slave is available the master
server is used for read-only commands as last resort.
- It is possible to discover the current replication configuration on the fly
by invoking the `discover()` method which internally relies on the output of
the command `INFO REPLICATION` executed against the master server or one of
the slaves. The backend can also be configured to do this automatically when
it fails to reach one of the servers.
- Implemented the `switchToMaster()` and `switchToSlave()` methods to make it
easier to force a switch to the master server or a random slave when needed.
v1.0.4 (2016-05-30)
================================================================================
- 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.
- Various improvements in the handling of redis-cluster:
- 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.
- 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`.
- __FIX__: broken values returned by `Predis\Collection\Iterator\HashKey` when
iterating hash keys containing integer fields (PR #330, ISSUE #331).
- __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).
v1.0.3 (2015-07-30)
================================================================================
- __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.
v1.0.2 (2015-07-30)
================================================================================
- IPv6 is now fully supported.
- 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).
- Added new or missing commands: `HSTRLEN` (>= 3.2), `ZREVRANGEBYLEX` (>= 2.8)
and `MIGRATE` (>= 2.6).
- 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.
- __FIX__: `Predis\Configuration\Options` must not trigger the autoloader when
option values are strings (ISSUE #257).
- __FIX__: `BITPOS` was not defined in the key-prefix processor (ISSUE #265) and
in the replication strategy.
v1.0.1 (2015-01-02)
================================================================================
+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/nrk/predis/issues)
Bugs or feature requests can be posted on the [GitHub issues](http://github.com/predis/predis/issues)
section of the project.
When reporting bugs, in addition to the obvious description of your issue you __must__ always provide
+9 -1
View File
@@ -18,6 +18,14 @@ 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
@@ -25,7 +33,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/nrk/predis/issues/29#issuecomment-1202624).
do that [on this issue](http://github.com/predis/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-2015 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
+213 -131
View File
@@ -1,50 +1,44 @@
# Predis #
[![Latest Stable Version](https://poser.pugx.org/predis/predis/v/stable.png)](https://packagist.org/packages/predis/predis)
[![Total Downloads](https://poser.pugx.org/predis/predis/downloads.png)](https://packagist.org/packages/predis/predis)
[![License](https://poser.pugx.org/predis/predis/license.svg)](https://packagist.org/packages/predis/predis)
[![Build Status](https://travis-ci.org/nrk/predis.svg?branch=master)](https://travis-ci.org/nrk/predis)
[![HHVM Status](http://hhvm.h4cc.de/badge/predis/predis.png)](http://hhvm.h4cc.de/package/predis/predis)
[![Software license][ico-license]](LICENSE)
[![Latest stable][ico-version-stable]][link-packagist]
[![Latest development][ico-version-dev]][link-packagist]
[![Monthly installs][ico-downloads-monthly]][link-downloads]
[![Build status][ico-travis]][link-travis]
Predis is a flexible and feature-complete [Redis](http://redis.io) client library for PHP >= 5.3.
Flexible and feature-complete [Redis](http://redis.io) client for PHP >= 5.3 and HHVM >= 2.3.0.
By default Predis does not require any additional C extension, but it can be optionally paired with
[phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of serializing and parsing the
Redis protocol. An asynchronous implementation of the client, albeit experimental, is also available
through [Predis\Async](https://github.com/nrk/predis-async).
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).
Predis can be used with [HHVM](http://www.hhvm.com) >= 2.3.0, but there are no guarantees you will
not run into unexpected issues (especially when the JIT compiler is enabled via `Eval.Jit = true`)
due to HHVM being still under heavy development, thus unstable and not yet 100% compatible with PHP.
More details about the project can be found in our [frequently asked questions](FAQ.md) section or
on the online [wiki](https://github.com/nrk/predis/wiki).
More details about this project can be found on the [frequently asked questions](FAQ.md).
## Main features ##
- Wide range of Redis versions supported (from __2.0__ to __3.0__ and __unstable__) using profiles.
- Cluster of nodes via client-side sharding using consistent hashing or custom distributors.
- Smart support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
- Support for master-slave replication configurations (write on master, read from slaves).
- Transparent key prefixing for all known Redis commands using a customizable prefixing strategy.
- Command pipelining (works on both single nodes and aggregate connections).
- Abstraction for Redis transactions (Redis >= 2.0) supporting CAS operations (Redis >= 2.2).
- Abstraction for Lua scripting (Redis >= 2.6) with automatic switching between `EVALSHA` or `EVAL`.
- 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).
- 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 to Redis are established lazily by the client upon the first command.
- Support for both TCP/IP and UNIX domain sockets and persistent connections.
- Support for [Webdis](http://webd.is) (both `ext-curl` and `ext-phpiredis` are needed).
- 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.
- 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 and registering custom sets of supported commands or profiles.
- Flexible system for defining custom commands and profiles and override the default ones.
## How to use Predis ##
## How to _install_ and use Predis ##
Predis is available on [Packagist](http://packagist.org/packages/predis/predis) which allows a quick
installation using [Composer](http://packagist.org/about-composer). Alternatively, the library can
be found on our [own PEAR channel](http://pear.nrk.io) for a more traditional installation via PEAR.
Ultimately, archives of each release are [available on GitHub](https://github.com/nrk/predis/tags).
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) 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/predis/predis/releases).
### Loading the library ###
@@ -52,7 +46,7 @@ Ultimately, archives of each release are [available on GitHub](https://github.co
Predis relies on the autoloading features of PHP to load its files when needed and complies with the
[PSR-4 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md).
Autoloading is handled automatically when dependencies are managed through Composer, but it is also
possible to leverage its own autoloader in projects or scripts not having any autoload facility:
possible to leverage its own autoloader in projects or scripts lacking any autoload facility:
```php
// Prepend a base path if Predis is not available in your "include_path".
@@ -61,17 +55,15 @@ require 'Predis/Autoloader.php';
Predis\Autoloader::register();
```
It is possible to easily create a [phar](http://www.php.net/manual/en/intro.phar.php) archive from
the repository just by launching `bin/create-phar`. The generated phar contains a stub defining an
autoloader function for Predis, so you just need to require the phar to start using the library.
Alternatively it is possible to generate one single PHP file holding every class like older versions
of Predis by launching `bin/create-single-file`, but this practice __is not__ encouraged.
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 ###
When not specifying any connection parameter to create a new client, Predis assumes `127.0.0.1` and
`6379` as the default host and port with a connection timeout of 5 seconds:
When creating a client instance without passing any connection parameter, Predis assumes `127.0.0.1`
and `6379` as default host and port. The default timeout for the `connect()` operation is 5 seconds:
```php
$client = new Predis\Client();
@@ -79,22 +71,57 @@ $client->set('foo', 'bar');
$value = $client->get('foo');
```
Connection parameters can be supplied either in the form of URI strings or named arrays. While the
latter is the preferred way to supply parameters, URI strings can be useful for quick configurations
or when parameters are read from a non-structured source:
Connection parameters can be supplied either in the form of URI strings or named arrays. The latter
is the preferred way to supply parameters, but URI strings can be useful when parameters are read
from non-structured or partially-structured sources:
```php
// Named array of connection parameters:
// Parameters passed using a named array:
$client = new Predis\Client([
'scheme' => 'tcp',
'host' => '10.0.0.1',
'port' => 6379,
]);
// Same set of parameters, but using an URI string:
// Same set of parameters, passed using an URI string:
$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');
```
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.
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.
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:
@@ -106,68 +133,133 @@ $client = new Predis\Client([
]);
```
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.
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 ###
Various aspects of the client can be configured simply by passing options to the second argument of
`Predis\Client::__construct()`:
Many aspects and behaviors of the client can be configured by passing specific client options to the
second argument of `Predis\Client::__construct()`:
```php
$client = new \Predis\Client(
$connection_parameters,
['profile' => '2.8', 'prefix' => 'sample:']
);
$client = new Predis\Client($parameters, ['profile' => '2.8', 'prefix' => 'sample:']);
```
Options are managed using a mini DI-alike container and their values
are usually lazily initialized only when needed. Predis by default supports the following options:
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:
- `profile`: which profile to use in order to match a specific version of Redis.
- `prefix`: a prefix string that is automatically applied to keys found in commands.
- `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`: connection backends or a connection factory to be used by the client.
- `cluster`: which backend to use for clustering (`predis`, `redis` or custom configuration).
- `replication`: which backend to use for replication (predis or custom configuration).
- `aggregate`: custom connections aggregator (overrides both `cluster` and `replication`).
- `connections`: list of connection backends or a connection factory instance.
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable object).
- `replication`: specifies a replication backend (`TRUE`, `sentinel` or callable object).
- `aggregate`: overrides `cluster` and `replication` to provide a custom connections aggregator.
- `parameters`: list of default connection parameters for aggregate connections.
Users can provide custom options with their values or lazy callable initializers that are stored in
the options container for later use through the library.
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.
### Aggregate connections ###
Predis is able to aggregate multiple connections which is the base for cluster and replication. By
default the client implements a cluster of nodes using either client-side sharding (default) or a
Redis-backed solution using [redis-cluster](http://redis.io/topics/cluster-tutorial).
As for replication, Predis can handle a single-master and multiple-slaves setup by executing read
operations on slaves and switching to the master for write operations. The replication behavior is
fully configurable.
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 when creating a new client instance.
#### Cluster ####
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'];
$client = new Predis\Client($parameters);
```
Along with Redis 3.0, a new supervised and coordinated type of clustering was introduced in the form
of [redis-cluster](http://redis.io/topics/cluster-tutorial). This kind of approach uses a different
algorithm to distribute the keyspaces, with Redis nodes coordinating themselves by communicating via
a gossip protocol to handle health status, rebalancing, nodes discovery and request redirection. In
order to connect to a cluster managed by redis-cluster, the client requires a list of its nodes (not
necessarily complete since it will automatically discover new nodes if necessary) and the `cluster`
client options set to `redis`:
```php
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
$options = ['cluster' => 'redis'];
$client = new Predis\Client($parameters, $options);
```
#### Replication ####
The client can be configured to work in a master / slave replication setup by executing read-only
commands on slave nodes and automatically switch to the master node as soon as a command performing
a write operation is executed. This is the basic configuration needed to work with replication:
The client can be configured to operate in a single master / multiple slaves setup to provide better
service availability. When using replication, Predis recognizes read-only commands and sends them to
a random slave in order to provide some sort of load-balancing and switches to the master as soon as
it detects a command that performs any kind of operation that would end up modifying the keyspace or
the value of a key. Instead of raising a connection error when a slave fails, the client attempts to
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 using the `alias` parameter set
to `master`) and one or more servers acting as slaves:
```php
// Parameters require one master node specifically marked with `alias=master`.
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2?alias=slave-01'];
$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);
```
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 is not
able to tell when a Lua script is safe to be executed on slaves. While this is the default behavior,
when certain Lua scripts do not perform write operations it is possible to provide an hint to tell
the client to stick with slaves for their execution.
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
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2?alias=slave-01'];
$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,
when certain Lua scripts do not perform write operations it is possible to provide an hint to tell
the client to stick with slaves for their execution:
```php
$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();
@@ -181,29 +273,8 @@ $client->eval($LUA_SCRIPT, 0); // Sticks to slave using `eval`...
$client->evalsha(sha1($LUA_SCRIPT), 0); // ... and `evalsha`, too.
```
The `examples` directory contains two complete scripts showing how replication can be configured for
[simple](examples/replication_simple.php) or [complex](examples/replication_complex.php) scenarios.
#### Cluster ####
Simply passing an array of connection parameters to the client constructor configures Predis to work
in cluster mode using client-side sharding. If you, on the other hand, want to leverage Redis >= 3.0
nodes coordinated by redis-cluster, then the client must be initialized like this:
```php
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2'];
$options = ['cluster' => 'redis'];
$client = new Predis\Client($parameters, $options);
```
When using redis-cluster, it is not necessary to pass all of the nodes that compose your cluster but
you can simply specify only a few nodes: Predis will automatically fetch the full and updated slots
map directly from Redis by contacting one of the servers.
__NOTE__: our support for redis-cluster does not currently consider master / slave replication but
this feature will be added in a future release of this library.
The [`examples`](examples/) directory contains a few scripts that demonstrate how the client can be
configured and used to leverage replication in both basic and complex scenarios.
### Command pipelines ###
@@ -214,7 +285,7 @@ The client can execute the pipeline inside a callable block or return a pipeline
ability to chain commands thanks to its fluent interface:
```php
// Executes a pipeline inside a given callable block:
// Executes a pipeline inside the given callable block:
$responses = $client->pipeline(function ($pipe) {
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
@@ -222,7 +293,7 @@ $responses = $client->pipeline(function ($pipe) {
}
});
// Returns a pipeline instance with fluent interface:
// Returns a pipeline that can be chained thanks to its fluent interface:
$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute();
```
@@ -233,13 +304,13 @@ The client provides an abstraction for Redis transactions based on `MULTI` and `
interface to command pipelines:
```php
// Executes a transaction inside a given callable block:
// Executes a transaction inside the given callable block:
$responses = $client->transaction(function ($tx) {
$tx->set('foo', 'bar');
$tx->get('foo');
});
// Returns a transaction instance with fluent interface:
// Returns a transaction that can be chained thanks to its fluent interface:
$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute();
```
@@ -251,9 +322,9 @@ of a transaction using CAS you can see [the following example](examples/transact
### Adding new commands ###
While we try to update Predis to stay up to date with all the commands available in Redis, you might
prefer to stick with an older version of the library or provide a different way to filter arguments
or parse responses for specific commands. To achieve that, Predis provides the ability to implement
new command classes to define or override commands in the server profiles used by the client:
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 server profiles used by the client:
```php
// Define a new command by extending Predis\Command\Command:
@@ -273,8 +344,8 @@ $response = $client->newcmd();
```
There is also a method to send raw commands without filtering their arguments or parsing responses.
Users must provide the arguments list as an array, following the command signatures as defined by
the [Redis documentation for commands](http://redis.io/commands):
Users must provide the list of arguments for the command as an array, following the signatures as
defined by the [Redis documentation for commands](http://redis.io/commands):
```php
$response = $client->executeRaw(['SET', 'foo', 'bar']);
@@ -284,16 +355,16 @@ $response = $client->executeRaw(['SET', 'foo', 'bar']);
### Script commands ###
While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using
[`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha), Predis
offers script commands as an higher level abstraction aiming to make things simple. 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) is used as a fall
back when needed:
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 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)
is used as a fall back when needed:
```php
// Define a new scriptable command by extending Predis\Command\ScriptCommand:
// Define a new script command by extending Predis\Command\ScriptCommand:
class ListPushRandomValue extends Predis\Command\ScriptCommand
{
public function getKeysCount()
@@ -312,7 +383,7 @@ LUA;
}
}
// Inject your script command in the current profile:
// Inject the script command in the current profile:
$client = new Predis\Client();
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');
@@ -325,19 +396,19 @@ $response = $client->lpushrand('random_values', $seed = mt_rand());
Predis can use different connection backends to connect to Redis. Two of them leverage a third party
extension such as [phpiredis](https://github.com/nrk/phpiredis) resulting in major performance gains
especially when dealing with big multibulk responses. While one is based on PHP streams, the other
is based on socket resources provided by `ext-socket`. Both support TCP/IP or UNIX domain sockets:
is based on socket resources provided by `ext-socket`. Both support TCP/IP and UNIX domain sockets:
```php
$client = new Predis\Client('tcp://127.0.0.1', [
'connections' => [
'tcp' => 'Predis\Connection\PhpiredisStreamConnection', // PHP streams
'unix' => 'Predis\Connection\PhpiredisSocketConnection', // ext-socket
'tcp' => 'Predis\Connection\PhpiredisStreamConnection', // PHP stream resources
'unix' => 'Predis\Connection\PhpiredisSocketConnection', // ext-socket resources
],
]);
```
Developers can create their own connection classes to support whole new network backends, extend
existing ones or provide completely different implementations. Connection classes must implement
existing classes or provide completely different implementations. Connection classes must implement
`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
```php
@@ -376,14 +447,14 @@ Predis has a comprehensive test suite covering every aspect of the library. This
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
By default the test suite is configured to execute integration tests using the profile for Redis 3.2
(which is the current stable version of Redis) but can optionally target a Redis instance built from
the `unstable` branch by modifying `phpunit.xml` and setting `REDIS_SERVER_VERSION` to `dev` so that
the development server profile will be used. You can refer to [the tests README](tests/README.md)
for more detailed information about testing Predis.
Predis uses Travis CI for continuous integration and the history for past and current builds can be
found [on its project page](http://travis-ci.org/nrk/predis).
found [on its project page](http://travis-ci.org/predis/predis).
## Other ##
@@ -391,9 +462,9 @@ found [on its project page](http://travis-ci.org/nrk/predis).
### Project related links ###
- [Source code](https://github.com/nrk/predis)
- [Wiki](https://wiki.github.com/nrk/predis)
- [Issue tracker](https://github.com/nrk/predis/issues)
- [Source code](https://github.com/predis/predis)
- [Wiki](https://github.com/predis/predis/wiki)
- [Issue tracker](https://github.com/predis/predis/issues)
- [PEAR channel](http://pear.nrk.io)
@@ -405,3 +476,14 @@ found [on its project page](http://travis-ci.org/nrk/predis).
### License ###
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
[ico-license]: https://img.shields.io/github/license/predis/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-travis]: https://img.shields.io/travis/predis/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-travis]: https://travis-ci.org/predis/predis
[link-downloads]: https://packagist.org/packages/predis/predis/stats
+1 -1
View File
@@ -1 +1 @@
1.1.0-dev
1.1.11-dev
+2 -2
View File
@@ -115,7 +115,7 @@ function addPackageFile($pkg, $fileinfo, $role, $baseDir = '')
function generatePackageXml($packageINI)
{
$XML = <<<XML
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<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"
@@ -197,7 +197,7 @@ function rewritePackageInstallAs($pkg)
function savePackageXml($xml)
{
$dom = new DOMDocument("1.0");
$dom = new DOMDocument("1.0", "UTF-8");
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
+1 -1
View File
@@ -59,7 +59,7 @@ spl_autoload_register(function (\$class) {
}
}
});
__HALT_COMPILER();
__halt_compiler();
EOSTUB;
}
+5 -5
View File
@@ -19,7 +19,7 @@
// 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.
// The current implementation is pretty naïve, but it should do for now.
// -------------------------------------------------------------------------- //
class CommandLine
@@ -27,10 +27,10 @@ class CommandLine
public static function getOptions()
{
$parameters = array(
's:' => 'source:',
'o:' => 'output:',
'e:' => 'exclude:',
'E:' => 'exclude-classes:',
's:' => 'source:',
'o:' => 'output:',
'e:' => 'exclude:',
'E:' => 'exclude-classes:',
);
$getops = getopt(implode(array_keys($parameters)), $parameters);
+22 -10
View File
@@ -1,36 +1,48 @@
{
"name": "predis/predis",
"type": "library",
"description": "Flexible and feature-complete PHP client library for Redis",
"description": "Flexible and feature-complete Redis client for PHP and HHVM",
"keywords": ["nosql", "redis", "predis"],
"homepage": "http://github.com/nrk/predis",
"homepage": "http://github.com/predis/predis",
"license": "MIT",
"support": {
"issues": "https://github.com/nrk/predis/issues"
"issues": "https://github.com/predis/predis/issues"
},
"authors": [
{
"name": "Daniele Alessandri",
"email": "suppakilla@gmail.com",
"homepage": "http://clorophilla.net"
"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"
}
],
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"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-master": "1.1-dev"
"psr-4": {
"Predis\\": "src/"
}
},
"scripts": {
"post-update-cmd": "@php -f tests/apply-patches.php"
}
}
+3 -3
View File
@@ -15,9 +15,9 @@ require __DIR__.'/shared.php';
// their own distributors used by the client to distribute keys among a cluster
// of servers.
use Predis\Cluster\PredisStrategy;
use Predis\Cluster\Distributor\DistributorInterface;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\Cluster\PredisStrategy;
use Predis\Connection\Aggregate\PredisCluster;
class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
@@ -34,7 +34,7 @@ class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
public function add($node, $weight = null)
{
$this->nodes[] = $node;
$this->nodesCount++;
++$this->nodesCount;
}
public function remove($node)
@@ -99,7 +99,7 @@ $options = array(
$client = new Predis\Client($multiple_servers, $options);
for ($i = 0; $i < 100; $i++) {
for ($i = 0; $i < 100; ++$i) {
$client->set("key:$i", str_pad($i, 4, '0', 0));
$client->get("key:$i");
}
+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
@@ -26,7 +26,7 @@ var_export($response); echo PHP_EOL;
$mkv = array(
'uid:0001' => '1st user',
'uid:0002' => '2nd user',
'uid:0003' => '3rd user'
'uid:0003' => '3rd user',
);
$client->mset($mkv);
@@ -45,7 +45,7 @@ array (
// their arguments are not filtered nor responses coming from Redis are parsed.
$response = $client->executeRaw(array(
'MGET', 'uid:0001', 'uid:0002', 'uid:0003'
'MGET', 'uid:0001', 'uid:0002', 'uid:0003',
));
var_export($response); echo PHP_EOL;
+1 -1
View File
@@ -55,7 +55,7 @@ $client = new Predis\Client($single_server, array(
$profile->defineCommand('increxby', 'IncrementExistingKeysBy');
return $profile;
}
},
));
$client->mset('foo', 10, 'foobar', 100);
+1 -1
View File
@@ -28,7 +28,7 @@ foreach (($monitor = $client->monitor()) as $event) {
// If we notice a ECHO command with the message QUIT_MONITOR, we stop the
// monitor consumer and then break the loop.
if ($event->command === 'ECHO' && $event->arguments === '"QUIT_MONITOR"') {
echo "Exiting the monitor loop...", PHP_EOL;
echo 'Exiting the monitor loop...', PHP_EOL;
$monitor->stop();
break;
}
+1 -1
View File
@@ -36,7 +36,7 @@ foreach ($pubsub as $message) {
case 'message':
if ($message->channel == 'control_channel') {
if ($message->payload == 'quit_loop') {
echo "Aborting pubsub loop...", PHP_EOL;
echo 'Aborting pubsub loop...', PHP_EOL;
$pubsub->unsubscribe();
} else {
echo "Received an unrecognized command: {$message->payload}.", PHP_EOL;
+1 -1
View File
@@ -34,7 +34,7 @@ $client = new Predis\Client($single_server, array('profile' => '2.8'));
// Prepare some keys for our example
$client->del('predis:set', 'predis:zset', 'predis:hash');
for ($i = 0; $i < 5; $i++) {
for ($i = 0; $i < 5; ++$i) {
$client->sadd('predis:set', "member:$i");
$client->zadd('predis:zset', -$i, "member:$i");
$client->hset('predis:hash', "field:$i", "value:$i");
+1 -1
View File
@@ -81,5 +81,5 @@ $hashes = $client->hmgetall('metavars', 'servers');
$replication = $client->getConnection();
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave');
echo "Is still on slave? ", $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL;
echo 'Is still on slave? ', $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL;
var_export($hashes);
+58
View File
@@ -0,0 +1,58 @@
<?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->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->alias}!", PHP_EOL;
// Read operation.
$bar = $client->get('foo');
$current = $client->getConnection()->getCurrent()->getParameters();
echo "We fetched 'foo' from {$current->alias} 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'.
*/
+1 -1
View File
@@ -25,7 +25,7 @@ require __DIR__.'/shared.php';
//
if (!interface_exists('SessionHandlerInterface')) {
die("ATTENTION: the session handler implemented by Predis requires PHP >= 5.4.0 ".
die('ATTENTION: the session handler implemented by Predis requires PHP >= 5.4.0 '.
"or a polyfill for SessionHandlerInterface provided by an external package.\n");
}
+13 -9
View File
@@ -9,6 +9,10 @@
* 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)
@@ -23,22 +27,22 @@ function redis_version($info)
}
$single_server = array(
'host' => '127.0.0.1',
'port' => 6379,
'database' => 15
'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',
),
);
+2 -2
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.
@@ -49,4 +49,4 @@ function zpop($client, $key)
$client = new Predis\Client($single_server);
$zpopped = zpop($client, 'zset');
echo isset($zpopped) ? "ZPOPed $zpopped" : "Nothing to ZPOP!", PHP_EOL;
echo isset($zpopped) ? "ZPOPed $zpopped" : 'Nothing to ZPOP!', PHP_EOL;
+2 -2
View File
@@ -7,10 +7,10 @@
[package]
name = "Predis"
desc = "Flexible and feature-complete PHP client library for Redis"
desc = "Flexible and feature-complete Redis client for PHP and HHVM"
homepage = "http://github.com/nrk/predis"
license = "MIT"
version = "1.1.0"
version = "1.1.11"
stability = "devel"
channel = "pear.nrk.io"
+13 -2
View File
@@ -1,6 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php" colors="true" strict="true">
<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>
@@ -27,7 +38,7 @@
<php>
<!-- Redis -->
<const name="REDIS_SERVER_VERSION" value="2.8" />
<const name="REDIS_SERVER_VERSION" value="3.2" />
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
<const name="REDIS_SERVER_PORT" value="6379" />
<const name="REDIS_SERVER_DBNUM" value="15" />
+13 -6
View File
@@ -1,6 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="tests/bootstrap.php" colors="true" strict="true">
<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>
@@ -25,13 +36,9 @@
</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_VERSION" value="3.2" />
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
<const name="REDIS_SERVER_PORT" value="6379" />
<const name="REDIS_SERVER_DBNUM" value="15" />
+3 -3
View File
@@ -29,7 +29,7 @@ class Autoloader
public function __construct($baseDirectory = __DIR__)
{
$this->directory = $baseDirectory;
$this->prefix = __NAMESPACE__ . '\\';
$this->prefix = __NAMESPACE__.'\\';
$this->prefixLength = strlen($this->prefix);
}
@@ -40,7 +40,7 @@ class Autoloader
*/
public static function register($prepend = false)
{
spl_autoload_register(array(new self, 'autoload'), true, $prepend);
spl_autoload_register(array(new self(), 'autoload'), true, $prepend);
}
/**
@@ -55,7 +55,7 @@ class Autoloader
$filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
if (is_file($filepath)) {
require($filepath);
require $filepath;
}
}
}
+52 -27
View File
@@ -11,15 +11,13 @@
namespace Predis;
use InvalidArgumentException;
use UnexpectedValueException;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\Command\ScriptCommand;
use Predis\Configuration\Options;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\AggregateConnectionInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Connection\ParametersInterface;
use Predis\Monitor\Consumer as MonitorConsumer;
use Predis\Pipeline\Pipeline;
@@ -40,9 +38,9 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class Client implements ClientInterface
class Client implements ClientInterface, \IteratorAggregate
{
const VERSION = '1.1.0-dev';
const VERSION = '1.1.11-dev';
protected $connection;
protected $options;
@@ -66,9 +64,9 @@ class Client implements ClientInterface
*
* @param mixed $options Client options.
*
* @return OptionsInterface
*
* @throws \InvalidArgumentException
*
* @return OptionsInterface
*/
protected function createOptions($options)
{
@@ -80,7 +78,7 @@ class Client implements ClientInterface
return $options;
}
throw new InvalidArgumentException("Invalid type for client options.");
throw new \InvalidArgumentException('Invalid type for client options.');
}
/**
@@ -98,9 +96,9 @@ class Client implements ClientInterface
*
* @param mixed $parameters Connection parameters or connection instance.
*
* @return ConnectionInterface
*
* @throws \InvalidArgumentException
*
* @return ConnectionInterface
*/
protected function createConnection($parameters)
{
@@ -122,13 +120,18 @@ class Client implements ClientInterface
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;
}
} elseif ($options->defined('replication')) {
$replication = $options->replication;
if ($replication instanceof AggregateConnectionInterface) {
$connection = $replication;
$options->connections->aggregate($connection, $parameters);
} else {
$initializer = $this->getConnectionInitializerWrapper($replication);
$connection = $initializer($parameters, $options);
}
} else {
$connection = $options->cluster;
$options->connections->aggregate($connection, $parameters);
}
@@ -142,7 +145,7 @@ class Client implements ClientInterface
return $connection;
}
throw new InvalidArgumentException('Invalid type for connection parameters.');
throw new \InvalidArgumentException('Invalid type for connection parameters.');
}
/**
@@ -159,7 +162,7 @@ class Client implements ClientInterface
$connection = call_user_func_array($callable, func_get_args());
if (!$connection instanceof ConnectionInterface) {
throw new UnexpectedValueException(
throw new \UnexpectedValueException(
'The callable connection initializer returned an invalid type.'
);
}
@@ -191,14 +194,14 @@ class Client implements ClientInterface
*
* @param string $connectionID Identifier of a connection.
*
* @return Client
*
* @throws \InvalidArgumentException
*
* @return Client
*/
public function getClientFor($connectionID)
{
if (!$connection = $this->getConnectionById($connectionID)) {
throw new InvalidArgumentException("Invalid connection ID: $connectionID.");
throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
}
return new static($connection, $this->options);
@@ -255,9 +258,9 @@ class Client implements ClientInterface
*
* @param string $connectionID Index or alias of the single connection.
*
* @return Connection\NodeConnectionInterface
*
* @throws NotSupportedException
*
* @return Connection\NodeConnectionInterface
*/
public function getConnectionById($connectionID)
{
@@ -275,7 +278,7 @@ class Client implements ClientInterface
* applying any prefix to keys or throwing exceptions on Redis errors even
* regardless of client options.
*
* It is possibile to indentify Redis error responses from normal responses
* It is possible to identify 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.
@@ -344,9 +347,9 @@ class Client implements ClientInterface
* @param CommandInterface $command Redis command that generated the error.
* @param ErrorResponseInterface $response Instance of the error response.
*
* @return mixed
*
* @throws ServerException
*
* @return mixed
*/
protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
{
@@ -478,7 +481,7 @@ class Client implements ClientInterface
}
/**
* Creates a new publis/subscribe context and returns it, or starts its loop
* Creates a new publish/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.
@@ -522,4 +525,26 @@ class Client implements ClientInterface
{
return new MonitorConsumer($this);
}
/**
* @return \Traversable<string, static>
*/
#[\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);
}
}
+27 -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 $keys)
* @method $this del(array|string $keys)
* @method $this dump($key)
* @method $this exists($key)
* @method $this expire($key, $seconds)
@@ -38,6 +38,8 @@ 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 decr($key)
* @method $this decrby($key, $decrement)
* @method $this get($key)
@@ -71,15 +73,16 @@ use Predis\Command\CommandInterface;
* @method $this hset($key, $field, $value)
* @method $this hsetnx($key, $field, $value)
* @method $this hvals($key)
* @method $this blpop(array $keys, $timeout)
* @method $this brpop(array $keys, $timeout)
* @method $this hstrlen($key, $field)
* @method $this blpop(array|string $keys, $timeout)
* @method $this brpop(array|string $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, $value)
* @method $this lpushx($key, array $values)
* @method $this lrange($key, $start, $stop)
* @method $this lrem($key, $count, $value)
* @method $this lset($key, $index, $value)
@@ -87,27 +90,27 @@ use Predis\Command\CommandInterface;
* @method $this rpop($key)
* @method $this rpoplpush($source, $destination)
* @method $this rpush($key, array $values)
* @method $this rpushx($key, $value)
* @method $this rpushx($key, array $values)
* @method $this sadd($key, array $members)
* @method $this scard($key)
* @method $this sdiff(array $keys)
* @method $this sdiffstore($destination, array $keys)
* @method $this sinter(array $keys)
* @method $this sinterstore($destination, array $keys)
* @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 sismember($key, $member)
* @method $this smembers($key)
* @method $this smove($source, $destination, $member)
* @method $this spop($key)
* @method $this spop($key, $count = null)
* @method $this srandmember($key, $count = null)
* @method $this srem($key, $member)
* @method $this sscan($key, $cursor, array $options = null)
* @method $this sunion(array $keys)
* @method $this sunionstore($destination, array $keys)
* @method $this sunion(array|string $keys)
* @method $this sunionstore($destination, array|string $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 $keys, array $options = null)
* @method $this zinterstore($destination, array|string $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)
@@ -115,17 +118,18 @@ 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, $min, $max, array $options = null)
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
* @method $this zrevrank($key, $member)
* @method $this zunionstore($destination, array $keys, array $options = null)
* @method $this zunionstore($destination, array|string $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)
* @method $this zrevrangebylex($key, $start, $stop, array $options = null)
* @method $this zremrangebylex($key, $min, $max)
* @method $this zlexcount($key, $min, $max)
* @method $this pfadd($key, array $elements)
* @method $this pfmerge($destinationKey, array $sourceKeys)
* @method $this pfcount(array $keys)
* @method $this pfmerge($destinationKey, array|string $sourceKeys)
* @method $this pfcount(array|string $keys)
* @method $this pubsub($subcommand, $argument)
* @method $this publish($channel, $message)
* @method $this discard()
@@ -154,12 +158,17 @@ use Predis\Command\CommandInterface;
* @method $this slowlog($subcommand, $argument = null)
* @method $this time()
* @method $this command()
* @method $this geoadd($key, $longitude, $latitude, $member)
* @method $this geohash($key, array $members)
* @method $this geopos($key, array $members)
* @method $this geodist($key, $member1, $member2, $unit = null)
* @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
interface ClientContextInterface
{
/**
* Sends the specified command instance to Redis.
*
+148 -138
View File
@@ -24,144 +24,154 @@ use Predis\Profile\ProfileInterface;
* and more friendly interface to ease programming which is described in the
* following list of methods:
*
* @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 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 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)
* @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, $min, $max, 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 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 del(array|string $keys)
* @method string|null 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|null 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|null bitfield($key, $subcommand, ...$subcommandArg)
* @method int bitpos($key, $bit, $start = null, $end = null)
* @method int decr($key)
* @method int decrby($key, $decrement)
* @method string|null get($key)
* @method int getbit($key, $offset)
* @method string getrange($key, $start, $end)
* @method string|null 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|null 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|null blpop(array|string $keys, $timeout)
* @method array|null brpop(array|string $keys, $timeout)
* @method string|null brpoplpush($source, $destination, $timeout)
* @method string|null lindex($key, $index)
* @method int linsert($key, $whence, $pivot, $value)
* @method int llen($key)
* @method string|null lpop($key)
* @method int lpush($key, array $values)
* @method int lpushx($key, array $values)
* @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|null rpop($key)
* @method string|null rpoplpush($source, $destination)
* @method int rpush($key, array $values)
* @method int rpushx($key, array $values)
* @method int sadd($key, array $members)
* @method int scard($key)
* @method array sdiff(array|string $keys)
* @method int sdiffstore($destination, array|string $keys)
* @method array sinter(array|string $keys)
* @method int sinterstore($destination, array|string $keys)
* @method int sismember($key, $member)
* @method array smembers($key)
* @method int smove($source, $destination, $member)
* @method string|null spop($key, $count = null)
* @method string|null srandmember($key, $count = null)
* @method int srem($key, $member)
* @method array sscan($key, $cursor, array $options = null)
* @method array sunion(array|string $keys)
* @method int sunionstore($destination, array|string $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|string $keys, array $options = null)
* @method array zrange($key, $start, $stop, array $options = null)
* @method array zrangebyscore($key, $min, $max, array $options = null)
* @method int|null 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|null zrevrank($key, $member)
* @method int zunionstore($destination, array|string $keys, array $options = null)
* @method string|null 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|string $sourceKeys)
* @method int pfcount(array|string $keys)
* @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($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|null 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>
*/
+181 -110
View File
@@ -11,7 +11,6 @@
namespace Predis\Cluster;
use InvalidArgumentException;
use Predis\Command\CommandInterface;
use Predis\Command\ScriptCommand;
@@ -44,126 +43,136 @@ abstract class ClusterStrategy implements StrategyInterface
return array(
/* commands operating on the key space */
'EXISTS' => $getKeyFromFirstArgument,
'DEL' => $getKeyFromAllArguments,
'TYPE' => $getKeyFromFirstArgument,
'EXPIRE' => $getKeyFromFirstArgument,
'EXPIREAT' => $getKeyFromFirstArgument,
'PERSIST' => $getKeyFromFirstArgument,
'PEXPIRE' => $getKeyFromFirstArgument,
'PEXPIREAT' => $getKeyFromFirstArgument,
'TTL' => $getKeyFromFirstArgument,
'PTTL' => $getKeyFromFirstArgument,
'SORT' => $getKeyFromFirstArgument, // TODO
'DUMP' => $getKeyFromFirstArgument,
'RESTORE' => $getKeyFromFirstArgument,
'EXISTS' => $getKeyFromAllArguments,
'DEL' => $getKeyFromAllArguments,
'TYPE' => $getKeyFromFirstArgument,
'EXPIRE' => $getKeyFromFirstArgument,
'EXPIREAT' => $getKeyFromFirstArgument,
'PERSIST' => $getKeyFromFirstArgument,
'PEXPIRE' => $getKeyFromFirstArgument,
'PEXPIREAT' => $getKeyFromFirstArgument,
'TTL' => $getKeyFromFirstArgument,
'PTTL' => $getKeyFromFirstArgument,
'SORT' => array($this, 'getKeyFromSortCommand'),
'DUMP' => $getKeyFromFirstArgument,
'RESTORE' => $getKeyFromFirstArgument,
/* commands operating on string values */
'APPEND' => $getKeyFromFirstArgument,
'DECR' => $getKeyFromFirstArgument,
'DECRBY' => $getKeyFromFirstArgument,
'GET' => $getKeyFromFirstArgument,
'GETBIT' => $getKeyFromFirstArgument,
'MGET' => $getKeyFromAllArguments,
'SET' => $getKeyFromFirstArgument,
'GETRANGE' => $getKeyFromFirstArgument,
'GETSET' => $getKeyFromFirstArgument,
'INCR' => $getKeyFromFirstArgument,
'INCRBY' => $getKeyFromFirstArgument,
'INCRBYFLOAT' => $getKeyFromFirstArgument,
'SETBIT' => $getKeyFromFirstArgument,
'SETEX' => $getKeyFromFirstArgument,
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
'SETNX' => $getKeyFromFirstArgument,
'SETRANGE' => $getKeyFromFirstArgument,
'STRLEN' => $getKeyFromFirstArgument,
'SUBSTR' => $getKeyFromFirstArgument,
'BITOP' => array($this, 'getKeyFromBitOp'),
'BITCOUNT' => $getKeyFromFirstArgument,
'APPEND' => $getKeyFromFirstArgument,
'DECR' => $getKeyFromFirstArgument,
'DECRBY' => $getKeyFromFirstArgument,
'GET' => $getKeyFromFirstArgument,
'GETBIT' => $getKeyFromFirstArgument,
'MGET' => $getKeyFromAllArguments,
'SET' => $getKeyFromFirstArgument,
'GETRANGE' => $getKeyFromFirstArgument,
'GETSET' => $getKeyFromFirstArgument,
'INCR' => $getKeyFromFirstArgument,
'INCRBY' => $getKeyFromFirstArgument,
'INCRBYFLOAT' => $getKeyFromFirstArgument,
'SETBIT' => $getKeyFromFirstArgument,
'SETEX' => $getKeyFromFirstArgument,
'MSET' => array($this, 'getKeyFromInterleavedArguments'),
'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
'SETNX' => $getKeyFromFirstArgument,
'SETRANGE' => $getKeyFromFirstArgument,
'STRLEN' => $getKeyFromFirstArgument,
'SUBSTR' => $getKeyFromFirstArgument,
'BITOP' => array($this, 'getKeyFromBitOp'),
'BITCOUNT' => $getKeyFromFirstArgument,
'BITFIELD' => $getKeyFromFirstArgument,
/* commands operating on lists */
'LINSERT' => $getKeyFromFirstArgument,
'LINDEX' => $getKeyFromFirstArgument,
'LLEN' => $getKeyFromFirstArgument,
'LPOP' => $getKeyFromFirstArgument,
'RPOP' => $getKeyFromFirstArgument,
'RPOPLPUSH' => $getKeyFromAllArguments,
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'),
'LPUSH' => $getKeyFromFirstArgument,
'LPUSHX' => $getKeyFromFirstArgument,
'RPUSH' => $getKeyFromFirstArgument,
'RPUSHX' => $getKeyFromFirstArgument,
'LRANGE' => $getKeyFromFirstArgument,
'LREM' => $getKeyFromFirstArgument,
'LSET' => $getKeyFromFirstArgument,
'LTRIM' => $getKeyFromFirstArgument,
'LINSERT' => $getKeyFromFirstArgument,
'LINDEX' => $getKeyFromFirstArgument,
'LLEN' => $getKeyFromFirstArgument,
'LPOP' => $getKeyFromFirstArgument,
'RPOP' => $getKeyFromFirstArgument,
'RPOPLPUSH' => $getKeyFromAllArguments,
'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'),
'LPUSH' => $getKeyFromFirstArgument,
'LPUSHX' => $getKeyFromFirstArgument,
'RPUSH' => $getKeyFromFirstArgument,
'RPUSHX' => $getKeyFromFirstArgument,
'LRANGE' => $getKeyFromFirstArgument,
'LREM' => $getKeyFromFirstArgument,
'LSET' => $getKeyFromFirstArgument,
'LTRIM' => $getKeyFromFirstArgument,
/* commands operating on sets */
'SADD' => $getKeyFromFirstArgument,
'SCARD' => $getKeyFromFirstArgument,
'SDIFF' => $getKeyFromAllArguments,
'SDIFFSTORE' => $getKeyFromAllArguments,
'SINTER' => $getKeyFromAllArguments,
'SINTERSTORE' => $getKeyFromAllArguments,
'SUNION' => $getKeyFromAllArguments,
'SUNIONSTORE' => $getKeyFromAllArguments,
'SISMEMBER' => $getKeyFromFirstArgument,
'SMEMBERS' => $getKeyFromFirstArgument,
'SSCAN' => $getKeyFromFirstArgument,
'SPOP' => $getKeyFromFirstArgument,
'SRANDMEMBER' => $getKeyFromFirstArgument,
'SREM' => $getKeyFromFirstArgument,
'SADD' => $getKeyFromFirstArgument,
'SCARD' => $getKeyFromFirstArgument,
'SDIFF' => $getKeyFromAllArguments,
'SDIFFSTORE' => $getKeyFromAllArguments,
'SINTER' => $getKeyFromAllArguments,
'SINTERSTORE' => $getKeyFromAllArguments,
'SUNION' => $getKeyFromAllArguments,
'SUNIONSTORE' => $getKeyFromAllArguments,
'SISMEMBER' => $getKeyFromFirstArgument,
'SMEMBERS' => $getKeyFromFirstArgument,
'SSCAN' => $getKeyFromFirstArgument,
'SPOP' => $getKeyFromFirstArgument,
'SRANDMEMBER' => $getKeyFromFirstArgument,
'SREM' => $getKeyFromFirstArgument,
/* commands operating on sorted sets */
'ZADD' => $getKeyFromFirstArgument,
'ZCARD' => $getKeyFromFirstArgument,
'ZCOUNT' => $getKeyFromFirstArgument,
'ZINCRBY' => $getKeyFromFirstArgument,
'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
'ZRANGE' => $getKeyFromFirstArgument,
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZRANK' => $getKeyFromFirstArgument,
'ZREM' => $getKeyFromFirstArgument,
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANGE' => $getKeyFromFirstArgument,
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANK' => $getKeyFromFirstArgument,
'ZSCORE' => $getKeyFromFirstArgument,
'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
'ZSCAN' => $getKeyFromFirstArgument,
'ZLEXCOUNT' => $getKeyFromFirstArgument,
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
'ZREVRANGEBYLEX' => $getKeyFromFirstArgument,
'ZADD' => $getKeyFromFirstArgument,
'ZCARD' => $getKeyFromFirstArgument,
'ZCOUNT' => $getKeyFromFirstArgument,
'ZINCRBY' => $getKeyFromFirstArgument,
'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
'ZRANGE' => $getKeyFromFirstArgument,
'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZRANK' => $getKeyFromFirstArgument,
'ZREM' => $getKeyFromFirstArgument,
'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANGE' => $getKeyFromFirstArgument,
'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
'ZREVRANK' => $getKeyFromFirstArgument,
'ZSCORE' => $getKeyFromFirstArgument,
'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
'ZSCAN' => $getKeyFromFirstArgument,
'ZLEXCOUNT' => $getKeyFromFirstArgument,
'ZRANGEBYLEX' => $getKeyFromFirstArgument,
'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
'ZREVRANGEBYLEX' => $getKeyFromFirstArgument,
/* commands operating on hashes */
'HDEL' => $getKeyFromFirstArgument,
'HEXISTS' => $getKeyFromFirstArgument,
'HGET' => $getKeyFromFirstArgument,
'HGETALL' => $getKeyFromFirstArgument,
'HMGET' => $getKeyFromFirstArgument,
'HMSET' => $getKeyFromFirstArgument,
'HINCRBY' => $getKeyFromFirstArgument,
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
'HKEYS' => $getKeyFromFirstArgument,
'HLEN' => $getKeyFromFirstArgument,
'HSET' => $getKeyFromFirstArgument,
'HSETNX' => $getKeyFromFirstArgument,
'HVALS' => $getKeyFromFirstArgument,
'HSCAN' => $getKeyFromFirstArgument,
'HDEL' => $getKeyFromFirstArgument,
'HEXISTS' => $getKeyFromFirstArgument,
'HGET' => $getKeyFromFirstArgument,
'HGETALL' => $getKeyFromFirstArgument,
'HMGET' => $getKeyFromFirstArgument,
'HMSET' => $getKeyFromFirstArgument,
'HINCRBY' => $getKeyFromFirstArgument,
'HINCRBYFLOAT' => $getKeyFromFirstArgument,
'HKEYS' => $getKeyFromFirstArgument,
'HLEN' => $getKeyFromFirstArgument,
'HSET' => $getKeyFromFirstArgument,
'HSETNX' => $getKeyFromFirstArgument,
'HVALS' => $getKeyFromFirstArgument,
'HSCAN' => $getKeyFromFirstArgument,
'HSTRLEN' => $getKeyFromFirstArgument,
/* commands operating on HyperLogLog */
'PFADD' => $getKeyFromFirstArgument,
'PFCOUNT' => $getKeyFromAllArguments,
'PFMERGE' => $getKeyFromAllArguments,
'PFADD' => $getKeyFromFirstArgument,
'PFCOUNT' => $getKeyFromAllArguments,
'PFMERGE' => $getKeyFromAllArguments,
/* scripting */
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
'EVAL' => array($this, 'getKeyFromScriptingCommands'),
'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
/* commands performing geospatial operations */
'GEOADD' => $getKeyFromFirstArgument,
'GEOHASH' => $getKeyFromFirstArgument,
'GEOPOS' => $getKeyFromFirstArgument,
'GEODIST' => $getKeyFromFirstArgument,
'GEORADIUS' => array($this, 'getKeyFromGeoradiusCommands'),
'GEORADIUSBYMEMBER' => array($this, 'getKeyFromGeoradiusCommands'),
);
}
@@ -202,8 +211,8 @@ abstract class ClusterStrategy implements StrategyInterface
}
if (!is_callable($callback)) {
throw new InvalidArgumentException(
"The argument must be a callable object or NULL."
throw new \InvalidArgumentException(
'The argument must be a callable object or NULL.'
);
}
@@ -261,6 +270,35 @@ abstract class ClusterStrategy implements StrategyInterface
}
}
/**
* Extracts the key from SORT command.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromSortCommand(CommandInterface $command)
{
$arguments = $command->getArguments();
$firstKey = $arguments[0];
if (1 === $argc = count($arguments)) {
return $firstKey;
}
$keys = array($firstKey);
for ($i = 1; $i < $argc; ++$i) {
if (strtoupper($arguments[$i]) === 'STORE') {
$keys[] = $arguments[++$i];
}
}
if ($this->checkSameSlotForKeys($keys)) {
return $firstKey;
}
}
/**
* Extracts the key from BLPOP and BRPOP commands.
*
@@ -293,6 +331,39 @@ abstract class ClusterStrategy implements StrategyInterface
}
}
/**
* Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands.
*
* @param CommandInterface $command Command instance.
*
* @return string|null
*/
protected function getKeyFromGeoradiusCommands(CommandInterface $command)
{
$arguments = $command->getArguments();
$argc = count($arguments);
$startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4;
if ($argc > $startIndex) {
$keys = array($arguments[0]);
for ($i = $startIndex; $i < $argc; ++$i) {
$argument = strtoupper($arguments[$i]);
if ($argument === 'STORE' || $argument === 'STOREDIST') {
$keys[] = $arguments[++$i];
}
}
if ($this->checkSameSlotForKeys($keys)) {
return $arguments[0];
} else {
return;
}
}
return $arguments[0];
}
/**
* Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
*
@@ -364,7 +435,7 @@ abstract class ClusterStrategy implements StrategyInterface
$currentSlot = $this->getSlotByKey($keys[0]);
for ($i = 1; $i < $count; $i++) {
for ($i = 1; $i < $count; ++$i) {
$nextSlot = $this->getSlotByKey($keys[$i]);
if ($currentSlot !== $nextSlot) {
@@ -11,13 +11,11 @@
namespace Predis\Cluster\Distributor;
use Exception;
/**
* Exception class that identifies empty rings.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class EmptyRingException extends Exception
class EmptyRingException extends \Exception
{
}
+4 -4
View File
@@ -24,7 +24,7 @@ use Predis\Cluster\Hash\HashGeneratorInterface;
class HashRing implements DistributorInterface, HashGeneratorInterface
{
const DEFAULT_REPLICAS = 128;
const DEFAULT_WEIGHT = 100;
const DEFAULT_WEIGHT = 100;
private $ring;
private $ringKeys;
@@ -55,7 +55,7 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
// last wins, thus the order in which nodes are added is significant.
$this->nodes[] = array(
'object' => $node,
'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT
'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT,
);
$this->reset();
@@ -160,8 +160,8 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
$nodeHash = $this->getNodeHash($nodeObject);
$replicas = (int) round($weightRatio * $totalNodes * $replicas);
for ($i = 0; $i < $replicas; $i++) {
$key = crc32("$nodeHash:$i");
for ($i = 0; $i < $replicas; ++$i) {
$key = $this->hash("$nodeHash:$i");
$ring[$key] = $nodeObject;
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ class KetamaRing extends HashRing
$nodeHash = $this->getNodeHash($nodeObject);
$replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4));
for ($i = 0; $i < $replicas; $i++) {
for ($i = 0; $i < $replicas; ++$i) {
$unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));
foreach ($unpackedDigest as $key) {
+3 -1
View File
@@ -61,9 +61,11 @@ 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++) {
for ($i = 0; $i < $strlen; ++$i) {
$crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
}
+1 -1
View File
@@ -56,7 +56,7 @@ class PredisStrategy extends ClusterStrategy
$currentKey = $this->extractKeyTag($keys[0]);
for ($i = 1; $i < $count; $i++) {
for ($i = 1; $i < $count; ++$i) {
$nextKey = $this->extractKeyTag($keys[$i]);
if ($currentKey !== $nextKey) {
+3 -3
View File
@@ -11,9 +11,9 @@
namespace Predis\Cluster;
use Predis\NotSupportedException;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\Cluster\Hash\CRC16;
use Predis\Cluster\Hash\HashGeneratorInterface;
use Predis\NotSupportedException;
/**
* Default class used by Predis to calculate hashes out of keys of
@@ -40,7 +40,7 @@ class RedisStrategy extends ClusterStrategy
*/
public function getSlotByKey($key)
{
$key = $this->extractKeyTag($key);
$key = $this->extractKeyTag($key);
$slot = $this->hashGenerator->hash($key) & 0x3FFF;
return $slot;
@@ -11,7 +11,6 @@
namespace Predis\Collection\Iterator;
use Iterator;
use Predis\ClientInterface;
use Predis\NotSupportedException;
@@ -28,7 +27,7 @@ use Predis\NotSupportedException;
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
abstract class CursorBasedIterator implements Iterator
abstract class CursorBasedIterator implements \Iterator
{
protected $client;
protected $match;
@@ -93,7 +92,7 @@ abstract class CursorBasedIterator implements Iterator
{
$options = array();
if (strlen($this->match) > 0) {
if (strlen(strval($this->match)) > 0) {
$options['MATCH'] = $this->match;
}
@@ -133,13 +132,15 @@ abstract class CursorBasedIterator implements Iterator
*/
protected function extractNext()
{
$this->position++;
++$this->position;
$this->current = array_shift($this->elements);
}
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -148,7 +149,9 @@ abstract class CursorBasedIterator implements Iterator
/**
* {@inheritdoc}
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -156,7 +159,9 @@ abstract class CursorBasedIterator implements Iterator
/**
* {@inheritdoc}
* @return int
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -164,7 +169,9 @@ abstract class CursorBasedIterator implements Iterator
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function next()
{
tryFetch: {
@@ -184,7 +191,9 @@ abstract class CursorBasedIterator implements Iterator
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+4 -1
View File
@@ -18,6 +18,7 @@ use Predis\ClientInterface;
* HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*
* @link http://redis.io/commands/scan
*/
class HashKey extends CursorBasedIterator
@@ -50,6 +51,8 @@ class HashKey extends CursorBasedIterator
protected function extractNext()
{
$this->position = key($this->elements);
$this->current = array_shift($this->elements);
$this->current = current($this->elements);
unset($this->elements[$this->position]);
}
}
+1
View File
@@ -18,6 +18,7 @@ use Predis\ClientInterface;
* SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*
* @link http://redis.io/commands/scan
*/
class Keyspace extends CursorBasedIterator
+14 -5
View File
@@ -11,8 +11,6 @@
namespace Predis\Collection\Iterator;
use InvalidArgumentException;
use Iterator;
use Predis\ClientInterface;
use Predis\NotSupportedException;
@@ -27,9 +25,10 @@ use Predis\NotSupportedException;
* times (trimmed, deleted, overwritten) during the iteration process.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*
* @link http://redis.io/commands/lrange
*/
class ListKey implements Iterator
class ListKey implements \Iterator
{
protected $client;
protected $count;
@@ -53,7 +52,7 @@ class ListKey implements Iterator
$this->requiredCommand($client, 'LRANGE');
if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) {
throw new InvalidArgumentException('The $count argument must be a positive integer.');
throw new \InvalidArgumentException('The $count argument must be a positive integer.');
}
$this->client = $client;
@@ -122,13 +121,15 @@ class ListKey implements Iterator
*/
protected function extractNext()
{
$this->position++;
++$this->position;
$this->current = array_shift($this->elements);
}
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->reset();
@@ -137,7 +138,9 @@ class ListKey implements Iterator
/**
* {@inheritdoc}
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
return $this->current;
@@ -145,7 +148,9 @@ class ListKey implements Iterator
/**
* {@inheritdoc}
* @return int
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->position;
@@ -153,7 +158,9 @@ class ListKey implements Iterator
/**
* {@inheritdoc}
* @return void
*/
#[\ReturnTypeWillChange]
public function next()
{
if (!$this->elements && $this->fetchmore) {
@@ -169,7 +176,9 @@ class ListKey implements Iterator
/**
* {@inheritdoc}
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->valid;
+1
View File
@@ -18,6 +18,7 @@ use Predis\ClientInterface;
* command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*
* @link http://redis.io/commands/scan
*/
class SetKey extends CursorBasedIterator
+4 -5
View File
@@ -18,6 +18,7 @@ use Predis\ClientInterface;
* ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*
* @link http://redis.io/commands/scan
*/
class SortedSetKey extends CursorBasedIterator
@@ -49,11 +50,9 @@ class SortedSetKey extends CursorBasedIterator
*/
protected function extractNext()
{
if ($kv = each($this->elements)) {
$this->position = $kv[0];
$this->current = $kv[1];
$this->position = key($this->elements);
$this->current = current($this->elements);
unset($this->elements[$this->position]);
}
unset($this->elements[$this->position]);
}
}
+1 -1
View File
@@ -104,7 +104,7 @@ abstract class Command implements CommandInterface
*/
public static function normalizeArguments(array $arguments)
{
if (count($arguments) === 1 && is_array($arguments[0])) {
if (count($arguments) === 1 && isset($arguments[0]) && is_array($arguments[0])) {
return $arguments[0];
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/auth
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionAuth extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/echo
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionEcho extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/ping
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionPing extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/quit
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionQuit extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/select
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class ConnectionSelect extends Command
+42
View File
@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/geoadd
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoAdd extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'GEOADD';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
foreach (array_pop($arguments) as $item) {
$arguments = array_merge($arguments, $item);
}
}
return $arguments;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/geodist
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoDist extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'GEODIST';
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/geohash
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoHash extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'GEOHASH';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$members = array_pop($arguments);
$arguments = array_merge($arguments, $members);
}
return $arguments;
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/geopos
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoPos extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'GEOPOS';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (count($arguments) === 2 && is_array($arguments[1])) {
$members = array_pop($arguments);
$arguments = array_merge($arguments, $members);
}
return $arguments;
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/georadius
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoRadius extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'GEORADIUS';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if ($arguments && is_array(end($arguments))) {
$options = array_change_key_case(array_pop($arguments), CASE_UPPER);
if (isset($options['WITHCOORD']) && $options['WITHCOORD'] == true) {
$arguments[] = 'WITHCOORD';
}
if (isset($options['WITHDIST']) && $options['WITHDIST'] == true) {
$arguments[] = 'WITHDIST';
}
if (isset($options['WITHHASH']) && $options['WITHHASH'] == true) {
$arguments[] = 'WITHHASH';
}
if (isset($options['COUNT'])) {
$arguments[] = 'COUNT';
$arguments[] = $options['COUNT'];
}
if (isset($options['SORT'])) {
$arguments[] = strtoupper($options['SORT']);
}
if (isset($options['STORE'])) {
$arguments[] = 'STORE';
$arguments[] = $options['STORE'];
}
if (isset($options['STOREDIST'])) {
$arguments[] = 'STOREDIST';
$arguments[] = $options['STOREDIST'];
}
}
return $arguments;
}
}
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/georadiusbymember
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class GeospatialGeoRadiusByMember extends GeospatialGeoRadius
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'GEORADIUSBYMEMBER';
}
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hdel
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashDelete extends Command
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hexists
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashExists extends Command
@@ -24,12 +25,4 @@ class HashExists extends Command
{
return 'HEXISTS';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hget
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGet extends Command
+2 -1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hgetall
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGetAll extends Command
@@ -32,7 +33,7 @@ class HashGetAll extends Command
{
$result = array();
for ($i = 0; $i < count($data); $i++) {
for ($i = 0; $i < count($data); ++$i) {
$result[$data[$i]] = $data[++$i];
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hmget
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashGetMultiple extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hincrby
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashIncrementBy extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hincrbyfloat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashIncrementByFloat extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hkeys
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashKeys extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashLength extends Command
+2 -1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hscan
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashScan extends Command
@@ -72,7 +73,7 @@ class HashScan extends Command
$fields = $data[1];
$result = array();
for ($i = 0; $i < count($fields); $i++) {
for ($i = 0; $i < count($fields); ++$i) {
$result[$fields[$i]] = $fields[++$i];
}
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSet extends Command
@@ -24,12 +25,4 @@ class HashSet extends Command
{
return 'HSET';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hmset
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSetMultiple extends Command
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hsetnx
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashSetPreserve extends Command
@@ -24,12 +25,4 @@ class HashSetPreserve extends Command
{
return 'HSETNX';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/hstrlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashStringLength extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'HSTRLEN';
}
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/hvals
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HashValues extends Command
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/pfadd
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HyperLogLogAdd extends Command
@@ -32,12 +33,4 @@ class HyperLogLogAdd extends Command
{
return self::normalizeVariadic($arguments);
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/pfcount
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HyperLogLogCount extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/pfmerge
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class HyperLogLogMerge extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/del
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDelete extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/dump
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyDump extends Command
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/exists
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExists extends Command
@@ -24,12 +25,4 @@ class KeyExists extends Command
{
return 'EXISTS';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/expire
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExpire extends Command
@@ -24,12 +25,4 @@ class KeyExpire extends Command
{
return 'EXPIRE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/expireat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyExpireAt extends Command
@@ -24,12 +25,4 @@ class KeyExpireAt extends Command
{
return 'EXPIREAT';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/keys
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyKeys extends Command
+50
View File
@@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command;
/**
* @link http://redis.io/commands/migrate
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyMigrate extends Command
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'MIGRATE';
}
/**
* {@inheritdoc}
*/
protected function filterArguments(array $arguments)
{
if (is_array(end($arguments))) {
foreach (array_pop($arguments) as $modifier => $value) {
$modifier = strtoupper($modifier);
if ($modifier === 'COPY' && $value == true) {
$arguments[] = $modifier;
}
if ($modifier === 'REPLACE' && $value == true) {
$arguments[] = $modifier;
}
}
}
return $arguments;
}
}
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/move
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyMove extends Command
@@ -24,12 +25,4 @@ class KeyMove extends Command
{
return 'MOVE';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/persist
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPersist extends Command
@@ -24,12 +25,4 @@ class KeyPersist extends Command
{
return 'PERSIST';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/pexpire
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPreciseExpire extends KeyExpire
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/pexpireat
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPreciseExpireAt extends KeyExpireAt
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/pttl
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyPreciseTimeToLive extends KeyTimeToLive
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/randomkey
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRandom extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/rename
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRename extends Command
+1 -8
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/renamenx
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRenamePreserve extends KeyRename
@@ -24,12 +25,4 @@ class KeyRenamePreserve extends KeyRename
{
return 'RENAMENX';
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
return (bool) $data;
}
}
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/restore
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyRestore extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/scan
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyScan extends Command
+1 -1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/sort
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeySort extends Command
@@ -59,7 +60,6 @@ class KeySort extends Command
if (isset($sortParams['LIMIT']) &&
is_array($sortParams['LIMIT']) &&
count($sortParams['LIMIT']) == 2) {
$query[] = 'LIMIT';
$query[] = $sortParams['LIMIT'][0];
$query[] = $sortParams['LIMIT'][1];
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/ttl
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyTimeToLive extends Command
+1
View File
@@ -13,6 +13,7 @@ namespace Predis\Command;
/**
* @link http://redis.io/commands/type
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class KeyType extends Command

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