Compare commits

...

60 Commits

Author SHA1 Message Date
Till Krüss c5b60884e8 tag v2.1.1 2023-01-17 12:57:35 -08:00
Till Krüss 836ab07d40 Fix support options array in ZINTERSTORE and ZUNIONSTORE (#1018)
* support old options array

* fix zinterstore as well

* single override
2023-01-17 12:50:15 -08:00
Fran Moreno 2bfc8a9316 Fix Client template (#1017) 2023-01-17 12:48:39 -08:00
Till Krüss d21576669d remove phpiredis suggestion 2023-01-17 09:42:23 -08:00
Till Krüss 65fe24e346 bump version 2023-01-17 09:42:15 -08:00
Till Krüss fbf270156f tweak workflow formatting 2023-01-16 15:33:05 -08:00
Till Krüss 24630fbd5b tag 2.1.0 2023-01-16 15:21:30 -08:00
Till Krüss bded73f516 tweak release draft template [skip ci] 2023-01-16 15:21:19 -08:00
Till Krüss 6a3f4a4c08 Update release-drafter-config.yml 2023-01-16 15:12:32 -08:00
Vladyslav Vildanov 47896f1be7 Extended core support by implementing BLMPOP command (#1015)
* Added support for LMPOP command

* Added support for BLMPOP command

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2023-01-16 08:46:59 -08:00
erikn69 586b039289 Fix builds badge (#1014) 2023-01-16 08:45:19 -08:00
Vladyslav Vildanov d1acca607e Added support for LMPOP command (#1013)
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2023-01-16 08:45:04 -08:00
Vladyslav Vildanov 721c829042 Extended server functionality by implementing FAILOVER command (#875)
* Added arrayable arguments classes

* Updated count trait to accept additional modifier argument

* Added new With traits, moved into separate directory

* Added traits for geo command arguments resolving

* Remove BaseWith trait, not working with traits nesting

* Added AscDesc trait

* Rename AscDesc traint into Sorting

* Fixed trait keyword

* Removed unnecessary traits

* Updated count trait to handle default argument value

* Fixed With traits offset check condition

* Added GeoFrom, GeoBy traits

* Fixes for Count and Sorting traits

* Fixed variable names in with traits

* [WIP] Added GEOSEARCH command support, without test coverage

* Changed functionality to support only lower case units (Redis 6.0 support)

* Changed namespace for WITHSCORES trait

* Added more test coverage, added response parsing

* Added test coverage for tratis

* Added support for FAILOVER command

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-29 12:19:17 -08:00
Vladyslav Vildanov b8cc7c643a Extended Key Space support by implementing COPY command (#866)
* Added support for COPY command

* Revert getCLient signature changes, used createClient instead

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-29 12:18:08 -08:00
Vladyslav Vildanov b47aa7ad3a Extended Geospatial support by implementing GEOSEARCHSTORE command (#873)
* Added arrayable arguments classes

* Updated count trait to accept additional modifier argument

* Added new With traits, moved into separate directory

* Added traits for geo command arguments resolving

* Remove BaseWith trait, not working with traits nesting

* Added AscDesc trait

* Rename AscDesc traint into Sorting

* Fixed trait keyword

* Removed unnecessary traits

* Updated count trait to handle default argument value

* Fixed With traits offset check condition

* Added GeoFrom, GeoBy traits

* Fixes for Count and Sorting traits

* Fixed variable names in with traits

* [WIP] Added GEOSEARCH command support, without test coverage

* Changed functionality to support only lower case units (Redis 6.0 support)

* Changed namespace for WITHSCORES trait

* Added more test coverage, added response parsing

* Added support for GEOSEARCHSTORE command

* Added test coverage for tratis

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
Co-authored-by: Chayim <chayim@users.noreply.github.com>
2022-12-29 12:16:51 -08:00
Vladyslav Vildanov bb65b31580 Extend Geospatial support by implementing GEOSEARCH command (#867)
* Added arrayable arguments classes

* Updated count trait to accept additional modifier argument

* Added new With traits, moved into separate directory

* Added traits for geo command arguments resolving

* Remove BaseWith trait, not working with traits nesting

* Added AscDesc trait

* Rename AscDesc traint into Sorting

* Fixed trait keyword

* Removed unnecessary traits

* Updated count trait to handle default argument value

* Fixed With traits offset check condition

* Added GeoFrom, GeoBy traits

* Fixes for Count and Sorting traits

* Fixed variable names in with traits

* [WIP] Added GEOSEARCH command support, without test coverage

* Changed functionality to support only lower case units (Redis 6.0 support)

* Changed namespace for WITHSCORES trait

* Added more test coverage, added response parsing

* Added test coverage for tratis

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
Co-authored-by: Chayim <chayim@users.noreply.github.com>
2022-12-28 09:10:03 -08:00
Vladyslav Vildanov e540ca062b Added support for SISMEMBER command (#871)
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-23 11:18:50 -08:00
Vladyslav Vildanov 9c543ef5b1 Added support for HRANDFIELD command (#870)
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-23 11:18:33 -08:00
Vladyslav Vildanov 83fb0020a1 Added support for GETEX command (#872)
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-23 11:17:24 -08:00
Vladyslav Vildanov 4a77aea9ef Added support for GETDEL command (#869)
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-20 09:32:58 -08:00
Vladyslav Vildanov 72bc11eb63 Extended Sorted Set support by adding ZINTER command (#859)
* Refactored zinterstore, zunionstore commands and command traits

* Merge conflicts resolve, update cluster strategy test with new arguments

* Updated assertion in case if command executed faster then duration minimal threshold

* Added support for ZINTER command

* Added command link and description

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-14 08:54:30 -08:00
Vladyslav Vildanov f26fd899f9 Extend Sorted Set support by implementing BZPOPMAX command (#864)
* Refactored zinterstore, zunionstore commands and command traits

* Merge conflicts resolve, update cluster strategy test with new arguments

* Updated assertion in case if command executed faster then duration minimal threshold

* Updated Keys trait to handle cases when no numkeys modifier needed

* Added support for BZPOPMIN command

* Added command link and description

* Added support for BZPOPMAX command

* Added absract command to fix inheritance chain

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-13 10:26:47 -08:00
Vladyslav Vildanov 65447807ed Extended Sorted Set support by adding ZUNION command (#860)
* Refactored zinterstore, zunionstore commands and command traits

* Merge conflicts resolve, update cluster strategy test with new arguments

* Updated assertion in case if command executed faster then duration minimal threshold

* Added support for ZUNION command

* Added command link and description

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-13 08:41:56 -08:00
Vladyslav Vildanov 5418cd5813 Extended Sorted Set support by adding BLMOVE command (#865)
* Added support for LMOVE command

* Added support for BLMOVE command

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-13 08:38:35 -08:00
Vladyslav Vildanov bc5892e260 Extended Sorted Set support by adding BZPOPMIN command (#862)
* Refactored zinterstore, zunionstore commands and command traits

* Merge conflicts resolve, update cluster strategy test with new arguments

* Updated assertion in case if command executed faster then duration minimal threshold

* Updated Keys trait to handle cases when no numkeys modifier needed

* Added support for BZPOPMIN command

* Added command link and description

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-12 08:38:26 -08:00
Vladyslav Vildanov 7c0cc7bdbe Extended Sorted Set support by adding ZINTERCARD command (#861)
* Refactored zinterstore, zunionstore commands and command traits

* Merge conflicts resolve, update cluster strategy test with new arguments

* Updated assertion in case if command executed faster then duration minimal threshold

* Updated Limit trait to handle integer values

* Updated Limit trait to return previous arguments if limit argument isn't set

* Added support for ZINTERCARD command

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-12 08:38:06 -08:00
Vladyslav Vildanov 87ecd7bde2 Added support for LMOVE command (#863)
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-12 08:37:28 -08:00
Vladyslav Vildanov f9e77a0562 Refactored ZUNIONSTORE, ZINTERSTORE, removed Numkeys trait, improved Withscores response parsing (#858) 2022-12-09 07:37:22 -08:00
Vladyslav Vildanov d2b05ff58a Added mixin annotations for traits (#835)
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-07 09:19:04 -08:00
Chayim e24ecbbf90 Fixing the release drafter, to honour the main branch (#857) 2022-12-07 12:08:16 +02:00
Vladyslav Vildanov 9f3bdd5616 Extend Sorted Set support by implementing ZRANGESTORE command (#829)
* Added support for ZRANGESTORE command

* Added missing test coverage, added new filter method

* Added test coverage for new traits

* Updated offset for unexpected value tests

* Added missing offset

* Removed version annotation for inconsistency

* Update ZDIFF.php

* Update ZRANGESTORE.php

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-12-06 08:40:06 -08:00
Chayim 57a24413c2 Adding the release drafter for release management (#811) 2022-12-05 09:25:47 -08:00
Vladyslav Vildanov 0e6126ba9d Added support for BZMPOP command (#833) 2022-12-05 08:33:43 -08:00
Vladyslav Vildanov 7904fed6da Added support for ZMPOP command (#831)
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-12-01 12:10:20 -08:00
Vladyslav Vildanov cfd6f030bc Extend Sorted Set support by implementing ZDIFFSTORE command (#828)
* Added support for ZDIFF, added new WITHSCORE trait

* Added trait to resolve numkeys argument

* Added trait for unpacking keys array

* Added static binding for Numkeys trait

* Added ZDIFFSTORE command support

* Changed access modifier type to more strict

* Removed version annotation for inconsistency

* Update ZDIFFSTORE.php

Co-authored-by: Vladislav <vladislav@Admins-MacBook-Pro.local>
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-11-30 13:23:08 -08:00
Vladyslav Vildanov 35d18d739a Added response parser for WithScore trait (#830)
* Added response parser for WithScore trait

* Added test coverage for new method

Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-11-30 13:20:56 -08:00
Vladyslav Vildanov 573c54ac4c Extend Sorted Set support by implementing ZDIFF command (#826)
* Added support for ZDIFF, added new WITHSCORE trait

* Added trait to resolve numkeys argument

* Added trait for unpacking keys array

* Added static binding for Numkeys trait

* Changed access modifier type to more strict

Co-authored-by: Vladislav <vladislav@Admins-MacBook-Pro.local>
Co-authored-by: Vladyslav Vildanov <vladyslavvildanov@Vladyslav-Vildanov-MacBook-Pro.local>
2022-11-28 13:16:30 -08:00
Vladyslav Vildanov 169604bf74 Extend Sorted Set support by implementing ZRANDMEMBER command (#825)
* Added support for ZRANDMEMBER, added test coverage

* Changed key to more specific on exception testing

Co-authored-by: Vladislav <vladislav@Admins-MacBook-Pro.local>
2022-11-21 08:37:04 -08:00
vladvildanov 94bd54b2af Extend Sorted Set support by implementing ZMSCORE (#823) 2022-11-17 07:48:16 -08:00
Josh England bdebad7d62 added annotations to suppress PHP 8.1 return type deprecation warning… (#810)
* added annotations to suppress PHP 8.1 return type deprecation warning for `IteratorAggregate::toIterator()`

* move import

Co-authored-by: Josh England <joshua.england@trinet.com>
Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-11-02 17:07:32 -07:00
Till Krüss 1a411d2ad7 back to dev version 2022-10-11 09:57:28 -07:00
Till Krüss 1400689df8 switch to actions/checkout@v3 [skip ci] 2022-10-11 09:56:57 -07:00
Till Krüss ff59f74581 bump version 2022-10-11 09:52:29 -07:00
Mehmet Tolga Avcioglu 1a0eb7ee85 sentinel default to no password when replica authentication is used (#807)
* set default nopass if password is not provided

* remove debug output

* added tests for noauth sentinel

* Update SentinelReplication.php

Co-authored-by: Mehmet Tolga Avcioglu <mehmet.avcioglu@pusula.net.tr>
Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-10-06 08:14:47 -07:00
Tobias Bengtsson bb6c1a7b6d Add partial support for Stream commands (#804)
Adds support for the following commands not related to groups/consumers:
XADD, XDEL, XLEN, XRANGE, XREVRANGE, XTRIM.
2022-09-27 09:39:17 -07:00
streamingsystems 42c33a406f Check if socket is resource before reading (#802) 2022-09-18 15:01:02 -07:00
Stefano Borghi 7ad74217c2 check resource before writing, w/ test (#800) 2022-09-16 17:03:13 -07:00
Alexandru Pătrănescu 72d7675563 improve PHP 8.2 support (#797)
* use static::class . '::<method>' instead of 'static::method'

* extract concatenation so it can be improved easier at a later point

one improvement at some point could be to replace static::class . '::<method>' with static::<method>(...) in PHP 8.1+

Co-authored-by: Alexandru Patranescu <alexandru.patranescu@payu.com>
2022-09-08 11:25:37 -07:00
Till Krüss 8b5fa92856 mention php version [no ci] 2022-09-06 07:34:14 -07:00
Till Krüss 076a62e3d3 bump version to 2.0.2 2022-09-06 07:32:44 -07:00
Dries Vints 85dc1752b7 PHP 8.2 Support (#796) 2022-09-06 07:13:46 -07:00
Till Krüss 7683215023 bump tag to v2.0.1 2022-09-04 14:57:36 -07:00
Till Krüss dd4ddf6504 Update CHANGELOG.md 2022-08-03 11:53:59 -07:00
Till Krüss 6468e6cc2e Update CHANGELOG.md 2022-08-03 11:51:22 -07:00
digital2real 12f6d30502 Add retry logic to support temporary redis cluster failure (#788)
* Add retry logic to support temporary redis cluster failure

* formatting

* Use exponential backoff retries.
$minRetryAfter cant be changed if needed with RedisCluster::setMinRetryAfter

* fix formating

* (hotfix) formaing + import import  \Predis\Response\Error and \Predis\Connection\ConnectionException

* Update RedisCluster.php

* formatting

* tweaks

* Update RedisCluster.php

* Update RedisCluster.php

* Update RedisCluster.php

* Update unit tests regarding renaming of "retryInterval"

* spacing

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-08-03 11:49:56 -07:00
Linc a602fc7425 Fix sentinel reconnect issue on long-running PHP processes (#784)
* Fix no sentinel server available for autodiscovery on Swoole

* Fix errors in PHPUnit 9.5

the reason: https://github.com/sebastianbergmann/phpunit/commit/68582043e149039cfa3596b42ed35753dcf54fb2?fbclid=IwAR1qy1jFNFeJ3hUt5k7XAflqyCTUTWZhEqVSeizkybVHUEqXjBZ1gJM739U

* add restart sentinel test
2022-07-12 09:44:28 -07:00
John Paul E. Balandan, CPA 4ec74cc5fe Declare $parameters property of Predis\Connection\Parameters (#781) 2022-06-29 07:58:14 -07:00
BrightQi 78fd4cb998 bugfix for srem type to accept array #779 (#780)
* bugfix for srem  type to accept array #779

* add test to test SREM accept members as array type #779
2022-06-28 08:23:35 -07:00
Tomáš Fedor 2a8dd6a7f1 Add array as possible return type to spop (#774)
* Add array as possible return type to spop

* Update ClientInterface.php

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-06-14 19:59:10 -07:00
Jayin Taung 7d7b3416a8 Add example for installing predis by Composer (#772)
* Add example for installing predis by Composer

* Update README.md

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2022-06-13 07:54:46 -07:00
145 changed files with 10485 additions and 605 deletions
+1 -1
View File
@@ -8,9 +8,9 @@ indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.php]
trim_trailing_whitespace = true
max_line_length = 120
[*.{md,yml}]
+49
View File
@@ -0,0 +1,49 @@
name-template: 'v$NEXT_MINOR_VERSION'
tag-template: 'v$NEXT_MINOR_VERSION'
change-template: '- $TITLE (#$NUMBER)'
autolabeler:
- label: 'maintenance'
files:
- '*.md'
- '.github/*'
- label: 'bug'
branch:
- '/bug-.+'
- label: 'maintenance'
branch:
- '/maintenance-.+'
- label: 'feature'
branch:
- '/feature-.+'
categories:
- title: 'Breaking Changes'
labels:
- 'breakingchange'
- title: 'Experimental Features'
labels:
- 'experimental'
- title: 'New Features'
labels:
- 'feature'
- 'enhancement'
- title: 'Bug Fixes'
labels:
- 'fix'
- 'bugfix'
- 'bug'
- 'BUG'
- title: 'Maintenance'
label: 'maintenance'
exclude-labels:
- 'skip-changelog'
template: |
$CHANGES
## Contributors
We'd like to thank all the contributors who worked on this release!
$CONTRIBUTORS
+21
View File
@@ -0,0 +1,21 @@
name: Release Drafter
on:
push:
branches:
- main
jobs:
update_release_draft:
name: Update release draft
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@v5
with:
config-name: release-drafter-config.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+43 -31
View File
@@ -7,46 +7,58 @@ on:
jobs:
predis:
name: PHP ${{ matrix.php-versions }} (Redis ${{ matrix.redis-versions }})
name: PHP ${{ matrix.php }} (Redis ${{ matrix.redis }})
runs-on: ubuntu-latest
services:
redis:
image: redis:${{ matrix.redis-versions }}
ports:
- 6379:6379
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1']
redis-versions: ['3', '4', '5', '6', '7']
php:
- '7.2'
- '7.3'
- '7.4'
- '8.0'
- '8.1'
- '8.2'
redis:
- 3
- 4
- 5
- 6
- 7
services:
redis:
image: redis:${{ matrix.redis }}
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
ports:
- 6379:6379
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP with Composer and extensions
with:
php-version: ${{ matrix.php-versions }}
uses: shivammathur/setup-php@v2
- name: Checkout
uses: actions/checkout@v3
- name: Get Composer cache directory
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Setup PHP with Composer and extensions
with:
php-version: ${{ matrix.php }}
uses: shivammathur/setup-php@v2
- name: Cache Composer dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Get Composer cache directory
id: composer-cache
run: echo "directory=$(composer config cache-dir)" >> $GITHUB_OUTPUT
- name: Install Composer dependencies
env:
PHP_VERSION: ${{ matrix.php-versions }}
run: composer install --no-progress --prefer-dist --optimize-autoloader $(if [ "$PHP_VERSION" == "8.0" ]; then echo "--ignore-platform-reqs"; fi;)
- name: Cache Composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.directory }}
key: tests-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: tests-php-${{ matrix.php }}-composer
- name: Test with PHPUnit
run: vendor/bin/phpunit
- name: Install Composer dependencies
env:
PHP_VERSION: ${{ matrix.php }}
run: composer install --ansi --no-progress --prefer-dist $(if [ "$PHP_VERSION" == "8.0" ]; then echo "--ignore-platform-reqs"; fi;)
- name: Run PHPUnit tests
run: vendor/bin/phpunit
+60 -2
View File
@@ -1,5 +1,63 @@
## Changelog
## v2.1.1 (2023-01-17)
## Bug Fixes
- Fix `@template` in `Predis\Client` (#1017)
- Fix support options array in `ZINTERSTORE` and `ZUNIONSTORE` (#1018)
## v2.1.0 (2023-01-16)
## New Features
- Implemented `GETEX` command (#872)
- Implemented `GETDEL` command (#869)
- Implemented `COPY` command (#866)
- Implemented `FAILOVER` command (#875)
- Implemented `LMOVE` command (#863)
- Implemented `LMPOP` command (#1013)
- Implemented `HRANDFIELD` command (#870)
- Implemented `SMISMEMBER` command (#871)
- Implemented `ZMPOP` command (#831)
- Implemented `BLMOVE` command (#865)
- Implemented `BLMPOP` command (#1015)
- Implemented `BZMPOP` command (#833)
- Implemented `BZPOPMIN` command (#862)
- Implemented `BZPOPMAX` command (#864)
- Implemented `ZUNION` command (#860)
- Implemented `ZINTER` command (#859)
- Implemented `ZINTERCARD` command (#861)
- Implemented `ZRANGESTORE` command (#829)
- Implemented `ZDIFFSTORE` command (#828)
- Implemented `ZDIFF` command (#826)
- Implemented `ZRANDMEMBER` command (#825)
- Implemented `ZMSCORE` (#823)
- Implemented `GEOSEARCH` command (#867)
- Implemented `GEOSEARCHSTORE` command (#873)
## Bug Fixes
- Added annotations to suppress PHP 8.1 return type deprecation warning (#810)
## Maintenance
- Added mixin annotations for traits (#835)
## v2.0.3 (2022-10-11)
- Improved PHP 8.2 support
- Call `is_resource()` before reading/writing
- Added partial support for Redis Stream commands
- Fixed Sentinel authentication issue
## v2.0.2 (2022-09-06)
- Fixed PHP 8.2 deprecation notice: Use of "static" in callables
## v2.0.1 (2022-09-04)
- Added retry interval to `RedisCluster` with a default of `10ms`
- Avoid PHP 8.2 warning in `Connection\Parameters`
- Fixed Sentinel reconnect issue in long-running PHP processes
## v2.0.0 (2022-06-08)
- Dropped support for PHP 7.1 and older
@@ -109,7 +167,7 @@
- Support Pub/Sub and Pipelines when using replication
- The class `Predis\Transaction\AbortedMultiExecException` now uses the correct
- The class `Predis\Transaction\AbortedMultiExecException` now uses the correct
default types for the `$code` (integer) parameter.
- __FIX__: using `strval` in `getScanOptions()` method, part of
@@ -117,7 +175,7 @@
string value of `$this->match` and not passing `null` to `strlen()` function.
- __FIX__: the value returned from `getArgument()` in `isReadOperation()` method,
part of `Predis\Replication\ReplicationStrategy` class, is checked to not pass
part of `Predis\Replication\ReplicationStrategy` class, is checked to not pass
`null` to `sha1` function.
- __FIX__: the value returned from `getArgument()` in `parseResponse()`method,
+5 -1
View File
@@ -35,6 +35,10 @@ This library can be found on [Packagist](http://packagist.org/packages/predis/pr
management of projects dependencies using [Composer](http://packagist.org/about-composer).
Compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
```shell
composer require predis/predis
```
### Loading the library ###
@@ -487,7 +491,7 @@ The code for Predis is distributed under the terms of the MIT license (see [LICE
[ico-version-stable]: https://img.shields.io/packagist/v/predis/predis.svg?style=flat-square
[ico-version-dev]: https://img.shields.io/packagist/vpre/predis/predis.svg?style=flat-square
[ico-downloads-monthly]: https://img.shields.io/packagist/dm/predis/predis.svg?style=flat-square
[ico-build]: https://img.shields.io/github/workflow/status/predis/predis/Tests/main?style=flat-square
[ico-build]: https://img.shields.io/github/actions/workflow/status/predis/predis/tests.yml?branch=main&style=flat-square
[link-packagist]: https://packagist.org/packages/predis/predis
[link-actions]: https://github.com/predis/predis/actions
+1 -1
View File
@@ -1 +1 @@
2.0.0
2.1.1
+6 -7
View File
@@ -9,16 +9,16 @@
"issues": "https://github.com/predis/predis/issues"
},
"authors": [
{
"name": "Daniele Alessandri",
"email": "suppakilla@gmail.com",
"homepage": "http://clorophilla.net",
"role": "Creator & Maintainer"
},
{
"name": "Till Krüss",
"homepage": "https://till.im",
"role": "Maintainer"
},
{
"name": "Daniele Alessandri",
"email": "suppakilla@gmail.com",
"homepage": "http://clorophilla.net",
"role": "Creator"
}
],
"funding": [
@@ -34,7 +34,6 @@
"phpunit/phpunit": "^8.0 || ~9.4.4"
},
"suggest": {
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
"ext-curl": "Allows access to Webdis when paired with phpiredis"
},
"autoload": {
+7 -5
View File
@@ -11,6 +11,8 @@
namespace Predis;
use Traversable;
use Predis\Command\CommandInterface;
use Predis\Command\RawCommand;
use Predis\Command\ScriptCommand;
@@ -34,13 +36,13 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
* abstractions are built. Internally it aggregates various other classes each
* one with its own responsibility and scope.
*
* {@inheritdoc}
* @template-implements \IteratorAggregate<string, static>
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class Client implements ClientInterface, \IteratorAggregate
{
const VERSION = '2.0.0';
const VERSION = '2.1.1';
/** @var OptionsInterface */
private $options;
@@ -378,7 +380,7 @@ class Client implements ClientInterface, \IteratorAggregate
: $this->$initializer(null, $argv[0]);
case 2:
list($arg0, $arg1) = $argv;
[$arg0, $arg1] = $argv;
return $this->$initializer($arg0, $arg1);
@@ -511,7 +513,7 @@ class Client implements ClientInterface, \IteratorAggregate
}
/**
* {@inheritdoc}
* @return Traversable<string, static>
*/
#[\ReturnTypeWillChange]
public function getIterator()
@@ -519,7 +521,7 @@ class Client implements ClientInterface, \IteratorAggregate
$clients = array();
$connection = $this->getConnection();
if (!$connection instanceof \Traversable) {
if (!$connection instanceof Traversable) {
return new \ArrayIterator(array(
(string) $connection => new static($connection, $this->getOptions())
));
+29 -2
View File
@@ -11,11 +11,15 @@
namespace Predis;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Server\To;
use Predis\Command\CommandInterface;
/**
* Interface defining a client-side context such as a pipeline or transaction.
*
* @method $this copy(string $source, string $destination, int $db = -1, bool $replace = false)
* @method $this del(array|string $keys)
* @method $this dump($key)
* @method $this exists($key)
@@ -40,11 +44,18 @@ use Predis\Command\CommandInterface;
* @method $this bitop($operation, $destkey, $key)
* @method $this bitfield($key, $subcommand, ...$subcommandArg)
* @method $this bitpos($key, $bit, $start = null, $end = null)
* @method $this blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
* @method $this bzpopmax(array $keys, int $timeout)
* @method $this bzpopmin(array $keys, int $timeout)
* @method $this bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
* @method $this decr($key)
* @method $this decrby($key, $decrement)
* @method $this failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method $this get($key)
* @method $this getbit($key, $offset)
* @method $this getex(string $key, $modifier = '', $value = false)
* @method $this getrange($key, $start, $end)
* @method $this getdel(string $key)
* @method $this getset($key, $value)
* @method $this incr($key)
* @method $this incrby($key, $increment)
@@ -69,17 +80,21 @@ use Predis\Command\CommandInterface;
* @method $this hlen($key)
* @method $this hmget($key, array $fields)
* @method $this hmset($key, array $dictionary)
* @method $this hrandfield(string $key, int $count = 1, bool $withValues = false)
* @method $this hscan($key, $cursor, array $options = null)
* @method $this hset($key, $field, $value)
* @method $this hsetnx($key, $field, $value)
* @method $this hvals($key)
* @method $this hstrlen($key, $field)
* @method $this blmove(string $source, string $destination, string $where, string $to, int $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 lmove(string $source, string $destination, string $where, string $to)
* @method $this lmpop(array $keys, string $modifier = 'left', int $count = 1)
* @method $this lpop($key)
* @method $this lpush($key, array $values)
* @method $this lpushx($key, array $values)
@@ -99,6 +114,7 @@ use Predis\Command\CommandInterface;
* @method $this sinterstore($destination, array|string $keys)
* @method $this sismember($key, $member)
* @method $this smembers($key)
* @method $this smismember(string $key, string ...$members)
* @method $this smove($source, $destination, $member)
* @method $this spop($key, $count = null)
* @method $this srandmember($key, $count = null)
@@ -109,10 +125,18 @@ use Predis\Command\CommandInterface;
* @method $this zadd($key, array $membersAndScoresDictionary)
* @method $this zcard($key)
* @method $this zcount($key, $min, $max)
* @method $this zdiff(array $keys, bool $withScores = false)
* @method $this zdiffstore(string $destination, array $keys)
* @method $this zincrby($key, $increment, $member)
* @method $this zinterstore($destination, array|string $keys, array $options = null)
* @method $this zintercard(array $keys, int $limit = 0)
* @method $this zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method $this zinter(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method $this zmpop(array $keys, string $modifier = 'min', int $count = 1)
* @method $this zmscore(string $key, string ...$member)
* @method $this zrandmember(string $key, int $count = 1, bool $withScores = false)
* @method $this zrange($key, $start, $stop, array $options = null)
* @method $this zrangebyscore($key, $min, $max, array $options = null)
* @method $this zrangestore(string $destination, string $source, int|string $min, string|int $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0)
* @method $this zrank($key, $member)
* @method $this zrem($key, $member)
* @method $this zremrangebyrank($key, $start, $stop)
@@ -120,7 +144,8 @@ use Predis\Command\CommandInterface;
* @method $this zrevrange($key, $start, $stop, array $options = null)
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
* @method $this zrevrank($key, $member)
* @method $this zunionstore($destination, array|string $keys, array $options = null)
* @method $this zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method $this zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method $this zscore($key, $member)
* @method $this zscan($key, $cursor, array $options = null)
* @method $this zrangebylex($key, $start, $stop, array $options = null)
@@ -164,6 +189,8 @@ use Predis\Command\CommandInterface;
* @method $this geodist($key, $member1, $member2, $unit = null)
* @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null)
* @method $this geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
* @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
+184 -151
View File
@@ -11,6 +11,9 @@
namespace Predis;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Server\To;
use Predis\Command\CommandInterface;
use Predis\Command\FactoryInterface;
use Predis\Configuration\OptionsInterface;
@@ -25,157 +28,187 @@ use Predis\Response\Status;
* and more friendly interface to ease programming which is described in the
* following list of methods:
*
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
* @method string|null dump(string $key)
* @method int exists(string $key)
* @method int expire(string $key, int $seconds)
* @method int expireat(string $key, int $timestamp)
* @method array keys(string $pattern)
* @method int move(string $key, int $db)
* @method mixed object($subcommand, string $key)
* @method int persist(string $key)
* @method int pexpire(string $key, int $milliseconds)
* @method int pexpireat(string $key, int $timestamp)
* @method int pttl(string $key)
* @method string|null randomkey()
* @method mixed rename(string $key, string $target)
* @method int renamenx(string $key, string $target)
* @method array scan($cursor, array $options = null)
* @method array sort(string $key, array $options = null)
* @method int ttl(string $key)
* @method mixed type(string $key)
* @method int append(string $key, $value)
* @method int bitcount(string $key, $start = null, $end = null)
* @method int bitop($operation, $destkey, $key)
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
* @method int bitpos(string $key, $bit, $start = null, $end = null)
* @method int decr(string $key)
* @method int decrby(string $key, int $decrement)
* @method string|null get(string $key)
* @method int getbit(string $key, $offset)
* @method string getrange(string $key, $start, $end)
* @method string|null getset(string $key, $value)
* @method int incr(string $key)
* @method int incrby(string $key, int $increment)
* @method string incrbyfloat(string $key, int|float $increment)
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method Status psetex(string $key, $milliseconds, $value)
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit(string $key, $offset, $value)
* @method Status setex(string $key, $seconds, $value)
* @method int setnx(string $key, $value)
* @method int setrange(string $key, $offset, $value)
* @method int strlen(string $key)
* @method int hdel(string $key, array $fields)
* @method int hexists(string $key, string $field)
* @method string|null hget(string $key, string $field)
* @method array hgetall(string $key)
* @method int hincrby(string $key, string $field, int $increment)
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
* @method array hkeys(string $key)
* @method int hlen(string $key)
* @method array hmget(string $key, array $fields)
* @method mixed hmset(string $key, array $dictionary)
* @method array hscan(string $key, $cursor, array $options = null)
* @method int hset(string $key, string $field, string $value)
* @method int hsetnx(string $key, string $field, string $value)
* @method array hvals(string $key)
* @method int hstrlen(string $key, string $field)
* @method array|null blpop(array|string $keys, int|float $timeout)
* @method array|null brpop(array|string $keys, int|float $timeout)
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
* @method string|null lindex(string $key, int $index)
* @method int linsert(string $key, $whence, $pivot, $value)
* @method int llen(string $key)
* @method string|null lpop(string $key)
* @method int lpush(string $key, array $values)
* @method int lpushx(string $key, array $values)
* @method string[] lrange(string $key, int $start, int $stop)
* @method int lrem(string $key, int $count, string $value)
* @method mixed lset(string $key, int $index, string $value)
* @method mixed ltrim(string $key, int $start, int $stop)
* @method string|null rpop(string $key)
* @method string|null rpoplpush(string $source, string $destination)
* @method int rpush(string $key, array $values)
* @method int rpushx(string $key, array $values)
* @method int sadd(string $key, array $members)
* @method int scard(string $key)
* @method string[] sdiff(array|string $keys)
* @method int sdiffstore(string $destination, array|string $keys)
* @method string[] sinter(array|string $keys)
* @method int sinterstore(string $destination, array|string $keys)
* @method int sismember(string $key, string $member)
* @method string[] smembers(string $key)
* @method int smove(string $source, string $destination, string $member)
* @method string|null spop(string $key, int $count = null)
* @method string|null srandmember(string $key, int $count = null)
* @method int srem(string $key, string $member)
* @method array sscan(string $key, int $cursor, array $options = null)
* @method string[] sunion(array|string $keys)
* @method int sunionstore(string $destination, array|string $keys)
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
* @method int zadd(string $key, array $membersAndScoresDictionary)
* @method int zcard(string $key)
* @method string zcount(string $key, int|string $min, int|string $max)
* @method string zincrby(string $key, int $increment, string $member)
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
* @method array zpopmin(string $key, int $count = 1)
* @method array zpopmax(string $key, int $count = 1)
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
* @method int|null zrank(string $key, string $member)
* @method int zrem(string $key, string ...$member)
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
* @method int|null zrevrank(string $key, string $member)
* @method int zunionstore(string $destination, array|string $keys, array $options = null)
* @method string|null zscore(string $key, string $member)
* @method array zscan(string $key, int $cursor, array $options = null)
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
* @method int zremrangebylex(string $key, string $min, string $max)
* @method int zlexcount(string $key, string $min, string $max)
* @method int pfadd(string $key, array $elements)
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed pubsub($subcommand, $argument)
* @method int publish($channel, $message)
* @method mixed discard()
* @method array|null exec()
* @method mixed multi()
* @method mixed unwatch()
* @method mixed watch(string $key)
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed script($subcommand, $argument = null)
* @method mixed auth(string $password)
* @method string echo(string $message)
* @method mixed ping(string $message = null)
* @method mixed select(int $database)
* @method mixed bgrewriteaof()
* @method mixed bgsave()
* @method mixed client($subcommand, $argument = null)
* @method mixed config($subcommand, $argument = null)
* @method int dbsize()
* @method mixed flushall()
* @method mixed flushdb()
* @method array info($section = null)
* @method int lastsave()
* @method mixed save()
* @method mixed slaveof(string $host, int $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method array time()
* @method array command()
* @method int geoadd(string $key, $longitude, $latitude, $member)
* @method array geohash(string $key, array $members)
* @method array geopos(string $key, array $members)
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
* @method int copy(string $source, string $destination, int $db = -1, bool $replace = false)
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
* @method string|null dump(string $key)
* @method int exists(string $key)
* @method int expire(string $key, int $seconds)
* @method int expireat(string $key, int $timestamp)
* @method array keys(string $pattern)
* @method int move(string $key, int $db)
* @method mixed object($subcommand, string $key)
* @method int persist(string $key)
* @method int pexpire(string $key, int $milliseconds)
* @method int pexpireat(string $key, int $timestamp)
* @method int pttl(string $key)
* @method string|null randomkey()
* @method mixed rename(string $key, string $target)
* @method int renamenx(string $key, string $target)
* @method array scan($cursor, array $options = null)
* @method array sort(string $key, array $options = null)
* @method int ttl(string $key)
* @method mixed type(string $key)
* @method int append(string $key, $value)
* @method int bitcount(string $key, $start = null, $end = null)
* @method int bitop($operation, $destkey, $key)
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
* @method int bitpos(string $key, $bit, $start = null, $end = null)
* @method array blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
* @method array bzpopmax(array $keys, int $timeout)
* @method array bzpopmin(array $keys, int $timeout)
* @method array bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
* @method int decr(string $key)
* @method int decrby(string $key, int $decrement)
* @method Status failover(?To $to = null, bool $abort = false, int $timeout = -1)
* @method string|null get(string $key)
* @method int getbit(string $key, $offset)
* @method int|null getex(string $key, $modifier = '', $value = false)
* @method string getrange(string $key, $start, $end)
* @method string getdel(string $key)
* @method string|null getset(string $key, $value)
* @method int incr(string $key)
* @method int incrby(string $key, int $increment)
* @method string incrbyfloat(string $key, int|float $increment)
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed mset(array $dictionary)
* @method int msetnx(array $dictionary)
* @method Status psetex(string $key, $milliseconds, $value)
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method int setbit(string $key, $offset, $value)
* @method Status setex(string $key, $seconds, $value)
* @method int setnx(string $key, $value)
* @method int setrange(string $key, $offset, $value)
* @method int strlen(string $key)
* @method int hdel(string $key, array $fields)
* @method int hexists(string $key, string $field)
* @method string|null hget(string $key, string $field)
* @method array hgetall(string $key)
* @method int hincrby(string $key, string $field, int $increment)
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
* @method array hkeys(string $key)
* @method int hlen(string $key)
* @method array hmget(string $key, array $fields)
* @method mixed hmset(string $key, array $dictionary)
* @method array hrandfield(string $key, int $count = 1, bool $withValues = false)
* @method array hscan(string $key, $cursor, array $options = null)
* @method int hset(string $key, string $field, string $value)
* @method int hsetnx(string $key, string $field, string $value)
* @method array hvals(string $key)
* @method int hstrlen(string $key, string $field)
* @method string blmove(string $source, string $destination, string $where, string $to, int $timeout)
* @method array|null blpop(array|string $keys, int|float $timeout)
* @method array|null brpop(array|string $keys, int|float $timeout)
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
* @method string|null lindex(string $key, int $index)
* @method int linsert(string $key, $whence, $pivot, $value)
* @method int llen(string $key)
* @method string lmove(string $source, string $destination, string $where, string $to)
* @method array|null lmpop(array $keys, string $modifier = 'left', int $count = 1)
* @method string|null lpop(string $key)
* @method int lpush(string $key, array $values)
* @method int lpushx(string $key, array $values)
* @method string[] lrange(string $key, int $start, int $stop)
* @method int lrem(string $key, int $count, string $value)
* @method mixed lset(string $key, int $index, string $value)
* @method mixed ltrim(string $key, int $start, int $stop)
* @method string|null rpop(string $key)
* @method string|null rpoplpush(string $source, string $destination)
* @method int rpush(string $key, array $values)
* @method int rpushx(string $key, array $values)
* @method int sadd(string $key, array $members)
* @method int scard(string $key)
* @method string[] sdiff(array|string $keys)
* @method int sdiffstore(string $destination, array|string $keys)
* @method string[] sinter(array|string $keys)
* @method int sinterstore(string $destination, array|string $keys)
* @method int sismember(string $key, string $member)
* @method string[] smembers(string $key)
* @method array smismember(string $key, string ...$members)
* @method int smove(string $source, string $destination, string $member)
* @method string|array|null spop(string $key, int $count = null)
* @method string|null srandmember(string $key, int $count = null)
* @method int srem(string $key, array|string $member)
* @method array sscan(string $key, int $cursor, array $options = null)
* @method string[] sunion(array|string $keys)
* @method int sunionstore(string $destination, array|string $keys)
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
* @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null)
* @method int xdel(string $key, string ...$id)
* @method int xlen(string $key)
* @method array xrevrange(string $key, string $end, string $start, ?int $count = null)
* @method array xrange(string $key, string $start, string $end, ?int $count = null)
* @method string xtrim(string $key, array|string $strategy, string $threshold, array $options = null)
* @method int zadd(string $key, array $membersAndScoresDictionary)
* @method int zcard(string $key)
* @method string zcount(string $key, int|string $min, int|string $max)
* @method array zdiff(array $keys, bool $withScores = false)
* @method int zdiffstore(string $destination, array $keys)
* @method string zincrby(string $key, int $increment, string $member)
* @method int zintercard(array $keys, int $limit = 0)
* @method int zinterstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method array zinter(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method array zmpop(array $keys, string $modifier = 'min', int $count = 1)
* @method array zmscore(string $key, string ...$member)
* @method array zpopmin(string $key, int $count = 1)
* @method array zpopmax(string $key, int $count = 1)
* @method mixed zrandmember(string $key, int $count = 1, bool $withScores = false)
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
* @method int zrangestore(string $destination, string $source, int|string $min, int|string $max, string|bool $by = false, bool $reversed = false, bool $limit = false, int $offset = 0, int $count = 0)
* @method int|null zrank(string $key, string $member)
* @method int zrem(string $key, string ...$member)
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
* @method int|null zrevrank(string $key, string $member)
* @method array zunion(array $keys, int[] $weights = [], string $aggregate = 'sum', bool $withScores = false)
* @method int zunionstore(string $destination, array $keys, int[] $weights = [], string $aggregate = 'sum')
* @method string|null zscore(string $key, string $member)
* @method array zscan(string $key, int $cursor, array $options = null)
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
* @method int zremrangebylex(string $key, string $min, string $max)
* @method int zlexcount(string $key, string $min, string $max)
* @method int pfadd(string $key, array $elements)
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed pubsub($subcommand, $argument)
* @method int publish($channel, $message)
* @method mixed discard()
* @method array|null exec()
* @method mixed multi()
* @method mixed unwatch()
* @method mixed watch(string $key)
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
* @method mixed script($subcommand, $argument = null)
* @method mixed auth(string $password)
* @method string echo(string $message)
* @method mixed ping(string $message = null)
* @method mixed select(int $database)
* @method mixed bgrewriteaof()
* @method mixed bgsave()
* @method mixed client($subcommand, $argument = null)
* @method mixed config($subcommand, $argument = null)
* @method int dbsize()
* @method mixed flushall()
* @method mixed flushdb()
* @method array info($section = null)
* @method int lastsave()
* @method mixed save()
* @method mixed slaveof(string $host, int $port)
* @method mixed slowlog($subcommand, $argument = null)
* @method array time()
* @method array command()
* @method int geoadd(string $key, $longitude, $latitude, $member)
* @method array geohash(string $key, array $members)
* @method array geopos(string $key, array $members)
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
* @method array geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
* @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
@@ -0,0 +1,16 @@
<?php
namespace Predis\Command\Argument;
/**
* Allows to use object-oriented approach to handle complex conditional arguments.
*/
interface ArrayableArgument
{
/**
* Get the instance as an array.
*
* @return array
*/
public function toArray(): array;
}
@@ -0,0 +1,36 @@
<?php
namespace Predis\Command\Argument\Geospatial;
use UnexpectedValueException;
abstract class AbstractBy implements ByInterface
{
/**
* @var string[]
*/
private static $unitEnum = ['m', 'km', 'ft', 'mi'];
/**
* @var string
*/
protected $unit;
/**
* @inheritDoc
*/
abstract public function toArray(): array;
/**
* @param string $unit
* @return void
*/
protected function setUnit(string $unit): void
{
if (!in_array($unit, self::$unitEnum, true)) {
throw new UnexpectedValueException('Wrong value given for unit');
}
$this->unit = $unit;
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Predis\Command\Argument\Geospatial;
class ByBox extends AbstractBy
{
private const KEYWORD = 'BYBOX';
/**
* @var int
*/
private $width;
/**
* @var int
*/
private $height;
public function __construct(int $width, int $height, string $unit)
{
$this->width = $width;
$this->height = $height;
$this->setUnit($unit);
}
/**
* @inheritDoc
*/
public function toArray(): array
{
return [self::KEYWORD, $this->width, $this->height, $this->unit];
}
}
@@ -0,0 +1,9 @@
<?php
namespace Predis\Command\Argument\Geospatial;
use Predis\Command\Argument\ArrayableArgument;
interface ByInterface extends ArrayableArgument
{
}
@@ -0,0 +1,27 @@
<?php
namespace Predis\Command\Argument\Geospatial;
class ByRadius extends AbstractBy
{
private const KEYWORD = 'BYRADIUS';
/**
* @var int
*/
private $radius;
public function __construct(int $radius, string $unit)
{
$this->radius = $radius;
$this->setUnit($unit);
}
/**
* @inheritDoc
*/
public function toArray(): array
{
return [self::KEYWORD, $this->radius, $this->unit];
}
}
@@ -0,0 +1,9 @@
<?php
namespace Predis\Command\Argument\Geospatial;
use Predis\Command\Argument\ArrayableArgument;
interface FromInterface extends ArrayableArgument
{
}
@@ -0,0 +1,32 @@
<?php
namespace Predis\Command\Argument\Geospatial;
class FromLonLat implements FromInterface
{
private const KEYWORD = 'FROMLONLAT';
/**
* @var float
*/
private $longitude;
/**
* @var float
*/
private $latitude;
public function __construct(float $longitude, float $latitude)
{
$this->longitude = $longitude;
$this->latitude = $latitude;
}
/**
* @inheritDoc
*/
public function toArray(): array
{
return [self::KEYWORD, $this->longitude, $this->latitude];
}
}
@@ -0,0 +1,26 @@
<?php
namespace Predis\Command\Argument\Geospatial;
class FromMember implements FromInterface
{
private const KEYWORD = 'FROMMEMBER';
/**
* @var string
*/
private $member;
public function __construct(string $member)
{
$this->member = $member;
}
/**
* @inheritDoc
*/
public function toArray(): array
{
return [self::KEYWORD, $this->member];
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Predis\Command\Argument\Server;
use Predis\Command\Argument\ArrayableArgument;
class To implements ArrayableArgument
{
private const KEYWORD = 'TO';
private const FORCE_KEYWORD = 'FORCE';
/**
* @var string
*/
private $host;
/**
* @var int
*/
private $port;
/**
* @var bool
*/
private $isForce;
public function __construct(string $host, int $port, bool $isForce = false)
{
$this->host = $host;
$this->port = $port;
$this->isForce = $isForce;
}
/**
* @inheritDoc
*/
public function toArray(): array
{
$arguments = [self::KEYWORD, $this->host, $this->port];
if ($this->isForce) {
$arguments[] = self::FORCE_KEYWORD;
}
return $arguments;
}
}
+12
View File
@@ -114,4 +114,16 @@ abstract class Command implements CommandInterface
return $arguments;
}
/**
* Remove all false values from arguments.
*
* @return void
*/
public function filterArguments(): void
{
$this->arguments = array_filter($this->arguments, static function ($argument) {
return $argument !== false;
});
}
}
+143 -131
View File
@@ -31,142 +31,154 @@ class KeyPrefixProcessor implements ProcessorInterface
public function __construct($prefix)
{
$this->prefix = $prefix;
$this->commands = array(
$prefixFirst = static::class . '::first';
$prefixAll = static::class . '::all';
$prefixInterleaved = static::class . '::interleaved';
$prefixSkipFirst = static::class . '::skipFirst';
$prefixSkipLast = static::class . '::skipLast';
$prefixSort = static::class . '::sort';
$prefixEvalKeys = static::class . '::evalKeys';
$prefixZsetStore = static::class . '::zsetStore';
$prefixMigrate = static::class . '::migrate';
$prefixGeoradius = static::class . '::georadius';
$this->commands = [
/* ---------------- Redis 1.2 ---------------- */
'EXISTS' => 'static::all',
'DEL' => 'static::all',
'TYPE' => 'static::first',
'KEYS' => 'static::first',
'RENAME' => 'static::all',
'RENAMENX' => 'static::all',
'EXPIRE' => 'static::first',
'EXPIREAT' => 'static::first',
'TTL' => 'static::first',
'MOVE' => 'static::first',
'SORT' => 'static::sort',
'DUMP' => 'static::first',
'RESTORE' => 'static::first',
'SET' => 'static::first',
'SETNX' => 'static::first',
'MSET' => 'static::interleaved',
'MSETNX' => 'static::interleaved',
'GET' => 'static::first',
'MGET' => 'static::all',
'GETSET' => 'static::first',
'INCR' => 'static::first',
'INCRBY' => 'static::first',
'DECR' => 'static::first',
'DECRBY' => 'static::first',
'RPUSH' => 'static::first',
'LPUSH' => 'static::first',
'LLEN' => 'static::first',
'LRANGE' => 'static::first',
'LTRIM' => 'static::first',
'LINDEX' => 'static::first',
'LSET' => 'static::first',
'LREM' => 'static::first',
'LPOP' => 'static::first',
'RPOP' => 'static::first',
'RPOPLPUSH' => 'static::all',
'SADD' => 'static::first',
'SREM' => 'static::first',
'SPOP' => 'static::first',
'SMOVE' => 'static::skipLast',
'SCARD' => 'static::first',
'SISMEMBER' => 'static::first',
'SINTER' => 'static::all',
'SINTERSTORE' => 'static::all',
'SUNION' => 'static::all',
'SUNIONSTORE' => 'static::all',
'SDIFF' => 'static::all',
'SDIFFSTORE' => 'static::all',
'SMEMBERS' => 'static::first',
'SRANDMEMBER' => 'static::first',
'ZADD' => 'static::first',
'ZINCRBY' => 'static::first',
'ZREM' => 'static::first',
'ZRANGE' => 'static::first',
'ZREVRANGE' => 'static::first',
'ZRANGEBYSCORE' => 'static::first',
'ZCARD' => 'static::first',
'ZSCORE' => 'static::first',
'ZREMRANGEBYSCORE' => 'static::first',
'EXISTS' => $prefixAll,
'DEL' => $prefixAll,
'TYPE' => $prefixFirst,
'KEYS' => $prefixFirst,
'RENAME' => $prefixAll,
'RENAMENX' => $prefixAll,
'EXPIRE' => $prefixFirst,
'EXPIREAT' => $prefixFirst,
'TTL' => $prefixFirst,
'MOVE' => $prefixFirst,
'SORT' => $prefixSort,
'DUMP' => $prefixFirst,
'RESTORE' => $prefixFirst,
'SET' => $prefixFirst,
'SETNX' => $prefixFirst,
'MSET' => $prefixInterleaved,
'MSETNX' => $prefixInterleaved,
'GET' => $prefixFirst,
'MGET' => $prefixAll,
'GETSET' => $prefixFirst,
'INCR' => $prefixFirst,
'INCRBY' => $prefixFirst,
'DECR' => $prefixFirst,
'DECRBY' => $prefixFirst,
'RPUSH' => $prefixFirst,
'LPUSH' => $prefixFirst,
'LLEN' => $prefixFirst,
'LRANGE' => $prefixFirst,
'LTRIM' => $prefixFirst,
'LINDEX' => $prefixFirst,
'LSET' => $prefixFirst,
'LREM' => $prefixFirst,
'LPOP' => $prefixFirst,
'RPOP' => $prefixFirst,
'RPOPLPUSH' => $prefixAll,
'SADD' => $prefixFirst,
'SREM' => $prefixFirst,
'SPOP' => $prefixFirst,
'SMOVE' => $prefixSkipLast,
'SCARD' => $prefixFirst,
'SISMEMBER' => $prefixFirst,
'SINTER' => $prefixAll,
'SINTERSTORE' => $prefixAll,
'SUNION' => $prefixAll,
'SUNIONSTORE' => $prefixAll,
'SDIFF' => $prefixAll,
'SDIFFSTORE' => $prefixAll,
'SMEMBERS' => $prefixFirst,
'SRANDMEMBER' => $prefixFirst,
'ZADD' => $prefixFirst,
'ZINCRBY' => $prefixFirst,
'ZREM' => $prefixFirst,
'ZRANGE' => $prefixFirst,
'ZREVRANGE' => $prefixFirst,
'ZRANGEBYSCORE' => $prefixFirst,
'ZCARD' => $prefixFirst,
'ZSCORE' => $prefixFirst,
'ZREMRANGEBYSCORE' => $prefixFirst,
/* ---------------- Redis 2.0 ---------------- */
'SETEX' => 'static::first',
'APPEND' => 'static::first',
'SUBSTR' => 'static::first',
'BLPOP' => 'static::skipLast',
'BRPOP' => 'static::skipLast',
'ZUNIONSTORE' => 'static::zsetStore',
'ZINTERSTORE' => 'static::zsetStore',
'ZCOUNT' => 'static::first',
'ZRANK' => 'static::first',
'ZREVRANK' => 'static::first',
'ZREMRANGEBYRANK' => 'static::first',
'HSET' => 'static::first',
'HSETNX' => 'static::first',
'HMSET' => 'static::first',
'HINCRBY' => 'static::first',
'HGET' => 'static::first',
'HMGET' => 'static::first',
'HDEL' => 'static::first',
'HEXISTS' => 'static::first',
'HLEN' => 'static::first',
'HKEYS' => 'static::first',
'HVALS' => 'static::first',
'HGETALL' => 'static::first',
'SUBSCRIBE' => 'static::all',
'UNSUBSCRIBE' => 'static::all',
'PSUBSCRIBE' => 'static::all',
'PUNSUBSCRIBE' => 'static::all',
'PUBLISH' => 'static::first',
'SETEX' => $prefixFirst,
'APPEND' => $prefixFirst,
'SUBSTR' => $prefixFirst,
'BLPOP' => $prefixSkipLast,
'BRPOP' => $prefixSkipLast,
'ZUNIONSTORE' => $prefixZsetStore,
'ZINTERSTORE' => $prefixZsetStore,
'ZCOUNT' => $prefixFirst,
'ZRANK' => $prefixFirst,
'ZREVRANK' => $prefixFirst,
'ZREMRANGEBYRANK' => $prefixFirst,
'HSET' => $prefixFirst,
'HSETNX' => $prefixFirst,
'HMSET' => $prefixFirst,
'HINCRBY' => $prefixFirst,
'HGET' => $prefixFirst,
'HMGET' => $prefixFirst,
'HDEL' => $prefixFirst,
'HEXISTS' => $prefixFirst,
'HLEN' => $prefixFirst,
'HKEYS' => $prefixFirst,
'HVALS' => $prefixFirst,
'HGETALL' => $prefixFirst,
'SUBSCRIBE' => $prefixAll,
'UNSUBSCRIBE' => $prefixAll,
'PSUBSCRIBE' => $prefixAll,
'PUNSUBSCRIBE' => $prefixAll,
'PUBLISH' => $prefixFirst,
/* ---------------- Redis 2.2 ---------------- */
'PERSIST' => 'static::first',
'STRLEN' => 'static::first',
'SETRANGE' => 'static::first',
'GETRANGE' => 'static::first',
'SETBIT' => 'static::first',
'GETBIT' => 'static::first',
'RPUSHX' => 'static::first',
'LPUSHX' => 'static::first',
'LINSERT' => 'static::first',
'BRPOPLPUSH' => 'static::skipLast',
'ZREVRANGEBYSCORE' => 'static::first',
'WATCH' => 'static::all',
'PERSIST' => $prefixFirst,
'STRLEN' => $prefixFirst,
'SETRANGE' => $prefixFirst,
'GETRANGE' => $prefixFirst,
'SETBIT' => $prefixFirst,
'GETBIT' => $prefixFirst,
'RPUSHX' => $prefixFirst,
'LPUSHX' => $prefixFirst,
'LINSERT' => $prefixFirst,
'BRPOPLPUSH' => $prefixSkipLast,
'ZREVRANGEBYSCORE' => $prefixFirst,
'WATCH' => $prefixAll,
/* ---------------- Redis 2.6 ---------------- */
'PTTL' => 'static::first',
'PEXPIRE' => 'static::first',
'PEXPIREAT' => 'static::first',
'PSETEX' => 'static::first',
'INCRBYFLOAT' => 'static::first',
'BITOP' => 'static::skipFirst',
'BITCOUNT' => 'static::first',
'HINCRBYFLOAT' => 'static::first',
'EVAL' => 'static::evalKeys',
'EVALSHA' => 'static::evalKeys',
'MIGRATE' => 'static::migrate',
'PTTL' => $prefixFirst,
'PEXPIRE' => $prefixFirst,
'PEXPIREAT' => $prefixFirst,
'PSETEX' => $prefixFirst,
'INCRBYFLOAT' => $prefixFirst,
'BITOP' => $prefixSkipFirst,
'BITCOUNT' => $prefixFirst,
'HINCRBYFLOAT' => $prefixFirst,
'EVAL' => $prefixEvalKeys,
'EVALSHA' => $prefixEvalKeys,
'MIGRATE' => $prefixMigrate,
/* ---------------- Redis 2.8 ---------------- */
'SSCAN' => 'static::first',
'ZSCAN' => 'static::first',
'HSCAN' => 'static::first',
'PFADD' => 'static::first',
'PFCOUNT' => 'static::all',
'PFMERGE' => 'static::all',
'ZLEXCOUNT' => 'static::first',
'ZRANGEBYLEX' => 'static::first',
'ZREMRANGEBYLEX' => 'static::first',
'ZREVRANGEBYLEX' => 'static::first',
'BITPOS' => 'static::first',
'SSCAN' => $prefixFirst,
'ZSCAN' => $prefixFirst,
'HSCAN' => $prefixFirst,
'PFADD' => $prefixFirst,
'PFCOUNT' => $prefixAll,
'PFMERGE' => $prefixAll,
'ZLEXCOUNT' => $prefixFirst,
'ZRANGEBYLEX' => $prefixFirst,
'ZREMRANGEBYLEX' => $prefixFirst,
'ZREVRANGEBYLEX' => $prefixFirst,
'BITPOS' => $prefixFirst,
/* ---------------- Redis 3.2 ---------------- */
'HSTRLEN' => 'static::first',
'BITFIELD' => 'static::first',
'GEOADD' => 'static::first',
'GEOHASH' => 'static::first',
'GEOPOS' => 'static::first',
'GEODIST' => 'static::first',
'GEORADIUS' => 'static::georadius',
'GEORADIUSBYMEMBER' => 'static::georadius',
);
'HSTRLEN' => $prefixFirst,
'BITFIELD' => $prefixFirst,
'GEOADD' => $prefixFirst,
'GEOHASH' => $prefixFirst,
'GEOPOS' => $prefixFirst,
'GEODIST' => $prefixFirst,
'GEORADIUS' => $prefixGeoradius,
'GEORADIUSBYMEMBER' => $prefixGeoradius,
];
}
/**
@@ -197,7 +209,7 @@ 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);
$this->commands[$commandID]($command, $this->prefix);
}
}
@@ -0,0 +1,32 @@
<?php
namespace Predis\Command\Redis\AbstractCommand;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;
abstract class BZPOPBase extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
protected static $keysArgumentPositionOffset = 0;
abstract public function getId(): string;
public function setArguments(array $arguments)
{
$this->setKeys($arguments, false);
}
public function parseResponse($data)
{
$key = array_shift($data);
if (null === $key) {
return [$key];
}
return array_combine([$key], [[$data[0] => $data[1]]]);
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Predis\Command\Redis;
class BLMOVE extends LMOVE
{
public function getId()
{
return 'BLMOVE';
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Predis\Command\Redis;
class BLMPOP extends LMPOP
{
protected static $keysArgumentPositionOffset = 1;
protected static $leftRightArgumentPositionOffset = 2;
protected static $countArgumentPositionOffset = 3;
public function getId()
{
return 'BLMPOP';
}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace Predis\Command\Redis;
/**
* @link https://redis.io/commands/bzmpop/
*
* BZMPOP is the blocking variant of ZMPOP.
*/
class BZMPOP extends ZMPOP
{
protected static $keysArgumentPositionOffset = 1;
protected static $countArgumentPositionOffset = 3;
protected static $modifierArgumentPositionOffset = 2;
public function getId()
{
return 'BZMPOP';
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Redis\AbstractCommand\BZPOPBase;
/**
* @link https://redis.io/commands/bzpopmax/
*
* BZPOPMAX is the blocking variant of the sorted set ZPOPMAX primitive.
*
* It is the blocking version because it blocks the connection when there are
* no members to pop from any of the given sorted sets.
* A member with the highest score is popped from first sorted set that is non-empty,
* with the given keys being checked in the order that they are given.
*/
class BZPOPMAX extends BZPOPBase
{
public function getId(): string
{
return 'BZPOPMAX';
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Redis\AbstractCommand\BZPOPBase;
/**
* @link https://redis.io/commands/bzpopmin/
*
* BZPOPMIN is the blocking variant of the sorted set ZPOPMIN primitive.
*
* It is the blocking version because it blocks the connection when there are
* no members to pop from any of the given sorted sets.
* A member with the lowest score is popped from first sorted set that is non-empty,
* with the given keys being checked in the order that they are given.
*/
class BZPOPMIN extends BZPOPBase
{
public function getId(): string
{
return 'BZPOPMIN';
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\DB;
use Predis\Command\Traits\Replace;
/**
* @link https://redis.io/commands/copy/
*
* This command copies the value stored at the source key to the destination key.
*/
class COPY extends RedisCommand
{
use DB {
DB::setArguments as setDB;
}
use Replace {
Replace::setArguments as setReplace;
}
protected static $dbArgumentPositionOffset = 2;
public function getId()
{
return 'COPY';
}
public function setArguments(array $arguments)
{
$this->setDB($arguments);
$arguments = $this->getArguments();
$this->setReplace($arguments);
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Timeout;
use Predis\Command\Traits\To\ServerTo;
class FAILOVER extends RedisCommand
{
use ServerTo {
ServerTo::setArguments as setTo;
}
use Timeout {
Timeout::setArguments as setTimeout;
}
protected static $toArgumentPositionOffset = 0;
protected static $timeoutArgumentPositionOffset = 2;
public function getId()
{
return 'FAILOVER';
}
public function setArguments(array $arguments)
{
if (array_key_exists(1, $arguments) && false !== $arguments[1]) {
$arguments[1] = 'ABORT';
}
$this->setTimeout($arguments);
$arguments = $this->getArguments();
$this->setTo($arguments);
$this->filterArguments();
}
}
+5
View File
@@ -14,6 +14,11 @@ namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @deprecated As of Redis version 6.2.0, this command is regarded as deprecated.
*
* It can be replaced by GEOSEARCH and GEOSEARCHSTORE with the BYRADIUS argument
* when migrating or writing new code.
*
* @link http://redis.io/commands/georadius
*
* @author Daniele Alessandri <suppakilla@gmail.com>
+5
View File
@@ -12,6 +12,11 @@
namespace Predis\Command\Redis;
/**
* @deprecated As of Redis version 6.2.0, this command is regarded as deprecated.
*
* It can be replaced by GEOSEARCH and GEOSEARCHSTORE with the FROMMEMBER arguments
* when migrating or writing new code.
*
* @link http://redis.io/commands/georadiusbymember
*
* @author Daniele Alessandri <suppakilla@gmail.com>
+112
View File
@@ -0,0 +1,112 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\By\GeoBy;
use Predis\Command\Traits\Count;
use Predis\Command\Traits\From\GeoFrom;
use Predis\Command\Traits\Sorting;
use Predis\Command\Traits\With\WithCoord;
use Predis\Command\Traits\With\WithDist;
use Predis\Command\Traits\With\WithHash;
/**
* @link https://redis.io/commands/geosearch/
*
* Return the members of a sorted set populated with geospatial information using GEOADD,
* which are within the borders of the area specified by a given shape.
*
* This command extends the GEORADIUS command, so in addition to searching
* within circular areas, it supports searching within rectangular areas.
*/
class GEOSEARCH extends RedisCommand
{
use GeoFrom {
GeoFrom::setArguments as setFrom;
}
use GeoBy {
GeoBy::setArguments as setBy;
}
use Sorting {
Sorting::setArguments as setSorting;
}
use Count {
Count::setArguments as setCount;
}
use WithCoord {
WithCoord::setArguments as setWithCoord;
}
use WithDist {
WithDist::setArguments as setWithDist;
}
use WithHash {
WithHash::setArguments as setWithHash;
}
protected static $sortArgumentPositionOffset = 3;
protected static $countArgumentPositionOffset = 4;
protected static $withCoordArgumentPositionOffset = 6;
protected static $withDistArgumentPositionOffset = 7;
protected static $withHashArgumentPositionOffset = 8;
public function getId()
{
return 'GEOSEARCH';
}
public function setArguments(array $arguments)
{
$this->setSorting($arguments);
$arguments = $this->getArguments();
$this->setWithCoord($arguments);
$arguments = $this->getArguments();
$this->setWithDist($arguments);
$arguments = $this->getArguments();
$this->setWithHash($arguments);
$arguments = $this->getArguments();
$this->setCount($arguments, $arguments[5] ?? false);
$arguments = $this->getArguments();
$this->setFrom($arguments);
$arguments = $this->getArguments();
$this->setBy($arguments);
$this->filterArguments();
}
public function parseResponse($data)
{
$parsedData = [];
$itemKey = '';
foreach ($data as $item) {
if (!is_array($item)) {
$parsedData[] = $item;
continue;
}
foreach ($item as $key => $itemRow) {
if ($key === 0) {
$itemKey = $itemRow;
continue;
}
if (is_string($itemRow)) {
$parsedData[$itemKey]['dist'] = round((float)$itemRow, 5);
} elseif (is_int($itemRow)) {
$parsedData[$itemKey]['hash'] = $itemRow;
} else {
$parsedData[$itemKey]['lng'] = round($itemRow[0], 5);
$parsedData[$itemKey]['lat'] = round($itemRow[1], 5);
}
}
}
return $parsedData;
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\By\GeoBy;
use Predis\Command\Traits\Count;
use Predis\Command\Traits\From\GeoFrom;
use Predis\Command\Traits\Sorting;
use Predis\Command\Traits\Storedist;
/**
* @link https://redis.io/commands/geosearchstore/
*
* This command is like GEOSEARCH, but stores the result in destination key.
*/
class GEOSEARCHSTORE extends RedisCommand
{
use GeoFrom {
GeoFrom::setArguments as setFrom;
}
use GeoBy {
GeoBy::setArguments as setBy;
}
use Sorting {
Sorting::setArguments as setSorting;
}
use Count {
Count::setArguments as setCount;
}
use Storedist {
Storedist::setArguments as setStoreDist;
}
protected static $sortArgumentPositionOffset = 4;
protected static $countArgumentPositionOffset = 5;
protected static $storeDistArgumentPositionOffset = 7;
public function getId()
{
return 'GEOSEARCHSTORE';
}
public function setArguments(array $arguments)
{
$this->setStoreDist($arguments);
$arguments = $this->getArguments();
$this->setCount($arguments, $arguments[6] ?? false);
$arguments = $this->getArguments();
$this->setSorting($arguments);
$arguments = $this->getArguments();
$this->setFrom($arguments);
$arguments = $this->getArguments();
$this->setBy($arguments);
$this->filterArguments();
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
class GETDEL extends RedisCommand
{
public function getId()
{
return 'GETDEL';
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use UnexpectedValueException;
class GETEX extends RedisCommand
{
/**
* @var string[]
*/
private static $modifierEnum = [
'ex' => 'EX',
'px' => 'PX',
'exat' => 'EXAT',
'pxat' => 'PXAT',
'persist' => 'PERSIST',
];
public function getId()
{
return 'GETEX';
}
public function setArguments(array $arguments)
{
if (!array_key_exists(1, $arguments) || $arguments[1] === '') {
parent::setArguments([$arguments[0]]);
return;
}
if (!in_array(strtoupper($arguments[1]), self::$modifierEnum)) {
$enumValues = implode(', ', array_keys(self::$modifierEnum));
throw new UnexpectedValueException("Modifier argument accepts only: {$enumValues} values");
}
if ($arguments[1] === 'persist') {
parent::setArguments([$arguments[0], self::$modifierEnum[$arguments[1]]]);
return;
}
$arguments[1] = self::$modifierEnum[$arguments[1]];
if (!array_key_exists(2, $arguments)) {
throw new UnexpectedValueException('You should provide value for current modifier');
}
parent::setArguments($arguments);
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\With\WithValues;
/**
* @link https://redis.io/commands/hrandfield/
*
* When called with just the key argument, return a random field from the hash value stored at key.
*
* If the provided count argument is positive, return an array of distinct fields.
* The array's length is either count or the hash's number of fields (HLEN), whichever is lower.
*/
class HRANDFIELD extends RedisCommand
{
use WithValues;
public function getId()
{
return 'HRANDFIELD';
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
class LMOVE extends RedisCommand
{
public function getId()
{
return 'LMOVE';
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Count;
use \Predis\Command\Traits\Keys;
use Predis\Command\Traits\LeftRight;
class LMPOP extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use LeftRight {
LeftRight::setArguments as setLeftRight;
}
use Count {
Count::setArguments as setCount;
}
protected static $keysArgumentPositionOffset = 0;
protected static $leftRightArgumentPositionOffset = 1;
protected static $countArgumentPositionOffset = 2;
public function getId()
{
return 'LMPOP';
}
public function setArguments(array $arguments)
{
$this->setCount($arguments);
$arguments = $this->getArguments();
$this->setLeftRight($arguments);
$arguments = $this->getArguments();
$this->setKeys($arguments);
$this->filterArguments();
}
public function parseResponse($data)
{
if (null !== $data) {
return [$data[0] => $data[1]];
}
return null;
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link https://redis.io/commands/smismember/
*
* Returns whether each member is a member of the set stored at key.
*/
class SMISMEMBER extends RedisCommand
{
public function getId()
{
return 'SMISMEMBER';
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xadd
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XADD extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XADD';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$args = array();
$args[] = $arguments[0];
$options = $arguments[3] ?? [];
if (isset($options['nomkstream']) && $options['nomkstream']) {
$args[] = 'NOMKSTREAM';
}
if (isset($options['trim']) && is_array($options['trim'])) {
array_push($args, ...$options['trim']);
if (isset($options['limit'])) {
$args[] = 'LIMIT';
$args[] = $options['limit'];
}
}
// ID, default to * to let Redis set it
$args[] = $arguments[2] ?? '*';
if (isset($arguments[1]) && is_array($arguments[1])) {
foreach ($arguments[1] as $key => $val) {
$args[] = $key;
$args[] = $val;
}
}
parent::setArguments($args);
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xdel
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XDEL extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XDEL';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$arguments = self::normalizeVariadic($arguments);
parent::setArguments($arguments);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xlen
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XLEN extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XLEN';
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xrange
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XRANGE extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XRANGE';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
if (count($arguments) === 4) {
$arguments[] = $arguments[3];
$arguments[3] = 'COUNT';
}
parent::setArguments($arguments);
}
/**
* {@inheritdoc}
*/
public function parseResponse($data)
{
$result = array();
foreach ($data as $entry) {
$processed = array();
$count = count($entry[1]);
for ($i = 0; $i < $count; ++$i) {
$processed[$entry[1][$i]] = $entry[1][++$i];
}
$result[$entry[0]] = $processed;
}
return $result;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
/**
* @link http://redis.io/commands/xrevrange
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XREVRANGE extends XRANGE
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XREVRANGE';
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link http://redis.io/commands/xtrim
*
* @author Daniele Alessandri <suppakilla@gmail.com>
*/
class XTRIM extends RedisCommand
{
/**
* {@inheritdoc}
*/
public function getId()
{
return 'XTRIM';
}
/**
* {@inheritdoc}
*/
public function setArguments(array $arguments)
{
$args = [];
$options = $arguments[3] ?? [];
$args[] = $arguments[0];
// Either e.g. 'MAXLEN' or ['MAXLEN', '~']
if (is_array($arguments[1])) {
array_push($args, ...$arguments[1]);
} else {
$args[] = $arguments[1];
}
$args[] = $arguments[2];
if (isset($options['limit'])) {
$args[] = 'LIMIT';
$args[] = $options['limit'];
}
parent::setArguments($args);
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\With\WithScores;
/**
* @link https://redis.io/commands/zdiff/
*
* This command is similar to ZDIFFSTORE, but instead of
* storing the resulting sorted set, it is returned to the client.
*/
class ZDIFF extends RedisCommand
{
use WithScores {
WithScores::setArguments as setWithScore;
}
use Keys {
Keys::setArguments as setKeys;
}
protected static $keysArgumentPositionOffset = 0;
public function getId()
{
return 'ZDIFF';
}
public function setArguments(array $arguments)
{
$this->setKeys($arguments);
$arguments = $this->getArguments();
$this->setWithScore($arguments);
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;
/**
* @link https://redis.io/commands/zdiffstore/
*
* Computes the difference between the first and all successive input sorted sets
* and stores the result in destination. The total number of input keys is specified by numkeys.
*
* Keys that do not exist are considered to be empty sets.
*
* If destination already exists, it is overwritten.
*/
class ZDIFFSTORE extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
public static $keysArgumentPositionOffset = 1;
public function getId()
{
return 'ZDIFFSTORE';
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Traits\With\WithScores;
/**
* @link https://redis.io/commands/zinter/
*
* This command is similar to ZINTERSTORE, but instead of
* storing the resulting sorted set, it is returned to the client.
*/
class ZINTER extends ZINTERSTORE
{
use WithScores;
protected static $keysArgumentPositionOffset = 0;
protected static $weightsArgumentPositionOffset = 1;
protected static $aggregateArgumentPositionOffset = 2;
public function getId()
{
return 'ZINTER';
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\Limit;
/**
* @link https://redis.io/commands/zintercard/
*
* This command is similar to ZINTER, but instead of returning the result set,
* it returns just the cardinality of the result.
*/
class ZINTERCARD extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use Limit {
Limit::setArguments as setLimit;
}
protected static $keysArgumentPositionOffset = 0;
protected static $limitArgumentPositionOffset = 1;
public function getId()
{
return 'ZINTERCARD';
}
public function setArguments(array $arguments)
{
$this->setLimit($arguments);
$arguments = $this->getArguments();
$this->setKeys($arguments);
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Count;
use Predis\Command\Traits\MinMaxModifier;
use Predis\Command\Traits\Keys;
/**
* @link https://redis.io/commands/zmpop/
*
* Pops one or more elements, that are member-score pairs,
* from the first non-empty sorted set in the provided list of key names.
*/
class ZMPOP extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use Count {
Count::setArguments as setCount;
}
use MinMaxModifier;
protected static $keysArgumentPositionOffset = 0;
protected static $countArgumentPositionOffset = 2;
protected static $modifierArgumentPositionOffset = 1;
public function getId()
{
return 'ZMPOP';
}
public function setArguments(array $arguments)
{
$this->setCount($arguments);
$arguments = $this->getArguments();
$this->resolveModifier(static::$modifierArgumentPositionOffset, $arguments);
$this->setKeys($arguments);
$arguments = $this->getArguments();
parent::setArguments($arguments);
}
public function parseResponse($data)
{
$key = array_shift($data);
if (null === $key) {
return [$key];
}
$data = $data[0];
$parsedData = [];
for ($i = 0, $iMax = count($data); $i < $iMax; $i++) {
for ($j = 0, $jMax = count($data[$i]); $j < $jMax; ++$j) {
if ($data[$i][$j + 1] ?? false) {
$parsedData[$data[$i][$j]] = $data[$i][++$j];
}
}
}
return array_combine([$key], [$parsedData]);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
/**
* @link https://redis.io/commands/zmscore/
*
* Returns the scores associated with the specified members
* in the sorted set stored at key.
*
* For every member that does not exist in the sorted set, a null value is returned.
*
*/
class ZMSCORE extends RedisCommand
{
/**
* @inheritDoc
*/
public function getId()
{
return 'ZMSCORE';
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\With\WithScores;
/**
* @link https://redis.io/commands/zrandmember/
*
* Return a random element from the sorted set value stored at key.
*
* If the provided count argument is positive, return an array of distinct elements.
*
* If called with a negative count, the behavior changes and the command
* is allowed to return the same element multiple times.
*
*/
class ZRANDMEMBER extends RedisCommand
{
use WithScores;
public function getId()
{
return 'ZRANDMEMBER';
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\By\ByLexByScore;
use Predis\Command\Traits\Limit;
use Predis\Command\Traits\Rev;
/**
* @link https://redis.io/commands/zrangestore/
*
* This command is like ZRANGE, but stores the result in the destination key.
*/
class ZRANGESTORE extends RedisCommand
{
use ByLexByScore {
ByLexByScore::setArguments as setByLexByScoreArgument;
}
use Rev {
Rev::setArguments as setReversedArgument;
}
use Limit {
Limit::setArguments as setLimitArguments;
}
protected static $byLexByScoreArgumentPositionOffset = 4;
protected static $revArgumentPositionOffset = 5;
protected static $limitArgumentPositionOffset = 6;
public function getId()
{
return 'ZRANGESTORE';
}
public function setArguments(array $arguments)
{
$this->setByLexByScoreArgument($arguments);
$arguments = $this->getArguments();
$this->setReversedArgument($arguments);
$arguments = $this->getArguments();
$this->setLimitArguments($arguments);
$this->filterArguments();
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Predis\Command\Redis;
use Predis\Command\Traits\With\WithScores;
/**
* @link https://redis.io/commands/zunion/
*
* This command is similar to ZUNIONSTORE, but instead of
* storing the resulting sorted set, it is returned to the client.
*/
class ZUNION extends ZUNIONSTORE
{
use WithScores;
protected static $keysArgumentPositionOffset = 0;
protected static $weightsArgumentPositionOffset = 1;
protected static $aggregateArgumentPositionOffset = 2;
public function getId()
{
return 'ZUNION';
}
}
+27 -39
View File
@@ -12,6 +12,9 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\Traits\Aggregate;
use Predis\Command\Traits\Keys;
use Predis\Command\Traits\Weights;
/**
* @link http://redis.io/commands/zunionstore
@@ -20,6 +23,20 @@ use Predis\Command\Command as RedisCommand;
*/
class ZUNIONSTORE extends RedisCommand
{
use Keys {
Keys::setArguments as setKeys;
}
use Weights {
Weights::setArguments as setWeights;
}
use Aggregate{
Aggregate::setArguments as setAggregate;
}
protected static $keysArgumentPositionOffset = 1;
protected static $weightsArgumentPositionOffset = 2;
protected static $aggregateArgumentPositionOffset = 3;
/**
* {@inheritdoc}
*/
@@ -33,48 +50,19 @@ class ZUNIONSTORE extends RedisCommand
*/
public function setArguments(array $arguments)
{
$options = array();
$argc = count($arguments);
if ($argc > 2 && is_array($arguments[$argc - 1])) {
$options = $this->prepareOptions(array_pop($arguments));
// support old `$options` array for backwards compatibility
if (! isset($arguments[3]) && (isset($arguments[2]['weights']) || isset($arguments[2]['aggregate']))) {
$options = array_pop($arguments);
array_push($arguments, $options['weights'] ?? []);
array_push($arguments, $options['aggregate'] ?? 'sum');
}
if (is_array($arguments[1])) {
$arguments = array_merge(
array($arguments[0], count($arguments[1])),
$arguments[1]
);
}
$this->setAggregate($arguments);
$arguments = $this->getArguments();
parent::setArguments(array_merge($arguments, $options));
}
$this->setWeights($arguments);
$arguments = $this->getArguments();
/**
* Returns a list of options and modifiers compatible with Redis.
*
* @param array $options List of options.
*
* @return array
*/
private function prepareOptions($options)
{
$opts = array_change_key_case($options, CASE_UPPER);
$finalizedOpts = array();
if (isset($opts['WEIGHTS']) && is_array($opts['WEIGHTS'])) {
$finalizedOpts[] = 'WEIGHTS';
foreach ($opts['WEIGHTS'] as $weight) {
$finalizedOpts[] = $weight;
}
}
if (isset($opts['AGGREGATE'])) {
$finalizedOpts[] = 'AGGREGATE';
$finalizedOpts[] = $opts['AGGREGATE'];
}
return $finalizedOpts;
$this->setKeys($arguments);
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait Aggregate
{
/**
* @var string[]
*/
private static $aggregateValuesEnum = [
'min' => 'MIN',
'max' => 'MAX',
'sum' => 'SUM'
];
/**
* @var string
*/
private static $aggregateModifier = 'AGGREGATE';
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (static::$aggregateArgumentPositionOffset >= $argumentsLength) {
parent::setArguments($arguments);
return;
}
$argument = $arguments[static::$aggregateArgumentPositionOffset];
if (is_string($argument) && in_array(strtoupper($argument), self::$aggregateValuesEnum)) {
$argument = self::$aggregateValuesEnum[$argument];
} else {
$enumValues = implode(', ', array_keys(self::$aggregateValuesEnum));
throw new UnexpectedValueException("Aggregate argument accepts only: {$enumValues} values");
}
$argumentsBefore = array_slice($arguments, 0, static::$aggregateArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$aggregateArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
[self::$aggregateModifier],
[$argument],
$argumentsAfter
));
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace Predis\Command\Traits\By;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait ByLexByScore
{
private static $argumentsEnum = [
'bylex' => 'BYLEX',
'byscore' => 'BYSCORE',
];
public function setArguments(array $arguments)
{
$argument = $arguments[static::$byLexByScoreArgumentPositionOffset];
if (false === $argument) {
parent::setArguments($arguments);
return;
}
if (is_string($argument) && in_array(strtoupper($argument), self::$argumentsEnum)) {
$argument = self::$argumentsEnum[$argument];
} else {
throw new UnexpectedValueException("By argument accepts only \"bylex\" and \"byscore\" values");
}
$argumentsBefore = array_slice($arguments, 0, static::$byLexByScoreArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$byLexByScoreArgumentPositionOffset + 1);
parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter));
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Predis\Command\Traits\By;
use InvalidArgumentException;
use Predis\Command\Argument\Geospatial\ByInterface;
trait GeoBy
{
public function setArguments(array $arguments)
{
$argumentPositionOffset = $this->getByArgumentPositionOffset($arguments);
if (null === $argumentPositionOffset) {
throw new InvalidArgumentException('Invalid BY argument value given');
}
$byArgumentObject = $arguments[$argumentPositionOffset];
$argumentsBefore = array_slice($arguments, 0, $argumentPositionOffset);
$argumentsAfter = array_slice($arguments, $argumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
$byArgumentObject->toArray(),
$argumentsAfter
));
}
private function getByArgumentPositionOffset(array $arguments): ?int
{
foreach ($arguments as $i => $value) {
if ($value instanceof ByInterface) {
return $i;
}
}
return null;
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait Count
{
private $countModifier = 'COUNT';
private $anyModifier = 'ANY';
public function setArguments(array $arguments, bool $any = false)
{
$argumentsLength = count($arguments);
if (static::$countArgumentPositionOffset >= $argumentsLength) {
parent::setArguments($arguments);
return;
}
if ($arguments[static::$countArgumentPositionOffset] === -1) {
array_splice($arguments, static::$countArgumentPositionOffset, 1, [false]);
parent::setArguments($arguments);
return;
}
if ($arguments[static::$countArgumentPositionOffset] < 1) {
throw new UnexpectedValueException('Wrong count argument value or position offset');
}
$countArgument = $arguments[static::$countArgumentPositionOffset];
$argumentsBefore = array_slice($arguments, 0, static::$countArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$countArgumentPositionOffset + 2);
if (!$any) {
$argumentsAfter = array_slice($arguments, static::$countArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
[$this->countModifier],
[$countArgument],
$argumentsAfter
));
return;
}
parent::setArguments(array_merge(
$argumentsBefore,
[$this->countModifier],
[$countArgument],
[$this->anyModifier],
$argumentsAfter
));
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace Predis\Command\Traits;
use UnexpectedValueException;
trait DB
{
private $dbModifier = 'DB';
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (static::$dbArgumentPositionOffset >= $argumentsLength) {
parent::setArguments($arguments);
return;
}
if (!is_numeric($arguments[static::$dbArgumentPositionOffset])) {
throw new UnexpectedValueException('DB argument should be a valid numeric value');
}
if ($arguments[static::$dbArgumentPositionOffset] < 0) {
array_splice($arguments, static::$dbArgumentPositionOffset, 1);
parent::setArguments($arguments);
return;
}
$argument = $arguments[static::$dbArgumentPositionOffset];
$argumentsBefore = array_slice($arguments, 0, static::$dbArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$dbArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
[$this->dbModifier],
[$argument],
$argumentsAfter
));
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace Predis\Command\Traits\From;
use InvalidArgumentException;
use Predis\Command\Argument\Geospatial\FromInterface;
trait GeoFrom
{
public function setArguments(array $arguments)
{
$argumentPositionOffset = $this->getFromArgumentPositionOffset($arguments);
if (null === $argumentPositionOffset) {
throw new InvalidArgumentException('Invalid FROM argument value given');
}
$fromArgumentObject = $arguments[$argumentPositionOffset];
$argumentsBefore = array_slice($arguments, 0, $argumentPositionOffset);
$argumentsAfter = array_slice($arguments, $argumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
$fromArgumentObject->toArray(),
$argumentsAfter
));
}
private function getFromArgumentPositionOffset(array $arguments): ?int
{
foreach ($arguments as $i => $value) {
if ($value instanceof FromInterface) {
return $i;
}
}
return null;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait Keys
{
public function setArguments(array $arguments, bool $withNumkeys = true)
{
$argumentsLength = count($arguments);
if (
static::$keysArgumentPositionOffset > $argumentsLength ||
!is_array($arguments[static::$keysArgumentPositionOffset])
) {
throw new UnexpectedValueException('Wrong keys argument type or position offset');
}
$keysArgument = $arguments[static::$keysArgumentPositionOffset];
$argumentsBeforeKeys = array_slice($arguments, 0, static::$keysArgumentPositionOffset);
$argumentsAfterKeys = array_slice($arguments, static::$keysArgumentPositionOffset + 1);
if ($withNumkeys) {
$numkeys = count($keysArgument);
parent::setArguments(array_merge($argumentsBeforeKeys, [$numkeys], $keysArgument, $argumentsAfterKeys));
return;
}
parent::setArguments(array_merge($argumentsBeforeKeys, $keysArgument, $argumentsAfterKeys));
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait LeftRight
{
/**
* @var array{string: string}
*/
private static $leftRightEnum = [
'left' => 'LEFT',
'right' => 'RIGHT',
];
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (static::$leftRightArgumentPositionOffset >= $argumentsLength) {
$arguments[] = 'LEFT';
parent::setArguments($arguments);
return;
}
$argument = $arguments[static::$leftRightArgumentPositionOffset];
if (is_string($argument) && in_array(strtoupper($argument), self::$leftRightEnum, true)) {
$argument = self::$leftRightEnum[$argument];
} else {
$enumValues = implode(', ', array_keys(self::$leftRightEnum));
throw new UnexpectedValueException("Left/Right argument accepts only: {$enumValues} values");
}
$argumentsBefore = array_slice($arguments, 0, static::$leftRightArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$leftRightArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
[$argument],
$argumentsAfter
));
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait Limit
{
private static $limitModifier = 'LIMIT';
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
$argumentsBefore = array_slice($arguments, 0, static::$limitArgumentPositionOffset);
if (
static::$limitArgumentPositionOffset >= $argumentsLength
|| false === $arguments[static::$limitArgumentPositionOffset]
) {
parent::setArguments($argumentsBefore);
return;
}
$argument = $arguments[static::$limitArgumentPositionOffset];
$argumentsAfter = array_slice($arguments, static::$limitArgumentPositionOffset + 1);
if (true === $argument) {
parent::setArguments(array_merge($argumentsBefore, [self::$limitModifier], $argumentsAfter));
return;
}
if (!is_int($argument)) {
throw new UnexpectedValueException('Wrong limit argument type');
}
parent::setArguments(array_merge($argumentsBefore, [self::$limitModifier], [$argument], $argumentsAfter));
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait MinMaxModifier
{
/**
* @var array{string: string}
*/
private $modifierEnum = [
'min' => 'MIN',
'max' => 'MAX',
];
public function resolveModifier(int $offset, array &$arguments): void
{
if ($offset >= count($arguments)) {
$arguments[$offset] = $this->modifierEnum['min'];
return;
}
if (!is_string($arguments[$offset]) || !array_key_exists($arguments[$offset], $this->modifierEnum)) {
throw new UnexpectedValueException('Wrong type of modifier given');
}
$arguments[$offset] = $this->modifierEnum[$arguments[$offset]];
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
/**
* @mixin Command
*/
trait Replace
{
public function setArguments(array $arguments)
{
$replace = array_pop($arguments);
if (is_bool($replace) && $replace) {
$arguments[] = 'REPLACE';
} else if (!is_bool($replace)) {
$arguments[] = $replace;
}
parent::setArguments($arguments);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait Rev
{
public function setArguments(array $arguments)
{
$argument = $arguments[static::$revArgumentPositionOffset];
if (false === $argument) {
parent::setArguments($arguments);
return;
}
if (true === $argument) {
$argument = 'REV';
} else {
throw new UnexpectedValueException('Wrong rev argument type');
}
$argumentsBefore = array_slice($arguments, 0, static::$revArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$revArgumentPositionOffset + 1);
parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter));
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace Predis\Command\Traits;
use UnexpectedValueException;
trait Sorting
{
private static $sortingEnum = [
'asc' => 'ASC',
'desc' => 'DESC',
];
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (static::$sortArgumentPositionOffset >= $argumentsLength) {
parent::setArguments($arguments);
return;
}
$argument = $arguments[static::$sortArgumentPositionOffset];
if (null === $argument) {
array_splice($arguments, static::$sortArgumentPositionOffset, 1, [false]);
parent::setArguments($arguments);
return;
}
if (!in_array(strtoupper($argument), self::$sortingEnum, true)) {
$enumValues = implode(', ', array_keys(self::$sortingEnum));
throw new UnexpectedValueException("Sorting argument accepts only: {$enumValues} values");
}
$argumentsBefore = array_slice($arguments, 0, static::$sortArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$sortArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
[self::$sortingEnum[$argument]],
$argumentsAfter
));
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait Storedist
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (
static::$storeDistArgumentPositionOffset >= $argumentsLength
|| false === $arguments[static::$storeDistArgumentPositionOffset]
) {
parent::setArguments($arguments);
return;
}
$argument = $arguments[static::$storeDistArgumentPositionOffset];
if (true === $argument) {
$argument = 'STOREDIST';
} else {
throw new UnexpectedValueException("Wrong STOREDIST argument type");
}
$argumentsBefore = array_slice($arguments, 0, static::$storeDistArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$storeDistArgumentPositionOffset + 1);
parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter));
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace Predis\Command\Traits;
use UnexpectedValueException;
trait Timeout
{
private static $timeoutModifier = 'TIMEOUT';
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (static::$timeoutArgumentPositionOffset >= $argumentsLength) {
parent::setArguments($arguments);
return;
}
if ($arguments[static::$timeoutArgumentPositionOffset] === -1) {
array_splice($arguments, static::$timeoutArgumentPositionOffset, 1, [false]);
parent::setArguments($arguments);
return;
}
if ($arguments[static::$timeoutArgumentPositionOffset] < 1) {
throw new UnexpectedValueException('Wrong timeout argument value or position offset');
}
$argument = $arguments[static::$timeoutArgumentPositionOffset];
$argumentsBefore = array_slice($arguments, 0, static::$timeoutArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$timeoutArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
[self::$timeoutModifier],
[$argument],
$argumentsAfter
));
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace Predis\Command\Traits\To;
use Predis\Command\Argument\Server\To;
trait ServerTo
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (static::$toArgumentPositionOffset >= $argumentsLength) {
parent::setArguments($arguments);
return;
}
/** @var To|null $toArgument */
$toArgument = $arguments[static::$toArgumentPositionOffset];
if (null === $toArgument) {
array_splice($arguments, static::$toArgumentPositionOffset, 1, [false]);
parent::setArguments($arguments);
return;
}
$argumentsBefore = array_slice($arguments, 0, static::$toArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$toArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
$toArgument->toArray(),
$argumentsAfter
));
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Predis\Command\Traits;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait Weights
{
/**
* @var string
*/
private static $weightsModifier = 'WEIGHTS';
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (static::$weightsArgumentPositionOffset >= $argumentsLength) {
parent::setArguments($arguments);
return;
}
if (!is_array($arguments[static::$weightsArgumentPositionOffset])) {
throw new UnexpectedValueException('Wrong weights argument type');
}
$weightsArray = $arguments[static::$weightsArgumentPositionOffset];
if (empty($weightsArray)) {
unset($arguments[static::$weightsArgumentPositionOffset]);
parent::setArguments($arguments);
return;
}
$argumentsBefore = array_slice($arguments, 0, static::$weightsArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$weightsArgumentPositionOffset + 1);
parent::setArguments(array_merge(
$argumentsBefore,
[self::$weightsModifier],
$weightsArray,
$argumentsAfter
));
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace Predis\Command\Traits\With;
use Predis\Command\Command;
use UnexpectedValueException;
/**
* @mixin Command
*/
trait WithCoord
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (
static::$withCoordArgumentPositionOffset >= $argumentsLength
|| false === $arguments[static::$withCoordArgumentPositionOffset]
) {
parent::setArguments($arguments);
return;
}
$argument = $arguments[static::$withCoordArgumentPositionOffset];
if (true === $argument) {
$argument = 'WITHCOORD';
} else {
throw new UnexpectedValueException("Wrong WITHCOORD argument type");
}
$argumentsBefore = array_slice($arguments, 0, static::$withCoordArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$withCoordArgumentPositionOffset + 1);
parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter));
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Predis\Command\Traits\With;
use UnexpectedValueException;
trait WithDist
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (
static::$withDistArgumentPositionOffset >= $argumentsLength
|| false === $arguments[static::$withDistArgumentPositionOffset]
) {
parent::setArguments($arguments);
return;
}
$argument = $arguments[static::$withDistArgumentPositionOffset];
if (true === $argument) {
$argument = 'WITHDIST';
} else {
throw new UnexpectedValueException("Wrong WITHDIST argument type");
}
$argumentsBefore = array_slice($arguments, 0, static::$withDistArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$withDistArgumentPositionOffset + 1);
parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter));
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Predis\Command\Traits\With;
use UnexpectedValueException;
trait WithHash
{
public function setArguments(array $arguments)
{
$argumentsLength = count($arguments);
if (
static::$withHashArgumentPositionOffset >= $argumentsLength
|| false === $arguments[static::$withHashArgumentPositionOffset]
) {
parent::setArguments($arguments);
return;
}
$argument = $arguments[static::$withHashArgumentPositionOffset];
if (true === $argument) {
$argument = 'WITHHASH';
} else {
throw new UnexpectedValueException("Wrong WITHHASH argument type");
}
$argumentsBefore = array_slice($arguments, 0, static::$withHashArgumentPositionOffset);
$argumentsAfter = array_slice($arguments, static::$withHashArgumentPositionOffset + 1);
parent::setArguments(array_merge($argumentsBefore, [$argument], $argumentsAfter));
}
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace Predis\Command\Traits\With;
use Predis\Command\Command;
/**
* Handles last argument passed into command as WITHSCORES
*
* @mixin Command
*/
trait WithScores
{
public function setArguments(array $arguments)
{
$withScores = array_pop($arguments);
if (is_bool($withScores) && $withScores) {
$arguments[] = 'WITHSCORES';
} else if (!is_bool($withScores)) {
$arguments[] = $withScores;
}
parent::setArguments($arguments);
}
/**
* Checks for the presence of the WITHSCORES modifier.
*
* @return bool
*/
private function isWithScoreModifier(): bool
{
$arguments = parent::getArguments();
$lastArgument = (!empty($arguments)) ? $arguments[count($arguments) - 1] : null;
return is_string($lastArgument) && strtoupper($lastArgument) === 'WITHSCORES';
}
public function parseResponse($data)
{
if ($this->isWithScoreModifier()) {
$result = [];
for ($i = 0, $iMax = count($data); $i < $iMax; ++$i) {
if ($data[$i + 1] ?? false) {
$result[$data[$i]] = $data[++$i];
}
}
return $result;
}
return $data;
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace Predis\Command\Traits\With;
use Predis\Command\Command;
/**
* @mixin Command
*/
trait WithValues
{
public function setArguments(array $arguments)
{
$withValues = array_pop($arguments);
if (is_bool($withValues) && $withValues) {
$arguments[] = 'WITHVALUES';
} else if (!is_bool($withValues)) {
$arguments[] = $withValues;
}
parent::setArguments($arguments);
}
}
+50 -7
View File
@@ -22,6 +22,8 @@ use Predis\Connection\FactoryInterface;
use Predis\Connection\NodeConnectionInterface;
use Predis\NotSupportedException;
use Predis\Response\ErrorInterface as ErrorResponseInterface;
use Predis\Response\ServerException;
use Predis\Response\Error as ErrorResponse;
/**
* Abstraction for a Redis-backed cluster of nodes (Redis >= 3.0.0).
@@ -54,6 +56,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
private $strategy;
private $connections;
private $retryLimit = 5;
private $retryInterval = 10;
/**
* @param FactoryInterface $connections Optional connection factory.
@@ -82,6 +85,26 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
$this->retryLimit = (int) $retry;
}
/**
* Sets the initial retry interval (milliseconds).
*
* @param int $retryInterval Milliseconds between retries.
*/
public function setRetryInterval($retryInterval)
{
$this->retryInterval = (int) $retryInterval;
}
/**
* Returns the retry interval (milliseconds).
*
* @return int Milliseconds between retries.
*/
public function getRetryInterval()
{
return (int) $this->retryInterval;
}
/**
* {@inheritdoc}
*/
@@ -207,6 +230,7 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
private function queryClusterNodeForSlotMap(NodeConnectionInterface $connection)
{
$retries = 0;
$retryAfter = $this->retryInterval;
$command = RawCommand::create('CLUSTER', 'SLOTS');
RETRY_COMMAND: {
@@ -226,7 +250,10 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
throw new ClientException('No connections left in the pool for `CLUSTER SLOTS`');
}
usleep($retryAfter * 1000);
$retryAfter = $retryAfter * 2;
++$retries;
goto RETRY_COMMAND;
}
}
@@ -482,24 +509,40 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable
*/
private function retryCommandOnFailure(CommandInterface $command, $method)
{
$failure = false;
$retries = 0;
$retryAfter = $this->retryInterval;
RETRY_COMMAND: {
try {
$response = $this->getConnectionByCommand($command)->$method($command);
} catch (ConnectionException $exception) {
$connection = $exception->getConnection();
$connection->disconnect();
$this->remove($connection);
if ($response instanceof ErrorResponse) {
$message = $response->getMessage();
if ($failure) {
if (strpos($message, 'CLUSTERDOWN') !== false) {
throw new ServerException($message);
}
}
} catch (\Throwable $exception) {
usleep($retryAfter * 1000);
$retryAfter = $retryAfter * 2;
if ($exception instanceof ConnectionException) {
$connection = $exception->getConnection();
if ($connection) {
$connection->disconnect();
$this->remove($connection);
}
}
if ($retries === $this->retryLimit) {
throw $exception;
} elseif ($this->useClusterSlots) {
$this->askSlotMap();
}
$failure = true;
++$retries;
goto RETRY_COMMAND;
}
+8
View File
@@ -26,6 +26,14 @@ class Parameters implements ParametersInterface
'port' => 6379,
);
/**
* Set of connection paramaters already filtered
* for NULL or 0-length string values.
*
* @var array
*/
protected $parameters;
/**
* @param array $parameters Named array of connection parameters.
*/
@@ -70,6 +70,11 @@ class SentinelReplication implements ReplicationInterface
*/
protected $sentinels = array();
/**
* @var int
*/
protected $sentinelIndex = 0;
/**
* @var NodeConnectionInterface
*/
@@ -264,6 +269,12 @@ class SentinelReplication implements ReplicationInterface
$parameters['database'] = null;
$parameters['username'] = null;
// don't leak password from between configurations
// https://github.com/predis/predis/pull/807/#discussion_r985764770
if (! isset($parameters['password'])) {
$parameters['password'] = null;
}
if (!isset($parameters['timeout'])) {
$parameters['timeout'] = $this->sentinelTimeout;
}
@@ -282,11 +293,13 @@ class SentinelReplication implements ReplicationInterface
public function getSentinelConnection()
{
if (!$this->sentinelConnection) {
if (!$this->sentinels) {
if ($this->sentinelIndex >= count($this->sentinels)) {
$this->sentinelIndex = 0;
throw new \Predis\ClientException('No sentinel server available for autodiscovery.');
}
$sentinel = array_shift($this->sentinels);
$sentinel = $this->sentinels[$this->sentinelIndex];
++$this->sentinelIndex;
$this->sentinelConnection = $this->createSentinelConnection($sentinel);
}
@@ -307,6 +320,7 @@ class SentinelReplication implements ReplicationInterface
);
$this->sentinels = array();
$this->sentinelIndex = 0;
// NOTE: sentinel server does not return itself, so we add it back.
$this->sentinels[] = $sentinel->getParameters()->toArray();
+2 -2
View File
@@ -260,7 +260,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;
@@ -304,7 +304,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.');
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace PHPUnit;
use PHPUnit\Framework\Constraint\Constraint;
class OneOfConstraint extends Constraint
{
/**
* @var array
*/
protected $array;
public function __construct(array $array)
{
$this->array = $array;
}
/**
* @param mixed $other
* @return bool
*/
protected function matches($other): bool
{
if (is_array($other)) {
return !empty(array_intersect($other, $this->array));
}
if (in_array($other, $this->array, true)) {
return true;
}
return false;
}
/**
* @param $other
* @return string
*/
protected function failureDescription($other): string
{
return $this->toString();
}
/**
* @inheritDoc
*/
public function toString(): string
{
return 'given value matches any values from array';
}
}
-1
View File
@@ -51,7 +51,6 @@ abstract class PredisCommandTestCase extends PredisTestCase
* Returns a new client instance.
*
* @param bool $flushdb Flush selected database before returning the client
*
* @return Client
*/
public function getClient(bool $flushdb = true): Client
+19 -1
View File
@@ -10,6 +10,8 @@
*/
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\OneOfConstraint;
use PHPUnit\Util\Test as TestUtil;
use Predis\Client;
use Predis\Command;
use Predis\Connection;
@@ -93,6 +95,19 @@ abstract class PredisTestCase extends \PHPUnit\Framework\TestCase
$this->assertThat($actual, new ArrayHasSameValuesConstraint($expected), $message);
}
/**
* Asserts that actual value is one of the values from expected array.
*
* @param mixed $expected Expected array.
* @param mixed $actual Actual value. If array given searching for any matching value between two arrays.
* @param string $message Optional assertion message
* @return void
*/
public function assertOneOf(array $expected, $actual, string $message = ''): void
{
$this->assertThat($actual, new OneOfConstraint($expected), $message);
}
/**
* Asserts that a string matches a given regular expression.
*
@@ -315,7 +330,10 @@ abstract class PredisTestCase extends \PHPUnit\Framework\TestCase
*/
protected function getRequiredRedisServerVersion(): ?string
{
$annotations = $this->getAnnotations();
$annotations = TestUtil::parseTestMethodAnnotations(
get_class($this),
$this->getName(false)
);
if (isset($annotations['method']['requiresRedisVersion'], $annotations['method']['group']) &&
!empty($annotations['method']['requiresRedisVersion']) &&
+1 -1
View File
@@ -148,7 +148,7 @@ class PredisStrategyTest extends PredisTestCase
{
$strategy = $this->getClusterStrategy();
$commands = $this->getCommandFactory();
$arguments = array('{key}:destination', 2, '{key}:1', '{key}:1', array('aggregate' => 'SUM'));
$arguments = array('{key}:destination', ['{key}:1', '{key}:1'], [], 'sum');
foreach ($this->getExpectedCommands('keys-zaggregated') as $commandID) {
$command = $commands->create($commandID, $arguments);
@@ -0,0 +1,75 @@
<?php
namespace Predis\Command\Redis\AbstractCommand;
use Predis\Command\CommandInterface;
use PredisTestCase;
class BZPOPBaseTest extends PredisTestCase
{
/**
* @var CommandInterface
*/
private $testCommand;
protected function setUp(): void
{
parent::setUp();
$this->testCommand = new class extends BZPOPBase {
public function getId(): string
{
return 'test';
}
};
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$this->testCommand->setArguments($actualArguments);
$this->assertSame($expectedArguments, $this->testCommand->getArguments());
}
/**
* @group disconnected
* @dataProvider responsesProvider
*/
public function testParseResponse(array $actualResponse, array $expectedResponse): void
{
$this->assertSame($expectedResponse, $this->testCommand->parseResponse($actualResponse));
}
public function argumentsProvider(): array
{
return [
'with one key' => [
[['key1'], 1],
['key1', 1]
],
'with multiple keys' => [
[['key1', 'key2', 'key3'], 1],
['key1', 'key2', 'key3', 1]
],
];
}
public function responsesProvider(): array
{
return [
'null-element array' => [
[null],
[null]
],
'three-element array' => [
['key', 'member', 'score'],
['key' => ['member' => 'score']]
],
];
}
}
+200
View File
@@ -0,0 +1,200 @@
<?php
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
class BLMOVE_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return BLMOVE::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'BLMOVE';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = ['list', 'argument1', 'argument2', 'argument3'];
$expected = ['list', 'argument1', 'argument2', 'argument3'];
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @dataProvider listsProvider
* @param array $firstList
* @param array $secondList
* @param string $where
* @param string $to
* @param string $expectedResponse
* @param array $expectedModifiedFirstList
* @param array $expectedModifiedSecondList
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testReturnsCorrectListElement(
array $firstList,
array $secondList,
string $where,
string $to,
string $expectedResponse,
array $expectedModifiedFirstList,
array $expectedModifiedSecondList
): void {
$redis = $this->getClient();
$redis->rpush('test-blmove1', $firstList);
$redis->rpush('test-blmove2', $secondList);
$actualResponse = $redis->blmove('test-blmove1', 'test-blmove2', $where, $to, 0);
$this->assertSame($expectedResponse, $actualResponse);
$this->assertSame($expectedModifiedFirstList, $redis->lrange('test-blmove1', 0, -1));
$this->assertSame($expectedModifiedSecondList, $redis->lrange('test-blmove2', 0, -1));
}
/**
* @group connected
* @dataProvider sameListProvider
* @param array $list
* @param string $where
* @param string $to
* @param string $expectedResponse
* @param array $expectedModifiedList
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testReturnsCorrectListElementAndListRotationPerformedOnTheSameListOperation(
array $list,
string $where,
string $to,
string $expectedResponse,
array $expectedModifiedList
): void {
$redis = $this->getClient();
$redis->rpush('test-blmove1', $list);
$actualResponse = $redis->blmove('test-blmove1', 'test-blmove1', $where, $to, 0);
$this->assertSame($expectedResponse, $actualResponse);
$this->assertSame($expectedModifiedList, $redis->lrange('test-blmove1', 0, -1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->blmove('foo', 'test-lmove', 'LEFT', 'LEFT', 0);
}
public function listsProvider(): array
{
return [
'move first element from list into head of another list' => [
['element1', 'element2', 'element3'],
['element4', 'element5', 'element6'],
'LEFT',
'LEFT',
'element1',
['element2', 'element3'],
['element1', 'element4', 'element5', 'element6'],
],
'move first element from list into tail of another list' => [
['element1', 'element2', 'element3'],
['element4', 'element5', 'element6'],
'LEFT',
'RIGHT',
'element1',
['element2', 'element3'],
['element4', 'element5', 'element6', 'element1'],
],
'move last element from list into head of another list' => [
['element1', 'element2', 'element3'],
['element4', 'element5', 'element6'],
'RIGHT',
'LEFT',
'element3',
['element1', 'element2'],
['element3', 'element4', 'element5', 'element6'],
],
'move last element from list into tail of another list' => [
['element1', 'element2', 'element3'],
['element4', 'element5', 'element6'],
'RIGHT',
'RIGHT',
'element3',
['element1', 'element2'],
['element4', 'element5', 'element6', 'element3'],
],
];
}
public function sameListProvider(): array
{
return [
'list rotation - head into tail' => [
['element1', 'element2', 'element3'],
'LEFT',
'RIGHT',
'element1',
['element2', 'element3', 'element1'],
],
'list rotation - tail into head' => [
['element1', 'element2', 'element3'],
'RIGHT',
'LEFT',
'element3',
['element3', 'element1', 'element2'],
],
'list rotation - head into head' => [
['element1', 'element2', 'element3'],
'LEFT',
'LEFT',
'element1',
['element1', 'element2', 'element3'],
],
'list rotation - tail into tail' => [
['element1', 'element2', 'element3'],
'RIGHT',
'RIGHT',
'element3',
['element1', 'element2', 'element3'],
],
];
}
}
+146
View File
@@ -0,0 +1,146 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
/**
* @group commands
* @group realm-list
*/
class BLMPOP_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return BLMPOP::class;
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'BLMPOP';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$raw = ['key', ['elem1', 'elem2']];
$expected = ['key' => ['elem1', 'elem2']];
$command = $this->getCommand();
$this->assertSame($expected, $command->parseResponse($raw));
}
/**
* @group connected
* @dataProvider listProvider
* @param int $timeout
* @param array $listArguments
* @param string $key
* @param string $modifier
* @param int $count
* @param array $expectedResponse
* @param array $expectedModifiedList
* @return void
* @requiresRedisVersion >= 7.0.0
*/
public function testPopElementsFromGivenList(
int $timeout,
array $listArguments,
string $key,
string $modifier,
int $count,
array $expectedResponse,
array $expectedModifiedList
): void {
$redis = $this->getClient();
$redis->lpush(...$listArguments);
$actualResponse = $redis->blmpop($timeout, ['key1', $key], $modifier, $count);
$this->assertSame($expectedResponse, $actualResponse);
$this->assertSame($expectedModifiedList, $redis->lrange($key, 0, -1));
}
public function argumentsProvider(): array
{
return [
'with default arguments' => [
[1, ['key']],
[1, 1, 'key', 'LEFT']
],
'with LEFT/RIGHT argument' => [
[1, ['key'], 'right'],
[1, 1, 'key', 'RIGHT']
],
'with COUNT argument' => [
[1, ['key'], 'left', 2],
[1, 1, 'key', 'LEFT', 'COUNT', 2]
],
'with all arguments' => [
[1, ['key1', 'key2'], 'right', 2],
[1, 2, 'key1', 'key2', 'RIGHT', 'COUNT', 2]
]
];
}
public function listProvider(): array
{
return [
'pops single element - left' => [
1,
['key', 'elem1', 'elem2', 'elem3'],
'key',
'left',
1,
['key' => ['elem3']],
['elem2', 'elem1']
],
'pops single element - right' => [
1,
['key', 'elem1', 'elem2', 'elem3'],
'key',
'right',
1,
['key' => ['elem1']],
['elem3', 'elem2']
],
'pops multiple elements' => [
1,
['key', 'elem1', 'elem2', 'elem3'],
'key',
'right',
2,
['key' => ['elem1', 'elem2']],
['elem3'],
],
];
}
}
+199
View File
@@ -0,0 +1,199 @@
<?php
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
use UnexpectedValueException;
class BZMPOP_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return BZMPOP::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'BZMPOP';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
* @dataProvider responsesProvider
*/
public function testParseResponse(array $actualResponse, array $expectedResponse): void
{
$this->assertSame($expectedResponse, $this->getCommand()->parseResponse($actualResponse));
}
/**
* @group connected
* @dataProvider sortedSetsProvider
* @param int $timeout
* @param array $sortedSetDictionary
* @param string $key
* @param string $modifier
* @param int $count
* @param array $expectedResponse
* @param array $expectedModifiedSortedSet
* @return void
* @requiresRedisVersion >= 7.0
*/
public function testReturnsPoppedElementsFromGivenSortedSet(
int $timeout,
array $sortedSetDictionary,
string $key,
string $modifier,
int $count,
array $expectedResponse,
array $expectedModifiedSortedSet
): void {
$redis = $this->getClient();
$redis->zadd($key, ...$sortedSetDictionary);
$actualResponse = $redis->bzmpop($timeout, [$key], $modifier, $count);
$this->assertSame($expectedResponse, $actualResponse);
$this->assertSame($expectedModifiedSortedSet, $redis->zrange($key, 0, -1));
}
/**
* @group connected
* @dataProvider unexpectedValuesProvider
* @param int $timeout
* @param array $keys
* @param string $modifier
* @param int $count
* @param string $expectedExceptionMessage
* @return void
* @requiresRedisVersion >= 7.0
*/
public function testThrowsExceptionOnUnexpectedValueGiven(
int $timeout,
array $keys,
string $modifier,
int $count,
string $expectedExceptionMessage
): void {
$redis = $this->getClient();
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage($expectedExceptionMessage);
$redis->bzmpop($timeout, $keys, $modifier, $count);
}
/**
* @group connected
* @requiresRedisVersion >= 7.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('bzmpop_foo', 'bar');
$redis->bzmpop(1, ['bzmpop_foo']);
}
public function argumentsProvider(): array
{
return [
'with one key' => [
[10, ['key1'], 'min', 1],
[10, 1, 'key1', 'MIN', 'COUNT', 1]
],
'with multiple keys' => [
[10, ['key1', 'key2', 'key3'], 'max', 1],
[10, 3, 'key1', 'key2', 'key3', 'MAX', 'COUNT', 1]
],
];
}
public function responsesProvider(): array
{
return [
'null-element array' => [
[null],
[null]
],
'two-element array' => [
['key', [['member1', 1, 'member2', 2, 'member3', 3]]],
['key' => ['member1' => 1, 'member2' => 2, 'member3' => 3]]
],
];
}
public function sortedSetsProvider(): array
{
return [
'with MIN modifier' => [
1,
[1, 'member1', 2, 'member2', 3, 'member3'],
'test-bzmpop',
'min',
1,
['test-bzmpop' => ['member1' => '1']],
['member2', 'member3'],
],
'with MAX modifier' => [
1,
[1, 'member1', 2, 'member2', 3, 'member3'],
'test-bzmpop',
'max',
1,
['test-bzmpop' => ['member3' => '3']],
['member1', 'member2'],
],
'with non-default COUNT' => [
1,
[1, 'member1', 2, 'member2', 3, 'member3'],
'test-bzmpop',
'max',
2,
['test-bzmpop' => ['member3' => '3', 'member2' => '2']],
['member1'],
]
];
}
public function unexpectedValuesProvider(): array
{
return [
'wrong modifier' => [
1,
['key1', 'key2'],
'wrong modifier',
1,
'Wrong type of modifier given'
],
'wrong count' => [
1,
['key1', 'key2'],
'min',
0,
'Wrong count argument value or position offset'
],
];
}
}
@@ -0,0 +1,73 @@
<?php
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
use UnexpectedValueException;
class BZPOPMAX_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return BZPOPMAX::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'BZPOPMAX';
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 5.0.0
*/
public function testReturnsPoppedMaxElementFromGivenNonEmptySortedSet(): void
{
$redis = $this->getClient();
$sortedSetDictionary = [1, 'member1', 2, 'member2', 3, 'member3'];
$expectedResponse = ['test-bzpopmax' => ['member3' => '3']];
$expectedModifiedSortedSet = ['member1', 'member2'];
$redis->zadd('test-bzpopmax', ...$sortedSetDictionary);
$this->assertSame($expectedResponse, $redis->bzpopmax(['empty sorted set','test-bzpopmax'], 0));
$this->assertSame($expectedModifiedSortedSet, $redis->zrange('test-bzpopmax', 0, -1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnUnexpectedValueGiven(): void
{
$redis = $this->getClient();
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Wrong keys argument type or position offset');
$redis->bzpopmax(1, 0);
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('bzpopmax_foo', 'bar');
$redis->bzpopmax(['bzpopmax_foo'], 0);
}
}
@@ -0,0 +1,73 @@
<?php
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
use UnexpectedValueException;
class BZPOPMIN_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return BZPOPMIN::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'BZPOPMIN';
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 5.0.0
*/
public function testReturnsPoppedMinElementFromGivenNonEmptySortedSet(): void
{
$redis = $this->getClient();
$sortedSetDictionary = [1, 'member1', 2, 'member2', 3, 'member3'];
$expectedResponse = ['test-bzpopmin' => ['member1' => '1']];
$expectedModifiedSortedSet = ['member2', 'member3'];
$redis->zadd('test-bzpopmin', ...$sortedSetDictionary);
$this->assertSame($expectedResponse, $redis->bzpopmin(['empty sorted set','test-bzpopmin'], 0));
$this->assertSame($expectedModifiedSortedSet, $redis->zrange('test-bzpopmin', 0, -1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnUnexpectedValueGiven(): void
{
$redis = $this->getClient();
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Wrong keys argument type or position offset');
$redis->bzpopmin(1, 0);
}
/**
* @group connected
* @requiresRedisVersion >= 5.0.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('bzpopmin_foo', 'bar');
$redis->bzpopmin(['bzpopmin_foo'], 0);
}
}
+135
View File
@@ -0,0 +1,135 @@
<?php
namespace Predis\Command\Redis;
class COPY_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return COPY::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'COPY';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testSuccessfullyCopyValueOnNonExistingDestinationKey(): void
{
$redis = $this->getClient();
$redis->set('key', 'value');
$actualResponse = $redis->copy('key', 'destination');
$this->assertSame(1, $actualResponse);
$this->assertSame($redis->get('key'), $redis->get('destination'));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testSuccessfullyCopyValueFromSourceToAnotherDb(): void
{
$defaultDatabaseIndexClient = $this->getClient();
$defaultDatabaseIndexClient->set('key', 'value');
$copyResponse = $defaultDatabaseIndexClient->copy('key', 'new_key', 14);
$anotherDatabaseIndexClient = $this->createClient(['database' => 14], null, false);
$actualValue = $anotherDatabaseIndexClient->get('new_key');
$anotherDatabaseIndexClient->flushdb();
$this->assertNull($anotherDatabaseIndexClient->get('new_key'));
$this->assertSame(1, $copyResponse);
$this->assertSame('value', $actualValue);
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testDoNotCopyValueOnAlreadyExistingDestinationKey(): void
{
$redis = $this->getClient();
$redis->set('key', 'value');
$redis->set('destination', 'destination_value');
$actualResponse = $redis->copy('key', 'destination');
$this->assertSame(0, $actualResponse);
$this->assertSame('destination_value', $redis->get('destination'));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testSuccessfullyCopyValueWithReplaceArgumentOnAlreadyExistingDestinationKey(): void
{
$redis = $this->getClient();
$redis->set('key', 'value');
$redis->set('destination', 'destination_value');
$actualResponse = $redis->copy('key', 'destination', -1, true);
$this->assertSame(1, $actualResponse);
$this->assertSame('value', $redis->get('destination'));
}
public function argumentsProvider(): array
{
return [
'with default arguments' => [
['source', 'destination'],
['source', 'destination']
],
'with DB argument' => [
['source', 'destination', 1],
['source', 'destination', 'DB', 1]
],
'with replace argument' => [
['source', 'destination', -1, true],
['source', 'destination', 'REPLACE']
],
'with all arguments' => [
['source', 'destination', 1, true],
['source', 'destination', 'DB', 1, 'REPLACE']
]
];
}
}
@@ -0,0 +1,116 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use Predis\Command\Argument\Server\To;
use UnexpectedValueException;
/**
* @group commands
* @group realm-server
*/
class FAILOVER_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return FAILOVER::class;
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'FAILOVER';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSameValues($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testPerformFailoverOfConnectedReplica(): void
{
$this->markTestSkipped('Test requires configured replica node connected to master');
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testThrowsExceptionOnUnexpectedValueGiven(): void
{
$redis = $this->getClient();
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage('Wrong timeout argument value or position offset');
$redis->failover(null, false, 0);
}
public function argumentsProvider(): array
{
return [
'without optional arguments - no arguments' => [
[],
[]
],
'without optional arguments - default arguments' => [
[null, false, -1],
[]
],
'with TO argument - no FORCE' => [
[new To('test', 9999)],
['TO', 'test', 9999]
],
'with TO argument - with FORCE' => [
[new To('test', 9999, true)],
['TO', 'test', 9999, 'FORCE']
],
'with ABORT modifier' => [
[null, true],
['ABORT']
],
'with TIMEOUT argument' => [
[null, false, 10],
['TIMEOUT', 10]
],
'with all arguments' => [
[new To('test', 9999, true), true, 10],
['TO', 'test', 9999, 'FORCE', 'ABORT', 'TIMEOUT', 10]
]
];
}
}
@@ -0,0 +1,354 @@
<?php
namespace Predis\Command\Redis;
use InvalidArgumentException;
use Predis\Command\Argument\Geospatial\ByBox;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\ByRadius;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Geospatial\FromLonLat;
use Predis\Command\Argument\Geospatial\FromMember;
use UnexpectedValueException;
class GEOSEARCHSTORE_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return GEOSEARCHSTORE::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'GEOSEARCHSTORE';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSameValues($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @dataProvider coordinatesProvider
* @param array $firstCoordinates
* @param array $secondCoordinates
* @param array $thirdCoordinates
* @param string $destination
* @param string $source
* @param FromInterface $from
* @param ByInterface $by
* @param string|null $sorting
* @param int $count
* @param bool $any
* @param int $expectedResultingElements
* @param array $expectedResponse
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testStoresCorrectGivenGeospatialCoordinates(
array $firstCoordinates,
array $secondCoordinates,
array $thirdCoordinates,
string $destination,
string $source,
FromInterface $from,
ByInterface $by,
?string $sorting,
int $count,
bool $any,
int $expectedResultingElements,
array $expectedResponse
): void {
$redis = $this->getClient();
$redis->geoadd(...$firstCoordinates);
$redis->geoadd(...$secondCoordinates);
$redis->geoadd(...$thirdCoordinates);
$actualResultingElements = $redis->geosearchstore(
$destination,
$source,
$from,
$by,
$sorting,
$count,
$any
);
$this->assertSame($expectedResultingElements, $actualResultingElements);
$this->assertSame($expectedResponse, $redis->geosearch($destination, $from, $by, $sorting, $count, $any));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testStoresInSortedSetWithStoreDistArgumentProvided(): void
{
$redis = $this->getClient();
$redis->geoadd('key', 1.1, 2, 'member1');
$redis->geoadd('key', 2.1, 3, 'member2');
$redis->geoadd('key', 3.1, 4, 'member3');
$actualResultingElements = $redis->geosearchstore(
'destination',
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
2,
false,
true
);
$this->assertSame(2, $actualResultingElements);
$this->assertSame(['member2', 'member1'], $redis->zrange('destination', 0, -1));
}
/**
* @group connected
* @dataProvider unexpectedValuesProvider
* @param array $arguments
* @param string $expectedException
* @param string $expectedExceptionMessage
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testThrowsExceptionOnUnexpectedValueProvided(
array $arguments,
string $expectedException,
string $expectedExceptionMessage
): void {
$redis = $this->getClient();
$this->expectException($expectedException);
$this->expectExceptionMessage($expectedExceptionMessage);
$redis->geosearchstore(...$arguments);
}
public function argumentsProvider(): array
{
return [
'with default arguments - FROMLONLAT, BYRADIUS' => [
['destination', 'source', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km')],
['destination', 'source', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km']
],
'with default arguments - FROMMEMBER, BYBOX' => [
['destination', 'source', new FromMember('member'), new ByBox(1,1, 'km')],
['destination', 'source', 'FROMMEMBER', 'member', 'BYBOX', 1, 1, 'km']
],
'with ASC sorting' => [
['destination', 'source', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), 'asc'],
['destination', 'source', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'ASC']
],
'with DESC sorting' => [
['destination', 'source', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), 'desc'],
['destination', 'source', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'DESC']
],
'with COUNT argument - without ANY option' => [
['destination', 'source', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, 20],
['destination', 'source', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'COUNT', 20]
],
'with COUNT argument - with ANY option' => [
['destination', 'source', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, 20, true],
['destination', 'source', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'COUNT', 20, 'ANY']
],
'with STOREDIST argument' => [
['destination', 'source', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, -1, false, true],
['destination', 'source', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'STOREDIST']
],
'with all arguments' => [
['destination', 'source', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), 'asc', 20, true, true],
['destination', 'source', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'ASC', 'COUNT', 20, 'ANY', 'STOREDIST']
]
];
}
public function coordinatesProvider(): array
{
return [
'with default arguments - FROMLONLAT, BYRADIUS - all members' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'destination',
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
-1,
false,
3,
['member1', 'member2', 'member3']
],
'with default arguments - FROMLONLAT, BYRADIUS - closest members' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'destination',
'key',
new FromLonLat(1, 3),
new ByRadius(200, 'km'),
null,
-1,
false,
2,
['member2', 'member1']
],
'with default arguments - FROMMEMBER, BYBOX - all members' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'destination',
'key',
new FromMember('member2'),
new ByBox(999, 999, 'km'),
null,
-1,
false,
3,
['member1', 'member2', 'member3']
],
'with default arguments - FROMMEMBER, BYBOX - closest members' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'destination',
'key',
new FromMember('member1'),
new ByBox(300, 300, 'km'),
null,
-1,
false,
2,
['member1', 'member2']
],
'with ASC modifier' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'destination',
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
'asc',
-1,
false,
3,
['member2', 'member1', 'member3']
],
'with DESC modifier' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'destination',
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
'desc',
-1,
false,
3,
['member3', 'member1', 'member2']
],
'with COUNT modifier - without ANY option' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'destination',
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
1,
false,
1,
['member2']
],
'with COUNT modifier - with ANY option' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'destination',
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
2,
true,
2,
['member1', 'member2']
],
'with all arguments' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'destination',
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
'asc',
2,
true,
2,
['member2', 'member1']
],
];
}
public function unexpectedValuesProvider(): array
{
return [
'with wrong FROM argument' => [
['destination', 'source', false, new ByRadius(9999, 'km'), null, -1, false, false],
InvalidArgumentException::class,
'Invalid FROM argument value given'
],
'with wrong BY argument' => [
['destination', 'source', new FromLonLat(1, 4), false, null, -1, false, false],
InvalidArgumentException::class,
'Invalid BY argument value given'
],
'with wrong sorting argument' => [
['destination', 'source', new FromLonLat(1, 4), new ByRadius(9999, 'km'), 'wrong', -1, false, false],
UnexpectedValueException::class,
'Sorting argument accepts only: asc, desc values'
],
'with wrong COUNT argument' => [
['destination', 'source', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, false],
UnexpectedValueException::class,
'Wrong count argument value or position offset'
],
'with wrong STOREDIST argument' => [
['destination', 'source', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, 'wrong'],
UnexpectedValueException::class,
'Wrong STOREDIST argument type'
],
];
}
}
@@ -0,0 +1,472 @@
<?php
namespace Predis\Command\Redis;
use InvalidArgumentException;
use Predis\Command\Argument\Geospatial\ByBox;
use Predis\Command\Argument\Geospatial\ByInterface;
use Predis\Command\Argument\Geospatial\ByRadius;
use Predis\Command\Argument\Geospatial\FromInterface;
use Predis\Command\Argument\Geospatial\FromLonLat;
use Predis\Command\Argument\Geospatial\FromMember;
use UnexpectedValueException;
class GEOSEARCH_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return GEOSEARCH::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'GEOSEARCH';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSameValues($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
* @dataProvider responsesProvider
*/
public function testParseResponse(array $actualResponse, array $expectedResponse): void
{
$this->assertSame($expectedResponse, $this->getCommand()->parseResponse($actualResponse));
}
/**
* @group connected
* @dataProvider coordinatesProvider
* @param array $firstCoordinates
* @param array $secondCoordinates
* @param array $thirdCoordinates
* @param string $key
* @param FromInterface $from
* @param ByInterface $by
* @param string|null $sorting
* @param int $count
* @param bool $any
* @param bool $withCoord
* @param bool $withDist
* @param bool $withHash
* @param array $expectedResponse
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testReturnsSearchedGeospatialCoordinates(
array $firstCoordinates,
array $secondCoordinates,
array $thirdCoordinates,
string $key,
FromInterface $from,
ByInterface $by,
?string $sorting,
int $count,
bool $any,
bool $withCoord,
bool $withDist,
bool $withHash,
array $expectedResponse
): void {
$redis = $this->getClient();
$redis->geoadd(...$firstCoordinates);
$redis->geoadd(...$secondCoordinates);
$redis->geoadd(...$thirdCoordinates);
$this->assertSame(
$expectedResponse,
$redis->geosearch($key, $from, $by, $sorting, $count, $any, $withCoord, $withDist, $withHash)
);
}
/**
* @group connected
* @dataProvider unexpectedValuesProvider
* @param array $arguments
* @param string $expectedException
* @param string $expectedExceptionMessage
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testThrowsExceptionOnUnexpectedValueProvided(
array $arguments,
string $expectedException,
string $expectedExceptionMessage
): void {
$redis = $this->getClient();
$this->expectException($expectedException);
$this->expectExceptionMessage($expectedExceptionMessage);
$redis->geosearch(...$arguments);
}
public function argumentsProvider(): array
{
return [
'with default arguments - FROMLONLAT, BYRADIUS' => [
['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km')],
['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km']
],
'with default arguments - FROMMEMBER, BYBOX' => [
['key', new FromMember('member'), new ByBox(1,1, 'km')],
['key', 'FROMMEMBER', 'member', 'BYBOX', 1, 1, 'km']
],
'with ASC sorting' => [
['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), 'asc'],
['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'ASC']
],
'with DESC sorting' => [
['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), 'desc'],
['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'DESC']
],
'with COUNT argument - without ANY option' => [
['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, 20],
['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'COUNT', 20]
],
'with COUNT argument - with ANY option' => [
['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, 20, true],
['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'COUNT', 20, 'ANY']
],
'with WITHCOORD argument' => [
['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, -1, false, true],
['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'WITHCOORD']
],
'with WITHDIST argument' => [
['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, -1, false, false, true],
['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'WITHDIST']
],
'with WITHHASH argument' => [
['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), null, -1, false, false, false, true],
['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'WITHHASH']
],
'with all arguments' => [
['key', new FromLonLat(1.1, 2.2), new ByRadius(1, 'km'), 'asc', 20, true, true, true, true],
['key', 'FROMLONLAT', 1.1, 2.2, 'BYRADIUS', 1, 'km', 'ASC', 'COUNT', 20, 'ANY', 'WITHCOORD', 'WITHDIST', 'WITHHASH']
]
];
}
public function responsesProvider(): array
{
return [
'without WITH modifiers' => [
['member1', 'member2', 'member3'],
['member1', 'member2', 'member3'],
],
'with WITHCOORD modifier' => [
[['member1', [1.1, 2.2]], ['member2', [2.2, 3.3]], ['member3', [3.3, 4.4]]],
[
'member1' => ['lng' => 1.1, 'lat' => 2.2],
'member2' => ['lng' => 2.2, 'lat' => 3.3],
'member3' => ['lng' => 3.3, 'lat' => 4.4]]
],
'with WITHDIST modifier' => [
[['member1', '111.111'], ['member2', '222.222'], ['member3', '333.333']],
[
'member1' => ['dist' => 111.111],
'member2' => ['dist' => 222.222],
'member3' => ['dist' => 333.333],
],
],
'with WITHHASH modifier' => [
[['member1', 1111], ['member2', 2222], ['member3', 3333]],
[
'member1' => ['hash' => 1111],
'member2' => ['hash' => 2222],
'member3' => ['hash' => 3333],
],
],
'with all WITH modifiers' => [
[
['member1', '111.111', 1111, [1.1, 2.2]],
['member2', '222.222', 2222, [2.2, 3.3]],
['member3', '333.333', 3333, [3.3, 4.4]],
],
[
'member1' => [
'dist' => 111.111,
'hash' => 1111,
'lng' => 1.1,
'lat' => 2.2,
],
'member2' => [
'dist' => 222.222,
'hash' => 2222,
'lng' => 2.2,
'lat' => 3.3,
],
'member3' => [
'dist' => 333.333,
'hash' => 3333,
'lng' => 3.3,
'lat' => 4.4,
],
],
]
];
}
public function coordinatesProvider(): array
{
return [
'with default arguments - FROMLONLAT, BYRADIUS - all members' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
-1,
false,
false,
false,
false,
['member1', 'member2', 'member3']
],
'with default arguments - FROMLONLAT, BYRADIUS - closest members' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 3),
new ByRadius(200, 'km'),
null,
-1,
false,
false,
false,
false,
['member2', 'member1']
],
'with default arguments - FROMMEMBER, BYBOX - all members' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromMember('member2'),
new ByBox(999, 999, 'km'),
null,
-1,
false,
false,
false,
false,
['member1', 'member2', 'member3']
],
'with default arguments - FROMMEMBER, BYBOX - closest members' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromMember('member1'),
new ByBox(300, 300, 'km'),
null,
-1,
false,
false,
false,
false,
['member1', 'member2']
],
'with ASC modifier' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
'asc',
-1,
false,
false,
false,
false,
['member2', 'member1', 'member3']
],
'with DESC modifier' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
'desc',
-1,
false,
false,
false,
false,
['member3', 'member1', 'member2']
],
'with COUNT modifier - without ANY option' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
1,
false,
false,
false,
false,
['member2']
],
'with COUNT modifier - with ANY option' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
2,
true,
false,
false,
false,
['member1', 'member2']
],
'with WITHCOORD modifier' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
-1,
false,
true,
false,
false,
[
'member1' => ['lng' => 1.1, 'lat' => 2.0],
'member2' => ['lng' => 2.1, 'lat' => 3.0],
'member3' => ['lng' => 3.1, 'lat' => 4.0],
]
],
'with WITHDIST modifier' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
-1,
false,
false,
true,
false,
[
'member1' => ['dist' => 222.7297],
'member2' => ['dist' => 165.1798],
'member3' => ['dist' => 233.006],
]
],
'with WITHHASH modifier' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
null,
-1,
false,
false,
false,
true,
[
'member1' => ['hash' => 3378086406303657],
'member2' => ['hash' => 3378965307136228],
'member3' => ['hash' => 3379626601756294],
]
],
'with all arguments' => [
['key', 1.1, 2, 'member1'],
['key', 2.1, 3, 'member2'],
['key', 3.1, 4, 'member3'],
'key',
new FromLonLat(1, 4),
new ByRadius(9999, 'km'),
'asc',
1,
true,
true,
true,
true,
[
'member1' => [
'dist' => 222.7297,
'hash' => 3378086406303657,
'lng' => 1.1,
'lat' => 2.0,
],
],
],
];
}
public function unexpectedValuesProvider(): array
{
return [
'with wrong FROM argument' => [
['key', false, new ByRadius(9999, 'km'), null, -1, false, false],
InvalidArgumentException::class,
'Invalid FROM argument value given'
],
'with wrong BY argument' => [
['key', new FromLonLat(1, 4), false, null, -1, false, false],
InvalidArgumentException::class,
'Invalid BY argument value given'
],
'with wrong sorting argument' => [
['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), 'wrong', -1, false, false],
UnexpectedValueException::class,
'Sorting argument accepts only: asc, desc values'
],
'with wrong COUNT argument' => [
['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, false],
UnexpectedValueException::class,
'Wrong count argument value or position offset'
],
'with wrong WITHCOORD argument' => [
['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, 'wrong'],
UnexpectedValueException::class,
'Wrong WITHCOORD argument type'
],
'with wrong WITHDIST argument' => [
['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, false, 'wrong'],
UnexpectedValueException::class,
'Wrong WITHDIST argument type'
],
'with wrong WITHHASH argument' => [
['key', new FromLonLat(1, 4), new ByRadius(9999, 'km'), null, 0, false, false, false, 'wrong'],
UnexpectedValueException::class,
'Wrong WITHHASH argument type'
],
];
}
}
@@ -0,0 +1,63 @@
<?php
namespace Predis\Command\Redis;
class GETDEL_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return GETDEL::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'GETDEL';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = ['key'];
$expected = ['key'];
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testReturnsValueForGivenKeyAndDeleteIt(): void
{
$redis = $this->getClient();
$expectedKey = 'key';
$expectedValue = 'value';
$redis->set($expectedKey, $expectedValue);
$actualResponse = $redis->getdel($expectedKey);
$this->assertSame($expectedValue, $actualResponse);
$this->assertNull($redis->get($expectedKey));
}
}
+180
View File
@@ -0,0 +1,180 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) Daniele Alessandri <suppakilla@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Redis;
use UnexpectedValueException;
/**
* @group commands
* @group realm-string
*/
class GETEX_Test extends PredisCommandTestCase
{
/**
* {@inheritdoc}
*/
protected function getExpectedCommand(): string
{
return GETEX::class;
}
/**
* {@inheritdoc}
*/
protected function getExpectedId(): string
{
return 'GETEX';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$command = $this->getCommand();
$this->assertSame(1, $command->parseResponse(1));
}
/**
* @group connected
* @dataProvider keysProvider
* @param array $kvPair
* @param array $arguments
* @param string $expectedResponse
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testReturnsValueAndSetExpirationTimeForGivenKey(
array $kvPair,
array $arguments,
string $expectedResponse
): void {
$redis = $this->getClient();
$redis->set(...$kvPair);
$this->assertSame($expectedResponse, $redis->getex(...$arguments));
}
/**
* @group connected
* @dataProvider unexpectedValuesProvider
* @param array $arguments
* @param string $expectedExceptionMessage
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testThrowsExceptionOnUnexpectedValueGiven(
array $arguments,
string $expectedExceptionMessage
): void {
$redis = $this->getClient();
$this->expectException(UnexpectedValueException::class);
$this->expectExceptionMessage($expectedExceptionMessage);
$redis->getex(...$arguments);
}
public function argumentsProvider(): array
{
return [
'with default arguments' => [
['key'],
['key']
],
'with EX modifier' => [
['key', 'ex', 1],
['key', 'EX', 1],
],
'with PX modifier' => [
['key', 'px', 1],
['key', 'PX', 1],
],
'with EXAT modifier' => [
['key', 'exat', 1],
['key', 'EXAT', 1],
],
'with PXAT modifier' => [
['key', 'pxat', 1],
['key', 'PXAT', 1],
],
'with PERSIST modifier' => [
['key', 'persist'],
['key', 'PERSIST']
]
];
}
public function keysProvider(): array
{
return [
'without expiration time' => [
['key', 'value'],
['key', '', false],
'value',
],
'with expiration - EX modifier' => [
['key', 'value'],
['key', 'ex', 10],
'value'
],
'with expiration - PX modifier' => [
['key', 'value'],
['key', 'px', 10],
'value'
],
'with expiration - EXAT modifier' => [
['key', 'value'],
['key', 'exat', 10],
'value'
],
'with expiration - PXAT modifier' => [
['key', 'value'],
['key', 'pxat', 10],
'value'
],
'with expiration - PERSIST modifier' => [
['key', 'value'],
['key', 'persist'],
'value'
],
];
}
public function unexpectedValuesProvider(): array
{
return [
'with wrong modifier' => [
['key', 'wrong', 1],
'Modifier argument accepts only: ex, px, exat, pxat, persist values'
],
'without value provided' => [
['key', 'ex'],
'You should provide value for current modifier'
]
];
}
}
@@ -0,0 +1,147 @@
<?php
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
class HRANDFIELD_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return HRANDFIELD::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'HRANDFIELD';
}
/**
* @group disconnected
* @dataProvider argumentsProvider
* @param array $actualArguments
* @param array $expectedArguments
* @return void
*/
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
{
$command = $this->getCommand();
$command->setArguments($actualArguments);
$this->assertSame($expectedArguments, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @dataProvider hashesProvider
* @param array $hash
* @param string $key
* @param int $count
* @param bool $withValues
* @param array $expectedResponse
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testReturnsRandomFieldsFromHash(
array $hash,
string $key,
int $count,
bool $withValues,
array $expectedResponse
): void {
$redis = $this->getClient();
$redis->hset($key, ...$hash);
$actualResponse = $redis->hrandfield($key, $count, $withValues);
$this->assertOneOf($expectedResponse, $actualResponse);
}
/**
* @group connected
* @requiresRedisVersion >= 6.2.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->hrandfield('foo');
}
public function argumentsProvider(): array
{
return [
'with default arguments' => [
['key'],
['key']
],
'with count argument' => [
['key', 1],
['key', 1]
],
'with WITHVALUES argument' => [
['key', 1, true],
['key', 1, 'WITHVALUES']
]
];
}
public function hashesProvider(): array
{
return [
'one field - without values' => [
['key1', 'value1', 'key2', 'value2', 'key3', 'value3'],
'key',
1,
false,
['key1', 'key2', 'key3']
],
'one field - with values' => [
['key1', 'value1', 'key2', 'value2', 'key3', 'value3'],
'key',
1,
true,
['key1', 'value1', 'key2', 'value2', 'key3', 'value3']
],
'multiple fields - without values' => [
['key1', 'value1', 'key2', 'value2', 'key3', 'value3'],
'key',
2,
false,
['key1', 'key2', 'key3'],
],
'multiple fields - with values' => [
['key1', 'value1', 'key2', 'value2', 'key3', 'value3'],
'key',
2,
true,
['key1', 'value1', 'key2', 'value2', 'key3', 'value3'],
],
'multiple fields - allows same fields' => [
['key1', 'value1', 'key2', 'value2', 'key3', 'value3'],
'key',
-2,
false,
['key1', 'key2', 'key3'],
],
];
}
}
+222
View File
@@ -0,0 +1,222 @@
<?php
namespace Predis\Command\Redis;
use Predis\Response\ServerException;
/**
* @group commands
* @group realm-list
*/
class LMOVE_Test extends PredisCommandTestCase
{
/**
* @inheritDoc
*/
protected function getExpectedCommand(): string
{
return LMOVE::class;
}
/**
* @inheritDoc
*/
protected function getExpectedId(): string
{
return 'LMOVE';
}
/**
* @group disconnected
*/
public function testFilterArguments(): void
{
$arguments = ['list', 'argument1', 'argument2', 'argument3'];
$expected = ['list', 'argument1', 'argument2', 'argument3'];
$command = $this->getCommand();
$command->setArguments($arguments);
$this->assertSame($expected, $command->getArguments());
}
/**
* @group disconnected
*/
public function testParseResponse(): void
{
$this->assertSame(1, $this->getCommand()->parseResponse(1));
}
/**
* @group connected
* @dataProvider listsProvider
* @param array $firstList
* @param array $secondList
* @param string $where
* @param string $to
* @param string $expectedResponse
* @param array $expectedModifiedFirstList
* @param array $expectedModifiedSecondList
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testReturnsCorrectListElement(
array $firstList,
array $secondList,
string $where,
string $to,
string $expectedResponse,
array $expectedModifiedFirstList,
array $expectedModifiedSecondList
): void {
$redis = $this->getClient();
$redis->rpush('test-lmove1', $firstList);
$redis->rpush('test-lmove2', $secondList);
$actualResponse = $redis->lmove('test-lmove1', 'test-lmove2', $where, $to);
$this->assertSame($expectedResponse, $actualResponse);
$this->assertSame($expectedModifiedFirstList, $redis->lrange('test-lmove1', 0, -1));
$this->assertSame($expectedModifiedSecondList, $redis->lrange('test-lmove2', 0, -1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testReturnsNullAndNoOperationPerformedOnNonExistingSource(): void
{
$redis = $this->getClient();
$expectedList = ['element1', 'element2', 'element3'];
$redis->rpush('test-lmove1', $expectedList);
$actualResponse = $redis->lmove('test-lmove2', 'test-lmove1', 'LEFT', 'LEFT');
$this->assertNull($actualResponse);
$this->assertSame($expectedList, $redis->lrange('test-lmove1', 0, -1));
}
/**
* @group connected
* @dataProvider sameListProvider
* @param array $list
* @param string $where
* @param string $to
* @param string $expectedResponse
* @param array $expectedModifiedList
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testReturnsCorrectListElementAndListRotationPerformedOnTheSameListOperation(
array $list,
string $where,
string $to,
string $expectedResponse,
array $expectedModifiedList
): void {
$redis = $this->getClient();
$redis->rpush('test-lmove1', $list);
$actualResponse = $redis->lmove('test-lmove1', 'test-lmove1', $where, $to);
$this->assertSame($expectedResponse, $actualResponse);
$this->assertSame($expectedModifiedList, $redis->lrange('test-lmove1', 0, -1));
}
/**
* @group connected
* @return void
* @requiresRedisVersion >= 6.2.0
*/
public function testThrowsExceptionOnWrongType(): void
{
$this->expectException(ServerException::class);
$this->expectExceptionMessage('Operation against a key holding the wrong kind of value');
$redis = $this->getClient();
$redis->set('foo', 'bar');
$redis->lmove('foo', 'test-lmove', 'LEFT', 'LEFT');
}
public function listsProvider(): array
{
return [
'move first element from list into head of another list' => [
['element1', 'element2', 'element3'],
['element4', 'element5', 'element6'],
'LEFT',
'LEFT',
'element1',
['element2', 'element3'],
['element1', 'element4', 'element5', 'element6'],
],
'move first element from list into tail of another list' => [
['element1', 'element2', 'element3'],
['element4', 'element5', 'element6'],
'LEFT',
'RIGHT',
'element1',
['element2', 'element3'],
['element4', 'element5', 'element6', 'element1'],
],
'move last element from list into head of another list' => [
['element1', 'element2', 'element3'],
['element4', 'element5', 'element6'],
'RIGHT',
'LEFT',
'element3',
['element1', 'element2'],
['element3', 'element4', 'element5', 'element6'],
],
'move last element from list into tail of another list' => [
['element1', 'element2', 'element3'],
['element4', 'element5', 'element6'],
'RIGHT',
'RIGHT',
'element3',
['element1', 'element2'],
['element4', 'element5', 'element6', 'element3'],
],
];
}
public function sameListProvider(): array
{
return [
'list rotation - head into tail' => [
['element1', 'element2', 'element3'],
'LEFT',
'RIGHT',
'element1',
['element2', 'element3', 'element1'],
],
'list rotation - tail into head' => [
['element1', 'element2', 'element3'],
'RIGHT',
'LEFT',
'element3',
['element3', 'element1', 'element2'],
],
'list rotation - head into head' => [
['element1', 'element2', 'element3'],
'LEFT',
'LEFT',
'element1',
['element1', 'element2', 'element3'],
],
'list rotation - tail into tail' => [
['element1', 'element2', 'element3'],
'RIGHT',
'RIGHT',
'element3',
['element1', 'element2', 'element3'],
],
];
}
}

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