mirror of
https://github.com/predis/predis.git
synced 2026-08-20 10:52:00 +00:00
Compare commits
151 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| deee2b6d60 | |||
| bb8cce7bcf | |||
| dc14604f3e | |||
| b000013f22 | |||
| d6da14daa8 | |||
| acb2460f4c | |||
| 213f00042f | |||
| ab0c46332c | |||
| ed19ec3483 | |||
| 90496e76b5 | |||
| 4231c35e09 | |||
| a2fb02d738 | |||
| ca1f398f7c | |||
| 5e07be785a | |||
| b3cd02e2ba | |||
| c50c3393bb | |||
| 4aa3014bb0 | |||
| 92adf32e88 | |||
| c4560304cf | |||
| cf5c118a07 | |||
| bb9a670c83 | |||
| 3e56e880e8 | |||
| 1c71a4e36e | |||
| 5004f5d6fc | |||
| e616f85679 | |||
| 9cd093be24 | |||
| 305d167b08 | |||
| 3271e1edcb | |||
| b240daa106 | |||
| 8fee6ce8c3 | |||
| 35dd44b222 | |||
| 545177f09f | |||
| 2f9337cb9b | |||
| 437d80a7a3 | |||
| 3a14a39d92 | |||
| 5c9457cc2e | |||
| c551fc21f9 | |||
| 0d84606723 | |||
| c021e380d6 | |||
| 5f4b87080f | |||
| 5ae4ac6b49 | |||
| 521ac8f0eb | |||
| fbbef2a0a2 | |||
| 9930e933c6 | |||
| 16957f3b39 | |||
| 9e5371f5ce | |||
| 8ca99f91e4 | |||
| c778843b0d | |||
| dca201291c | |||
| 2e76410e65 | |||
| ca468b785c | |||
| 4a7fb55058 | |||
| fdf663ebba | |||
| 880ad09cad | |||
| 5eccc65d42 | |||
| f225c55a01 | |||
| 8be2418f01 | |||
| b9ebe8a376 | |||
| e2d26926d2 | |||
| 309248042f | |||
| 87452e62d0 | |||
| 31baa9b241 | |||
| 6f942400a1 | |||
| 5e220a0b57 | |||
| 0eb2633348 | |||
| cbbd6bba88 | |||
| 3702f24efc | |||
| 82fefe057b | |||
| 42336537dc | |||
| 802abac55e | |||
| 61e75816ce | |||
| 17f0c08cb6 | |||
| 2b8af1eef2 | |||
| c5f2b441b4 | |||
| 2440a61cba | |||
| 7100884201 | |||
| 2a932d7292 | |||
| 0a6306b73e | |||
| 4363345f7c | |||
| 2ce537d75e | |||
| 7489690306 | |||
| 7376902b20 | |||
| a6bd25412e | |||
| 60cc950a58 | |||
| db82dd9f23 | |||
| 1bc08d9bcc | |||
| 11e4bb44d0 | |||
| cddb849812 | |||
| f2298dd186 | |||
| bceda1d8e6 | |||
| 9f87c849c5 | |||
| 1382146b03 | |||
| cbc08aa5e7 | |||
| de9cd905bb | |||
| 6dd55d7f96 | |||
| 82eb18c6c3 | |||
| ac6b813ba9 | |||
| 6482956d96 | |||
| cf15dd9857 | |||
| 597c61a098 | |||
| 9aa99349b1 | |||
| b6f60f0d72 | |||
| 10235d62dc | |||
| ec2b1eacd2 | |||
| 9c3605ab33 | |||
| 55e6094954 | |||
| 15314b726b | |||
| 292604edbf | |||
| fc8f9f5604 | |||
| 77132e6c6b | |||
| a7571ef0c7 | |||
| 4901408b08 | |||
| dd3897b755 | |||
| 6e7dbe05fb | |||
| f0f9d68a8e | |||
| bb6f1ee0e3 | |||
| ffcc4bba8c | |||
| 83359dff9a | |||
| 710b4f901b | |||
| f3e46683b9 | |||
| 945e05819b | |||
| a64b7354e1 | |||
| 39ff616e86 | |||
| 738ec0c50f | |||
| 1828222e27 | |||
| 111d100ee3 | |||
| 893215a1d4 | |||
| f5d8b9fe2d | |||
| 722b513cea | |||
| 62774c6cc5 | |||
| 8aa2ded5b0 | |||
| 795c3e41b0 | |||
| 59e616b124 | |||
| 157f658f0b | |||
| 343afd312a | |||
| d77b77bbd9 | |||
| 34b678192f | |||
| 4de246cfee | |||
| 6fd6933580 | |||
| c7b38abe71 | |||
| 574ef78f86 | |||
| 1ebabaf8f8 | |||
| 98ec0cc27e | |||
| caf8a46cbb | |||
| 944a16d7c0 | |||
| d1290dfde0 | |||
| 9489d9e5aa | |||
| 94ccb2cc3b | |||
| 6c848db2a0 | |||
| 6ab10b2b70 | |||
| 2d4377fc81 |
+7
-2
@@ -1,10 +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 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
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
github: tillkruss
|
||||
custom: "https://www.paypal.me/tillkruss"
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -2,6 +2,7 @@
|
||||
*.phar
|
||||
.php-version
|
||||
.php_cs.cache
|
||||
.phpunit.result.cache
|
||||
phpunit.xml
|
||||
package.xml
|
||||
composer.lock
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
$PREDIS_HEADER = <<<EOS
|
||||
This file is part of the Predis package.
|
||||
|
||||
(c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
|
||||
For the full copyright and license information, please view the LICENSE
|
||||
file that was distributed with this source code.
|
||||
EOS;
|
||||
|
||||
Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($PREDIS_HEADER);
|
||||
|
||||
return Symfony\CS\Config\Config::create()
|
||||
->setUsingCache(true)
|
||||
->level(Symfony\CS\FixerInterface::SYMFONY_LEVEL)
|
||||
->fixers(array(
|
||||
// Symfony
|
||||
'-unalign_equals',
|
||||
'-unalign_double_arrow',
|
||||
|
||||
// Contribs
|
||||
'header_comment',
|
||||
'ordered_use',
|
||||
'phpdoc_order',
|
||||
'long_array_syntax',
|
||||
))
|
||||
->finder(
|
||||
Symfony\CS\Finder\DefaultFinder::create()
|
||||
->in(__DIR__.'/bin')
|
||||
->in(__DIR__.'/src')
|
||||
->in(__DIR__.'/tests')
|
||||
->in(__DIR__.'/examples')
|
||||
);
|
||||
@@ -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
-14
@@ -1,23 +1,36 @@
|
||||
language: php
|
||||
sudo: false
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- hhvm
|
||||
|
||||
branches:
|
||||
except:
|
||||
- v0.5
|
||||
- v0.6
|
||||
- v0.6-PHP_5.2
|
||||
- documentation
|
||||
services: redis-server
|
||||
- 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:
|
||||
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
|
||||
|
||||
+124
@@ -1,3 +1,127 @@
|
||||
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)
|
||||
================================================================================
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -33,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? ###
|
||||
|
||||
|
||||
@@ -5,15 +5,12 @@
|
||||
[![Latest development][ico-version-dev]][link-packagist]
|
||||
[![Monthly installs][ico-downloads-monthly]][link-downloads]
|
||||
[![Build status][ico-travis]][link-travis]
|
||||
[![HHVM support][ico-hhvm]][link-hhvm]
|
||||
[![Gitter room][ico-gitter]][link-gitter]
|
||||
|
||||
Flexible and feature-complete [Redis](http://redis.io) client for PHP >= 5.3 and HHVM >= 2.3.0.
|
||||
|
||||
Predis does not require any additional C extension by default, but it can be optionally paired with
|
||||
[phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of the serialization and parsing
|
||||
of the [Redis RESP Protocol](http://redis.io/topics/protocol). For an __experimental__ asynchronous
|
||||
implementation of the client you can refer to [Predis\Async](https://github.com/nrk/predis-async).
|
||||
of the [Redis RESP Protocol](http://redis.io/topics/protocol).
|
||||
|
||||
More details about this project can be found on the [frequently asked questions](FAQ.md).
|
||||
|
||||
@@ -41,7 +38,7 @@ More details about this project can be found on the [frequently asked questions]
|
||||
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/nrk/predis/tags).
|
||||
compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
|
||||
|
||||
|
||||
### Loading the library ###
|
||||
@@ -90,6 +87,9 @@ $client = new Predis\Client([
|
||||
$client = new Predis\Client('tcp://10.0.0.1:6379');
|
||||
```
|
||||
|
||||
Password protected servers can be accessed by adding `password` to the parameters set. When ACLs are
|
||||
enabled on Redis >= 6.0, both `username` and `password` are required for user authentication.
|
||||
|
||||
It is also possible to connect to local instances of Redis using UNIX domain sockets, in this case
|
||||
the parameters must use the `unix` scheme and specify a path for the socket file:
|
||||
|
||||
@@ -108,7 +108,7 @@ of suitable [options](http://php.net/manual/context.ssl.php) passed via the `ssl
|
||||
$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');
|
||||
@@ -454,7 +454,7 @@ the development server profile will be used. You can refer to [the tests README]
|
||||
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 ##
|
||||
@@ -462,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)
|
||||
|
||||
|
||||
@@ -477,16 +477,13 @@ found [on its project page](http://travis-ci.org/nrk/predis).
|
||||
|
||||
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
|
||||
|
||||
[ico-license]: https://img.shields.io/github/license/nrk/predis.svg?style=flat-square
|
||||
[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/nrk/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
|
||||
[ico-gitter]: https://img.shields.io/gitter/room/nrk/predis.svg?style=flat-square
|
||||
|
||||
[link-packagist]: https://packagist.org/packages/predis/predis
|
||||
[link-travis]: https://travis-ci.org/nrk/predis
|
||||
[link-travis]: https://travis-ci.org/predis/predis
|
||||
[link-downloads]: https://packagist.org/packages/predis/predis/stats
|
||||
[link-hhvm]: http://hhvm.h4cc.de/package/predis/predis
|
||||
[link-gitter]: https://gitter.im/nrk/predis
|
||||
|
||||
+2
-2
@@ -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
@@ -59,7 +59,7 @@ spl_autoload_register(function (\$class) {
|
||||
}
|
||||
}
|
||||
});
|
||||
__HALT_COMPILER();
|
||||
__halt_compiler();
|
||||
EOSTUB;
|
||||
}
|
||||
|
||||
|
||||
+21
-4
@@ -3,16 +3,28 @@
|
||||
"type": "library",
|
||||
"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": {
|
||||
@@ -26,6 +38,11 @@
|
||||
"ext-curl": "Allows access to Webdis when paired with phpiredis"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {"Predis\\": "src/"}
|
||||
"psr-4": {
|
||||
"Predis\\": "src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-update-cmd": "@php -f tests/apply-patches.php"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ name = "Predis"
|
||||
desc = "Flexible and feature-complete Redis client for PHP and HHVM"
|
||||
homepage = "http://github.com/nrk/predis"
|
||||
license = "MIT"
|
||||
version = "1.1.1"
|
||||
stability = "stable"
|
||||
version = "1.1.11"
|
||||
stability = "devel"
|
||||
channel = "pear.nrk.io"
|
||||
|
||||
author = "Daniele Alessandri \"nrk\" <suppakilla@gmail.com>"
|
||||
|
||||
@@ -36,10 +36,6 @@
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" />
|
||||
</logging>
|
||||
|
||||
<php>
|
||||
<!-- Redis -->
|
||||
<const name="REDIS_SERVER_VERSION" value="3.2" />
|
||||
|
||||
+6
-3
@@ -40,7 +40,7 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
|
||||
*/
|
||||
class Client implements ClientInterface, \IteratorAggregate
|
||||
{
|
||||
const VERSION = '1.1.1';
|
||||
const VERSION = '1.1.11-dev';
|
||||
|
||||
protected $connection;
|
||||
protected $options;
|
||||
@@ -527,15 +527,18 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return \Traversable<string, static>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
$clients = array();
|
||||
$connection = $this->getConnection();
|
||||
|
||||
if (!$connection instanceof \Traversable) {
|
||||
throw new ClientException('The underlying connection is not traversable');
|
||||
return new \ArrayIterator(array(
|
||||
(string) $connection => new static($connection, $this->getOptions())
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($connection as $node) {
|
||||
|
||||
@@ -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)
|
||||
@@ -39,6 +39,7 @@ use Predis\Command\CommandInterface;
|
||||
* @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)
|
||||
@@ -73,15 +74,15 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this hsetnx($key, $field, $value)
|
||||
* @method $this hvals($key)
|
||||
* @method $this hstrlen($key, $field)
|
||||
* @method $this blpop(array $keys, $timeout)
|
||||
* @method $this brpop(array $keys, $timeout)
|
||||
* @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)
|
||||
@@ -89,13 +90,13 @@ 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)
|
||||
@@ -103,13 +104,13 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this srandmember($key, $count = null)
|
||||
* @method $this srem($key, $member)
|
||||
* @method $this sscan($key, $cursor, array $options = null)
|
||||
* @method $this sunion(array $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)
|
||||
@@ -117,9 +118,9 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this zremrangebyrank($key, $start, $stop)
|
||||
* @method $this zremrangebyscore($key, $min, $max)
|
||||
* @method $this zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method $this zrevrangebyscore($key, $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)
|
||||
@@ -127,8 +128,8 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this zremrangebylex($key, $min, $max)
|
||||
* @method $this zlexcount($key, $min, $max)
|
||||
* @method $this pfadd($key, array $elements)
|
||||
* @method $this pfmerge($destinationKey, array $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()
|
||||
|
||||
+148
-147
@@ -24,153 +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 array bitfield($key, $subcommand, ...$subcommandArg)
|
||||
* @method int decr($key)
|
||||
* @method int decrby($key, $decrement)
|
||||
* @method string get($key)
|
||||
* @method int getbit($key, $offset)
|
||||
* @method string getrange($key, $start, $end)
|
||||
* @method string getset($key, $value)
|
||||
* @method int incr($key)
|
||||
* @method int incrby($key, $increment)
|
||||
* @method string incrbyfloat($key, $increment)
|
||||
* @method array mget(array $keys)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method mixed psetex($key, $milliseconds, $value)
|
||||
* @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit($key, $offset, $value)
|
||||
* @method int setex($key, $seconds, $value)
|
||||
* @method int setnx($key, $value)
|
||||
* @method int setrange($key, $offset, $value)
|
||||
* @method int strlen($key)
|
||||
* @method int hdel($key, array $fields)
|
||||
* @method int hexists($key, $field)
|
||||
* @method string hget($key, $field)
|
||||
* @method array hgetall($key)
|
||||
* @method int hincrby($key, $field, $increment)
|
||||
* @method string hincrbyfloat($key, $field, $increment)
|
||||
* @method array hkeys($key)
|
||||
* @method int hlen($key)
|
||||
* @method array hmget($key, array $fields)
|
||||
* @method mixed hmset($key, array $dictionary)
|
||||
* @method array hscan($key, $cursor, array $options = null)
|
||||
* @method int hset($key, $field, $value)
|
||||
* @method int hsetnx($key, $field, $value)
|
||||
* @method array hvals($key)
|
||||
* @method int hstrlen($key, $field)
|
||||
* @method array blpop(array $keys, $timeout)
|
||||
* @method array brpop(array $keys, $timeout)
|
||||
* @method array brpoplpush($source, $destination, $timeout)
|
||||
* @method string lindex($key, $index)
|
||||
* @method int linsert($key, $whence, $pivot, $value)
|
||||
* @method int llen($key)
|
||||
* @method string lpop($key)
|
||||
* @method int lpush($key, array $values)
|
||||
* @method int lpushx($key, $value)
|
||||
* @method array lrange($key, $start, $stop)
|
||||
* @method int lrem($key, $count, $value)
|
||||
* @method mixed lset($key, $index, $value)
|
||||
* @method mixed ltrim($key, $start, $stop)
|
||||
* @method string rpop($key)
|
||||
* @method string rpoplpush($source, $destination)
|
||||
* @method int rpush($key, array $values)
|
||||
* @method int rpushx($key, $value)
|
||||
* @method int sadd($key, array $members)
|
||||
* @method int scard($key)
|
||||
* @method array sdiff(array $keys)
|
||||
* @method int sdiffstore($destination, array $keys)
|
||||
* @method array sinter(array $keys)
|
||||
* @method int sinterstore($destination, array $keys)
|
||||
* @method int sismember($key, $member)
|
||||
* @method array smembers($key)
|
||||
* @method int smove($source, $destination, $member)
|
||||
* @method string spop($key, $count = null)
|
||||
* @method string srandmember($key, $count = null)
|
||||
* @method int srem($key, $member)
|
||||
* @method array sscan($key, $cursor, array $options = null)
|
||||
* @method array sunion(array $keys)
|
||||
* @method int sunionstore($destination, array $keys)
|
||||
* @method int zadd($key, array $membersAndScoresDictionary)
|
||||
* @method int zcard($key)
|
||||
* @method string zcount($key, $min, $max)
|
||||
* @method string zincrby($key, $increment, $member)
|
||||
* @method int zinterstore($destination, array $keys, array $options = null)
|
||||
* @method array zrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method int zrank($key, $member)
|
||||
* @method int zrem($key, $member)
|
||||
* @method int zremrangebyrank($key, $start, $stop)
|
||||
* @method int zremrangebyscore($key, $min, $max)
|
||||
* @method array zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrevrangebyscore($key, $max, $min, array $options = null)
|
||||
* @method int zrevrank($key, $member)
|
||||
* @method int zunionstore($destination, array $keys, array $options = null)
|
||||
* @method string zscore($key, $member)
|
||||
* @method array zscan($key, $cursor, array $options = null)
|
||||
* @method array zrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method array zrevrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method int zremrangebylex($key, $min, $max)
|
||||
* @method int zlexcount($key, $min, $max)
|
||||
* @method int pfadd($key, array $elements)
|
||||
* @method mixed pfmerge($destinationKey, array $sourceKeys)
|
||||
* @method int pfcount(array $keys)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed discard()
|
||||
* @method array exec()
|
||||
* @method mixed multi()
|
||||
* @method mixed unwatch()
|
||||
* @method mixed watch($key)
|
||||
* @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method mixed auth($password)
|
||||
* @method string echo($message)
|
||||
* @method mixed ping($message = null)
|
||||
* @method mixed select($database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof($host, $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
* @method int geoadd($key, $longitude, $latitude, $member)
|
||||
* @method array geohash($key, array $members)
|
||||
* @method array geopos($key, array $members)
|
||||
* @method string geodist($key, $member1, $member2, $unit = null)
|
||||
* @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method array georadiusbymember($key, $member, $radius, $unit, array $options = null)
|
||||
* @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>
|
||||
*/
|
||||
|
||||
@@ -161,7 +161,7 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
|
||||
$replicas = (int) round($weightRatio * $totalNodes * $replicas);
|
||||
|
||||
for ($i = 0; $i < $replicas; ++$i) {
|
||||
$key = crc32("$nodeHash:$i");
|
||||
$key = $this->hash("$nodeHash:$i");
|
||||
$ring[$key] = $nodeObject;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ 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) {
|
||||
|
||||
@@ -92,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;
|
||||
}
|
||||
|
||||
@@ -138,7 +138,9 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
$this->reset();
|
||||
@@ -147,7 +149,9 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->current;
|
||||
@@ -155,7 +159,9 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
@@ -163,7 +169,9 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
tryFetch: {
|
||||
@@ -183,7 +191,9 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->valid;
|
||||
|
||||
@@ -50,11 +50,9 @@ class HashKey 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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,9 @@ class ListKey implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
$this->reset();
|
||||
@@ -136,7 +138,9 @@ class ListKey implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->current;
|
||||
@@ -144,7 +148,9 @@ class ListKey implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
@@ -152,7 +158,9 @@ class ListKey implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
if (!$this->elements && $this->fetchmore) {
|
||||
@@ -168,7 +176,9 @@ class ListKey implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->valid;
|
||||
|
||||
@@ -50,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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -197,7 +197,13 @@ class KeyPrefixProcessor implements ProcessorInterface
|
||||
if ($command instanceof PrefixableCommandInterface) {
|
||||
$command->prefixKeys($this->prefix);
|
||||
} elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) {
|
||||
call_user_func($this->commands[$commandID], $command, $this->prefix);
|
||||
$callable = $this->commands[$commandID];
|
||||
|
||||
if (is_string($callable) && 0 === strpos($callable, 'static::')) {
|
||||
$callable = substr_replace($callable, get_class($this), 0, 6);
|
||||
}
|
||||
|
||||
call_user_func($callable, $command, $this->prefix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
|
||||
/**
|
||||
* Returns an iterator over the list of command processor in the chain.
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
* @return \Traversable<int, ProcessorInterface>
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
@@ -89,8 +89,9 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($index)
|
||||
{
|
||||
return isset($this->processors[$index]);
|
||||
@@ -98,7 +99,9 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return ProcessorInterface|null
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($index)
|
||||
{
|
||||
return $this->processors[$index];
|
||||
@@ -106,7 +109,9 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($index, $processor)
|
||||
{
|
||||
if (!$processor instanceof ProcessorInterface) {
|
||||
@@ -121,7 +126,9 @@ class ProcessorChain implements \ArrayAccess, ProcessorInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($index)
|
||||
{
|
||||
unset($this->processors[$index]);
|
||||
|
||||
@@ -31,7 +31,10 @@ class ServerSentinel extends Command
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
switch (strtolower($this->getArgument(0))) {
|
||||
$argument = $this->getArgument(0);
|
||||
$argument = is_null($argument) ? null : strtolower($argument);
|
||||
|
||||
switch ($argument) {
|
||||
case 'masters':
|
||||
case 'slaves':
|
||||
return self::processMastersOrSlaves($data);
|
||||
|
||||
@@ -34,7 +34,12 @@ abstract class CommunicationException extends PredisException
|
||||
$code = null,
|
||||
\Exception $innerException = null
|
||||
) {
|
||||
parent::__construct($message, $code, $innerException);
|
||||
parent::__construct(
|
||||
is_null($message) ? '' : $message,
|
||||
is_null($code) ? 0 : $code,
|
||||
$innerException
|
||||
);
|
||||
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,16 +11,22 @@
|
||||
|
||||
namespace Predis\Configuration;
|
||||
|
||||
use Predis\Command\Processor\ProcessorInterface;
|
||||
use Predis\Connection\Aggregate\ClusterInterface;
|
||||
use Predis\Connection\Aggregate\ReplicationInterface;
|
||||
use Predis\Connection\FactoryInterface;
|
||||
use Predis\Profile\ProfileInterface;
|
||||
|
||||
/**
|
||||
* Interface defining a container for client options.
|
||||
*
|
||||
* @property-read mixed aggregate Custom connection aggregator.
|
||||
* @property-read mixed cluster Aggregate connection for clustering.
|
||||
* @property-read mixed connections Connection factory.
|
||||
* @property-read mixed exceptions Toggles exceptions in client for -ERR responses.
|
||||
* @property-read mixed prefix Key prefixing strategy using the given prefix.
|
||||
* @property-read mixed profile Server profile.
|
||||
* @property-read mixed replication Aggregate connection for replication.
|
||||
* @property-read callable $aggregate Custom connection aggregator.
|
||||
* @property-read ClusterInterface $cluster Aggregate connection for clustering.
|
||||
* @property-read FactoryInterface $connections Connection factory.
|
||||
* @property-read bool $exceptions Toggles exceptions in client for -ERR responses.
|
||||
* @property-read ProcessorInterface $prefix Key prefixing strategy using the given prefix.
|
||||
* @property-read ProfileInterface $profile Server profile.
|
||||
* @property-read ReplicationInterface $replication Aggregate connection for replication.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
|
||||
@@ -36,10 +36,6 @@ class ReplicationOption implements OptionInterface
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_bool($value) || $value === null) {
|
||||
return $value ? $this->getDefault($options) : null;
|
||||
}
|
||||
|
||||
if ($value === 'sentinel') {
|
||||
return function ($sentinels, $options) {
|
||||
return new SentinelReplication($options->service, $sentinels, $options->connections);
|
||||
@@ -50,7 +46,13 @@ class ReplicationOption implements OptionInterface
|
||||
!is_object($value) &&
|
||||
null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
|
||||
) {
|
||||
return $asbool ? $this->getDefault($options) : null;
|
||||
if (true === $asbool) {
|
||||
return $this->getDefault($options);
|
||||
} else {
|
||||
throw new \InvalidArgumentException(
|
||||
"Values evaluating to FALSE are not accepted for `replication`"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(
|
||||
|
||||
@@ -176,16 +176,18 @@ class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return \Traversable<string|int, NodeConnectionInterface>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->pool);
|
||||
|
||||
@@ -600,16 +600,18 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return \Traversable<int, NodeConnectionInterface>
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
if ($this->useClusterSlots) {
|
||||
|
||||
@@ -20,6 +20,7 @@ use Predis\Connection\NodeConnectionInterface;
|
||||
use Predis\Connection\Parameters;
|
||||
use Predis\Replication\ReplicationStrategy;
|
||||
use Predis\Replication\RoleException;
|
||||
use Predis\Response\Error;
|
||||
use Predis\Response\ErrorInterface as ErrorResponseInterface;
|
||||
use Predis\Response\ServerException;
|
||||
|
||||
@@ -146,14 +147,14 @@ class SentinelReplication implements ReplicationInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time to wait (in seconds) before fetching a new configuration
|
||||
* Sets the time to wait (in milliseconds) before fetching a new configuration
|
||||
* from one of the sentinels.
|
||||
*
|
||||
* @param float $seconds Time to wait before the next attempt.
|
||||
* @param float $milliseconds Time to wait before the next attempt.
|
||||
*/
|
||||
public function setRetryWait($seconds)
|
||||
public function setRetryWait($milliseconds)
|
||||
{
|
||||
$this->retryWait = (float) $seconds;
|
||||
$this->retryWait = (float) $milliseconds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,9 +240,14 @@ class SentinelReplication implements ReplicationInterface
|
||||
}
|
||||
|
||||
if (is_array($parameters)) {
|
||||
// We explicitly set "database" and "password" to null,
|
||||
// so that no AUTH and SELECT command is send to the sentinels.
|
||||
// NOTE: sentinels do not accept AUTH and SELECT commands so we must
|
||||
// explicitly set them to NULL to avoid problems when using default
|
||||
// parameters set via client options. Actually AUTH is supported for
|
||||
// sentinels starting with Redis 5 but we have to differentiate from
|
||||
// sentinels passwords and nodes passwords, this will be implemented
|
||||
// in a later release.
|
||||
$parameters['database'] = null;
|
||||
$parameters['username'] = null;
|
||||
$parameters['password'] = null;
|
||||
|
||||
if (!isset($parameters['timeout'])) {
|
||||
@@ -515,14 +521,20 @@ class SentinelReplication implements ReplicationInterface
|
||||
/**
|
||||
* Asserts that the specified connection matches an expected role.
|
||||
*
|
||||
* @param NodeConnectionInterface $sentinel Connection to a redis server.
|
||||
* @param string $role Expected role of the server ("master", "slave" or "sentinel").
|
||||
* @param NodeConnectionInterface $connection Connection to a redis server.
|
||||
* @param string $role Expected role of the server ("master", "slave" or "sentinel").
|
||||
*
|
||||
* @throws RoleException|ConnectionException
|
||||
*/
|
||||
protected function assertConnectionRole(NodeConnectionInterface $connection, $role)
|
||||
{
|
||||
$role = strtolower($role);
|
||||
$actualRole = $connection->executeCommand(RawCommand::create('ROLE'));
|
||||
|
||||
if ($actualRole instanceof Error) {
|
||||
throw new ConnectionException($connection, $actualRole->getMessage());
|
||||
}
|
||||
|
||||
if ($role !== $actualRole[0]) {
|
||||
throw new RoleException($connection, "Expected $role but got $actualRole[0] [$connection]");
|
||||
}
|
||||
|
||||
@@ -173,13 +173,17 @@ class Factory implements FactoryInterface
|
||||
{
|
||||
$parameters = $connection->getParameters();
|
||||
|
||||
if (isset($parameters->password)) {
|
||||
if (isset($parameters->password) && strlen($parameters->password)) {
|
||||
$cmdAuthArgs = isset($parameters->username) && strlen($parameters->username)
|
||||
? array('AUTH', $parameters->username, $parameters->password)
|
||||
: array('AUTH', $parameters->password);
|
||||
|
||||
$connection->addConnectCommand(
|
||||
new RawCommand(array('AUTH', $parameters->password))
|
||||
new RawCommand($cmdAuthArgs)
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($parameters->database)) {
|
||||
if (isset($parameters->database) && strlen($parameters->database)) {
|
||||
$connection->addConnectCommand(
|
||||
new RawCommand(array('SELECT', $parameters->database))
|
||||
);
|
||||
|
||||
@@ -109,8 +109,17 @@ class Parameters implements ParametersInterface
|
||||
}
|
||||
|
||||
if (stripos($uri, 'redis') === 0) {
|
||||
if (isset($parsed['user'])) {
|
||||
if (strlen($parsed['user'])) {
|
||||
$parsed['username'] = $parsed['user'];
|
||||
}
|
||||
unset($parsed['user']);
|
||||
}
|
||||
|
||||
if (isset($parsed['pass'])) {
|
||||
$parsed['password'] = $parsed['pass'];
|
||||
if (strlen($parsed['pass'])) {
|
||||
$parsed['password'] = $parsed['pass'];
|
||||
}
|
||||
unset($parsed['pass']);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,9 +66,9 @@ class PhpiredisSocketConnection extends AbstractConnection
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
phpiredis_reader_destroy($this->reader);
|
||||
|
||||
parent::__destruct();
|
||||
|
||||
phpiredis_reader_destroy($this->reader);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,9 +227,7 @@ class PhpiredisSocketConnection extends AbstractConnection
|
||||
$protocol = SOL_TCP;
|
||||
}
|
||||
|
||||
$socket = @socket_create($domain, SOCK_STREAM, $protocol);
|
||||
|
||||
if (!is_resource($socket)) {
|
||||
if (false === $socket = @socket_create($domain, SOCK_STREAM, $protocol)) {
|
||||
$this->emitSocketError();
|
||||
}
|
||||
|
||||
@@ -344,7 +342,9 @@ class PhpiredisSocketConnection extends AbstractConnection
|
||||
public function disconnect()
|
||||
{
|
||||
if ($this->isConnected()) {
|
||||
phpiredis_reader_reset($this->reader);
|
||||
socket_close($this->getResource());
|
||||
|
||||
parent::disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,19 @@ class PhpiredisStreamConnection extends StreamConnection
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
phpiredis_reader_destroy($this->reader);
|
||||
|
||||
parent::__destruct();
|
||||
|
||||
phpiredis_reader_destroy($this->reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
phpiredis_reader_reset($this->reader);
|
||||
|
||||
parent::disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,9 +97,23 @@ class PhpiredisStreamConnection extends StreamConnection
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function assertSslSupport(ParametersInterface $parameters)
|
||||
protected function assertParameters(ParametersInterface $parameters)
|
||||
{
|
||||
throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.');
|
||||
switch ($parameters->scheme) {
|
||||
case 'tcp':
|
||||
case 'redis':
|
||||
case 'unix':
|
||||
break;
|
||||
|
||||
case 'tls':
|
||||
case 'rediss':
|
||||
throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.');
|
||||
|
||||
default:
|
||||
throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,8 +123,9 @@ class PhpiredisStreamConnection extends StreamConnection
|
||||
{
|
||||
$socket = null;
|
||||
$timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
|
||||
$context = stream_context_create();
|
||||
|
||||
$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags);
|
||||
$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags, $context);
|
||||
|
||||
if (!$resource) {
|
||||
$this->onConnectionError(trim($errstr), $errno);
|
||||
|
||||
@@ -123,8 +123,9 @@ class StreamConnection extends AbstractConnection
|
||||
protected function createStreamSocket(ParametersInterface $parameters, $address, $flags)
|
||||
{
|
||||
$timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
|
||||
$context = stream_context_create();
|
||||
|
||||
if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags)) {
|
||||
if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags, $context)) {
|
||||
$this->onConnectionError(trim($errstr), $errno);
|
||||
}
|
||||
|
||||
@@ -288,7 +289,7 @@ class StreamConnection extends AbstractConnection
|
||||
$socket = $this->getResource();
|
||||
|
||||
while (($length = strlen($buffer)) > 0) {
|
||||
$written = @fwrite($socket, $buffer);
|
||||
$written = is_resource($socket) ? @fwrite($socket, $buffer) : false;
|
||||
|
||||
if ($length === $written) {
|
||||
return;
|
||||
@@ -332,7 +333,7 @@ class StreamConnection extends AbstractConnection
|
||||
$bytesLeft = ($size += 2);
|
||||
|
||||
do {
|
||||
$chunk = fread($socket, min($bytesLeft, 4096));
|
||||
$chunk = is_resource($socket) ? fread($socket, min($bytesLeft, 4096)) : false;
|
||||
|
||||
if ($chunk === false || $chunk === '') {
|
||||
$this->onConnectionError('Error while reading bytes from the server.');
|
||||
@@ -387,7 +388,7 @@ class StreamConnection extends AbstractConnection
|
||||
$buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n";
|
||||
|
||||
foreach ($arguments as $argument) {
|
||||
$arglen = strlen($argument);
|
||||
$arglen = strlen(strval($argument));
|
||||
$buffer .= "\${$arglen}\r\n{$argument}\r\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ class WebdisConnection implements NodeConnectionInterface
|
||||
$parameters = $this->getParameters();
|
||||
$timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0) * 1000;
|
||||
|
||||
if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) {
|
||||
if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
$host = "[$host]";
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class Consumer implements \Iterator
|
||||
{
|
||||
private $client;
|
||||
private $valid;
|
||||
private $position;
|
||||
private $position = 0;
|
||||
|
||||
/**
|
||||
* @param ClientInterface $client Client instance used by the consumer.
|
||||
@@ -90,7 +90,9 @@ class Consumer implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
// NOOP
|
||||
@@ -101,6 +103,7 @@ class Consumer implements \Iterator
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->getValue();
|
||||
@@ -108,7 +111,9 @@ class Consumer implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
@@ -116,7 +121,9 @@ class Consumer implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
++$this->position;
|
||||
@@ -127,6 +134,7 @@ class Consumer implements \Iterator
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->valid;
|
||||
|
||||
@@ -225,6 +225,19 @@ class Pipeline implements ClientContextInterface
|
||||
return $this->responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the buffer holding all of the commands and responses.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->responses = array();
|
||||
$this->pipeline = new \SplQueue();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the pipeline should throw exceptions on server errors.
|
||||
*
|
||||
|
||||
@@ -30,7 +30,7 @@ abstract class AbstractConsumer implements \Iterator
|
||||
const STATUS_SUBSCRIBED = 2; // 0b0010
|
||||
const STATUS_PSUBSCRIBED = 4; // 0b0100
|
||||
|
||||
private $position = null;
|
||||
private $position = 0;
|
||||
private $statusFlags = self::STATUS_VALID;
|
||||
|
||||
/**
|
||||
@@ -150,7 +150,9 @@ abstract class AbstractConsumer implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
// NOOP
|
||||
@@ -160,8 +162,9 @@ abstract class AbstractConsumer implements \Iterator
|
||||
* Returns the last message payload retrieved from the server and generated
|
||||
* by one of the active subscriptions.
|
||||
*
|
||||
* @return array
|
||||
* @return \stdClass
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->getValue();
|
||||
@@ -169,7 +172,9 @@ abstract class AbstractConsumer implements \Iterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
@@ -178,6 +183,7 @@ abstract class AbstractConsumer implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
if ($this->valid()) {
|
||||
@@ -192,6 +198,7 @@ abstract class AbstractConsumer implements \Iterator
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
$isValid = $this->isFlagSet(self::STATUS_VALID);
|
||||
@@ -213,7 +220,7 @@ abstract class AbstractConsumer implements \Iterator
|
||||
* Waits for a new message from the server generated by one of the active
|
||||
* subscriptions and returns it when available.
|
||||
*
|
||||
* @return array
|
||||
* @return \stdClass
|
||||
*/
|
||||
abstract protected function getValue();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ use Predis\ClientInterface;
|
||||
use Predis\Command\Command;
|
||||
use Predis\Connection\AggregateConnectionInterface;
|
||||
use Predis\NotSupportedException;
|
||||
use Predis\Response\ServerException;
|
||||
use Predis\Response\Error;
|
||||
|
||||
/**
|
||||
* PUB/SUB consumer abstraction.
|
||||
@@ -116,6 +118,10 @@ class Consumer extends AbstractConsumer
|
||||
{
|
||||
$response = $this->client->getConnection()->read();
|
||||
|
||||
if ($response instanceof Error) {
|
||||
throw new ServerException($response->getMessage());
|
||||
}
|
||||
|
||||
switch ($response[0]) {
|
||||
case self::SUBSCRIBE:
|
||||
case self::UNSUBSCRIBE:
|
||||
|
||||
@@ -62,7 +62,8 @@ class ReplicationStrategy
|
||||
}
|
||||
|
||||
if (($eval = $id === 'EVAL') || $id === 'EVALSHA') {
|
||||
$sha1 = $eval ? sha1($command->getArgument(0)) : $command->getArgument(0);
|
||||
$argument = $command->getArgument(0);
|
||||
$sha1 = $eval ? sha1(strval($argument)) : $argument;
|
||||
|
||||
if (isset($this->readonlySHA1[$sha1])) {
|
||||
if (true === $readonly = $this->readonlySHA1[$sha1]) {
|
||||
@@ -89,31 +90,6 @@ class ReplicationStrategy
|
||||
return isset($this->disallowed[$command->getId()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a SORT command is a readable operation by parsing the arguments
|
||||
* array of the specified commad instance.
|
||||
*
|
||||
* @param CommandInterface $command Command instance.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isSortReadOnly(CommandInterface $command)
|
||||
{
|
||||
$arguments = $command->getArguments();
|
||||
$argc = count($arguments);
|
||||
|
||||
if ($argc > 1) {
|
||||
for ($i = 1; $i < $argc; ++$i) {
|
||||
$argument = strtoupper($arguments[$i]);
|
||||
if ($argument === 'STORE') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if BITFIELD performs a read-only operation by looking for certain
|
||||
* SET and INCRYBY modifiers in the arguments array of the command.
|
||||
@@ -292,7 +268,6 @@ class ReplicationStrategy
|
||||
'BITPOS' => true,
|
||||
'TIME' => true,
|
||||
'PFCOUNT' => true,
|
||||
'SORT' => array($this, 'isSortReadOnly'),
|
||||
'BITFIELD' => array($this, 'isBitfieldReadOnly'),
|
||||
'GEOHASH' => true,
|
||||
'GEOPOS' => true,
|
||||
|
||||
@@ -33,7 +33,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
// NOOP
|
||||
@@ -41,7 +43,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->current;
|
||||
@@ -49,7 +53,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
@@ -57,7 +63,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return void
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
if (++$this->position < $this->size) {
|
||||
@@ -67,7 +75,9 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->position < $this->size;
|
||||
@@ -82,6 +92,7 @@ abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInter
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return $this->size;
|
||||
|
||||
@@ -60,7 +60,9 @@ class MultiBulkTuple extends MultiBulk implements \OuterIterator
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return \Iterator
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getInnerIterator()
|
||||
{
|
||||
return $this->iterator;
|
||||
|
||||
@@ -64,7 +64,9 @@ class Handler implements \SessionHandlerInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function open($save_path, $session_id)
|
||||
{
|
||||
// NOOP
|
||||
@@ -73,7 +75,9 @@ class Handler implements \SessionHandlerInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function close()
|
||||
{
|
||||
// NOOP
|
||||
@@ -82,7 +86,9 @@ class Handler implements \SessionHandlerInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return int|bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
// NOOP
|
||||
@@ -91,7 +97,9 @@ class Handler implements \SessionHandlerInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function read($session_id)
|
||||
{
|
||||
if ($data = $this->client->get($session_id)) {
|
||||
@@ -102,7 +110,9 @@ class Handler implements \SessionHandlerInterface
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
$this->client->setex($session_id, $this->ttl, $session_data);
|
||||
@@ -112,7 +122,9 @@ class Handler implements \SessionHandlerInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function destroy($session_id)
|
||||
{
|
||||
$this->client->del($session_id);
|
||||
|
||||
@@ -29,7 +29,7 @@ class AbortedMultiExecException extends PredisException
|
||||
*/
|
||||
public function __construct(MultiExec $transaction, $message, $code = null)
|
||||
{
|
||||
parent::__construct($message, $code);
|
||||
parent::__construct($message, is_null($code) ? 0 : $code);
|
||||
$this->transaction = $transaction;
|
||||
}
|
||||
|
||||
|
||||
@@ -337,4 +337,16 @@ abstract class PredisTestCase extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->checkRequiredRedisServerVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks current test skipped when test suite is running on CI environments.
|
||||
*
|
||||
* @param string $message
|
||||
*/
|
||||
protected function markTestSkippedOnCIEnvironment($message = 'Test skipped on CI environment')
|
||||
{
|
||||
if (getenv('GITHUB_ACTIONS') || getenv('TRAVIS')) {
|
||||
$this->markTestSkipped($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -853,15 +853,17 @@ class ClientTest extends PredisTestCase
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @expectedException \Predis\ClientException
|
||||
* @expectedExceptionMessage The underlying connection is not traversable
|
||||
*/
|
||||
public function testGetIteratorWithNonTraversableConnectionThrowsException()
|
||||
public function testGetIteratorWithNonTraversableConnectionNoException()
|
||||
{
|
||||
$connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
|
||||
$connection = $this->getMockConnection('tcp://127.0.0.1:6381');
|
||||
$client = new Client($connection);
|
||||
|
||||
$client->getIterator();
|
||||
$iterator = $client->getIterator();
|
||||
|
||||
$this->assertInstanceOf('\Predis\Client', $nodeClient = $iterator->current());
|
||||
$this->assertSame($connection, $nodeClient->getConnection());
|
||||
$this->assertSame('127.0.0.1:6381', $iterator->key());
|
||||
}
|
||||
|
||||
// ******************************************************************** //
|
||||
|
||||
@@ -36,4 +36,15 @@ class CRC16Test extends PredisTestCase
|
||||
$this->assertSame(25343, $crc16->hash('key:008'));
|
||||
$this->assertSame(29406, $crc16->hash('key:009'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testHashGenerationWithIntegerValues()
|
||||
{
|
||||
$crc16 = new CRC16();
|
||||
|
||||
$this->assertSame(13907, $crc16->hash(0));
|
||||
$this->assertSame(55177, $crc16->hash(1234));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,8 @@ class HashKeyTest extends PredisTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://github.com/nrk/predis/pull/330
|
||||
* @link https://github.com/nrk/predis/issues/331
|
||||
* @see https://github.com/predis/predis/pull/330
|
||||
* @see https://github.com/predis/predis/issues/331
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIterationWithIntegerFields()
|
||||
|
||||
@@ -57,7 +57,7 @@ class SortedSetKeyTest extends PredisTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://github.com/nrk/predis/issues/216
|
||||
* @see https://github.com/predis/predis/issues/216
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIterationWithIntegerMembers()
|
||||
|
||||
@@ -71,12 +71,24 @@ class ConnectionSelectTest extends PredisCommandTestCase
|
||||
/**
|
||||
* @group connected
|
||||
* @expectedException \Predis\Response\ServerException
|
||||
* @expectedExceptionMessage ERR invalid DB index
|
||||
* @expectedExceptionMessageRegExp /ERR.*DB index/
|
||||
*/
|
||||
public function testThrowsExceptionOnUnexpectedDatabase()
|
||||
public function testThrowsExceptionOnUnexpectedDatabaseRange()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->select(100000000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @expectedException \Predis\Response\ServerException
|
||||
* @expectedExceptionMessageRegExp /ERR (invalid DB index|value is not)/
|
||||
*/
|
||||
public function testThrowsExceptionOnUnexpectedDatabaseName()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->select('x');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class KeyMoveTest extends PredisCommandTestCase
|
||||
/**
|
||||
* @group connected
|
||||
* @expectedException \Predis\Response\ServerException
|
||||
* @expectedExceptionMessage ERR index out of range
|
||||
* @expectedExceptionMessageRegEx /ERR.*out of range/
|
||||
*/
|
||||
public function testThrowsExceptionOnInvalidDatabases()
|
||||
{
|
||||
|
||||
@@ -145,7 +145,7 @@ class PubSubSubscribeByPatternTest extends PredisCommandTestCase
|
||||
/**
|
||||
* @group connected
|
||||
* @expectedException \Predis\Response\ServerException
|
||||
* @expectedExceptionMessageRegExp /ERR only .* allowed in this context/
|
||||
* @expectedExceptionMessageRegExp /ERR.*only .* allowed in this context/
|
||||
*/
|
||||
public function testCannotSendOtherCommandsAfterPsubscribe()
|
||||
{
|
||||
|
||||
@@ -145,7 +145,7 @@ class PubSubSubscribeTest extends PredisCommandTestCase
|
||||
/**
|
||||
* @group connected
|
||||
* @expectedException \Predis\Response\ServerException
|
||||
* @expectedExceptionMessageRegExp /ERR only .* allowed in this context/
|
||||
* @expectedExceptionMessageRegExp /ERR.*only .* allowed in this context/
|
||||
*/
|
||||
public function testCannotSendOtherCommandsAfterSubscribe()
|
||||
{
|
||||
|
||||
@@ -74,10 +74,29 @@ class RawCommandTest extends PredisTestCase
|
||||
* argument is missing, PHP emits an E_WARNING.
|
||||
*
|
||||
* @group disconnected
|
||||
* @expectedException \PHPUnit_Framework_Error_Warning
|
||||
*/
|
||||
public function testPHPWarningOnMissingCommandIDWithStaticCreate()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, "7.1", '>')) {
|
||||
$this->markTestSkipped('only for PHP < 7.1');
|
||||
}
|
||||
$this->setExpectedException('PHPUnit_Framework_Error_Warning');
|
||||
RawCommand::create();
|
||||
}
|
||||
|
||||
/**
|
||||
* The signature of RawCommand::create() requires one argument which is the
|
||||
* ID of the command (other arguments are fetched dinamically). If the first
|
||||
* argument is missing, PHP 7.1 throw an exception
|
||||
*
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPHPWarningOnMissingCommandIDWithStaticCreate71()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, "7.1", '<')) {
|
||||
$this->markTestSkipped('only for PHP > 7.1');
|
||||
}
|
||||
$this->setExpectedException('ArgumentCountError');
|
||||
RawCommand::create();
|
||||
}
|
||||
|
||||
|
||||
@@ -102,13 +102,24 @@ class ServerCommandTest extends PredisCommandTestCase
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$expected = array(array('get', 2, array('readonly', 'fast'), 1, 1, 1));
|
||||
|
||||
// NOTE: starting with Redis 6.0 and the introduction of Access Control
|
||||
// Lists, COMMAND INFO returns an additional array for each specified
|
||||
// command in yhe request with a list of the ACL categories associated
|
||||
// to a command. We simply append this additional array in the expected
|
||||
// response if the test suite is executed against Redis >= 6.0.
|
||||
if ($this->isRedisServerVersion('>=', '6.0')) {
|
||||
$expected[0][] = array('@read', '@string', '@fast');
|
||||
}
|
||||
|
||||
$this->assertCount(1, $response = $redis->command('INFO', 'GET'));
|
||||
|
||||
// NOTE: we use assertEquals instead of assertSame because Redis returns
|
||||
// flags as +STATUS responses, represented by Predis with instances of
|
||||
// Predis\Response\Status instead of plain strings. This class responds
|
||||
// to __toString() so the string conversion is implicit, but assertSame
|
||||
// checks for strict equality while assertEquals is loose.
|
||||
$expected = array(array('get', 2, array('readonly', 'fast'), 1, 1, 1));
|
||||
$this->assertCount(1, $response = $redis->command('INFO', 'GET'));
|
||||
$this->assertEquals($expected, $response);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ class StringIncrementByFloatTest extends PredisCommandTestCase
|
||||
$redis->set('foo', 2);
|
||||
|
||||
// We use round() to avoid errors on some platforms, see the following
|
||||
// issue https://github.com/nrk/predis/issues/220 for reference.
|
||||
// issue https://github.com/predis/predis/issues/220 for reference.
|
||||
$this->assertEquals(22.123, $redis->incrbyfloat('foo', 20.123));
|
||||
$this->assertEquals(10, round($redis->incrbyfloat('foo', -12.123), 5));
|
||||
$this->assertEquals(-100.01, round($redis->incrbyfloat('foo', -110.01), 5));
|
||||
|
||||
@@ -31,26 +31,49 @@ class ReplicationOptionTest extends PredisTestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @return array
|
||||
*/
|
||||
public function testAcceptsValuesThatCanBeInterpretedAsBooleans()
|
||||
public function provideValuesEvaluatingTrue()
|
||||
{
|
||||
return array(array(true), array(1), array('true'), array('on'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider provideValuesEvaluatingTrue
|
||||
*/
|
||||
public function testAcceptsValuesThatCanBeInterpretedAsBooleanTrue($value)
|
||||
{
|
||||
$option = new ReplicationOption();
|
||||
$options = $this->getMock('Predis\Configuration\OptionsInterface');
|
||||
|
||||
$this->assertNull($option->filter($options, null));
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\MasterSlaveReplication', $option->filter($options, $value));
|
||||
}
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\ReplicationInterface', $option->filter($options, true));
|
||||
$this->assertNull($option->filter($options, false));
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function provideValuesEvaluatingFalse()
|
||||
{
|
||||
return array(array(false), array(0), array('false'), array('off'));
|
||||
}
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\ReplicationInterface', $option->filter($options, 1));
|
||||
$this->assertNull($option->filter($options, 0));
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider provideValuesEvaluatingFalse
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Values evaluating to FALSE are not accepted for `replication`
|
||||
*/
|
||||
public function testDoesNotAcceptValuesThatCanBeInterpretedAsBooleanFalse($value)
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.4', '<')) {
|
||||
$this->markTestSkipped('Skip test, because.');
|
||||
}
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\ReplicationInterface', $option->filter($options, 'true'));
|
||||
$this->assertNull($option->filter($options, 'false'));
|
||||
$option = new ReplicationOption();
|
||||
$options = $this->getMock('Predis\Configuration\OptionsInterface');
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\Aggregate\ReplicationInterface', $option->filter($options, 'on'));
|
||||
$this->assertNull($option->filter($options, 'off'));
|
||||
$option->filter($options, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -541,29 +541,6 @@ class MasterSlaveReplicationTest extends PredisTestCase
|
||||
$replication->executeCommand($cmdEval);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSortTriggersSwitchToMasterConnectionOnStoreModifier()
|
||||
{
|
||||
$profile = Profile\Factory::get('dev');
|
||||
$cmdSortNormal = $profile->createCommand('sort', array('key'));
|
||||
$cmdSortStore = $profile->createCommand('sort', array('key', array('store' => 'key:store')));
|
||||
|
||||
$master = $this->getMockConnection('tcp://host1?alias=master');
|
||||
$master->expects($this->once())->method('executeCommand')->with($cmdSortStore);
|
||||
|
||||
$slave1 = $this->getMockConnection('tcp://host2?alias=slave1');
|
||||
$slave1->expects($this->once())->method('executeCommand')->with($cmdSortNormal);
|
||||
|
||||
$replication = new MasterSlaveReplication();
|
||||
$replication->add($master);
|
||||
$replication->add($slave1);
|
||||
|
||||
$replication->executeCommand($cmdSortNormal);
|
||||
$replication->executeCommand($cmdSortStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
|
||||
@@ -245,8 +245,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
public function testGetIteratorReturnsConnectionsMappedInSlotsMapWhenUseClusterSlotsIsDisabled()
|
||||
{
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10922');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10923-16383');
|
||||
$connection4 = $this->getMockConnection('tcp://127.0.0.1:6384');
|
||||
|
||||
$cluster = new RedisCluster(new Connection\Factory());
|
||||
@@ -274,8 +274,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
{
|
||||
$slotsmap = array(
|
||||
array(0, 5460, array('127.0.0.1', 6381), array()),
|
||||
array(5461, 10921, array('127.0.0.1', 6383), array()),
|
||||
array(10922, 16383, array('127.0.0.1', 6384), array()),
|
||||
array(5461, 10922, array('127.0.0.1', 6383), array()),
|
||||
array(10923, 16383, array('127.0.0.1', 6384), array()),
|
||||
);
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
||||
@@ -286,7 +286,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
))
|
||||
->will($this->returnValue($slotsmap));
|
||||
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10922');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383');
|
||||
$connection4 = $this->getMockConnection('tcp://127.0.0.1:6384');
|
||||
|
||||
@@ -398,8 +398,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
public function testCanAssignConnectionsToRangeOfSlotsFromParameters()
|
||||
{
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-5460');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-10921');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10922-16383');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-10922');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10923-16383');
|
||||
|
||||
$cluster = new RedisCluster(new Connection\Factory());
|
||||
$cluster->add($connection1);
|
||||
@@ -410,8 +410,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
|
||||
$expectedMap = array_merge(
|
||||
array_fill(0, 5461, '127.0.0.1:6379'),
|
||||
array_fill(5460, 5461, '127.0.0.1:6380'),
|
||||
array_fill(10921, 5462, '127.0.0.1:6381')
|
||||
array_fill(5460, 5462, '127.0.0.1:6380'),
|
||||
array_fill(10923, 5461, '127.0.0.1:6381')
|
||||
);
|
||||
|
||||
$actualMap = $cluster->getSlotsMap();
|
||||
@@ -426,8 +426,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
public function testCanAssignConnectionsToSingleSlotOrRangesOfSlotsFromParameters()
|
||||
{
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-5460,5500-5600,11000');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-5499,5600-10921');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10922-10999,11001-16383');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-5499,5600-10922');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10923-10999,11001-16383');
|
||||
|
||||
$cluster = new RedisCluster(new Connection\Factory());
|
||||
$cluster->add($connection1);
|
||||
@@ -440,8 +440,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
array_fill(0, 5461, '127.0.0.1:6379'),
|
||||
array_fill(5460, 39, '127.0.0.1:6380'),
|
||||
array_fill(5499, 101, '127.0.0.1:6379'),
|
||||
array_fill(5599, 5321, '127.0.0.1:6380'),
|
||||
array_fill(10921, 78, '127.0.0.1:6381'),
|
||||
array_fill(5599, 5322, '127.0.0.1:6380'),
|
||||
array_fill(10923, 77, '127.0.0.1:6381'),
|
||||
array_fill(11000, 1, '127.0.0.1:6379'),
|
||||
array_fill(11000, 5383, '127.0.0.1:6381')
|
||||
);
|
||||
@@ -555,8 +555,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
{
|
||||
$slotsmap = array(
|
||||
array(0, 5460, array('127.0.0.1', 9381), array()),
|
||||
array(5461, 10921, array('127.0.0.1', 6382), array()),
|
||||
array(10922, 16383, array('127.0.0.1', 6383), array()),
|
||||
array(5461, 10922, array('127.0.0.1', 6382), array()),
|
||||
array(10923, 16383, array('127.0.0.1', 6383), array()),
|
||||
);
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
||||
@@ -569,7 +569,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
|
||||
));
|
||||
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10922');
|
||||
$connection2->expects($this->any())
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
@@ -577,7 +577,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
))
|
||||
->will($this->returnValue($slotsmap));
|
||||
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10923-16383');
|
||||
$connection3->expects($this->any())
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
@@ -720,8 +720,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
{
|
||||
$slotsmap = array(
|
||||
array(0, 5460, array('127.0.0.1', 9381), array()),
|
||||
array(5461, 10921, array('127.0.0.1', 6382), array()),
|
||||
array(10922, 16383, array('127.0.0.1', 6383), array()),
|
||||
array(5461, 10922, array('127.0.0.1', 6382), array()),
|
||||
array(10923, 16383, array('127.0.0.1', 6383), array()),
|
||||
);
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
||||
@@ -734,7 +734,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
|
||||
));
|
||||
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10922');
|
||||
$connection2->expects($this->once())
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
@@ -744,7 +744,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
new Connection\ConnectionException($connection2, 'Unknown connection error [127.0.0.1:6383]')
|
||||
));
|
||||
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10923-16383');
|
||||
$connection3->expects($this->once())
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
@@ -777,8 +777,8 @@ class RedisClusterTest extends PredisTestCase
|
||||
{
|
||||
$slotsmap = array(
|
||||
array(0, 5460, array('127.0.0.1', 9381), array()),
|
||||
array(5461, 10921, array('127.0.0.1', 6382), array()),
|
||||
array(10922, 16383, array('127.0.0.1', 6383), array()),
|
||||
array(5461, 10922, array('127.0.0.1', 6382), array()),
|
||||
array(10923, 16383, array('127.0.0.1', 6383), array()),
|
||||
);
|
||||
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=0-5460');
|
||||
@@ -791,7 +791,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
new Connection\ConnectionException($connection1, 'Unknown connection error [127.0.0.1:6381]')
|
||||
));
|
||||
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10921');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6382?slots=5461-10922');
|
||||
$connection2->expects($this->any())
|
||||
->method('executeCommand')
|
||||
->with($this->isRedisCommand(
|
||||
@@ -801,7 +801,7 @@ class RedisClusterTest extends PredisTestCase
|
||||
new Connection\ConnectionException($connection2, 'Unknown connection error [127.0.0.1:6382]')
|
||||
));
|
||||
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10922-16383');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6383?slots=10923-16383');
|
||||
$connection3->expects($this->never())
|
||||
->method('executeCommand');
|
||||
|
||||
@@ -1142,9 +1142,9 @@ class RedisClusterTest extends PredisTestCase
|
||||
*/
|
||||
public function testCanBeSerialized()
|
||||
{
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-1364');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=1365-2729');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=2730-4095');
|
||||
$connection1 = $this->getMockConnection('tcp://127.0.0.1:6379?slots=0-5460');
|
||||
$connection2 = $this->getMockConnection('tcp://127.0.0.1:6380?slots=5461-10922');
|
||||
$connection3 = $this->getMockConnection('tcp://127.0.0.1:6381?slots=10923-16383');
|
||||
|
||||
$cluster = new RedisCluster(new Connection\Factory());
|
||||
$cluster->add($connection1);
|
||||
|
||||
@@ -36,15 +36,19 @@ class SentinelReplicationTest extends PredisTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParametersForSentinelConnectionShouldNotUseDatabaseAndPassword()
|
||||
public function testParametersForSentinelConnectionShouldNotUseDatabaseAndUsernameAndPassword()
|
||||
{
|
||||
$replication = $this->getReplicationConnection('svc', array(
|
||||
'tcp://127.0.0.1:5381?alias=sentinel1&database=1&password=secret',
|
||||
'tcp://127.0.0.1:5381?alias=sentinel1&database=1&username=myusername&password=secret',
|
||||
));
|
||||
|
||||
$parameters = $replication->getSentinelConnection()->getParameters()->toArray();
|
||||
|
||||
$this->assertArraySubset(array('database' => null, 'password' => null), $parameters);
|
||||
$this->assertArraySubset(array(
|
||||
'database' => null,
|
||||
'username' => null,
|
||||
'password' => null
|
||||
), $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -280,8 +280,6 @@ class FactoryTest extends PredisTestCase
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*
|
||||
* @todo This test smells but there's no other way around it right now.
|
||||
*/
|
||||
public function testCreateConnectionWithInitializationCommands()
|
||||
{
|
||||
@@ -303,6 +301,111 @@ class FactoryTest extends PredisTestCase
|
||||
|
||||
$factory = new Factory();
|
||||
|
||||
// TODO: using reflection to make a protected method accessible :facepalm:
|
||||
$reflection = new \ReflectionObject($factory);
|
||||
$prepareConnection = $reflection->getMethod('prepareConnection');
|
||||
$prepareConnection->setAccessible(true);
|
||||
$prepareConnection->invoke($factory, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCreateConnectionWithPasswordAndNoUsernameAddsInitializationCommandAuthWithOneArgument()
|
||||
{
|
||||
$parameters = new Parameters(array(
|
||||
'password' => 'foobar',
|
||||
));
|
||||
|
||||
$connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
|
||||
$connection->expects($this->once())
|
||||
->method('getParameters')
|
||||
->will($this->returnValue($parameters));
|
||||
$connection->expects($this->once(1))
|
||||
->method('addConnectCommand')
|
||||
->with($this->isRedisCommand('AUTH', array('foobar')));
|
||||
|
||||
$factory = new Factory();
|
||||
|
||||
// TODO: using reflection to make a protected method accessible :facepalm:
|
||||
$reflection = new \ReflectionObject($factory);
|
||||
$prepareConnection = $reflection->getMethod('prepareConnection');
|
||||
$prepareConnection->setAccessible(true);
|
||||
$prepareConnection->invoke($factory, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCreateConnectionWithPasswordAndUsernameAddsInitializationCommandAuthWithTwoArguments()
|
||||
{
|
||||
$parameters = new Parameters(array(
|
||||
'username' => 'myusername',
|
||||
'password' => 'foobar',
|
||||
));
|
||||
|
||||
$connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
|
||||
$connection->expects($this->once())
|
||||
->method('getParameters')
|
||||
->will($this->returnValue($parameters));
|
||||
$connection->expects($this->once(1))
|
||||
->method('addConnectCommand')
|
||||
->with($this->isRedisCommand('AUTH', array('myusername', 'foobar')));
|
||||
|
||||
$factory = new Factory();
|
||||
|
||||
// TODO: using reflection to make a protected method accessible :facepalm:
|
||||
$reflection = new \ReflectionObject($factory);
|
||||
$prepareConnection = $reflection->getMethod('prepareConnection');
|
||||
$prepareConnection->setAccessible(true);
|
||||
$prepareConnection->invoke($factory, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCreateConnectionWithUsernameAndNoPasswordDoesNotAddInitializationCommands()
|
||||
{
|
||||
$parameters = new Parameters(array(
|
||||
'username' => 'myusername',
|
||||
));
|
||||
|
||||
$connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
|
||||
$connection->expects($this->once())
|
||||
->method('getParameters')
|
||||
->will($this->returnValue($parameters));
|
||||
$connection->expects($this->never())
|
||||
->method('addConnectCommand');
|
||||
|
||||
$factory = new Factory();
|
||||
|
||||
// TODO: using reflection to make a protected method accessible :facepalm:
|
||||
$reflection = new \ReflectionObject($factory);
|
||||
$prepareConnection = $reflection->getMethod('prepareConnection');
|
||||
$prepareConnection->setAccessible(true);
|
||||
$prepareConnection->invoke($factory, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider provideEmptyParametersForInitializationCommands
|
||||
*/
|
||||
public function testCreateConnectionWithEmptyParametersDoesNotAddInitializationCommands($parameter, $value)
|
||||
{
|
||||
$parameters = new Parameters(array(
|
||||
$parameter => $value,
|
||||
));
|
||||
|
||||
$connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
|
||||
$connection->expects($this->once())
|
||||
->method('getParameters')
|
||||
->will($this->returnValue($parameters));
|
||||
$connection->expects($this->never())
|
||||
->method('addConnectCommand');
|
||||
|
||||
$factory = new Factory();
|
||||
|
||||
// TODO: using reflection to make a protected method accessible :facepalm:
|
||||
$reflection = new \ReflectionObject($factory);
|
||||
$prepareConnection = $reflection->getMethod('prepareConnection');
|
||||
$prepareConnection->setAccessible(true);
|
||||
@@ -487,4 +590,28 @@ class FactoryTest extends PredisTestCase
|
||||
|
||||
return array($connection, get_class($connection));
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides empty values for specific parameters.
|
||||
*
|
||||
* These parameters usually trigger the addition of initializatin commands
|
||||
* to connection instances like `password` => AUTH and `database` => SELECT,
|
||||
* but they should not be added when their values are NULL or empty strings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provideEmptyParametersForInitializationCommands()
|
||||
{
|
||||
return array(
|
||||
// AUTH
|
||||
array('username', ''),
|
||||
array('username', null),
|
||||
array('password', ''),
|
||||
array('password', null),
|
||||
|
||||
// SELECT
|
||||
array('database', ''),
|
||||
array('database', null),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ class ParametersTest extends PredisTestCase
|
||||
*/
|
||||
public function testParsingURIWithRedisScheme()
|
||||
{
|
||||
$uri = 'redis://:secret@10.10.10.10:6400/5?timeout=0.5&persistent=1';
|
||||
$uri = 'redis://predis:secret@10.10.10.10:6400/5?timeout=0.5&persistent=1';
|
||||
|
||||
$expected = array(
|
||||
'scheme' => 'redis',
|
||||
@@ -164,20 +164,48 @@ class ParametersTest extends PredisTestCase
|
||||
'port' => 6400,
|
||||
'timeout' => '0.5',
|
||||
'persistent' => '1',
|
||||
'username' => 'predis',
|
||||
'password' => 'secret',
|
||||
'database' => '5',
|
||||
);
|
||||
|
||||
$parameters = Parameters::parse($uri);
|
||||
|
||||
// TODO: parse_url() in PHP >= 5.6 returns an empty "user" entry in the
|
||||
// dictionary when no username has been provided in the URI string. This
|
||||
// actually makes sense, but let's keep the test ugly & simple for now.
|
||||
unset($parameters['user']);
|
||||
|
||||
$this->assertSame($expected, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testRedisSchemeOverridesUsernameAndPasswordInQueryString()
|
||||
{
|
||||
$parameters = Parameters::parse('redis://predis:secret@10.10.10.10/5?username=ignored&password=ignored');
|
||||
|
||||
$this->assertSame('predis', $parameters['username']);
|
||||
$this->assertSame('secret', $parameters['password']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testRedisSchemeDoesNotOverridesUsernameAndPasswordInQueryStringOnEmptyAuthFragment()
|
||||
{
|
||||
$parameters = Parameters::parse('redis://:@10.10.10.10/5?username=predis&password=secret');
|
||||
|
||||
$this->assertSame('predis', $parameters['username']);
|
||||
$this->assertSame('secret', $parameters['password']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testRedisSchemeOverridesDatabaseInQueryString()
|
||||
{
|
||||
$parameters = Parameters::parse('redis://10.10.10.10/5?database=10');
|
||||
|
||||
$this->assertSame('5', $parameters['database']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
@@ -197,17 +225,6 @@ class ParametersTest extends PredisTestCase
|
||||
$this->assertSame($expected, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testRedisSchemeOverridesPasswordAndDatabaseInQueryString()
|
||||
{
|
||||
$parameters = Parameters::parse('redis://:secret@10.10.10.10/5?password=ignored&database=4');
|
||||
|
||||
$this->assertSame('secret', $parameters['password']);
|
||||
$this->assertSame('5', $parameters['database']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
|
||||
@@ -76,6 +76,28 @@ class PhpiredisSocketConnectionTest extends PredisConnectionTestCase
|
||||
// ---- INTEGRATION TESTS --------------------------------------------- //
|
||||
// ******************************************************************** //
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
*/
|
||||
public function testClearsPendingResponsesInReaderBufferOnDisconnect()
|
||||
{
|
||||
$profile = $this->getCurrentProfile();
|
||||
$connection = $this->createConnection();
|
||||
|
||||
$cmdECHOBefore = $profile->createCommand('echo', array('BEFORE DISCONNECT'));
|
||||
$cmdECHOAfter = $profile->createCommand('echo', array('AFTER DISCONNECT'));
|
||||
|
||||
$connection->writeRequest($cmdECHOBefore);
|
||||
$connection->writeRequest($cmdECHOBefore);
|
||||
$connection->writeRequest($cmdECHOBefore);
|
||||
$connection->readResponse($cmdECHOBefore);
|
||||
$connection->disconnect();
|
||||
|
||||
$response = $connection->executeCommand($cmdECHOAfter);
|
||||
|
||||
$this->assertSame('AFTER DISCONNECT', $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @expectedException \Predis\Connection\ConnectionException
|
||||
|
||||
@@ -76,6 +76,28 @@ class PhpiredisStreamConnectionTest extends PredisConnectionTestCase
|
||||
// ---- INTEGRATION TESTS --------------------------------------------- //
|
||||
// ******************************************************************** //
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
*/
|
||||
public function testClearsPendingResponsesInReaderBufferOnDisconnect()
|
||||
{
|
||||
$profile = $this->getCurrentProfile();
|
||||
$connection = $this->createConnection();
|
||||
|
||||
$cmdECHOBefore = $profile->createCommand('echo', array('BEFORE DISCONNECT'));
|
||||
$cmdECHOAfter = $profile->createCommand('echo', array('AFTER DISCONNECT'));
|
||||
|
||||
$connection->writeRequest($cmdECHOBefore);
|
||||
$connection->writeRequest($cmdECHOBefore);
|
||||
$connection->writeRequest($cmdECHOBefore);
|
||||
$connection->readResponse($cmdECHOBefore);
|
||||
$connection->disconnect();
|
||||
|
||||
$response = $connection->executeCommand($cmdECHOAfter);
|
||||
|
||||
$this->assertSame('AFTER DISCONNECT', $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group slow
|
||||
|
||||
@@ -234,6 +234,59 @@ class PipelineTest extends PredisTestCase
|
||||
$this->assertSame(array('one', 'two', 'three', 'four'), $pipeline->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testClearBuffer()
|
||||
{
|
||||
$connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
|
||||
$connection->expects($this->never())
|
||||
->method('writeRequest');
|
||||
$connection->expects($this->never())
|
||||
->method('readResponse')
|
||||
->will($this->returnCallback($this->getReadCallback()));
|
||||
|
||||
$pipeline = new Pipeline(new Client($connection));
|
||||
|
||||
$pipeline->echo('one');
|
||||
$pipeline->echo('two');
|
||||
|
||||
$pipeline->clear();
|
||||
|
||||
$this->assertSame(array(), $pipeline->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testClearResponses()
|
||||
{
|
||||
$connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
|
||||
$connection->expects($this->exactly(4))
|
||||
->method('writeRequest');
|
||||
$connection->expects($this->exactly(4))
|
||||
->method('readResponse')
|
||||
->will($this->returnCallback($this->getReadCallback()));
|
||||
|
||||
$pipeline = new Pipeline(new Client($connection));
|
||||
|
||||
$pipeline->echo('one');
|
||||
$pipeline->echo('two');
|
||||
|
||||
$this->assertSame(array('one', 'two'), $pipeline->execute());
|
||||
|
||||
$pipeline->clear();
|
||||
|
||||
$pipeline->echo('three');
|
||||
$pipeline->echo('four');
|
||||
|
||||
$this->assertSame(array('three', 'four'), $pipeline->execute());
|
||||
|
||||
$pipeline->clear();
|
||||
|
||||
$this->assertSame(array(), $pipeline->execute());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
|
||||
@@ -297,11 +297,25 @@ class ConsumerTest extends PredisTestCase
|
||||
// ---- INTEGRATION TESTS --------------------------------------------- //
|
||||
// ******************************************************************** //
|
||||
|
||||
// NOTE: the following 2 tests fail at random without any apparent reason
|
||||
// when executed on our CI environments and these failures are not tied
|
||||
// to a particular version of PHP or Redis. It is most likely some weird
|
||||
// timing issue on busy systems as it is really rare to get it triggered
|
||||
// locally. The chances it is a bug in the library are pretty low so for
|
||||
// now we just mark this test skipped on our CI environments (but still
|
||||
// enabled for local test runs) and "debug" this issue using a separate
|
||||
// branch to avoid having spurious failures on main development branches
|
||||
// which is utterly annoying.
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
*/
|
||||
public function testPubSubAgainstRedisServer()
|
||||
{
|
||||
$this->markTestSkippedOnCIEnvironment(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test' // TODO
|
||||
);
|
||||
|
||||
$parameters = array(
|
||||
'host' => REDIS_SERVER_HOST,
|
||||
'port' => REDIS_SERVER_PORT,
|
||||
@@ -347,6 +361,10 @@ class ConsumerTest extends PredisTestCase
|
||||
*/
|
||||
public function testPubSubAgainstRedisServerBlocking()
|
||||
{
|
||||
$this->markTestSkippedOnCIEnvironment(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test' // TODO
|
||||
);
|
||||
|
||||
$parameters = array(
|
||||
'host' => REDIS_SERVER_HOST,
|
||||
'port' => REDIS_SERVER_PORT,
|
||||
@@ -408,4 +426,32 @@ class ConsumerTest extends PredisTestCase
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @expectedException \Predis\Response\ServerException
|
||||
*/
|
||||
public function testInvalidSubscriptionThrowsServerException()
|
||||
{
|
||||
$parameters = array(
|
||||
'host' => REDIS_SERVER_HOST,
|
||||
'port' => REDIS_SERVER_PORT,
|
||||
'database' => REDIS_SERVER_DBNUM,
|
||||
'read_write_timeout' => -1, // -1 to set blocking reads
|
||||
);
|
||||
|
||||
$options = array('profile' => REDIS_SERVER_VERSION);
|
||||
|
||||
$consumer = new Client($parameters, $options);
|
||||
$consumer->connect();
|
||||
|
||||
$pubsub = $consumer->pubSubLoop();
|
||||
|
||||
// calling psubscribe with invalid empty parameter list
|
||||
$pubsub->psubscribe(array());
|
||||
|
||||
foreach ($pubsub as $message) {
|
||||
// should trigger the ServerException
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,25 @@ class DispatcherLoopTest extends PredisTestCase
|
||||
// ---- INTEGRATION TESTS --------------------------------------------- //
|
||||
// ******************************************************************** //
|
||||
|
||||
// NOTE: the following 2 tests fail at random without any apparent reason
|
||||
// when executed on our CI environments and these failures are not tied
|
||||
// to a particular version of PHP or Redis. It is most likely some weird
|
||||
// timing issue on busy systems as it is really rare to get it triggered
|
||||
// locally. The chances it is a bug in the library are pretty low so for
|
||||
// now we just mark this test skipped on our CI environments (but still
|
||||
// enabled for local test runs) and "debug" this issue using a separate
|
||||
// branch to avoid having spurious failures on main development branches
|
||||
// which is utterly annoying.
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
*/
|
||||
public function testDispatcherLoopAgainstRedisServer()
|
||||
{
|
||||
$this->markTestSkippedOnCIEnvironment(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test' // TODO
|
||||
);
|
||||
|
||||
$parameters = array(
|
||||
'host' => REDIS_SERVER_HOST,
|
||||
'port' => REDIS_SERVER_PORT,
|
||||
@@ -87,6 +101,10 @@ class DispatcherLoopTest extends PredisTestCase
|
||||
*/
|
||||
public function testDispatcherLoopAgainstRedisServerWithPrefix()
|
||||
{
|
||||
$this->markTestSkippedOnCIEnvironment(
|
||||
'Test temporarily skipped on CI environments, see note in the body of the test' // TODO
|
||||
);
|
||||
|
||||
$parameters = array(
|
||||
'host' => REDIS_SERVER_HOST,
|
||||
'port' => REDIS_SERVER_PORT,
|
||||
|
||||
@@ -81,15 +81,15 @@ class ReplicationStrategyTest extends PredisTestCase
|
||||
$profile = Profile\Factory::getDevelopment();
|
||||
$strategy = new ReplicationStrategy();
|
||||
|
||||
$cmdReadSort = $profile->createCommand('SORT', array('key:list'));
|
||||
$this->assertTrue(
|
||||
$strategy->isReadOperation($cmdReadSort),
|
||||
'SORT is expected to be a read operation.'
|
||||
$cmdReturnSort = $profile->createCommand('SORT', array('key:list'));
|
||||
$this->assertFalse(
|
||||
$strategy->isReadOperation($cmdReturnSort),
|
||||
'SORT is expected to be a write operation.'
|
||||
);
|
||||
|
||||
$cmdWriteSort = $profile->createCommand('SORT', array('key:list', array('store' => 'key:stored')));
|
||||
$cmdStoreSort = $profile->createCommand('SORT', array('key:list', array('store' => 'key:stored')));
|
||||
$this->assertFalse(
|
||||
$strategy->isReadOperation($cmdWriteSort),
|
||||
$strategy->isReadOperation($cmdStoreSort),
|
||||
'SORT with STORE is expected to be a write operation.'
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ $ phpunit --exclude-group slow
|
||||
### Testing Redis commands ###
|
||||
|
||||
We also provide an helper script in the `bin` directory that can be used to automatically generate a
|
||||
file with the scheleton of a test case to test a Redis command by specifying the name of the class
|
||||
file with the skeleton of a test case to test a Redis command by specifying the name of the class
|
||||
in the `Predis\Command` namespace (only classes in this namespace are considered valid). For example
|
||||
to generate a test case for `SET` (represented by the `Predis\Command\StringSet` class):
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* Apply patches to support newer PHP versions.
|
||||
*/
|
||||
|
||||
$patches = array(
|
||||
'phpunit_mock_objects.patch' => 'phpunit/phpunit-mock-objects',
|
||||
'phpunit_php7.patch' => 'phpunit/phpunit',
|
||||
'phpunit_php8.patch' => 'phpunit/phpunit',
|
||||
'phpunit_php81.patch' => 'phpunit/phpunit',
|
||||
);
|
||||
|
||||
foreach ($patches as $patch => $package) {
|
||||
chdir(__DIR__.'/../vendor/'.$package);
|
||||
passthru(sprintf('patch -p1 -f < ../../../tests/%s', $patch));
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
diff --git a/src/Framework/MockObject/Generator.php b/src/Framework/MockObject/Generator.php
|
||||
index 3df3abb..fc76d5d 100644
|
||||
--- a/src/Framework/MockObject/Generator.php
|
||||
+++ b/src/Framework/MockObject/Generator.php
|
||||
@@ -1031,16 +1031,29 @@ protected function getMethodParameters(ReflectionMethod $method, $forCall = fals
|
||||
$typeDeclaration = '';
|
||||
|
||||
if (!$forCall) {
|
||||
+ if (PHP_VERSION_ID >= 80000) {
|
||||
+ $isArray = $parameter->getType() && $parameter->getType()->getName() === 'array';
|
||||
+ $isCallable = $parameter->getType() && $parameter->getType()->getName() === 'callable';
|
||||
+ } else {
|
||||
+ $isArray = $parameter->isArray();
|
||||
+ $isCallable = version_compare(PHP_VERSION, '5.4.0', '>=') ? $parameter->isCallable() : false;
|
||||
+ }
|
||||
+
|
||||
if ($this->hasType($parameter)) {
|
||||
- $typeDeclaration = (string) $parameter->getType() . ' ';
|
||||
- } elseif ($parameter->isArray()) {
|
||||
+ $type = $parameter->getType();
|
||||
+ $typeDeclaration = (PHP_VERSION_ID >= 70100 ? $type->getName() : (string) $type) . ' ';
|
||||
+ } elseif ($isArray) {
|
||||
$typeDeclaration = 'array ';
|
||||
} elseif ((defined('HHVM_VERSION') || version_compare(PHP_VERSION, '5.4.0', '>='))
|
||||
- && $parameter->isCallable()) {
|
||||
+ && $isCallable) {
|
||||
$typeDeclaration = 'callable ';
|
||||
} else {
|
||||
try {
|
||||
- $class = $parameter->getClass();
|
||||
+ if (PHP_VERSION_ID >= 80000) {
|
||||
+ $class = $parameter->getType();
|
||||
+ } else {
|
||||
+ $class = $parameter->getClass();
|
||||
+ }
|
||||
} catch (ReflectionException $e) {
|
||||
throw new PHPUnit_Framework_MockObject_RuntimeException(
|
||||
sprintf(
|
||||
@@ -0,0 +1,60 @@
|
||||
diff --git a/src/Util/Getopt.php b/src/Util/Getopt.php
|
||||
index ba21be3..96931a3 100644
|
||||
--- a/src/Util/Getopt.php
|
||||
+++ b/src/Util/Getopt.php
|
||||
@@ -35,7 +35,15 @@ class PHPUnit_Util_Getopt
|
||||
reset($args);
|
||||
array_map('trim', $args);
|
||||
|
||||
- while (list($i, $arg) = each($args)) {
|
||||
+ while (true) {
|
||||
+ $arg = current($args);
|
||||
+ $i = key($args);
|
||||
+ next($args);
|
||||
+
|
||||
+ if ($arg === false) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
if ($arg == '') {
|
||||
continue;
|
||||
}
|
||||
@@ -94,11 +102,14 @@ class PHPUnit_Util_Getopt
|
||||
if ($i + 1 < $argLen) {
|
||||
$opts[] = array($opt, substr($arg, $i + 1));
|
||||
break;
|
||||
- } elseif (list(, $opt_arg) = each($args)) {
|
||||
} else {
|
||||
- throw new PHPUnit_Framework_Exception(
|
||||
- "option requires an argument -- $opt"
|
||||
- );
|
||||
+ $opt_arg = current($args);
|
||||
+ next($args);
|
||||
+ if ($opt_arg === false) {
|
||||
+ throw new PHPUnit_Framework_Exception(
|
||||
+ "option requires an argument -- $opt"
|
||||
+ );
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,11 +150,14 @@ class PHPUnit_Util_Getopt
|
||||
|
||||
if (substr($long_opt, -1) == '=') {
|
||||
if (substr($long_opt, -2) != '==') {
|
||||
- if (!strlen($opt_arg) &&
|
||||
- !(list(, $opt_arg) = each($args))) {
|
||||
- throw new PHPUnit_Framework_Exception(
|
||||
- "option --$opt requires an argument"
|
||||
- );
|
||||
+ if (!strlen($opt_arg)) {
|
||||
+ $opt_arg = current($args);
|
||||
+ next($args);
|
||||
+ if ($opt_arg === false) {
|
||||
+ throw new PHPUnit_Framework_Exception(
|
||||
+ "option --$opt requires an argument"
|
||||
+ );
|
||||
+ }
|
||||
}
|
||||
}
|
||||
} elseif ($opt_arg) {
|
||||
@@ -0,0 +1,39 @@
|
||||
diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php
|
||||
index 638f42513..b4c7d3a5e 100644
|
||||
--- a/src/Framework/TestCase.php
|
||||
+++ b/src/Framework/TestCase.php
|
||||
@@ -905,7 +905,7 @@ protected function runTest()
|
||||
try {
|
||||
$testResult = $method->invokeArgs(
|
||||
$this,
|
||||
- array_merge($this->data, $this->dependencyInput)
|
||||
+ array_values(array_merge($this->data, $this->dependencyInput))
|
||||
);
|
||||
} catch (Throwable $_e) {
|
||||
$e = $_e;
|
||||
diff --git a/src/Util/Configuration.php b/src/Util/Configuration.php
|
||||
index 5c1041608..b2f7a7bd0 100644
|
||||
--- a/src/Util/Configuration.php
|
||||
+++ b/src/Util/Configuration.php
|
||||
@@ -162,7 +162,7 @@ protected function __construct($filename)
|
||||
/**
|
||||
* @since Method available since Release 3.4.0
|
||||
*/
|
||||
- final private function __clone()
|
||||
+ private function __clone()
|
||||
{
|
||||
}
|
||||
|
||||
diff --git a/src/Util/PHP/Template/TestCaseMethod.tpl.dist b/src/Util/PHP/Template/TestCaseMethod.tpl.dist
|
||||
index b48f354cd..d59cdeea7 100644
|
||||
--- a/src/Util/PHP/Template/TestCaseMethod.tpl.dist
|
||||
+++ b/src/Util/PHP/Template/TestCaseMethod.tpl.dist
|
||||
@@ -78,7 +78,7 @@ if ('' !== $configurationFilePath) {
|
||||
unset($configuration);
|
||||
}
|
||||
|
||||
-function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext)
|
||||
+function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
diff --git a/src/Util/Configuration.php b/src/Util/Configuration.php
|
||||
index b2f7a7bd0c6..1a1a444d378 100644
|
||||
--- a/src/Util/Configuration.php
|
||||
+++ b/src/Util/Configuration.php
|
||||
@@ -514,22 +514,17 @@ public function handlePHPConfiguration()
|
||||
|
||||
foreach (array('var', 'post', 'get', 'cookie', 'server', 'files', 'request') as $array) {
|
||||
// See https://github.com/sebastianbergmann/phpunit/issues/277
|
||||
- switch ($array) {
|
||||
- case 'var':
|
||||
- $target = &$GLOBALS;
|
||||
- break;
|
||||
-
|
||||
- case 'server':
|
||||
- $target = &$_SERVER;
|
||||
- break;
|
||||
-
|
||||
- default:
|
||||
- $target = &$GLOBALS['_' . strtoupper($array)];
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
foreach ($configuration[$array] as $name => $value) {
|
||||
- $target[$name] = $value;
|
||||
+ switch ($array) {
|
||||
+ case 'var':
|
||||
+ $GLOBALS[$name] = $value;
|
||||
+ break;
|
||||
+ case 'server':
|
||||
+ $_SERVER[$name] = $value;
|
||||
+ break;
|
||||
+ default:
|
||||
+ $GLOBALS['_' . strtoupper($array)][$name] = $value;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/Util/Getopt.php b/src/Util/Getopt.php
|
||||
index 370e97d73f8..5bd8553970b 100644
|
||||
--- a/src/Util/Getopt.php
|
||||
+++ b/src/Util/Getopt.php
|
||||
@@ -144,7 +144,7 @@ protected static function parseLongOption($arg, $long_options, &$opts, &$args)
|
||||
|
||||
if (substr($long_opt, -1) == '=') {
|
||||
if (substr($long_opt, -2) != '==') {
|
||||
- if (!strlen($opt_arg)) {
|
||||
+ if (null === $opt_arg || strlen($opt_arg) < 1) {
|
||||
$opt_arg = current($args);
|
||||
next($args);
|
||||
if (!($opt_arg)) {
|
||||
Reference in New Issue
Block a user