Compare commits

..

57 Commits

Author SHA1 Message Date
Till Krüss 153097374b Prepare v3.3.0 release (#1612) 2025-11-24 09:48:50 -08:00
Vladyslav Vildanov 6ef87493ca Added testing with 8.4-GA (#1611) 2025-11-19 08:30:39 -08:00
Vladyslav Vildanov 80430b4cde Added new experimental CAS/CAD functionallity (#1609)
* Added new CAS/CAD functionallity

* Updated CHANGELOG.md

* Codestyle fixes

* Fixed version constraint

* Marked feature as experimental

* Updated version constraints, added missing annotations
2025-11-12 10:14:06 +02:00
Vladyslav Vildanov d0f37f0bac Added temporary XREADGROUP_CLAIM command (#1608)
* Added temporary XREADGROUP_CLAIM command

* Updated CHANGELOG.md

* Added deprecation annotation
2025-11-12 09:14:57 +02:00
Vladyslav Vildanov f11e855047 Added experimental support for FT.HYBRID (#1607)
* Added basic structures and test coverage

* Added most of the test coverage

* Added more test coverage

* Codestyle fixes

* Fixed static analysis

* ANother static analysis fix

* Codestyle fixes

* Fixed version constraints

* Added missing annotations

* Use class context

* Added more test coverage

* Codestyle fixes

* Added experimental mentioning
2025-11-11 15:31:10 +02:00
Vladyslav Vildanov ce853ecec2 Added support for MSET command (#1610)
* Added support for MSET command

* Codestyle fixes
2025-11-11 14:49:07 +02:00
Vladyslav Vildanov f4dcf6ce57 Added testing with 8.4-RC1-pre.2 (#1605)
* Added testing with 8.4-RC1-pre.2

* Updated CHANGELOG.md
2025-11-03 16:49:12 +02:00
Vladyslav Vildanov 72a95b3d75 Added testing with 8.4-RC1-pre image (#1602)
* Added testing with 8.4-RC1-pre image

* Updated CHANGELOG.md
2025-10-27 09:47:24 -07:00
Pavlo Yatsukhnenko 62f4ebcc37 Add AUTH command to Relay connection tests (#1601) 2025-10-20 10:34:31 -07:00
Pavlo Yatsukhnenko f26a13c8f4 Improve Relay compatibility (#1599) 2025-10-20 07:52:40 -07:00
Till Krüss 44e4546ccd add remaining @group relay-fixme 2025-10-17 13:09:13 -07:00
Pavlo Yatsukhnenko da7b1cedb7 Improve Relay compatibility (#1597) 2025-10-17 13:03:29 -07:00
Vladyslav Vildanov fd1a0fc278 Added testing with 8.4-M01 (#1593) 2025-10-17 10:42:36 -07:00
Vladyslav Vildanov 7a15da293b Added prefixable interface for HEXPIRE, HEXPIRETIME (#1592) 2025-10-17 10:27:44 -07:00
Viktor Szépe d9dfefd247 Fix typos (#1579) 2025-10-03 16:22:43 -07:00
Vladyslav Vildanov 8a0c70baa5 Added testing with 8.2.1-pre (#1588) 2025-08-15 09:13:51 -07:00
Vladyslav Vildanov 053cb4b6ac Refactor pipeline data writing depends on connection type (#1586)
* Refactor pipeline data writing depends on connection type

* Updated CHANGELOG.md
2025-08-13 09:59:39 +03:00
Róbert Kelčák 0f4bc7653c Fix stream commands cluster support (#1587)
* Fix stream commands cluster support

* Update CHANGELOG.md
2025-08-12 13:33:48 +03:00
Vladyslav Vildanov 9e9deec4df Tag v3.2.0 (#1585)
* Tag v3.2.0

* Updated 8.2 test image
2025-08-06 09:41:24 +03:00
Vladyslav Vildanov c1845d96cc Added test coverage for updated Vamana (#1584)
* Added test coverage for updated Vamana

* Updated CHANGELOG.md

* Increased timeouts

* Removed assertion cause Relay removes total field

* Added realy-resp3 group

* Revert assertions
2025-08-03 22:20:07 +03:00
Elena Kolevska 8f18de1c12 Updates 8.2 test image to rc2 (#1583) 2025-08-01 12:42:25 -07:00
Hristo Temelski 6d37dc45b0 Added new stream commands, extended existing ones (#1580)
* Added new stream commands, extended existing ones

* updated the changelog

* fixed linter errors

* fixed regressions

* more linter fixes

* XADD fix

* lint fix

* Update CHANGELOG.md

* update interfaces, address PR comments

* fixed linter errors

* fixed xdelex return type

* updated new command signatures

---------

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2025-07-29 12:59:06 +03:00
Vladyslav Vildanov 1c96f704fc Added missing VSIM argument (#1582)
* Added missing VSIM argument

* Updated CHANGELOG.md
2025-07-28 15:57:35 +03:00
Till Krüss b10cf7c96a back to dev version 2025-07-22 08:38:59 -07:00
Till Krüss 202e0c5322 bump version 2025-07-22 08:37:44 -07:00
Till Krüss 6387da61d4 bump dev version 2025-07-22 08:33:40 -07:00
Till Krüss 97bce0062d cleanup changelog 2025-07-22 08:31:37 -07:00
Till Krüss fe1ea1b8f9 tag v3.0.2 2025-07-22 08:30:03 -07:00
Hristo Temelski 302b63b742 Added test to ensure ftcreate vamana compatibility (#1578)
* Added test to ensure ftcreate vamana compatibility

* updated changelog
2025-07-21 09:48:12 -07:00
ridgey-dev 8e7afbe679 Retry all exceptions from Sentinel replicas (#1577)
* Retry SentinelReplication::retryCommandOnFailure on all classes with Throwable interface

* Retry SentinelReplication::retryCommandOnFailure on all classes with Throwable interface

* Fix coding style
2025-07-18 09:21:40 +03:00
Vladyslav Vildanov c60003a717 Marked missing commands as Prefixable (#1576)
* Marked missing commands as Prefixable

* Codestyle fixes

* Updated CHANGELOG.md
2025-07-09 19:03:22 +03:00
Vladyslav Vildanov 4dcc66f47e Updated Redis 8.2 test image (#1573)
* Updated Redis 8.2 test image

* Updated CHANGELOG.md
2025-07-03 16:53:00 +03:00
Hristo Temelski e0a03c6590 BITOP added operation validation (#1566)
* BITOP added operation validation

* Ensure that the arguments are not an empty list before doing operation check

* Updated changelog, fixed linter errors

* added test for the new bitop operations

* fixed liniting

* update requiresRedisVersion

---------

Co-authored-by: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com>
2025-06-21 11:03:17 +03:00
Vladyslav Vildanov 5e59123f0a Added testing with 8.2 (#1567)
* Added testing with 8.2

* Updated CHANGELOG.md

* Updated conditions for Relay stack tests

* Removed redundat condition

* Removed stack port overriding
2025-06-20 18:39:26 +03:00
alschastny 7509e5f42d XINFO command parse response changes + more tests (#1560)
Co-authored-by: alschastny <>
2025-06-19 10:32:39 +03:00
Till Krüss efc2c3798c Update CODEOWNERS (#1564) 2025-06-17 07:00:17 -07:00
alschastny ec375afa4a fix XREAD parse null response (#1563)
Co-authored-by: aleksanders <alexander.s@seranking.com>
2025-06-17 10:39:31 +03:00
Vladyslav Vildanov 9992595711 Updated Redis 8.0 test image (#1562)
* Updated Redis 8.0 test image

* Updated CHANGELOG.md

* Updated version restriction

* Marked test as relay-incompatible
2025-06-16 09:42:56 -07:00
alschastny 33e1174a4f implement XSETID command (#1559)
* implement XSETID command

* modify CHANGELOG.md

* split test to basic and extended version

---------

Co-authored-by: aleksanders <alexander.s@seranking.com>
2025-06-16 10:11:51 +03:00
alschastny 1e390b1814 implement XPENDING command (#1558)
* implement XPENDING command

* modify CHANGELOG.md

---------

Co-authored-by: aleksanders <alexander.s@seranking.com>
Co-authored-by: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com>
2025-06-13 10:34:52 +03:00
alschastny 0a8fe54b44 implement XCLAIM command (#1557)
* implement XCLAIM command

* modify CHANGELOG.md

* add empty line before return

* test xclaim with all available options

* review refactoring

* remove fast-track check for empty answer

---------

Co-authored-by: aleksanders <alexander.s@seranking.com>
2025-06-13 10:07:44 +03:00
alschastny b5b486a254 add XACK command (#1555)
* add XACK command

* add XACK to ClientContextInterface

* modify CHANGELOG.md

---------

Co-authored-by: aleksanders <alexander.s@seranking.com>
Co-authored-by: Vladyslav Vildanov <117659936+vladvildanov@users.noreply.github.com>
2025-06-11 09:28:13 -07:00
Vladyslav Vildanov 5e18e91ffc fix(commands): Fixed wrong command API call on prefix processing (#1554)
* fix(commands): Fixed wrong command API call on prefix processing

* Updated CHANGELOG.md

* Updated test case
2025-06-11 10:46:25 +03:00
Vladyslav Vildanov c2b95dfd2a Backport #1551 (#1552)
* fix(conn): Removed automatic conn_uid assignment (#1551)

* fix(conn): Removed automatic conn_uid assignment

* Restrict redis version

* Updated CHANGELOG.md

* Added test case

* Update CHANGELOG.md

---------

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>

* Removed duplicated disconnect

* Updated CHANGELOg.md

---------

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2025-06-05 18:34:21 +03:00
Vladyslav Vildanov 30e48f566e feat(commands): Add support for VectorSet commands (#1550)
* feat(commands): Add support for VectorSet commands

* Codestyle fixes

* Fixed test

* Added logging for test purposes

* Marked tests as relay incompatible

* Missing incompatible tests

* Updated CHANGELOG to reflect experimental nature of feature

* Marked properties as nullable

* Changed static variables to constants

* Codestyle fixe

* Revert changes

* Update CHANGELOG.md

---------

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2025-06-05 09:28:31 +03:00
Vladyslav Vildanov d7f60099a6 fix(stream): Fixed throwing CommunicationException when stream is EOF (#1548)
* fix(stream): Fixed return type to match function signature

* Updated CHANGELOG.md

* Fix syntax error

* Updated CHANGELOG.md entry
2025-05-30 10:26:17 +03:00
Mustafa AKBEL 6021daf9cd Treat LOADING Errors as Connection Failures to Retry with Next Replica (#1536)
* fix(sentinel-replication): Treat LOADING error responses as ConnectionException to allow retry mechanism to discard the current replica and switch to the next available one.

* fix(sentinel-replication): add changelog entries for LOADING response handling

* Update CHANGELOG.md

* Update CHANGELOG.md

---------

Co-authored-by: Mustafa AKBEL <mustafa.akbel@pusula.net.tr>
Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2025-05-28 15:11:13 +03:00
Till Krüss 2372b5c767 Update CHANGELOG.md 2025-05-27 09:55:23 -07:00
Vladyslav Vildanov 87e08a3709 Backport #1546 (#1547)
* Fix return type for ZCOUNT (#1546)

* Fix return type for ZCOUNT

ZCOUNT returns an `int`:

> Integer reply: the number of members in the specified score range.

* Update CHANGELOG.md

---------

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>

* Added changelog entry

---------

Co-authored-by: Tim Düsterhus <timwolla@googlemail.com>
Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2025-05-27 09:54:46 -07:00
Vladyslav Vildanov 421827625e Backport #1503 (#1545)
* Backport #1503

* Update CHANGELOG.md

---------

Co-authored-by: Till Krüss <tillkruss@users.noreply.github.com>
2025-05-27 10:17:35 +03:00
Till Krüss afa8b55aa3 bump dev version 2025-05-16 11:33:03 -07:00
Till Krüss 34fb0a7da0 bump version (#1542) 2025-05-16 11:30:32 -07:00
Vladyslav Vildanov 06c904af37 fix(auth): Adds default user if only password used for authentication (#1535) 2025-05-13 09:01:21 -07:00
Pavlo Yatsukhnenko 9b1c6d2c8f Update tests against Relay (#1537) 2025-05-12 09:00:40 -07:00
Vladyslav Vildanov 3cc35471e6 Testing with Redis 8.0.1 (#1538) 2025-05-08 06:09:26 -07:00
Till Krüss 7d86f7afb3 Prepare 3.0 release (#1533) 2025-05-02 16:18:59 -07:00
Vladyslav Vildanov aa72649e43 8.0-RC2 testing (#1531) 2025-04-29 08:25:18 -07:00
362 changed files with 12106 additions and 346 deletions
+2 -14
View File
@@ -1,14 +1,2 @@
# Default
* @tillkruss
# Redis commands
src/Command/Redis/* @predis/team @tillkruss
# Redis Stack
src/Command/Redis/Json/* @predis/team
src/Command/Redis/Search/* @predis/team
src/Command/Redis/TopK/* @predis/team
src/Command/Redis/TDigest/* @predis/team
src/Command/Redis/BloomFilter/* @predis/team
src/Command/Redis/CuckooFilter/* @predis/team
src/Command/Redis/CountMinSketch/* @predis/team
* @predis/maintainers
src/Relay* @tillkruss
+31 -8
View File
@@ -3,7 +3,7 @@
services:
redis-official:
image: ${REDIS_IMAGE_NAME:-redis:7.4}
image: ${REDIS_IMAGE_NAME:-redis:8.0}
container_name: redis-official
healthcheck:
test: [ "CMD", "redis-cli", "PING" ]
@@ -12,20 +12,23 @@ services:
retries: 3
ports:
- "6379:6379"
command:
- --requirepass "foobar"
profiles:
- all
- official
- standalone
redis-clients:
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:7.4.2}
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:8.0.2}
container_name: redis-standalone
environment:
- TLS_ENABLED=yes
- REDIS_CLUSTER=no
- REDIS_PASSWORD=foobar
- PORT=6379
- TLS_PORT=6666
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save "" --requirepass "foobar"}
ports:
- 6379:6379
- 6666:6666 # TLS port
@@ -36,19 +39,38 @@ services:
- clients
- standalone
redis-unprotected:
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:8.0.2}
container_name: redis-unprotected
environment:
- TLS_ENABLED=no
- REDIS_CLUSTER=no
- PORT=6379
- TLS_PORT=6666
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
ports:
- 6380:6379
volumes:
- "./dockers/unprotected:/redis/work"
profiles:
- all
- clients
- unprotected
redis-cluster:
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:7.4.2}
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:8.0.2}
container_name: redis-cluster
environment:
- REDIS_CLUSTER=yes
- REDIS_PASSWORD=foobar
- NODES=6
- REPLICAS=1
- TLS_ENABLED=yes
- PORT=16379
- PORT=6372
- TLS_PORT=27379
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save "" --requirepass "foobar"}
ports:
- "6372-6377:16379-16384"
- "6372-6377:6372-6377"
volumes:
- "./dockers/cluster:/redis/work"
profiles:
@@ -63,8 +85,9 @@ services:
- "6479:6379"
environment:
- REDIS_CLUSTER=no
- REDIS_PASSWORD=foobar
- PORT=6379
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --save ""}
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --save "" --requirepass "foobar"}
volumes:
- "./dockers/redis-stack:/redis/work"
profiles:
+1 -1
View File
@@ -1,7 +1,7 @@
name-template: 'v$NEXT_MINOR_VERSION'
tag-template: 'v$NEXT_MINOR_VERSION'
change-template: '- $TITLE (#$NUMBER)'
filter-by-comitish: true
filter-by-commitish: true
commitish: v3.x
autolabeler:
+1
View File
@@ -4,6 +4,7 @@ CAS
Customizable
ElastiCache
FPM
GC
IANA
Lua
PSR
+37 -20
View File
@@ -31,6 +31,8 @@ jobs:
- '7.2'
- '7.4'
- '8.0'
- '8.2'
- '8.4'
steps:
@@ -38,7 +40,9 @@ jobs:
run: |
# Mapping of original redis versions to client test containers
declare -A redis_clients_version_mapping=(
["8.0"]="8.0-M05-pre"
["8.4"]="8.4.0"
["8.2"]="8.2.2-pre"
["8.0"]="8.0.2"
["7.4"]="7.4.2"
["7.2"]="7.2.7"
)
@@ -78,6 +82,13 @@ jobs:
compose-file: .github/docker-compose.yml
services: ${{ env.DOCKER_SERVICE }}
- name: Start Redis unprotected image
uses: hoverkraft-tech/compose-action@v2.0.1
if: ${{ matrix.redis > '4.0' }}
with:
compose-file: .github/docker-compose.yml
services: redis-unprotected
- name: Start Redis stack image
id: stack_infra
uses: hoverkraft-tech/compose-action@v2.0.1
@@ -99,7 +110,7 @@ jobs:
with:
php-version: ${{ matrix.php }}
extensions: relay
coverage: ${{ (matrix.php == '8.4' && matrix.redis == '7.4') && 'xdebug' || 'none' }}
coverage: ${{ (matrix.php == '8.4' && matrix.redis == '8.0') && 'xdebug' || 'none' }}
- name: Install Composer dependencies
uses: ramsey/composer-install@v2
@@ -108,68 +119,74 @@ jobs:
composer-options: ${{ matrix.php == '8.0' && '--ignore-platform-reqs' || '' }}
- name: Run tests
if: ${{ matrix.php != '8.4' || matrix.redis != '7.4' }}
if: ${{ matrix.php != '8.4' || matrix.redis != '8.0' }}
run: vendor/bin/phpunit
- name: Run tests with coverage
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
run: vendor/bin/phpunit --coverage-php build/cov/coverage-predis.cov --coverage-filter ./src
- name: Run tests using Relay
if: ${{ matrix.php != '8.4' && matrix.redis >= '7.2' }}
if: ${{ matrix.php != '8.4' && matrix.redis >= '8.0' }}
run: vendor/bin/phpunit -c phpunit.relay.xml
- name: Run tests using Relay with coverage
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
run: vendor/bin/phpunit -c phpunit.relay.xml --coverage-php build/cov/coverage-relay.cov --coverage-filter ./src
- name: Run tests against unprotected Redis
if: ${{ (matrix.php != '8.4' || matrix.redis != '8.0') && matrix.redis > '4.0'}}
run: vendor/bin/phpunit --group unprotected
- name: Run tests against unprotected Redis with coverage
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
run: vendor/bin/phpunit --group unprotected --coverage-php build/cov/coverage-unprotected.cov --coverage-filter ./src
- name: Run stack tests
if: ${{ (matrix.php != '8.4' || matrix.redis != '7.4') && matrix.redis >= '7.2' }}
if: ${{ (matrix.php != '8.4' || matrix.redis != '8.0') && matrix.redis >= '7.2' }}
run: vendor/bin/phpunit --group realm-stack
- name: Run stack tests with coverage
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' && steps.stack_infra.conclusion == 'success' }}
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
run: vendor/bin/phpunit --group realm-stack --coverage-php build/cov/coverage-stack.cov --coverage-filter ./src
env:
REDIS_STACK_SERVER_PORT: 6479
- name: Run stack tests using Relay
if: ${{ matrix.php != '8.4' && matrix.redis == '7.4' }}
if: ${{ (matrix.php != '8.4' || matrix.redis != '8.0') && matrix.redis >= '7.2' }}
run: vendor/bin/phpunit --group realm-stack -c phpunit.relay.xml
- name: Run stack tests using Relay with coverage
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
run: vendor/bin/phpunit --group realm-stack -c phpunit.relay.xml --coverage-php build/cov/coverage-stack-relay.cov --coverage-filter ./src
env:
REDIS_STACK_SERVER_PORT: 6479
- name: Run tests against cluster
if: ${{ (matrix.php != '8.4' || matrix.redis != '7.4') && steps.cluster_infra.conclusion == 'success' }}
if: ${{ (matrix.php != '8.4' || matrix.redis != '8.0') && steps.cluster_infra.conclusion == 'success' }}
run: |
vendor/bin/phpunit --group cluster
- name: Run tests against cluster with coverage
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' && steps.cluster_infra.conclusion == 'success' }}
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' && steps.cluster_infra.conclusion == 'success' }}
run: |
vendor/bin/phpunit --group cluster --coverage-php build/cov/coverage-cluster.cov --coverage-filter ./src
- name: Run tests against cluster using Relay
if: ${{ matrix.php != '8.4' && matrix.redis == '7.4' }}
if: ${{ matrix.php != '8.4' && matrix.redis == '8.0' }}
run: |
sleep 5
vendor/bin/phpunit -c phpunit.relay.xml --group cluster
- name: Run tests against cluster using Relay with coverage
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
run: |
sleep 5
vendor/bin/phpunit -c phpunit.relay.xml --group cluster --coverage-php build/cov/coverage-cluster-relay.cov --coverage-filter ./src
- name: Merge coverage reports
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
run: php vendor/bin/phpcov merge --clover build/logs/clover.xml build/cov
- name: Send coverage to Coveralls
uses: coverallsapp/github-action@v2
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel: true
+68 -28
View File
@@ -1,46 +1,85 @@
## Changelog
## Unreleased
## v3.0.0-RC1 (2025-04-17)
### Breaking Changes
- Remove deprecated Triggers & Functions support (#1524)
- Changed default `DIALECT` to 2 (#1516)
- Implemented PSR-7 compatible stream abstraction (#1450)
- Improved pipeline abstractions (#1438)
- Improved connection handshake (#1431)
- Added support for transactions for clustered connections (#1497)
- Extended ClusterInterface with new `getClusterStrategy()` method (#1497)
- Extended StrategyInterface with new `checkSameSlotForKeys()` method that previously was protected (#1497)
## v3.3.0 (2025-11-24)
### Added
- Added Redis 7.4 `XREAD` and `HSCAN` interface changes (#1452)
- Added support for transactions for clustered connections (#1497)
- Added cluster support for `XADD`, `XDEL` and `XRANGE` (#1587)
- Added prefixable interface for `HEXPIRE` and `HEXPIRETIME` (#1592)
- Added new experimental CAS/CAD functionality (#1609)
- Added temporary `XREADGROUP_CLAIM` command (#1608)
- Added support for `MSET` command (#1610)
- Added experimental support for `FT.HYBRID` (#1607)
### Changed
- Improved compatiblity with Relay (#1597)
- Refactor pipeline data writing depends on connection type (#1586)
## v3.2.0 (2025-08-05)
### Added
- Added support for `XDELEX` and `XACKDEL` (#1580)
- Added missing VSIM argument (#1582)
### Changed
- Extended `XTRIM` and `XADD` commands with new parameters (#1580)
### Maintenance
- Updated Redis 8.2 test image (#1583)
- Added test coverage for updated Vamana (#1584)
## v3.1.0 (2025-07-22)
### Added
- Add experimental support for vector sets commands (#1550)
- Added support for `XACK` command (#1555)
- Added support for `XCLAIM` command (#1557)
- Added support for `XPENDING` command (#1558)
- Added support for `XSETID` command (#1559)
- Added validation and support for the new `BITOP` command operations (#1566)
### Changed
- Handle and retry `LOADING` errors from Sentinel replicas (#1536)
- Retry all exceptions from Sentinel replicas (#1577)
### Fixed
- Fixed single persistent connection using the same resource (#1512)
- Fixed PHP 8.4 deprecated call to `stream_context_set_option()` (#1545)
- Fixed return type for `ZCOUNT` to be `int` (#1547)
- Fixed throwing `CommunicationException` when stream is EOF (#1548)
- Removed automatic `conn_uid` parameter assignment (#1552)
- Fixed wrong command API call on prefix processing (#1554)
- Fixed `XREAD` response parsing while read null (#1563)
- Fixed `XINFO` command responses parsing (#1560)
- Marked missing commands as Prefixable (#1576)
## v3.0.0-alpha1 (2024-01-19)
Predis v3.0 introduces support for new communication protocol [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) and [new features](README.md#resp3) based on it.
### Breaking Changes
- Refactored Relay integration (#1423)
- Changed interface of `CLIENT` command (#1337)
- Changed interfaces for commands to support Redis 6.2—7.0 arguments (#1330)
## v3.0.1 (2025-05-16)
### Fixed
- Send `default` user if only password is used for authentication (#1535)
## v3.0.0 (2025-05-02)
### Added
- ⚠️ Added support for transactions for clustered connections (#1497)
- Added support for RESP3 communication protocol (#1047)
- Added support for [Push notifications](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#push-type) (#1316)
- Added support for RESP3 push notifications (#1316)
- Added support for Sharded Pub/Sub (#1303)
- Added support for `XGROUP` commands (#1324)
- Added support for `XREADGROUP` command (#1327)
- Added support for `XAUTOCLAIM` command (#1328)
- Added support for `XINFO` commands (#1331)
- Added support for Redis Gears triggered functions API (#1348)
- Added support for Redis 7.4 `XREAD` and `HSCAN` interface changes (#1452)
## v2.4.0-RC1 (2024-11-21)
### Changed
- ⚠️ Refactored Relay integration (#1423)
- ⚠️ Changed interface of `CLIENT` command (#1337)
- ⚠️ Changed interfaces for commands to support Redis 6.2 — 7.0 arguments (#1330)
- ⚠️ Changed default search `DIALECT` to `2` (#1516)
- ⚠️ Implemented PSR-7 compatible stream abstraction (#1450)
- ⚠️ Improved pipeline abstractions (#1438)
- ⚠️ Improved connection handshake (#1431)
### Fixed
- Fixed single persistent connection using the same resource (#1512)
### Maintenance
- Revisit incompatible tests against Relay (#1537)
## v2.4.0 (2025-04-30)
### Added
- Added new hash-field expiration commands (#1520)
- Added missing `FT._LIST` and `BITFIELD_RO` commands (#1521)
@@ -54,6 +93,7 @@ Predis v3.0 introduces support for new communication protocol [RESP3](https://gi
- Fixed PHP 8.4 compatibility with `stream_context_set_option()` (#1503)
- Prevent named arguments runtime failure (#1509)
- Mark `GEOSEARCH` as read-only to ensure execution on replica (#1481)
- Fixed protocol loss during redis cluster `MOVED` / `ASK` (#1530)
### Maintenance
- Added CI testing with Redis 8.0 (#1510)
+47 -1
View File
@@ -138,6 +138,50 @@ it is still desired to have control of when the connection is opened or closed:
achieved by invoking `$client->connect()` and `$client->disconnect()`. Please note that the effect
of these methods on aggregate connections may differ depending on each specific implementation.
#### Persistent connections ####
To increase a performance of your application you may set up a client to use persistent TCP connection, this way
client saves a time on socket creation and connection handshake. By default, connection is created on first-command
execution and will be automatically closed by GC before the process is being killed.
However, if your application is backed by PHP-FPM the processes are idle, and you may set up it to be persistent and
reusable across multiple script execution within the same process.
To enable the persistent connection mode you should provide following configuration:
```php
// Standalone
$client = new Predis\Client(['persistent' => true]);
// Cluster
$client = new Predis\Client(
['tcp://host:port', 'tcp://host:port', 'tcp://host:port'],
['cluster' => 'redis', 'parameters' => ['persistent' => true]]
);
```
**Important**
If you operate on multiple clients within the same application, and they communicate with the same resource, by default
they will share the same socket (that's the default behaviour of persistent sockets). So in this case you would need
to additionally provide a `conn_uid` identifier for each client, this way each client will create its own socket so
the connection context won't be shared across clients. This socket behaviour explained
[here](https://www.php.net/manual/en/function.stream-socket-client.php#105393)
```php
// Standalone
$client1 = new Predis\Client(['persistent' => true, 'conn_uid' => 'id_1']);
$client2 = new Predis\Client(['persistent' => true, 'conn_uid' => 'id_2']);
// Cluster
$client1 = new Predis\Client(
['tcp://host:port', 'tcp://host:port', 'tcp://host:port'],
['cluster' => 'redis', 'parameters' => ['persistent' => true, 'conn_uid' => 'id_1']]
);
$client2 = new Predis\Client(
['tcp://host:port', 'tcp://host:port', 'tcp://host:port'],
['cluster' => 'redis', 'parameters' => ['persistent' => true, 'conn_uid' => 'id_2']]
);
```
### Client configuration ###
@@ -468,7 +512,7 @@ implementation of the standard connection classes available in the `Predis\Conne
## RESP3 ##
### Connection ###
To establish the connection using RESP3 protocol, you need to set parameter `protocol => 3`. Default protocol is still RESP2.
To establish the connection using the [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) protocol, you need to set parameter `protocol => 3`. The default protocol is RESP2.
You can pass parameter as configuration option in array or as a query parameter in `redis_url`
@@ -506,6 +550,7 @@ $client = new \Predis\Client(['protocol' => 3]);
// RESP3 introduces new double type, that corresponds to PHP float.
var_dump($client->geopos('my_geo', ['member1']));
```
#### Aggregate types ####
In RESP3 new aggregate type [Map](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#map-type)
was introduced, that represents the sequence of field-value pairs. So it simplifies parsing, since we don't need to specify
@@ -514,6 +559,7 @@ parsing strategy per command (RESP2) and instead relies on the type defined by p
In most cases RESP2 responses shouldn't differ from RESP3, since we added additional parsing for those
command that return field-value pairs. However, since RESP2 requires additional parsing, it could be that some commands
had lack of it and return unhandled responses. In this case there would be difference like this:
```php
$client = new \Predis\Client();
+1 -1
View File
@@ -1 +1 @@
3.0.0-RC1
3.3.0
+1 -1
View File
@@ -15,7 +15,7 @@
// For example, to generate a test case for SET (which is represented by the
// Predis\Command\Redis\StringSet class):
//
// $ ./bin/generate-command-test --class=StringSet
// $ ./bin/create-command-test --class=StringSet
//
// Here is a list of optional arguments:
//
+2 -2
View File
@@ -34,11 +34,11 @@ class SimpleDebuggableConnection extends StreamConnection
private function storeDebug(CommandInterface $command, $direction)
{
$firtsArg = $command->getArgument(0);
$firstArg = $command->getArgument(0);
$timestamp = round(microtime(true) - $this->tstart, 4);
$debug = $command->getId();
$debug .= isset($firtsArg) ? " $firtsArg " : ' ';
$debug .= isset($firstArg) ? " $firstArg " : ' ';
$debug .= "$direction $this";
$debug .= " [{$timestamp}s]";
+7 -1
View File
@@ -29,6 +29,7 @@
<group>ext-curl</group>
<group>cluster</group>
<group>gears</group>
<group>unprotected</group>
</exclude>
</groups>
@@ -41,12 +42,17 @@
<php>
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
<const name="REDIS_SERVER_PORT" value="6379" />
<const name="REDIS_UNPROTECTED_SERVER_PORT" value="6380" />
<const name="REDIS_SERVER_DBNUM" value="0" />
<const name="REDIS_PASSWORD" value="foobar" />
<env name="USE_RELAY" value="true" />
<env name="REDIS_STACK_SERVER_PORT" value="6479" />
<!-- Redis Cluster -->
<!-- Only master nodes endpoints included -->
<const name="REDIS_CLUSTER_ENDPOINTS" value="127.0.0.1:6372,127.0.0.1:6373,127.0.0.1:6374" />
<const
name="REDIS_CLUSTER_ENDPOINTS"
value="127.0.0.1:6372?password=foobar,127.0.0.1:6373?password=foobar,127.0.0.1:6374?password=foobar"
/>
</php>
</phpunit>
+10 -1
View File
@@ -10,6 +10,9 @@
stopOnFailure="false"
beStrictAboutTestsThatDoNotTestAnything="true"
>
<php>
<ini name="error_reporting" value="E_ALL &amp; ~E_DEPRECATED"/>
</php>
<testsuites>
<testsuite name="Predis Test Suite">
@@ -29,6 +32,7 @@
<group>cluster</group>
<group>gears</group>
<group>gears-cluster</group>
<group>unprotected</group>
<!-- <group>connected</group> -->
<!-- <group>disconnected</group> -->
<!-- <group>commands</group> -->
@@ -45,12 +49,17 @@
<php>
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
<const name="REDIS_SERVER_PORT" value="6379" />
<const name="REDIS_UNPROTECTED_SERVER_PORT" value="6380" />
<const name="REDIS_SERVER_DBNUM" value="0" />
<const name="REDIS_PASSWORD" value="foobar" />
<env name="USE_RELAY" value="false" />
<env name="REDIS_STACK_SERVER_PORT" value="6479" />
<!-- Redis Cluster -->
<!-- Only master nodes endpoints included -->
<const name="REDIS_CLUSTER_ENDPOINTS" value="127.0.0.1:6372,127.0.0.1:6373,127.0.0.1:6374" />
<const
name="REDIS_CLUSTER_ENDPOINTS"
value="127.0.0.1:6372?password=foobar,127.0.0.1:6373?password=foobar,127.0.0.1:6374?password=foobar"
/>
</php>
</phpunit>
+1 -1
View File
@@ -54,7 +54,7 @@ use Traversable;
*/
class Client implements ClientInterface, IteratorAggregate
{
public const VERSION = '3.0.0-RC1';
public const VERSION = '3.3.0';
/** @var OptionsInterface */
private $options;
+34 -1
View File
@@ -19,6 +19,7 @@ use Predis\Command\Argument\Search\AlterArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\DropArguments;
use Predis\Command\Argument\Search\ExplainArguments;
use Predis\Command\Argument\Search\HybridSearch\HybridSearchQuery;
use Predis\Command\Argument\Search\ProfileArguments;
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Argument\Search\SearchArguments;
@@ -45,12 +46,15 @@ use Predis\Command\Container\Json\JSONDEBUG;
use Predis\Command\Container\Search\FTCONFIG;
use Predis\Command\Container\Search\FTCURSOR;
use Predis\Command\Container\XGROUP;
use Predis\Command\Redis\VADD;
/**
* 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 delex(string $key, string $flag, $flagValue)
* @method $this digest(string $key)
* @method $this dump($key)
* @method $this exists($key)
* @method $this expire($key, $seconds, string $expireOption = '')
@@ -125,6 +129,7 @@ use Predis\Command\Container\XGROUP;
* @method $this ftdictdump(string $dict)
* @method $this ftdropindex(string $index, ?DropArguments $arguments = null)
* @method $this ftexplain(string $index, string $query, ?ExplainArguments $arguments = null)
* @method $this fthybrid(string $index, HybridSearchQuery $query)
* @method $this ftinfo(string $index)
* @method $this ftprofile(string $index, ProfileArguments $arguments)
* @method $this ftsearch(string $index, string $query, ?SearchArguments $arguments = null)
@@ -147,9 +152,10 @@ use Predis\Command\Container\XGROUP;
* @method $this incrbyfloat($key, $increment)
* @method $this mget(array $keys)
* @method $this mset(array $dictionary)
* @method $this msetex(array $dictionary, ?string $existModifier = null, ?string $expireResolution = null, ?int $expireTTL = null)
* @method $this msetnx(array $dictionary)
* @method $this psetex($key, $milliseconds, $value)
* @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null, $flagValue = null)
* @method $this setbit($key, $offset, $value)
* @method $this setex($key, $seconds, $value)
* @method $this setnx($key, $value)
@@ -283,6 +289,22 @@ use Predis\Command\Container\XGROUP;
* @method $this tsqueryindex(string ...$filterExpression)
* @method $this tsrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
* @method $this tsrevrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
* @method $this xack(string $key, string $group, string ...$id)
* @method $this xackdel(string $key, string $group, string $mode, array $ids)
* @method $this xadd(string $key, array $dictionary, string $id = '*', array $options = null)
* @method $this xautoclaim(string $key, string $group, string $consumer, int $minIdleTime, string $start, ?int $count = null, bool $justId = false)
* @method $this xclaim(string $key, string $group, string $consumer, int $minIdleTime, string|array $ids, ?int $idle = null, ?int $time = null, ?int $retryCount = null, bool $force = false, bool $justId = false, ?string $lastId = null)
* @method $this xdel(string $key, string ...$id)
* @method $this xdelex(string $key, string $mode, array $ids)
* @method $this xlen(string $key)
* @method $this xpending(string $key, string $group, ?int $minIdleTime = null, ?string $start = null, ?string $end = null, ?int $count = null, ?string $consumer = null)
* @method $this xrevrange(string $key, string $end, string $start, ?int $count = null)
* @method $this xrange(string $key, string $start, string $end, ?int $count = null)
* @method $this xread(int $count = null, int $block = null, array $streams = null, string ...$id)
* @method $this xreadgroup(string $group, string $consumer, ?int $count = null, ?int $blockMs = null, bool $noAck = false, string ...$keyOrId)
* @method $this xreadgroup_claim(string $group, string $consumer, array $keyIdDict, ?int $count = null, ?int $blockMs = null, bool $noAck = false, ?int $claim = null)
* @method $this xsetid(string $key, string $lastId, ?int $entriesAdded = null, ?string $maxDeleteId = null)
* @method $this xtrim(string $key, array|string $strategy, string $threshold, array $options = null)
* @method $this zadd($key, array $membersAndScoresDictionary)
* @method $this zcard($key)
* @method $this zcount($key, $min, $max)
@@ -325,6 +347,17 @@ use Predis\Command\Container\XGROUP;
* @method $this unwatch()
* @method $this waitaof(int $numLocal, int $numReplicas, int $timeout)
* @method $this unsubscribe(string ...$channels)
* @method $this vadd(string $key, string|array $vector, string $elem, int $dim = null, bool $cas = false, string $quant = VADD::QUANT_DEFAULT, ?int $BEF = null, string|array $attributes = null, int $numlinks = null)
* @method $this vcard(string $key)
* @method $this vdim(int $key)
* @method $this vemb(string $key, string $elem, bool $raw = false)
* @method $this vgetattr(string $key, string $elem, bool $asJson = false)
* @method $this vinfo(string $key)
* @method $this vlinks(string $key, string $elem, bool $withScores = false)
* @method $this vrandmember(string $key, int $count = null)
* @method $this vrem(string $key, string $elem)
* @method $this vsetattr(string $key, string $elem, string|array $attributes)
* @method $this vsim(string $key, string|array $vectorOrElem, bool $isElem = false, bool $withScores = false, int $count = null, float $epsilon = null, int $ef = null, string $filter = null, int $filterEf = null, bool $truth = false, bool $noThread = false)
* @method $this watch($key)
* @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
* @method $this eval_ro(string $script, array $keys, ...$argument)
+26 -2
View File
@@ -19,6 +19,7 @@ use Predis\Command\Argument\Search\AlterArguments;
use Predis\Command\Argument\Search\CreateArguments;
use Predis\Command\Argument\Search\DropArguments;
use Predis\Command\Argument\Search\ExplainArguments;
use Predis\Command\Argument\Search\HybridSearch\HybridSearchQuery;
use Predis\Command\Argument\Search\ProfileArguments;
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Argument\Search\SearchArguments;
@@ -47,6 +48,7 @@ use Predis\Command\Container\Search\FTCURSOR;
use Predis\Command\Container\XGROUP;
use Predis\Command\Container\XINFO;
use Predis\Command\FactoryInterface;
use Predis\Command\Redis\VADD;
use Predis\Configuration\OptionsInterface;
use Predis\Connection\ConnectionInterface;
use Predis\Response\Status;
@@ -61,6 +63,8 @@ use Predis\Response\Status;
*
* @method int copy(string $source, string $destination, int $db = -1, bool $replace = false)
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
* @method int delex(string $key, string $flag, $flagValue)
* @method string digest(string $key)
* @method string|null dump(string $key)
* @method int exists(string $key)
* @method int expire(string $key, int $seconds, string $expireOption = '')
@@ -135,6 +139,7 @@ use Predis\Response\Status;
* @method array ftdictdump(string $dict)
* @method Status ftdropindex(string $index, ?DropArguments $arguments = null)
* @method string ftexplain(string $index, string $query, ?ExplainArguments $arguments = null)
* @method array fthybrid(string $index, HybridSearchQuery $query)
* @method array ftinfo(string $index)
* @method array ftprofile(string $index, ProfileArguments $arguments)
* @method array ftsearch(string $index, string $query, ?SearchArguments $arguments = null)
@@ -157,9 +162,10 @@ use Predis\Response\Status;
* @method string incrbyfloat(string $key, int|float $increment)
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
* @method mixed mset(array $dictionary)
* @method array msetex(array $dictionary, ?string $existModifier = null, ?string $expireResolution = null, ?int $expireTTL = null)
* @method int msetnx(array $dictionary)
* @method Status psetex(string $key, $milliseconds, $value)
* @method Status|null set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method Status|null set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null, $flagValue = null)
* @method int setbit(string $key, $offset, $value)
* @method Status setex(string $key, $seconds, $value)
* @method int setnx(string $key, $value)
@@ -294,18 +300,25 @@ use Predis\Response\Status;
* @method array tsqueryindex(string ...$filterExpression)
* @method array tsrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
* @method array tsrevrange(string $key, $fromTimestamp, $toTimestamp, ?RangeArguments $arguments = null)
* @method int xack(string $key, string $group, string ...$id)
* @method array xackdel(string $key, string $group, string $mode, array $ids)
* @method string xadd(string $key, array $dictionary, string $id = '*', array $options = null)
* @method array xautoclaim(string $key, string $group, string $consumer, int $minIdleTime, string $start, ?int $count = null, bool $justId = false)
* @method array xclaim(string $key, string $group, string $consumer, int $minIdleTime, string|array $ids, ?int $idle = null, ?int $time = null, ?int $retryCount = null, bool $force = false, bool $justId = false, ?string $lastId = null)
* @method int xdel(string $key, string ...$id)
* @method array xdelex(string $key, string $mode, array $ids)
* @method int xlen(string $key)
* @method array xpending(string $key, string $group, ?int $minIdleTime = null, ?string $start = null, ?string $end = null, ?int $count = null, ?string $consumer = null)
* @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 array|null xread(int $count = null, int $block = null, array $streams = null, string ...$id)
* @method array xreadgroup(string $group, string $consumer, ?int $count = null, ?int $blockMs = null, bool $noAck = false, string ...$keyOrId)
* @method array xreadgroup_claim(string $group, string $consumer, array $keyIdDict, ?int $count = null, ?int $blockMs = null, bool $noAck = false, ?int $claim = null)
* @method Status xsetid(string $key, string $lastId, ?int $entriesAdded = null, ?string $maxDeleteId = 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 int 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)
@@ -346,6 +359,17 @@ use Predis\Response\Status;
* @method mixed multi()
* @method mixed unwatch()
* @method array unsubscribe(string ...$channels)
* @method bool vadd(string $key, string|array $vector, string $elem, int $dim = null, bool $cas = false, string $quant = VADD::QUANT_DEFAULT, int $bef = null, string|array $attributes = null, int $numlinks = null)
* @method int vcard(string $key)
* @method int vdim(string $key)
* @method array vemb(string $key, string $elem, bool $raw = false)
* @method string|array|null vgetattr(string $key, string $elem, bool $asJson = false)
* @method array|null vinfo(string $key)
* @method array|null vlinks(string $key, string $elem, bool $withScores = false)
* @method string|array|null vrandmember(string $key, int $count = null)
* @method bool vrem(string $key, string $elem)
* @method array vsim(string $key, string|array $vectorOrElem, bool $isElem = false, bool $withScores = false, int $count = null, float $epsilon = null, int $ef = null, string $filter = null, int $filterEf = null, bool $truth = false, bool $noThread = false)
* @method bool vsetattr(string $key, string $elem, string|array $attributes)
* @method array waitaof(int $numLocal, int $numReplicas, int $timeout)
* @method mixed watch(string[]|string $keyOrKeys)
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
+5
View File
@@ -155,6 +155,11 @@ abstract class ClusterStrategy implements StrategyInterface
'HSCAN' => $getKeyFromFirstArgument,
'HSTRLEN' => $getKeyFromFirstArgument,
/* commands operating on streams */
'XADD' => $getKeyFromFirstArgument,
'XDEL' => $getKeyFromFirstArgument,
'XRANGE' => $getKeyFromFirstArgument,
/* commands operating on HyperLogLog */
'PFADD' => $getKeyFromFirstArgument,
'PFCOUNT' => $getKeyFromAllArguments,
@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch\Combine;
use Predis\Command\Argument\ArrayableArgument;
abstract class BaseCombine implements ArrayableArgument
{
/**
* @var array
*/
protected $arguments = ['COMBINE'];
/**
* @var array
*/
protected $as = [];
/**
* @param string $alias
* @return $this
*/
public function as(string $alias): self
{
array_push($this->as, 'YIELD_SCORE_AS', $alias);
return $this;
}
/**
* {@inheritDoc}
*/
abstract public function toArray(): array;
}
@@ -0,0 +1,79 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch\Combine;
class LinearCombineConfig extends BaseCombine
{
/**
* @var float
*/
protected $alpha;
/**
* @var float
*/
protected $beta;
/**
* The weight for the text score (a value between 0 and 1).
*
* @param float $alpha
* @return $this
*/
public function alpha(float $alpha): self
{
$this->alpha = $alpha;
return $this;
}
/**
* The weight for the vector score (a value between 0 and 1).
*
* @param float $beta
* @return $this
*/
public function beta(float $beta): self
{
$this->beta = $beta;
return $this;
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
$this->arguments[] = 'LINEAR';
$tokens = [];
if ($this->alpha !== null) {
array_push($tokens, 'ALPHA', $this->alpha);
}
if ($this->beta !== null) {
array_push($tokens, 'BETA', $this->beta);
}
if ($this->as) {
array_push($tokens, ...$this->as);
}
if (!empty($tokens)) {
array_push($this->arguments, count($tokens), ...$tokens);
}
return $this->arguments;
}
}
@@ -0,0 +1,79 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch\Combine;
class RRFCombineConfig extends BaseCombine
{
/**
* @var int
*/
protected $window;
/**
* @var int
*/
protected $rrfConstant;
/**
* The number of top results from each search type to consider for fusion. Defaults to 50.
*
* @param int $window
* @return $this
*/
public function window(int $window): self
{
$this->window = $window;
return $this;
}
/**
* The RRF ranking constant. A smaller value gives more weight to top-ranked items. Defaults to 60.
*
* @param int $constant
* @return $this
*/
public function rrfConstant(int $constant): self
{
$this->rrfConstant = $constant;
return $this;
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
$this->arguments[] = 'RRF';
$tokens = [];
if ($this->window !== null) {
array_push($tokens, 'WINDOW', $this->window);
}
if ($this->rrfConstant !== null) {
array_push($tokens, 'CONSTANT', $this->rrfConstant);
}
if ($this->as) {
array_push($tokens, ...$this->as);
}
if (!empty($tokens)) {
array_push($this->arguments, count($tokens), ...$tokens);
}
return $this->arguments;
}
}
@@ -0,0 +1,352 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch;
use Predis\Command\Argument\ArrayableArgument;
use Predis\Command\Argument\Search\HybridSearch\Combine\LinearCombineConfig;
use Predis\Command\Argument\Search\HybridSearch\Combine\RRFCombineConfig;
use Predis\Command\Argument\Search\HybridSearch\VectorSearch\KNNVectorSearchConfig;
use Predis\Command\Argument\Search\HybridSearch\VectorSearch\RangeVectorSearchConfig;
use Predis\Command\Redis\Utils\CommandUtility;
use ValueError;
class HybridSearchQuery implements ArrayableArgument
{
public const SORT_ASC = 'ASC';
public const SORT_DESC = 'DESC';
/**
* @var SearchConfig
*/
protected $searchConfig;
/**
* The vector search portion of the query.
*
* @var KNNVectorSearchConfig|RangeVectorSearchConfig
*/
protected $vectorSearchConfig;
/**
* Configuration for the score fusion method (optional).
* If not provided, Reciprocal Rank Fusion (RRF) is used with server-side default parameters.
*
* @var RRFCombineConfig|LinearCombineConfig
*/
protected $combineConfig;
/**
* @var array
*/
protected $load = [];
/**
* @var array
*/
protected $groupBy = [];
/**
* @var array
*/
protected $apply = [];
/**
* @var array
*/
protected $sortBy = [];
/**
* @var string
*/
protected $filter;
/**
* @var array
*/
protected $limit = [];
/**
* @var array
*/
protected $params = [];
/**
* @var bool
*/
protected $explainScore = false;
/**
* @var bool
*/
protected $timeout = false;
/**
* @var array
*/
protected $withCursor = [];
/**
* @var array
*/
protected $arguments = [];
/**
* @param string $vectorSearchMethod Class type of desired vector search method
* @param string $combineMethod Class type of desired combine method
*/
public function __construct(
string $vectorSearchMethod = KNNVectorSearchConfig::class,
string $combineMethod = RRFCombineConfig::class
) {
$this->searchConfig = new SearchConfig();
$this->vectorSearchConfig = new $vectorSearchMethod();
$this->combineConfig = new $combineMethod();
}
/**
* @param callable(SearchConfig): void $callable
* @return $this
*/
public function buildSearchConfig(callable $callable): self
{
$callable($this->searchConfig);
return $this;
}
/**
* @param callable(KNNVectorSearchConfig|RangeVectorSearchConfig): void $callable
* @return $this
*/
public function buildVectorSearchConfig(callable $callable): self
{
$callable($this->vectorSearchConfig);
return $this;
}
/**
* @param callable(RRFCombineConfig|LinearCombineConfig): void $callable
* @return $this
*/
public function buildCombineConfig(callable $callable): self
{
$callable($this->combineConfig);
return $this;
}
/**
* The list of fields to return in the results.
*
* @param array $fields
* @return $this
*/
public function load(array $fields): self
{
array_push($this->load, 'LOAD', count($fields), ...$fields);
return $this;
}
/**
* @param array $fields
* @param Reducer[] $reducers
* @return $this
*/
public function groupBy(array $fields, array $reducers): self
{
array_push($this->groupBy, 'GROUPBY', count($fields), ...$fields);
foreach ($reducers as $reducer) {
array_push($this->groupBy, 'REDUCE', ...$reducer->toArray());
}
return $this;
}
/**
* @param array $expressionFieldDict field => function dictionary
* @return $this
*/
public function apply(array $expressionFieldDict): self
{
foreach ($expressionFieldDict as $field => $function) {
array_push($this->apply, 'APPLY', $function, 'AS', $field);
}
return $this;
}
/**
* Sorts the final results by a specific field.
*
* @param array<string, string> $fields Dictionary with fields and sort direction. Check class constants.
* @return $this
*/
public function sortBy(array $fields): self
{
$fieldsArray = [];
foreach ($fields as $field => $direction) {
if (!in_array(strtoupper($direction), [self::SORT_ASC, self::SORT_DESC])) {
throw new ValueError('Sort direction must be one of "ASC" or "DESC".');
}
array_push($fieldsArray, $field, $direction);
}
array_push($this->sortBy, 'SORTBY', count($fieldsArray), ...$fieldsArray);
return $this;
}
/**
* Final result filtering.
*
* @param string $expression
* @return $this
*/
public function filter(string $expression): self
{
$this->filter = $expression;
return $this;
}
/**
* @param int $offset
* @param int $num
* @return $this
*/
public function limit(int $offset, int $num): self
{
array_push($this->limit, 'LIMIT', $offset, $num);
return $this;
}
/**
* Binds values to named parameters in the query string.
*
* @param array $params
* @return $this
*/
public function params(array $params): self
{
$arrayParams = CommandUtility::dictionaryToArray($params);
array_push($this->params, 'PARAMS', count($arrayParams), ...$arrayParams);
return $this;
}
/**
* @return $this
*/
public function explainScore(): self
{
$this->explainScore = true;
return $this;
}
/**
* @return $this
*/
public function timeout(): self
{
$this->timeout = true;
return $this;
}
/**
* @param int|null $readSize
* @param int|null $idleTime
* @return $this
*/
public function withCursor(?int $readSize = null, ?int $idleTime = null): self
{
$this->withCursor[] = 'WITHCURSOR';
if ($readSize) {
array_push($this->withCursor, 'COUNT', $readSize);
}
if ($idleTime) {
array_push($this->withCursor, 'MAXIDLE', $idleTime);
}
return $this;
}
/**
* {@inheritDoc}
*/
public function toArray(): array
{
$this->arguments = array_merge(
$this->arguments,
$this->searchConfig->toArray(),
$this->vectorSearchConfig->toArray()
);
$combineConfig = $this->combineConfig->toArray();
// Only add if any configuration was applied
if (count($combineConfig) > 2) {
$this->arguments = array_merge($this->arguments, $combineConfig);
}
if ($this->load) {
$this->arguments = array_merge($this->arguments, $this->load);
}
if ($this->groupBy) {
$this->arguments = array_merge($this->arguments, $this->groupBy);
}
if ($this->apply) {
$this->arguments = array_merge($this->arguments, $this->apply);
}
if ($this->sortBy) {
$this->arguments = array_merge($this->arguments, $this->sortBy);
}
if ($this->filter) {
array_push($this->arguments, 'FILTER', $this->filter);
}
if ($this->limit) {
$this->arguments = array_merge($this->arguments, $this->limit);
}
if ($this->params) {
$this->arguments = array_merge($this->arguments, $this->params);
}
if ($this->explainScore) {
$this->arguments[] = 'EXPLAINSCORE';
}
if ($this->timeout) {
$this->arguments[] = 'TIMEOUT';
}
if ($this->withCursor) {
$this->arguments = array_merge($this->arguments, $this->withCursor);
}
return $this->arguments;
}
}
@@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch;
use Predis\Command\Argument\ArrayableArgument;
class Reducer implements ArrayableArgument
{
public const REDUCE_COUNT = 'COUNT';
public const REDUCE_COUNT_DISTINCT = 'COUNT_DISTINCT';
public const REDUCE_COUNT_DISTINCTISH = 'COUNT_DISTINCTISH';
public const REDUCE_SUM = 'SUM';
public const REDUCE_MIN = 'MIN';
public const REDUCE_MAX = 'MAX';
public const REDUCE_AVG = 'AVG';
public const REDUCE_STDDEV = 'STDDEV';
public const REDUCE_QUANTILE = 'QUANTILE';
/**
* @var array
*/
protected $arguments = [];
/**
* @param string $function One of the available functions. Check class constants.
* @param array $arguments List of properties
*/
public function __construct(string $function = self::REDUCE_COUNT, array $arguments = [], ?string $alias = null)
{
array_push($this->arguments, $function, count($arguments), ...$arguments);
if ($alias) {
array_push($this->arguments, 'AS', $alias);
}
}
public function toArray(): array
{
return $this->arguments;
}
}
@@ -0,0 +1,60 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch;
use Predis\Command\Argument\ArrayableArgument;
class ScorerConfig implements ArrayableArgument
{
public const TYPE_BM25 = 'BM25';
public const TYPE_TFIDF = 'TFIDF';
public const TYPE_DISMAX = 'DISMAX';
public const TYPE_DOCSCORE = 'DOCSCORE';
/**
* @var array
*/
protected $arguments = [];
/**
* The text scoring algorithm. Defaults to BM25.
*
* @param string $type
* @return $this
*/
public function type(string $type = self::TYPE_BM25): self
{
$this->arguments[] = $type;
return $this;
}
/**
* An alias for the text score field in the results.
* The aliased field will be included in the `value` object of each returned document.
*
* @param string $alias
* @return $this
*/
public function as(string $alias): self
{
array_push($this->arguments, 'YIELD_SCORE_AS', $alias);
return $this;
}
public function toArray(): array
{
return $this->arguments;
}
}
@@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch;
use Predis\Command\Argument\ArrayableArgument;
class SearchConfig implements ArrayableArgument
{
/**
* @var array
*/
protected $arguments = ['SEARCH'];
/**
* @var ScorerConfig
*/
protected $scorerConfig;
public function __construct()
{
$this->scorerConfig = new ScorerConfig();
}
/**
* Search query.
*
* @param string $query
* @return $this
*/
public function query(string $query): self
{
$this->arguments[] = $query;
return $this;
}
/**
* @param string $alias
* @return $this
*/
public function as(string $alias): self
{
array_push($this->arguments, 'YIELD_SCORE_AS', $alias);
return $this;
}
/**
* @param callable(ScorerConfig): void $callable
* @return $this
*/
public function buildScorerConfig(callable $callable): self
{
$callable($this->scorerConfig);
return $this;
}
public function toArray(): array
{
$scorerConfig = $this->scorerConfig->toArray();
if (!empty($scorerConfig)) {
$this->arguments[] = 'SCORER';
$this->arguments = array_merge($this->arguments, $scorerConfig);
}
return $this->arguments;
}
}
@@ -0,0 +1,85 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch\VectorSearch;
use Predis\Command\Argument\ArrayableArgument;
use Predis\Command\Redis\Utils\VectorUtility;
abstract class BaseVectorSearchConfig implements ArrayableArgument
{
public const POLICY_ADHOC = 'ADHOC';
public const POLICY_BATCHES = 'BATCHES';
public const POLICY_ACORN = 'ACORN';
/**
* @var array
*/
protected $vector = [];
/**
* @var array
*/
protected $filter = [];
/**
* @var array
*/
protected $as = [];
/**
* @var array
*/
protected $arguments = ['VSIM'];
/**
* Vector to perform search against.
*
* @param string $field The vector field name to search against. Must start with "@".
* @param string|float[] $value Binary vector representation or array of floats as vector.
* @return self
*/
public function vector(string $field, $value): self
{
if (is_array($value)) {
array_push($this->vector, $field, VectorUtility::toBlob($value));
} else {
array_push($this->vector, $field, $value);
}
return $this;
}
/**
* @param string $expression
* @return $this
*/
public function filter(string $expression): self
{
array_push($this->filter, 'FILTER', $expression);
return $this;
}
/**
* @param string $alias
* @return $this
*/
public function as(string $alias): self
{
array_push($this->as, 'YIELD_SCORE_AS', $alias);
return $this;
}
abstract public function toArray(): array;
}
@@ -0,0 +1,91 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch\VectorSearch;
use ValueError;
class KNNVectorSearchConfig extends BaseVectorSearchConfig
{
/**
* @var int
*/
protected $k;
/**
* @var int
*/
protected $ef;
/**
* The number of nearest neighbors to find. Defaults to 10 on server side.
*
* @param int $k
* @return self
*/
public function k(int $k): self
{
$this->k = $k;
return $this;
}
/**
* The HNSW `ef_runtime` parameter for tuning the accuracy/speed trade-off.
*
* @param int $ef
* @return $this
*/
public function ef(int $ef): self
{
$this->ef = $ef;
return $this;
}
public function toArray(): array
{
if (!$this->vector) {
throw new ValueError('Vector configuration not specified.');
}
$this->arguments = array_merge($this->arguments, $this->vector);
if ($this->k || $this->ef) {
$this->arguments[] = 'KNN';
}
$tokens = [];
if ($this->k !== null) {
array_push($tokens, 'K', $this->k);
}
if ($this->ef !== null) {
array_push($tokens, 'EF_RUNTIME', $this->ef);
}
if (!empty($tokens)) {
array_push($this->arguments, count($tokens), ...$tokens);
}
if ($this->filter) {
$this->arguments = array_merge($this->arguments, $this->filter);
}
if ($this->as) {
array_push($this->arguments, ...$this->as);
}
return $this->arguments;
}
}
@@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Predis\Command\Argument\Search\HybridSearch\VectorSearch;
use ValueError;
class RangeVectorSearchConfig extends BaseVectorSearchConfig
{
/**
* @var int
*/
protected $radius;
/**
* @var float
*/
protected $epsilon;
/**
* The search radius/threshold. Finds all vectors within this distance.
*
* @param int $radius
* @return $this
*/
public function radius(int $radius): self
{
$this->radius = $radius;
return $this;
}
/**
* @param float $epsilon
* @return $this
*/
public function epsilon(float $epsilon): self
{
$this->epsilon = $epsilon;
return $this;
}
public function toArray(): array
{
if (!$this->vector) {
throw new ValueError('Vector configuration not specified.');
}
$this->arguments = array_merge($this->arguments, $this->vector);
if ($this->radius || $this->epsilon) {
$this->arguments[] = 'RANGE';
}
$tokens = [];
if ($this->radius !== null) {
array_push($tokens, 'RADIUS', $this->radius);
}
if ($this->epsilon !== null) {
array_push($tokens, 'EPSILON', $this->epsilon);
}
if (!empty($tokens)) {
array_push($this->arguments, count($tokens), ...$tokens);
}
if ($this->filter) {
$this->arguments = array_merge($this->arguments, $this->filter);
}
if ($this->as) {
array_push($this->arguments, ...$this->as);
}
return $this->arguments;
}
}
+2
View File
@@ -17,8 +17,10 @@ use Predis\Response\Status;
/**
* @method array cat(string $category = null)
* @method Status dryRun(string $username, string $command, ...$arguments)
* @method int delUser(string ...$username)
* @method array getUser(string $username)
* @method Status setUser(string $username, string ...$rules)
* @method string whoami()
*/
class ACL extends AbstractContainer
{
+2 -2
View File
@@ -32,7 +32,7 @@ abstract class PrefixableCommand extends Command implements PrefixableCommandInt
*/
public function applyPrefixForAllArguments(string $prefix): void
{
$this->setArguments(
$this->setRawArguments(
array_map(static function ($key) use ($prefix) {
return $prefix . $key;
}, $this->getArguments())
@@ -49,7 +49,7 @@ abstract class PrefixableCommand extends Command implements PrefixableCommandInt
{
$arguments = $this->getArguments();
$arguments[0] = $prefix . $arguments[0];
$this->setArguments($arguments);
$this->setRawArguments($arguments);
}
/**
+10
View File
@@ -12,6 +12,7 @@
namespace Predis\Command\Redis;
use InvalidArgumentException;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
@@ -19,6 +20,8 @@ use Predis\Command\PrefixableCommand as RedisCommand;
*/
class BITOP extends RedisCommand
{
private const VALID_OPERATIONS = ['AND', 'OR', 'XOR', 'NOT', 'DIFF', 'DIFF1', 'ANDOR', 'ONE'];
/**
* {@inheritdoc}
*/
@@ -38,6 +41,13 @@ class BITOP extends RedisCommand
array_unshift($arguments, $operation, $destination);
}
if (!empty($arguments)) {
$operation = strtoupper($arguments[0]);
if (!in_array($operation, self::VALID_OPERATIONS, false)) {
throw new InvalidArgumentException('BITOP operation must be one of: AND, OR, XOR, NOT, DIFF, DIFF1, ANDOR, ONE');
}
}
parent::setArguments($arguments);
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\BloomFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/bf.add/
@@ -26,4 +26,9 @@ class BFADD extends RedisCommand
{
return 'BF.ADD';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\BloomFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/bf.exists/
@@ -25,4 +25,9 @@ class BFEXISTS extends RedisCommand
{
return 'BF.EXISTS';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\BloomFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
use UnexpectedValueException;
/**
@@ -76,4 +76,9 @@ class BFINFO extends RedisCommand
return $data;
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\BloomFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
use Predis\Command\Traits\BloomFilters\Capacity;
use Predis\Command\Traits\BloomFilters\Error;
use Predis\Command\Traits\BloomFilters\Expansion;
@@ -48,6 +48,11 @@ class BFINSERT extends RedisCommand
return 'BF.INSERT';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
public function setArguments(array $arguments)
{
$this->setNoCreate($arguments);
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\BloomFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/bf.loadchunk/
@@ -25,4 +25,9 @@ class BFLOADCHUNK extends RedisCommand
{
return 'BF.LOADCHUNK';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\BloomFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/bf.madd/
@@ -26,4 +26,9 @@ class BFMADD extends RedisCommand
{
return 'BF.MADD';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\BloomFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/bf.mexists/
@@ -25,4 +25,9 @@ class BFMEXISTS extends RedisCommand
{
return 'BF.MEXISTS';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\BloomFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
use Predis\Command\Traits\BloomFilters\Expansion;
/**
@@ -46,4 +46,9 @@ class BFRESERVE extends RedisCommand
$this->setExpansion($arguments);
$this->filterArguments();
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\BloomFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/bf.scandump/
@@ -26,4 +26,9 @@ class BFSCANDUMP extends RedisCommand
{
return 'BF.SCANDUMP';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+17 -3
View File
@@ -32,9 +32,23 @@ class COMMAND extends BaseCommand
*/
public function parseResponse($data)
{
// Relay (RESP3) uses maps and it might be good
// to make the return value a breaking change
if (!is_array($data)) {
return $data;
}
return $data;
if ($data === array_values($data)) {
return array_map(function ($item) {
return $this->parseResponse($item);
}, $data);
}
// Relay
$result = [];
foreach ($data as $key => $value) {
$result[] = $key;
$result[] = $this->parseResponse($value);
}
return $result;
}
}
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CountMinSketch;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cms.incrby/
@@ -26,4 +26,9 @@ class CMSINCRBY extends RedisCommand
{
return 'CMS.INCRBY';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CountMinSketch;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cms.info/
@@ -42,4 +42,9 @@ class CMSINFO extends RedisCommand
return $data;
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CountMinSketch;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cms.initbydim/
@@ -25,4 +25,9 @@ class CMSINITBYDIM extends RedisCommand
{
return 'CMS.INITBYDIM';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CountMinSketch;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cms.initbyprob/
@@ -25,4 +25,9 @@ class CMSINITBYPROB extends RedisCommand
{
return 'CMS.INITBYPROB';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+14 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CountMinSketch;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cms.merge/
@@ -39,4 +39,17 @@ class CMSMERGE extends RedisCommand
parent::setArguments($processedArguments);
}
public function prefixKeys($prefix)
{
if ($arguments = $this->getArguments()) {
$arguments[0] = $prefix . $arguments[0];
for ($i = 2, $iMax = (int) $arguments[1] + 2; $i < $iMax; $i++) {
$arguments[$i] = $prefix . $arguments[$i];
}
$this->setRawArguments($arguments);
}
}
}
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CountMinSketch;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cms.query/
@@ -25,4 +25,9 @@ class CMSQUERY extends RedisCommand
{
return 'CMS.QUERY';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cf.add/
@@ -25,4 +25,9 @@ class CFADD extends RedisCommand
{
return 'CF.ADD';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cf.addnx/
@@ -25,4 +25,9 @@ class CFADDNX extends RedisCommand
{
return 'CF.ADDNX';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cf.count/
@@ -26,4 +26,9 @@ class CFCOUNT extends RedisCommand
{
return 'CF.COUNT';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cf.del/
@@ -27,4 +27,9 @@ class CFDEL extends RedisCommand
{
return 'CF.DEL';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cf.exists/
@@ -25,4 +25,9 @@ class CFEXISTS extends RedisCommand
{
return 'CF.EXISTS';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cf.info/
@@ -42,4 +42,9 @@ class CFINFO extends RedisCommand
return $data;
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
use Predis\Command\Traits\BloomFilters\Capacity;
use Predis\Command\Traits\BloomFilters\Items;
use Predis\Command\Traits\BloomFilters\NoCreate;
@@ -49,4 +49,9 @@ class CFINSERT extends RedisCommand
$this->setCapacity($arguments);
$this->filterArguments();
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cf.loadchunk/
@@ -26,4 +26,9 @@ class CFLOADCHUNK extends RedisCommand
{
return 'CF.LOADCHUNK';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cf.mexists/
@@ -25,4 +25,9 @@ class CFMEXISTS extends RedisCommand
{
return 'CF.MEXISTS';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
use Predis\Command\Traits\BloomFilters\BucketSize;
use Predis\Command\Traits\BloomFilters\Expansion;
use Predis\Command\Traits\BloomFilters\MaxIterations;
@@ -49,4 +49,9 @@ class CFRESERVE extends RedisCommand
$this->setBucketSize($arguments);
$this->filterArguments();
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\CuckooFilter;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/cf.scandump/
@@ -26,4 +26,9 @@ class CFSCANDUMP extends RedisCommand
{
return 'CF.SCANDUMP';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* 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\PrefixableCommand;
class DELEX extends PrefixableCommand
{
/**
* {@inheritDoc}
*/
public function getId()
{
return 'DELEX';
}
/**
* {@inheritDoc}
*/
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* 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\PrefixableCommand;
class DIGEST extends PrefixableCommand
{
/**
* {@inheritDoc}
*/
public function getId()
{
return 'DIGEST';
}
/**
* {@inheritDoc}
*/
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/expiretime/
@@ -26,4 +26,9 @@ class EXPIRETIME extends RedisCommand
{
return 'EXPIRETIME';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+22
View File
@@ -127,4 +127,26 @@ class FUNCTIONS extends RedisCommand
parent::setArguments($processedArguments);
}
public function parseResponse($data)
{
if (!is_array($data)) {
return $data;
}
if ($data === array_values($data)) {
return array_map(function ($item) {
return $this->parseResponse($item);
}, $data);
}
// Relay
$result = [];
foreach ($data as $key => $value) {
$result[] = $key;
$result[] = $this->parseResponse($value);
}
return $result;
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
use Predis\Command\Traits\By\GeoBy;
use Predis\Command\Traits\Count;
use Predis\Command\Traits\From\GeoFrom;
@@ -119,4 +119,9 @@ class GEOSEARCH extends RedisCommand
return $parsedData;
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
class GETDEL extends RedisCommand
{
@@ -20,4 +20,9 @@ class GETDEL extends RedisCommand
{
return 'GETDEL';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
use UnexpectedValueException;
class HEXPIRE extends RedisCommand
@@ -48,4 +48,9 @@ class HEXPIRE extends RedisCommand
parent::setArguments($processedArguments);
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
class HEXPIRETIME extends RedisCommand
{
@@ -28,4 +28,9 @@ class HEXPIRETIME extends RedisCommand
parent::setArguments($processedArguments);
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.arrappend/
@@ -25,4 +25,9 @@ class JSONARRAPPEND extends RedisCommand
{
return 'JSON.ARRAPPEND';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.arrindex/
@@ -25,4 +25,9 @@ class JSONARRINDEX extends RedisCommand
{
return 'JSON.ARRINDEX';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.arrinsert/
@@ -25,4 +25,9 @@ class JSONARRINSERT extends RedisCommand
{
return 'JSON.ARRINSERT';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.arrlen/
@@ -25,4 +25,9 @@ class JSONARRLEN extends RedisCommand
{
return 'JSON.ARRLEN';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.arrpop/
@@ -25,4 +25,9 @@ class JSONARRPOP extends RedisCommand
{
return 'JSON.ARRPOP';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.arrtrim/
@@ -25,4 +25,9 @@ class JSONARRTRIM extends RedisCommand
{
return 'JSON.ARRTRIM';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.clear/
@@ -25,4 +25,9 @@ class JSONCLEAR extends RedisCommand
{
return 'JSON.CLEAR';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.del/
@@ -25,4 +25,9 @@ class JSONDEL extends RedisCommand
{
return 'JSON.DEL';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.forget/
@@ -25,4 +25,9 @@ class JSONFORGET extends RedisCommand
{
return 'JSON.FORGET';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
use Predis\Command\Traits\Json\Indent;
use Predis\Command\Traits\Json\Newline;
use Predis\Command\Traits\Json\Space;
@@ -54,4 +54,9 @@ class JSONGET extends RedisCommand
$this->setIndent($arguments);
$this->filterArguments();
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.merge/
@@ -26,4 +26,9 @@ class JSONMERGE extends RedisCommand
{
return 'JSON.MERGE';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
class JSONMGET extends RedisCommand
{
@@ -33,4 +33,9 @@ class JSONMGET extends RedisCommand
parent::setArguments($unpackedArguments);
}
public function prefixKeys($prefix)
{
$this->applyPrefixSkippingLastArgument($prefix);
}
}
+12 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.mset/
@@ -25,4 +25,15 @@ class JSONMSET extends RedisCommand
{
return 'JSON.MSET';
}
public function prefixKeys($prefix)
{
if ($arguments = $this->getArguments()) {
for ($i = 0, $l = count($arguments); $i < $l; $i += 3) {
$arguments[$i] = $prefix . $arguments[$i];
}
$this->setArguments($arguments);
}
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.numincrby/
@@ -25,4 +25,9 @@ class JSONNUMINCRBY extends RedisCommand
{
return 'JSON.NUMINCRBY';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.objkeys/
@@ -25,4 +25,9 @@ class JSONOBJKEYS extends RedisCommand
{
return 'JSON.OBJKEYS';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.objlen/
@@ -25,4 +25,9 @@ class JSONOBJLEN extends RedisCommand
{
return 'JSON.OBJLEN';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.resp/
@@ -25,4 +25,9 @@ class JSONRESP extends RedisCommand
{
return 'JSON.RESP';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
use Predis\Command\Traits\Json\NxXxArgument;
/**
@@ -38,4 +38,9 @@ class JSONSET extends RedisCommand
$this->setSubcommand($arguments);
$this->filterArguments();
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.strappend/
@@ -25,4 +25,9 @@ class JSONSTRAPPEND extends RedisCommand
{
return 'JSON.STRAPPEND';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.strlen/
@@ -25,4 +25,9 @@ class JSONSTRLEN extends RedisCommand
{
return 'JSON.STRLEN';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.toggle/
@@ -25,4 +25,9 @@ class JSONTOGGLE extends RedisCommand
{
return 'JSON.TOGGLE';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Json;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/json.type/
@@ -25,4 +25,9 @@ class JSONTYPE extends RedisCommand
{
return 'JSON.TYPE';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+11 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
class LMOVE extends RedisCommand
{
@@ -20,4 +20,14 @@ class LMOVE extends RedisCommand
{
return 'LMOVE';
}
public function prefixKeys($prefix)
{
if ($arguments = $this->getArguments()) {
$arguments[0] = $prefix . $arguments[0];
$arguments[1] = $prefix . $arguments[1];
$this->setRawArguments($arguments);
}
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Predis package.
*
* (c) 2009-2020 Daniele Alessandri
* (c) 2021-2025 Till Krüss
*
* 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\PrefixableCommand;
use ValueError;
class MSETEX extends PrefixableCommand
{
/**
* {@inheritDoc}
*/
public function getId()
{
return 'MSETEX';
}
public function setArguments(array $arguments)
{
$processedArguments = [count(array_keys($arguments[0]))];
array_walk($arguments[0], function ($value, $key) use (&$processedArguments) {
array_push($processedArguments, $key, $value);
});
if (isset($arguments[1])) {
if (!in_array(strtoupper($arguments[1]), ['NX', 'XX'])) {
throw new ValueError('Incorrect exist modifier. Should be one of: NX, XX.');
}
$processedArguments[] = strtoupper($arguments[1]);
}
if (isset($arguments[2])) {
if (!isset($arguments[3]) && strtoupper($arguments[2]) !== 'KEEPTTL') {
throw new ValueError('TTL should be specified along with expire resolution parameter');
}
if (!in_array(strtoupper($arguments[2]), ['EX', 'PX', 'EXAT', 'PXAT', 'KEEPTTL'])) {
throw new ValueError('Incorrect expire modifier. Should be one of: EX, PX, EXAT, PXAT, KEEPTTL');
}
if (strtoupper($arguments[2]) !== 'KEEPTTL') {
array_push($processedArguments, strtoupper($arguments[2]), $arguments[3]);
} else {
$processedArguments[] = strtoupper($arguments[2]);
}
}
parent::setArguments($processedArguments);
}
/**
* {@inheritDoc}
*/
public function prefixKeys($prefix)
{
$arguments = $this->getArguments();
$keysCount = $arguments[0];
$currentKeyIndex = 1;
while ($keysCount > 0) {
$arguments[$currentKeyIndex] = $prefix . $arguments[$currentKeyIndex];
$keysCount--;
$currentKeyIndex += 2;
}
parent::setRawArguments($arguments);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/smismember/
@@ -25,4 +25,9 @@ class SMISMEMBER extends RedisCommand
{
return 'SMISMEMBER';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/ft.aggregate/
@@ -50,4 +50,9 @@ class FTAGGREGATE extends RedisCommand
$commandArguments
));
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/ft.aliasadd/
@@ -25,4 +25,9 @@ class FTALIASADD extends RedisCommand
{
return 'FT.ALIASADD';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForAllArguments($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/ft.aliasdel/
@@ -25,4 +25,9 @@ class FTALIASDEL extends RedisCommand
{
return 'FT.ALIASDEL';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/ft.aliasupdate/
@@ -26,4 +26,9 @@ class FTALIASUPDATE extends RedisCommand
{
return 'FT.ALIASUPDATE';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForAllArguments($prefix);
}
}
+6 -1
View File
@@ -13,7 +13,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
class FTALTER extends RedisCommand
{
@@ -39,4 +39,9 @@ class FTALTER extends RedisCommand
$schema
));
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+16
View File
@@ -31,4 +31,20 @@ class FTCONFIG extends RedisCommand
{
return 'FT.CONFIG';
}
public function parseResponse($data)
{
if (!is_array($data) || $data === array_values($data)) {
return $data;
}
// Relay
$result = [];
foreach ($data as $key => $value) {
$group = [$key, $value];
$result[] = $group;
}
return $result;
}
}
+6 -1
View File
@@ -13,7 +13,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Argument\Search\SchemaFields\FieldInterface;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/ft.create/
@@ -44,4 +44,9 @@ class FTCREATE extends RedisCommand
$schema
));
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
class FTCURSOR extends RedisCommand
{
@@ -31,4 +31,9 @@ class FTCURSOR extends RedisCommand
$commandArguments
));
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/ft.dictadd/
@@ -25,4 +25,9 @@ class FTDICTADD extends RedisCommand
{
return 'FT.DICTADD';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/ft.dictdel/
@@ -25,4 +25,9 @@ class FTDICTDEL extends RedisCommand
{
return 'FT.DICTDEL';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}
+6 -1
View File
@@ -12,7 +12,7 @@
namespace Predis\Command\Redis\Search;
use Predis\Command\Command as RedisCommand;
use Predis\Command\PrefixableCommand as RedisCommand;
/**
* @see https://redis.io/commands/ft.dictdump/
@@ -25,4 +25,9 @@ class FTDICTDUMP extends RedisCommand
{
return 'FT.DICTDUMP';
}
public function prefixKeys($prefix)
{
$this->applyPrefixForFirstArgument($prefix);
}
}

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