mirror of
https://github.com/predis/predis.git
synced 2026-08-18 05:31:44 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ebdd22ff61 | |||
| 25b33f50ae | |||
| 3864a7b125 | |||
| 6f4ac2a012 | |||
| 72f6baef26 | |||
| 7bf1f6eb29 | |||
| 067b051900 | |||
| df4a318b80 | |||
| a904a3a273 | |||
| 8b240d15a5 | |||
| 02e2a7e1cc |
@@ -37,7 +37,7 @@ services:
|
||||
- standalone
|
||||
|
||||
redis-cluster:
|
||||
image: ${REDIS_CLUSTER_IMAGE_NAME:-redislabs/client-libs-test:7.4.2}
|
||||
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:7.4.2}
|
||||
container_name: redis-cluster
|
||||
environment:
|
||||
- REDIS_CLUSTER=yes
|
||||
|
||||
+63
-18
@@ -44,8 +44,16 @@ jobs:
|
||||
["6.2"]="6.2.17"
|
||||
)
|
||||
|
||||
# Mapping of redis version to stack version
|
||||
declare -A redis_stack_version_mapping=(
|
||||
["7.4"]="rs-7.4.0-v3"
|
||||
["7.2"]="rs-7.2.0-v15"
|
||||
["6.2"]="rs-6.2.6-v19"
|
||||
)
|
||||
|
||||
if [[ -v redis_clients_version_mapping[${{ matrix.redis }}] ]]; then
|
||||
echo "REDIS_IMAGE_NAME=redislabs/client-libs-test:${redis_clients_version_mapping[${{ matrix.redis }}]}" >> $GITHUB_ENV
|
||||
echo "REDIS_STACK_IMAGE_NAME=redislabs/client-libs-test:${redis_stack_version_mapping[${{ matrix.redis }}]}" >> $GITHUB_ENV
|
||||
echo "DOCKER_SERVICE=redis-clients" >> $GITHUB_ENV
|
||||
|
||||
redis_major_version=$(echo "${{ matrix.redis }}" | grep -oP '^\d+')
|
||||
@@ -53,7 +61,11 @@ jobs:
|
||||
# Some configuration options available since Redis > 7
|
||||
if (( redis_major_version < 7 )); then
|
||||
echo "REDIS_EXTRA_ARGS="--tls-auth-clients optional --save ''"" >> $GITHUB_ENV
|
||||
else
|
||||
# Since 8.0 modules are bundled with core
|
||||
echo "REDIS_STACK_SERVER_PORT=6379" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
else
|
||||
echo "REDIS_IMAGE_NAME=redis:${{ matrix.redis }}" >> $GITHUB_ENV
|
||||
echo "DOCKER_SERVICE=redis-official" >> $GITHUB_ENV
|
||||
@@ -69,15 +81,17 @@ jobs:
|
||||
services: ${{ env.DOCKER_SERVICE }}
|
||||
|
||||
- name: Start Redis stack image
|
||||
id: stack_infra
|
||||
uses: hoverkraft-tech/compose-action@v2.0.1
|
||||
if: ${{ matrix.redis == '7.4' }}
|
||||
if: ${{ matrix.redis >= '6.2' && matrix.redis < '8.0' }}
|
||||
with:
|
||||
compose-file: .github/docker-compose.yml
|
||||
services: redis-stack
|
||||
|
||||
- name: Start Redis cluster image
|
||||
id: cluster_infra
|
||||
uses: hoverkraft-tech/compose-action@v2.0.1
|
||||
if: ${{ matrix.redis == '7.4' }}
|
||||
if: ${{ matrix.redis > '4.0' }}
|
||||
with:
|
||||
compose-file: .github/docker-compose.yml
|
||||
services: redis-cluster
|
||||
@@ -87,7 +101,7 @@ jobs:
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: relay
|
||||
coverage: ${{ (matrix.php == '8.1' && matrix.redis == '7.4') && 'xdebug' || 'none' }}
|
||||
coverage: ${{ (matrix.php == '8.4' && matrix.redis == '7.4') && 'xdebug' || 'none' }}
|
||||
|
||||
- name: Install Composer dependencies
|
||||
uses: ramsey/composer-install@v2
|
||||
@@ -96,43 +110,74 @@ jobs:
|
||||
composer-options: ${{ matrix.php == '8.0' && '--ignore-platform-reqs' || '' }}
|
||||
|
||||
- name: Run tests
|
||||
if: ${{ matrix.php != '8.1' || matrix.redis != '7.4' }}
|
||||
if: ${{ matrix.php != '8.4' || matrix.redis != '7.4' }}
|
||||
run: vendor/bin/phpunit
|
||||
|
||||
- name: Run tests with coverage
|
||||
if: ${{ matrix.php == '8.1' && matrix.redis == '7.4' }}
|
||||
run: vendor/bin/phpunit --coverage-clover build/logs/clover-default.xml --coverage-filter ./src
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
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 >= '6.2' }}
|
||||
run: vendor/bin/phpunit -c phpunit.relay.xml
|
||||
|
||||
- name: Run tests using Relay with coverage
|
||||
if: ${{ matrix.php == '8.1' && matrix.redis == '7.4' }}
|
||||
run: vendor/bin/phpunit -c phpunit.relay.xml --coverage-clover build/logs/clover-relay.xml --coverage-filter ./src
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
run: vendor/bin/phpunit -c phpunit.relay.xml --coverage-php build/cov/coverage-relay.cov --coverage-filter ./src
|
||||
|
||||
- name: Run stack tests
|
||||
if: ${{ matrix.redis == '7.4' }}
|
||||
if: ${{ (matrix.php != '8.4' || matrix.redis != '7.4') && matrix.redis >= '6.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' }}
|
||||
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.redis == '7.4' }}
|
||||
if: ${{ matrix.php != '8.4' && matrix.redis == '7.4' }}
|
||||
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' }}
|
||||
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.redis == '7.4' }}
|
||||
if: ${{ (matrix.php != '8.4' || matrix.redis != '7.4') && 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' }}
|
||||
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.redis == '7.4' }}
|
||||
if: ${{ matrix.php != '8.4' && matrix.redis == '7.4' }}
|
||||
run: |
|
||||
vendor/bin/phpunit -c phpunit.relay.xml --group cluster
|
||||
|
||||
- name: Merge coverage reports
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
run: php vendor/bin/phpcov merge --clover build/logs/clover.xml build/cov
|
||||
|
||||
- name: Send coverage to Coveralls
|
||||
env:
|
||||
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
if: ${{ env.COVERALLS_REPO_TOKEN && matrix.php == '8.1' && matrix.redis == '7.4' }}
|
||||
run: |
|
||||
wget "https://github.com/php-coveralls/php-coveralls/releases/download/v2.5.3/php-coveralls.phar"
|
||||
php ./php-coveralls.phar -v
|
||||
uses: coverallsapp/github-action@v2
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
parallel: true
|
||||
|
||||
finish:
|
||||
name: Finish Coverall
|
||||
needs: predis
|
||||
if: ${{ always() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Coveralls Finished
|
||||
uses: coverallsapp/github-action@v2
|
||||
with:
|
||||
parallel-finished: true
|
||||
|
||||
+15
-1
@@ -2,7 +2,21 @@
|
||||
|
||||
## Unreleased
|
||||
### Fixed
|
||||
- Fixed PHP 8.4 compatibility with `stream_context_set_option()`
|
||||
- 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)
|
||||
- Fix eval_ro cluster support (#1449)
|
||||
|
||||
### Changed
|
||||
- Use parallel on PHP-CS-Fixer (#1489)
|
||||
- Update watch command accepting string and string[] (#1476)
|
||||
- Optimize redis cluster slotmap with compact slot range object (#1493)
|
||||
|
||||
### Added
|
||||
- Added testing with 8.0 (#1510)
|
||||
- Added test coverage for compatibility with Redis 8.0 (#1513)
|
||||
- Added missing FT._LIST and BITFIELD_RO commands (#1521)
|
||||
- Added missing types for commands API (#1522)
|
||||
|
||||
## v2.3.0 (2024-11-21)
|
||||
### Added
|
||||
|
||||
@@ -81,65 +81,17 @@ An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which m
|
||||
|
||||
## When should I use Relay? ##
|
||||
|
||||
If you care about performance, __always__. [Relay](https://github.com/cachewerk/relay) is free to use.
|
||||
If you care about performance, __always__. [Relay][relay] is free to use.
|
||||
|
||||
## When should I use PhpRedis? ###
|
||||
|
||||
Predis is fast enough when Redis is located on the same machine as PHP, more on that later.
|
||||
Predis is fast enough when Redis is located on the same machine as PHP.
|
||||
|
||||
[PhpRedis](https://github.com/phpredis/phpredis) (and Relay) perform significantly better when
|
||||
network I/O is involved, due to their ability to compress data by ~75%. Fewer bytes and received
|
||||
sent over the network [means faster operations](https://akalongman.medium.com/phpredis-vs-predis-comparison-on-real-production-data-a819b48cbadb),
|
||||
and potentially cost savings when network traffic isn't free (e.g. AWS ElastiCache Inter-AZ transfer costs).
|
||||
[PhpRedis][phpredis] and [Relay][relay] perform significantly better when network I/O is involved,
|
||||
due to its ability to compress data by ~75%. Fewer bytes and received sent over the network
|
||||
[means faster operations][performance], and potentially cost savings when network traffic isn't
|
||||
free (e.g. AWS ElastiCache Inter-AZ transfer costs).
|
||||
|
||||
## Predis is a pure-PHP implementation: it can not be fast enough! ##
|
||||
|
||||
It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you a
|
||||
couple of easy numbers with a simple test that uses a single client and is executed by PHP 5.5.6
|
||||
against a local instance of Redis 2.8 that runs under Ubuntu 13.10 on a Intel Q6600:
|
||||
|
||||
```
|
||||
21000 SET/sec using 12 bytes for both key and value.
|
||||
21000 GET/sec while retrieving the very same values.
|
||||
0.130 seconds to fetch 30000 keys using _KEYS *_.
|
||||
```
|
||||
|
||||
How does it compare with [__PhpRedis__](http://github.com/phpredis/phpredis), a nice C extension
|
||||
providing an efficient client for Redis?
|
||||
|
||||
```
|
||||
30100 SET/sec using 12 bytes for both key and value
|
||||
29400 GET/sec while retrieving the very same values
|
||||
0.035 seconds to fetch 30000 keys using "KEYS *"".
|
||||
```
|
||||
|
||||
Wow __PhpRedis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so
|
||||
lower numbers are quite expected but there is a fundamental flaw in them: is this really how you are
|
||||
going to use Redis in your application? Are you really going to send thousands of commands using a
|
||||
for-loop on each page request using a single client instance? If so... well I guess you are probably
|
||||
doing something wrong. Also, if you need to `SET` or `GET` multiple keys you should definitely use
|
||||
commands such as `MSET` and `MGET`. You can also use pipelining to get more performances when this
|
||||
technique can be used.
|
||||
|
||||
There is one more thing: we have tested the overhead of Predis by connecting on a localhost instance
|
||||
of Redis but how these numbers change when we hit the physical network by connecting to remote Redis
|
||||
instances?
|
||||
|
||||
```
|
||||
Using Predis:
|
||||
3200 SET/sec using 12 bytes for both key and value
|
||||
3200 GET/sec while retrieving the very same values
|
||||
0.132 seconds to fetch 30000 keys using "KEYS *".
|
||||
|
||||
Using PhpRedis:
|
||||
3500 SET/sec using 12 bytes for both key and value
|
||||
3500 GET/sec while retrieving the very same values
|
||||
0.045 seconds to fetch 30000 keys using "KEYS *".
|
||||
```
|
||||
|
||||
There you go, you get almost the same average numbers and the reason is simple: network latency is a
|
||||
real performance killer and you cannot do (almost) anything about that. As a disclaimer, remember
|
||||
that we are measuring the overhead of client libraries implementations and the effects of network
|
||||
round-trip times, so we are not really measuring how fast Redis is. Redis shines best with thousands
|
||||
of concurrent clients doing requests! Also, actual performances should be measured according to how
|
||||
your application will use Redis.
|
||||
[phpredis]: https://github.com/phpredis/phpredis
|
||||
[relay]: [https://github.com/phpredis/phpredis](https://github.com/cachewerk/relay)
|
||||
[performance]: https://akalongman.medium.com/phpredis-vs-predis-comparison-on-real-production-data-a819b48cbadb
|
||||
|
||||
+2
-1
@@ -27,7 +27,8 @@
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.3",
|
||||
"phpstan/phpstan": "^1.9",
|
||||
"phpunit/phpunit": "^8.0 || ^9.4"
|
||||
"phpunit/phpunit": "^8.0 || ^9.4",
|
||||
"phpunit/phpcov": "^6.0 || ^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
|
||||
|
||||
+1
-1
@@ -38,9 +38,9 @@
|
||||
<php>
|
||||
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
|
||||
<const name="REDIS_SERVER_PORT" value="6379" />
|
||||
<const name="REDIS_STACK_SERVER_PORT" value="6479" />
|
||||
<const name="REDIS_SERVER_DBNUM" value="0" />
|
||||
<env name="USE_RELAY" value="true" />
|
||||
<env name="REDIS_STACK_SERVER_PORT" value="6479" />
|
||||
|
||||
<!-- Redis Cluster -->
|
||||
<!-- Only master nodes endpoints included -->
|
||||
|
||||
+1
-1
@@ -50,9 +50,9 @@
|
||||
<php>
|
||||
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
|
||||
<const name="REDIS_SERVER_PORT" value="6379" />
|
||||
<const name="REDIS_STACK_SERVER_PORT" value="6479" />
|
||||
<const name="REDIS_SERVER_DBNUM" value="0" />
|
||||
<env name="USE_RELAY" value="false" />
|
||||
<env name="REDIS_STACK_SERVER_PORT" value="6479" />
|
||||
|
||||
<!-- Redis Cluster -->
|
||||
<!-- Only master nodes endpoints included -->
|
||||
|
||||
@@ -57,7 +57,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this expiretime(string $key)
|
||||
* @method $this keys($pattern)
|
||||
* @method $this move($key, $db)
|
||||
* @method $this object($subcommand, $key)
|
||||
* @method $this object(string $subcommand, string $key)
|
||||
* @method $this persist($key)
|
||||
* @method $this pexpire($key, $milliseconds)
|
||||
* @method $this pexpireat($key, $timestamp)
|
||||
@@ -65,36 +65,37 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this randomkey()
|
||||
* @method $this rename($key, $target)
|
||||
* @method $this renamenx($key, $target)
|
||||
* @method $this scan($cursor, ?array $options = null)
|
||||
* @method $this scan(int|string $cursor, ?array $options = null)
|
||||
* @method $this sort($key, ?array $options = null)
|
||||
* @method $this sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false)
|
||||
* @method $this ttl($key)
|
||||
* @method $this type($key)
|
||||
* @method $this append($key, $value)
|
||||
* @method $this bfadd(string $key, $item)
|
||||
* @method $this bfexists(string $key, $item)
|
||||
* @method $this bfadd(string $key, string $item)
|
||||
* @method $this bfexists(string $key, string $item)
|
||||
* @method $this bfinfo(string $key, string $modifier = '')
|
||||
* @method $this bfinsert(string $key, int $capacity = -1, float $error = -1, int $expansion = -1, bool $noCreate = false, bool $nonScaling = false, string ...$item)
|
||||
* @method $this bfloadchunk(string $key, int $iterator, $data)
|
||||
* @method $this bfmadd(string $key, ...$item)
|
||||
* @method $this bfmexists(string $key, ...$item)
|
||||
* @method $this bfmadd(string $key, string ...$item)
|
||||
* @method $this bfmexists(string $key, string ...$item)
|
||||
* @method $this bfreserve(string $key, float $errorRate, int $capacity, int $expansion = -1, bool $nonScaling = false)
|
||||
* @method $this bfscandump(string $key, int $iterator)
|
||||
* @method $this bitcount(string $key, $start = null, $end = null, string $index = 'byte')
|
||||
* @method $this bitop($operation, $destkey, $key)
|
||||
* @method $this bitfield($key, $subcommand, ...$subcommandArg)
|
||||
* @method $this bitpos($key, $bit, $start = null, $end = null, string $index = 'byte')
|
||||
* @method $this bitop(string $operation, string $destkey, string $key)
|
||||
* @method $this bitfield($key, string $subcommand, ...$subcommandArg)
|
||||
* @method $this bitfield_ro(string $key, ?array $encodingOffsetMap = null)
|
||||
* @method $this bitpos(string $key, int $bit, ?int $start = null, ?int $end = null, string $index = 'byte')
|
||||
* @method $this blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
|
||||
* @method $this bzpopmax(array $keys, int $timeout)
|
||||
* @method $this bzpopmin(array $keys, int $timeout)
|
||||
* @method $this bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
|
||||
* @method $this cfadd(string $key, $item)
|
||||
* @method $this cfaddnx(string $key, $item)
|
||||
* @method $this cfcount(string $key, $item)
|
||||
* @method $this cfdel(string $key, $item)
|
||||
* @method $this cfexists(string $key, $item)
|
||||
* @method $this cfadd(string $key, string $item)
|
||||
* @method $this cfaddnx(string $key, string $item)
|
||||
* @method $this cfcount(string $key, string $item)
|
||||
* @method $this cfdel(string $key, string $item)
|
||||
* @method $this cfexists(string $key, string $item)
|
||||
* @method $this cfloadchunk(string $key, int $iterator, $data)
|
||||
* @method $this cfmexists(string $key, ...$item)
|
||||
* @method $this cfmexists(string $key, string ...$item)
|
||||
* @method $this cfinfo(string $key)
|
||||
* @method $this cfinsert(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
|
||||
* @method $this cfinsertnx(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
|
||||
@@ -111,14 +112,15 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this failover(?To $to = null, bool $abort = false, int $timeout = -1)
|
||||
* @method $this fcall(string $function, array $keys, ...$args)
|
||||
* @method $this fcall_ro(string $function, array $keys, ...$args)
|
||||
* @method $this ft_list()
|
||||
* @method $this ftaggregate(string $index, string $query, ?AggregateArguments $arguments = null)
|
||||
* @method $this ftaliasadd(string $alias, string $index)
|
||||
* @method $this ftaliasdel(string $alias)
|
||||
* @method $this ftaliasupdate(string $alias, string $index)
|
||||
* @method $this ftalter(string $index, FieldInterface[] $schema, ?AlterArguments $arguments = null)
|
||||
* @method $this ftcreate(string $index, FieldInterface[] $schema, ?CreateArguments $arguments = null)
|
||||
* @method $this ftdictadd(string $dict, ...$term)
|
||||
* @method $this ftdictdel(string $dict, ...$term)
|
||||
* @method $this ftdictadd(string $dict, string ...$term)
|
||||
* @method $this ftdictdel(string $dict, string ...$term)
|
||||
* @method $this ftdictdump(string $dict)
|
||||
* @method $this ftdropindex(string $index, ?DropArguments $arguments = null)
|
||||
* @method $this ftexplain(string $index, string $query, ?ExplainArguments $arguments = null)
|
||||
@@ -134,23 +136,23 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this ftsynupdate(string $index, string $synonymGroupId, ?SynUpdateArguments $arguments = null, string ...$terms)
|
||||
* @method $this fttagvals(string $index, string $fieldName)
|
||||
* @method $this get($key)
|
||||
* @method $this getbit($key, $offset)
|
||||
* @method $this getex(string $key, $modifier = '', $value = false)
|
||||
* @method $this getrange($key, $start, $end)
|
||||
* @method $this getbit(string $key, int $offset)
|
||||
* @method $this getex(string $key, string $modifier = '', string|int $value = false)
|
||||
* @method $this getrange(string $key, int $start, int $end)
|
||||
* @method $this getdel(string $key)
|
||||
* @method $this getset($key, $value)
|
||||
* @method $this getset(string $key, string|int|float $value)
|
||||
* @method $this incr($key)
|
||||
* @method $this incrby($key, $increment)
|
||||
* @method $this incrbyfloat($key, $increment)
|
||||
* @method $this mget(array $keys)
|
||||
* @method $this mset(array $dictionary)
|
||||
* @method $this msetnx(array $dictionary)
|
||||
* @method $this psetex($key, $milliseconds, $value)
|
||||
* @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method $this setbit($key, $offset, $value)
|
||||
* @method $this setex($key, $seconds, $value)
|
||||
* @method $this setnx($key, $value)
|
||||
* @method $this setrange($key, $offset, $value)
|
||||
* @method $this psetex(string $key, int $milliseconds, string|int|float $value)
|
||||
* @method $this set(string $key, string|int|float $value, ?string $expireResolution = null, ?int $expireTTL = null, ?string $flag = null)
|
||||
* @method $this setbit(string $key, int $offset, string|int|float $value)
|
||||
* @method $this setex(string $key, int $seconds, string|int|float $value)
|
||||
* @method $this setnx(string $key, string|int|float $value)
|
||||
* @method $this setrange(string $key, int $offset, string|int|float $value)
|
||||
* @method $this strlen($key)
|
||||
* @method $this hdel($key, array $fields)
|
||||
* @method $this hexists($key, $field)
|
||||
@@ -170,14 +172,14 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this hmget($key, array $fields)
|
||||
* @method $this hmset($key, array $dictionary)
|
||||
* @method $this hrandfield(string $key, int $count = 1, bool $withValues = false)
|
||||
* @method $this hscan($key, $cursor, ?array $options = null)
|
||||
* @method $this hscan(string $key, int|string $cursor, ?array $options = null)
|
||||
* @method $this hset($key, $field, $value)
|
||||
* @method $this hsetnx($key, $field, $value)
|
||||
* @method $this httl(string $key, array $fields)
|
||||
* @method $this hpttl(string $key, array $fields)
|
||||
* @method $this hvals($key)
|
||||
* @method $this hstrlen($key, $field)
|
||||
* @method $this jsonarrappend(string $key, string $path = '$', ...$value)
|
||||
* @method $this jsonarrappend(string $key, string $path = '$', string ...$value)
|
||||
* @method $this jsonarrindex(string $key, string $path, string $value, int $start = 0, int $stop = 0)
|
||||
* @method $this jsonarrinsert(string $key, string $path, int $index, string ...$value)
|
||||
* @method $this jsonarrlen(string $key, string $path = '$')
|
||||
@@ -205,7 +207,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this brpoplpush($source, $destination, $timeout)
|
||||
* @method $this lcs(string $key1, string $key2, bool $len = false, bool $idx = false, int $minMatchLen = 0, bool $withMatchLen = false)
|
||||
* @method $this lindex($key, $index)
|
||||
* @method $this linsert($key, $whence, $pivot, $value)
|
||||
* @method $this linsert(string $key, string $whence, string|int|float $pivot, string|int|float $value)
|
||||
* @method $this llen($key)
|
||||
* @method $this lmove(string $source, string $destination, string $where, string $to)
|
||||
* @method $this lmpop(array $keys, string $modifier = 'left', int $count = 1)
|
||||
@@ -251,11 +253,11 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this tdigestreset(string $key)
|
||||
* @method $this tdigestrevrank(string $key, float ...$value)
|
||||
* @method $this tdigesttrimmed_mean(string $key, float $lowCutQuantile, float $highCutQuantile)
|
||||
* @method $this topkadd(string $key, ...$items)
|
||||
* @method $this topkincrby(string $key, ...$itemIncrement)
|
||||
* @method $this topkadd(string $key, string ...$items)
|
||||
* @method $this topkincrby(string $key, string|int|float ...$itemIncrement)
|
||||
* @method $this topkinfo(string $key)
|
||||
* @method $this topklist(string $key, bool $withCount = false)
|
||||
* @method $this topkquery(string $key, ...$items)
|
||||
* @method $this topkquery(string $key, string ...$items)
|
||||
* @method $this topkreserve(string $key, int $topK, int $width = 8, int $depth = 7, float $decay = 0.9)
|
||||
* @method $this tsadd(string $key, int $timestamp, float $value, ?AddArguments $arguments = null)
|
||||
* @method $this tsalter(string $key, ?TSAlterArguments $arguments = null)
|
||||
@@ -269,11 +271,11 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this tsinfo(string $key, ?InfoArguments $arguments = null)
|
||||
* @method $this tsmadd(mixed ...$keyTimestampValue)
|
||||
* @method $this tsmget(MGetArguments $arguments, string ...$filterExpression)
|
||||
* @method $this tsmrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
|
||||
* @method $this tsmrevrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
|
||||
* @method $this tsmrange(string|int|float $fromTimestamp, string|int|float $toTimestamp, MRangeArguments $arguments)
|
||||
* @method $this tsmrevrange(string|int|float $fromTimestamp, string|int|float $toTimestamp, MRangeArguments $arguments)
|
||||
* @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 tsrange(string $key, string|int|float $fromTimestamp, string|int|float $toTimestamp, ?RangeArguments $arguments = null)
|
||||
* @method $this tsrevrange(string $key, string|int|float $fromTimestamp, string|int|float $toTimestamp, ?RangeArguments $arguments = null)
|
||||
* @method $this zadd($key, array $membersAndScoresDictionary)
|
||||
* @method $this zcard($key)
|
||||
* @method $this zcount($key, $min, $max)
|
||||
@@ -308,8 +310,8 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this pfadd($key, array $elements)
|
||||
* @method $this pfmerge($destinationKey, array|string $sourceKeys)
|
||||
* @method $this pfcount(array|string $keys)
|
||||
* @method $this pubsub($subcommand, $argument)
|
||||
* @method $this publish($channel, $message)
|
||||
* @method $this pubsub(string $subcommand, string|int|float $argument)
|
||||
* @method $this publish(string $channel, string|int|float $message)
|
||||
* @method $this discard()
|
||||
* @method $this exec()
|
||||
* @method $this multi()
|
||||
@@ -320,7 +322,7 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this eval_ro(string $script, array $keys, ...$argument)
|
||||
* @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method $this evalsha_ro(string $sha1, array $keys, ...$argument)
|
||||
* @method $this script($subcommand, $argument = null)
|
||||
* @method $this script(string $subcommand, $argument = null)
|
||||
* @method $this shutdown(?bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false)
|
||||
* @method $this auth($password)
|
||||
* @method $this echo($message)
|
||||
@@ -328,24 +330,24 @@ use Predis\Command\Redis\Container\Search\FTCURSOR;
|
||||
* @method $this select($database)
|
||||
* @method $this bgrewriteaof()
|
||||
* @method $this bgsave()
|
||||
* @method $this client($subcommand, $argument = null)
|
||||
* @method $this config($subcommand, $argument = null)
|
||||
* @method $this client(string $subcommand, $argument = null)
|
||||
* @method $this config(string $subcommand, $argument = null)
|
||||
* @method $this dbsize()
|
||||
* @method $this flushall()
|
||||
* @method $this flushdb()
|
||||
* @method $this info($section = null)
|
||||
* @method $this info(?string $section = null)
|
||||
* @method $this lastsave()
|
||||
* @method $this save()
|
||||
* @method $this slaveof($host, $port)
|
||||
* @method $this slowlog($subcommand, $argument = null)
|
||||
* @method $this slowlog(string $subcommand, $argument = null)
|
||||
* @method $this time()
|
||||
* @method $this command()
|
||||
* @method $this geoadd($key, $longitude, $latitude, $member)
|
||||
* @method $this geoadd(string $key, string|float $longitude, string|float $latitude, string $member)
|
||||
* @method $this geohash($key, array $members)
|
||||
* @method $this geopos($key, array $members)
|
||||
* @method $this geodist($key, $member1, $member2, $unit = null)
|
||||
* @method $this georadius($key, $longitude, $latitude, $radius, $unit, ?array $options = null)
|
||||
* @method $this georadiusbymember($key, $member, $radius, $unit, ?array $options = null)
|
||||
* @method $this geodist(string $key, string $member1, string $member2, string $unit = null)
|
||||
* @method $this georadius(string $key, string|float $longitude, string|float $latitude, int $radius, string $unit, ?array $options = null)
|
||||
* @method $this georadiusbymember(string $key, string $member, int $radius, string $unit, ?array $options = null)
|
||||
* @method $this geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
|
||||
* @method $this geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
|
||||
*
|
||||
|
||||
+50
-48
@@ -66,7 +66,7 @@ use Predis\Response\Status;
|
||||
* @method int expiretime(string $key)
|
||||
* @method array keys(string $pattern)
|
||||
* @method int move(string $key, int $db)
|
||||
* @method mixed object($subcommand, string $key)
|
||||
* @method mixed object(string $subcommand, string $key)
|
||||
* @method int persist(string $key)
|
||||
* @method int pexpire(string $key, int $milliseconds)
|
||||
* @method int pexpireat(string $key, int $timestamp)
|
||||
@@ -74,36 +74,37 @@ use Predis\Response\Status;
|
||||
* @method string|null randomkey()
|
||||
* @method mixed rename(string $key, string $target)
|
||||
* @method int renamenx(string $key, string $target)
|
||||
* @method array scan($cursor, ?array $options = null)
|
||||
* @method array scan(int|string $cursor, ?array $options = null)
|
||||
* @method array sort(string $key, ?array $options = null)
|
||||
* @method array sort_ro(string $key, ?string $byPattern = null, ?LimitOffsetCount $limit = null, array $getPatterns = [], ?string $sorting = null, bool $alpha = false)
|
||||
* @method int ttl(string $key)
|
||||
* @method mixed type(string $key)
|
||||
* @method int append(string $key, $value)
|
||||
* @method int bfadd(string $key, $item)
|
||||
* @method int bfexists(string $key, $item)
|
||||
* @method int bfadd(string $key, string $item)
|
||||
* @method int bfexists(string $key, string $item)
|
||||
* @method array bfinfo(string $key, string $modifier = '')
|
||||
* @method array bfinsert(string $key, int $capacity = -1, float $error = -1, int $expansion = -1, bool $noCreate = false, bool $nonScaling = false, string ...$item)
|
||||
* @method Status bfloadchunk(string $key, int $iterator, $data)
|
||||
* @method array bfmadd(string $key, ...$item)
|
||||
* @method array bfmexists(string $key, ...$item)
|
||||
* @method array bfmadd(string $key, string ...$item)
|
||||
* @method array bfmexists(string $key, string ...$item)
|
||||
* @method Status bfreserve(string $key, float $errorRate, int $capacity, int $expansion = -1, bool $nonScaling = false)
|
||||
* @method array bfscandump(string $key, int $iterator)
|
||||
* @method int bitcount(string $key, $start = null, $end = null, string $index = 'byte')
|
||||
* @method int bitop($operation, $destkey, $key)
|
||||
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
|
||||
* @method int bitpos(string $key, $bit, $start = null, $end = null, string $index = 'byte')
|
||||
* @method int bitop(string $operation, string $destkey, string $key)
|
||||
* @method array|null bitfield(string $key, string $subcommand, ...$subcommandArg)
|
||||
* @method array|null bitfield_ro(string $key, ?array $encodingOffsetMap = null)
|
||||
* @method int bitpos(string $key, int $bit, ?int $start = null, ?int $end = null, string $index = 'byte')
|
||||
* @method array blmpop(int $timeout, array $keys, string $modifier = 'left', int $count = 1)
|
||||
* @method array bzpopmax(array $keys, int $timeout)
|
||||
* @method array bzpopmin(array $keys, int $timeout)
|
||||
* @method array bzmpop(int $timeout, array $keys, string $modifier = 'min', int $count = 1)
|
||||
* @method int cfadd(string $key, $item)
|
||||
* @method int cfaddnx(string $key, $item)
|
||||
* @method int cfcount(string $key, $item)
|
||||
* @method int cfdel(string $key, $item)
|
||||
* @method int cfexists(string $key, $item)
|
||||
* @method int cfadd(string $key, string $item)
|
||||
* @method int cfaddnx(string $key, string $item)
|
||||
* @method int cfcount(string $key, string $item)
|
||||
* @method int cfdel(string $key, string $item)
|
||||
* @method int cfexists(string $key, string $item)
|
||||
* @method Status cfloadchunk(string $key, int $iterator, $data)
|
||||
* @method int cfmexists(string $key, ...$item)
|
||||
* @method int cfmexists(string $key, string ...$item)
|
||||
* @method array cfinfo(string $key)
|
||||
* @method array cfinsert(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
|
||||
* @method array cfinsertnx(string $key, int $capacity = -1, bool $noCreate = false, string ...$item)
|
||||
@@ -120,14 +121,15 @@ use Predis\Response\Status;
|
||||
* @method Status failover(?To $to = null, bool $abort = false, int $timeout = -1)
|
||||
* @method mixed fcall(string $function, array $keys, ...$args)
|
||||
* @method mixed fcall_ro(string $function, array $keys, ...$args)
|
||||
* @method array ft_list()
|
||||
* @method array ftaggregate(string $index, string $query, ?AggregateArguments $arguments = null)
|
||||
* @method Status ftaliasadd(string $alias, string $index)
|
||||
* @method Status ftaliasdel(string $alias)
|
||||
* @method Status ftaliasupdate(string $alias, string $index)
|
||||
* @method Status ftalter(string $index, FieldInterface[] $schema, ?AlterArguments $arguments = null)
|
||||
* @method Status ftcreate(string $index, FieldInterface[] $schema, ?CreateArguments $arguments = null)
|
||||
* @method int ftdictadd(string $dict, ...$term)
|
||||
* @method int ftdictdel(string $dict, ...$term)
|
||||
* @method int ftdictadd(string $dict, string ...$term)
|
||||
* @method int ftdictdel(string $dict, string ...$term)
|
||||
* @method array ftdictdump(string $dict)
|
||||
* @method Status ftdropindex(string $index, ?DropArguments $arguments = null)
|
||||
* @method string ftexplain(string $index, string $query, ?ExplainArguments $arguments = null)
|
||||
@@ -143,23 +145,23 @@ use Predis\Response\Status;
|
||||
* @method Status ftsynupdate(string $index, string $synonymGroupId, ?SynUpdateArguments $arguments = null, string ...$terms)
|
||||
* @method array fttagvals(string $index, string $fieldName)
|
||||
* @method string|null get(string $key)
|
||||
* @method int getbit(string $key, $offset)
|
||||
* @method int|null getex(string $key, $modifier = '', $value = false)
|
||||
* @method string getrange(string $key, $start, $end)
|
||||
* @method int getbit(string $key, int $offset)
|
||||
* @method int|null getex(string $key, string $modifier = '', int|string $value = false)
|
||||
* @method string getrange(string $key, int $start, int $end)
|
||||
* @method string getdel(string $key)
|
||||
* @method string|null getset(string $key, $value)
|
||||
* @method string|null getset(string $key, string|int|float $value)
|
||||
* @method int incr(string $key)
|
||||
* @method int incrby(string $key, int $increment)
|
||||
* @method string incrbyfloat(string $key, int|float $increment)
|
||||
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method Status psetex(string $key, $milliseconds, $value)
|
||||
* @method Status|null set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit(string $key, $offset, $value)
|
||||
* @method Status setex(string $key, $seconds, $value)
|
||||
* @method int setnx(string $key, $value)
|
||||
* @method int setrange(string $key, $offset, $value)
|
||||
* @method Status psetex(string $key, int $milliseconds, string|int|float $value)
|
||||
* @method Status|null set(string $key, string|int|float $value, ?string $expireResolution = null, ?int $expireTTL = null, ?string $flag = null)
|
||||
* @method int setbit(string $key, int $offset, string|int|float $value)
|
||||
* @method Status setex(string $key, int $seconds, string|int|float $value)
|
||||
* @method int setnx(string $key, string|int|float $value)
|
||||
* @method int setrange(string $key, int $offset, string|int|float $value)
|
||||
* @method int strlen(string $key)
|
||||
* @method int hdel(string $key, array $fields)
|
||||
* @method int hexists(string $key, string $field)
|
||||
@@ -179,14 +181,14 @@ use Predis\Response\Status;
|
||||
* @method array hmget(string $key, array $fields)
|
||||
* @method mixed hmset(string $key, array $dictionary)
|
||||
* @method array hrandfield(string $key, int $count = 1, bool $withValues = false)
|
||||
* @method array hscan(string $key, $cursor, ?array $options = null)
|
||||
* @method array hscan(string $key, int|string $cursor, ?array $options = null)
|
||||
* @method int hset(string $key, string $field, string $value)
|
||||
* @method int hsetnx(string $key, string $field, string $value)
|
||||
* @method array|null httl(string $key, array $fields)
|
||||
* @method array|null hpttl(string $key, array $fields)
|
||||
* @method array hvals(string $key)
|
||||
* @method int hstrlen(string $key, string $field)
|
||||
* @method array jsonarrappend(string $key, string $path = '$', ...$value)
|
||||
* @method array jsonarrappend(string $key, string $path = '$', string ...$value)
|
||||
* @method array jsonarrindex(string $key, string $path, string $value, int $start = 0, int $stop = 0)
|
||||
* @method array jsonarrinsert(string $key, string $path, int $index, string ...$value)
|
||||
* @method array jsonarrlen(string $key, string $path = '$')
|
||||
@@ -214,7 +216,7 @@ use Predis\Response\Status;
|
||||
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
|
||||
* @method mixed lcs(string $key1, string $key2, bool $len = false, bool $idx = false, int $minMatchLen = 0, bool $withMatchLen = false)
|
||||
* @method string|null lindex(string $key, int $index)
|
||||
* @method int linsert(string $key, $whence, $pivot, $value)
|
||||
* @method int linsert(string $key, string $whence, string|int|float $pivot, string|int|float $value)
|
||||
* @method int llen(string $key)
|
||||
* @method string lmove(string $source, string $destination, string $where, string $to)
|
||||
* @method array|null lmpop(array $keys, string $modifier = 'left', int $count = 1)
|
||||
@@ -261,11 +263,11 @@ use Predis\Response\Status;
|
||||
* @method Status tdigestreset(string $key)
|
||||
* @method array tdigestrevrank(string $key, float ...$value)
|
||||
* @method string tdigesttrimmed_mean(string $key, float $lowCutQuantile, float $highCutQuantile)
|
||||
* @method array topkadd(string $key, ...$items)
|
||||
* @method array topkincrby(string $key, ...$itemIncrement)
|
||||
* @method array topkadd(string $key, string ...$items)
|
||||
* @method array topkincrby(string $key, string|int|float ...$itemIncrement)
|
||||
* @method array topkinfo(string $key)
|
||||
* @method array topklist(string $key, bool $withCount = false)
|
||||
* @method array topkquery(string $key, ...$items)
|
||||
* @method array topkquery(string $key, string|int|float ...$items)
|
||||
* @method Status topkreserve(string $key, int $topK, int $width = 8, int $depth = 7, float $decay = 0.9)
|
||||
* @method int tsadd(string $key, int $timestamp, float $value, ?AddArguments $arguments = null)
|
||||
* @method Status tsalter(string $key, ?TSAlterArguments $arguments = null)
|
||||
@@ -279,11 +281,11 @@ use Predis\Response\Status;
|
||||
* @method array tsinfo(string $key, ?InfoArguments $arguments = null)
|
||||
* @method array tsmadd(mixed ...$keyTimestampValue)
|
||||
* @method array tsmget(MGetArguments $arguments, string ...$filterExpression)
|
||||
* @method array tsmrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
|
||||
* @method array tsmrevrange($fromTimestamp, $toTimestamp, MRangeArguments $arguments)
|
||||
* @method array tsmrange(string|int|float $fromTimestamp, string|int|float $toTimestamp, MRangeArguments $arguments)
|
||||
* @method array tsmrevrange(string|int|float $fromTimestamp, string|int|float $toTimestamp, MRangeArguments $arguments)
|
||||
* @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 array tsrange(string $key, string|int|float $fromTimestamp, string|int|float $toTimestamp, ?RangeArguments $arguments = null)
|
||||
* @method array tsrevrange(string $key, string|int|float $fromTimestamp, string|int|float $toTimestamp, ?RangeArguments $arguments = null)
|
||||
* @method string xadd(string $key, array $dictionary, string $id = '*', ?array $options = null)
|
||||
* @method int xdel(string $key, string ...$id)
|
||||
* @method int xlen(string $key)
|
||||
@@ -327,8 +329,8 @@ use Predis\Response\Status;
|
||||
* @method int pfadd(string $key, array $elements)
|
||||
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
|
||||
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed pubsub(string $subcommand, string|int|float $argument)
|
||||
* @method int publish(string $channel, string|int|float $message)
|
||||
* @method mixed discard()
|
||||
* @method array|null exec()
|
||||
* @method mixed multi()
|
||||
@@ -339,7 +341,7 @@ use Predis\Response\Status;
|
||||
* @method mixed eval_ro(string $script, array $keys, ...$argument)
|
||||
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
|
||||
* @method mixed evalsha_ro(string $sha1, array $keys, ...$argument)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method mixed script(string $subcommand, $argument = null)
|
||||
* @method Status shutdown(?bool $noSave = null, bool $now = false, bool $force = false, bool $abort = false)
|
||||
* @method mixed auth(string $password)
|
||||
* @method string echo(string $message)
|
||||
@@ -347,24 +349,24 @@ use Predis\Response\Status;
|
||||
* @method mixed select(int $database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method mixed client(string $subcommand, $argument = null)
|
||||
* @method mixed config(string $subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method array info(?string $section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof(string $host, int $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method mixed slowlog(string $subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
* @method int geoadd(string $key, $longitude, $latitude, $member)
|
||||
* @method int geoadd(string $key, string|float $longitude, string|float $latitude, string $member)
|
||||
* @method array geohash(string $key, array $members)
|
||||
* @method array geopos(string $key, array $members)
|
||||
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
|
||||
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, ?array $options = null)
|
||||
* @method array georadiusbymember(string $key, $member, $radius, $unit, ?array $options = null)
|
||||
* @method string|null geodist(string $key, string $member1, string $member2, string $unit = null)
|
||||
* @method array georadius(string $key, string|float $longitude, string|float $latitude, int $radius, string $unit, ?array $options = null)
|
||||
* @method array georadiusbymember(string $key, string $member, int $radius, string $unit, ?array $options = null)
|
||||
* @method array geosearch(string $key, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $withCoord = false, bool $withDist = false, bool $withHash = false)
|
||||
* @method int geosearchstore(string $destination, string $source, FromInterface $from, ByInterface $by, ?string $sorting = null, int $count = -1, bool $any = false, bool $storeDist = false)
|
||||
*
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?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\Cluster;
|
||||
|
||||
/**
|
||||
* Represents the gap between slot ranges.
|
||||
*/
|
||||
class NullSlotRange extends SlotRange
|
||||
{
|
||||
public function __construct(int $start, int $end)
|
||||
{
|
||||
parent::__construct($start, $end, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?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\Cluster;
|
||||
|
||||
use ArrayAccess;
|
||||
use ArrayIterator;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use OutOfBoundsException;
|
||||
use Predis\Connection\NodeConnectionInterface;
|
||||
use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Slot map for redis-cluster.
|
||||
*/
|
||||
class SimpleSlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
{
|
||||
private $slots = [];
|
||||
|
||||
/**
|
||||
* Checks if the given slot is valid.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($slot)
|
||||
{
|
||||
return $slot >= 0x0000 && $slot <= 0x3FFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given slot range is valid.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidRange($first, $last)
|
||||
{
|
||||
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the slot map.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->slots = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the slot map is empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slot map as a dictionary of $slot => $node.
|
||||
*
|
||||
* The order of the slots in the dictionary is not guaranteed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of unique nodes in the slot map.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNodes()
|
||||
{
|
||||
return array_keys(array_flip($this->slots));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot range to a node.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @throws OutOfBoundsException
|
||||
*/
|
||||
public function setSlots($first, $last, $connection)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified slot range.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSlots($first, $last)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last");
|
||||
}
|
||||
|
||||
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified slot is assigned.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetExists($slot)
|
||||
{
|
||||
return isset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($slot)
|
||||
{
|
||||
return $this->slots[$slot] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot to a node.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetSet($slot, $connection)
|
||||
{
|
||||
if (!static::isValid($slot)) {
|
||||
throw new OutOfBoundsException("Invalid slot $slot for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots[(int) $slot] = (string) $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetUnset($slot)
|
||||
{
|
||||
unset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current number of assigned slots.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the slot map.
|
||||
*
|
||||
* @return Traversable<int, string>
|
||||
*/
|
||||
#[ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->slots);
|
||||
}
|
||||
}
|
||||
+225
-17
@@ -22,11 +22,16 @@ use ReturnTypeWillChange;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Slot map for redis-cluster.
|
||||
* Compact slot map for redis-cluster.
|
||||
*/
|
||||
class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
{
|
||||
private $slots = [];
|
||||
/**
|
||||
* Slot ranges list.
|
||||
*
|
||||
* @var SlotRange[]
|
||||
*/
|
||||
private $slotRanges = [];
|
||||
|
||||
/**
|
||||
* Checks if the given slot is valid.
|
||||
@@ -37,7 +42,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public static function isValid($slot)
|
||||
{
|
||||
return $slot >= 0x0000 && $slot <= 0x3FFF;
|
||||
return $slot >= 0 && $slot <= SlotRange::MAX_SLOTS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +55,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public static function isValidRange($first, $last)
|
||||
{
|
||||
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
|
||||
return SlotRange::isValidRange($first, $last);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +63,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->slots = [];
|
||||
$this->slotRanges = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,7 +73,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->slots);
|
||||
return empty($this->slotRanges);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +85,13 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->slots;
|
||||
return array_reduce(
|
||||
$this->slotRanges,
|
||||
function ($carry, $slotRange) {
|
||||
return $carry + $slotRange->toArray();
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +101,22 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
*/
|
||||
public function getNodes()
|
||||
{
|
||||
return array_keys(array_flip($this->slots));
|
||||
return array_unique(array_map(
|
||||
function ($slotRange) {
|
||||
return $slotRange->getConnection();
|
||||
},
|
||||
$this->slotRanges
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of slot ranges.
|
||||
*
|
||||
* @return SlotRange[]
|
||||
*/
|
||||
public function getSlotRanges()
|
||||
{
|
||||
return $this->slotRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +134,31 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
|
||||
$targetSlotRange = new SlotRange($first, $last, (string) $connection);
|
||||
|
||||
// Get gaps of slot ranges list.
|
||||
$gaps = $this->getGaps($this->slotRanges);
|
||||
|
||||
$results = $this->slotRanges;
|
||||
|
||||
foreach ($gaps as $gap) {
|
||||
if (!$gap->hasIntersectionWith($targetSlotRange)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get intersection of the gap and target slot range.
|
||||
$results[] = new SlotRange(
|
||||
max($gap->getStart(), $targetSlotRange->getStart()),
|
||||
min($gap->getEnd(), $targetSlotRange->getEnd()),
|
||||
$targetSlotRange->getConnection()
|
||||
);
|
||||
}
|
||||
|
||||
$this->sortSlotRanges($results);
|
||||
|
||||
$results = $this->compactSlotRanges($results);
|
||||
|
||||
$this->slotRanges = $results;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +167,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return array
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public function getSlots($first, $last)
|
||||
{
|
||||
@@ -125,7 +175,28 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
throw new OutOfBoundsException("Invalid slot range $first-$last");
|
||||
}
|
||||
|
||||
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
|
||||
$placeHolder = new NullSlotRange($first, $last);
|
||||
|
||||
$intersections = [];
|
||||
foreach ($this->slotRanges as $slotRange) {
|
||||
if (!$placeHolder->hasIntersectionWith($slotRange)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$intersections[] = new SlotRange(
|
||||
max($placeHolder->getStart(), $slotRange->getStart()),
|
||||
min($placeHolder->getEnd(), $slotRange->getEnd()),
|
||||
$slotRange->getConnection()
|
||||
);
|
||||
}
|
||||
|
||||
return array_reduce(
|
||||
$intersections,
|
||||
function ($carry, $slotRange) {
|
||||
return $carry + $slotRange->toArray();
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +209,7 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetExists($slot)
|
||||
{
|
||||
return isset($this->slots[$slot]);
|
||||
return $this->findRangeBySlot($slot) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +222,9 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetGet($slot)
|
||||
{
|
||||
return $this->slots[$slot] ?? null;
|
||||
$found = $this->findRangeBySlot($slot);
|
||||
|
||||
return $found ? $found->getConnection() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +242,8 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
throw new OutOfBoundsException("Invalid slot $slot for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots[(int) $slot] = (string) $connection;
|
||||
$this->offsetUnset($slot);
|
||||
$this->setSlots($slot, $slot, $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,7 +256,26 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function offsetUnset($slot)
|
||||
{
|
||||
unset($this->slots[$slot]);
|
||||
if (!static::isValid($slot)) {
|
||||
throw new OutOfBoundsException("Invalid slot $slot");
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($this->slotRanges as $slotRange) {
|
||||
if (!$slotRange->hasSlot($slot)) {
|
||||
$results[] = $slotRange;
|
||||
}
|
||||
|
||||
if (static::isValidRange($slotRange->getStart(), $slot - 1)) {
|
||||
$results[] = new SlotRange($slotRange->getStart(), $slot - 1, $slotRange->getConnection());
|
||||
}
|
||||
|
||||
if (static::isValidRange($slot + 1, $slotRange->getEnd())) {
|
||||
$results[] = new SlotRange($slot + 1, $slotRange->getEnd(), $slotRange->getConnection());
|
||||
}
|
||||
}
|
||||
|
||||
$this->slotRanges = $results;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +286,12 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->slots);
|
||||
return array_sum(array_map(
|
||||
function ($slotRange) {
|
||||
return $slotRange->count();
|
||||
},
|
||||
$this->slotRanges
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,6 +302,116 @@ class SlotMap implements ArrayAccess, IteratorAggregate, Countable
|
||||
#[ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->slots);
|
||||
return new ArrayIterator($this->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the slot range which contains the specific slot index.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return SlotRange|false The slot range object or false if not found.
|
||||
*/
|
||||
protected function findRangeBySlot(int $slot)
|
||||
{
|
||||
foreach ($this->slotRanges as $slotRange) {
|
||||
if ($slotRange->hasSlot($slot)) {
|
||||
return $slotRange;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get gaps between sorted slot ranges with NullSlotRange object.
|
||||
*
|
||||
* @param SlotRange[] $slotRanges
|
||||
*
|
||||
* @return SlotRange[]
|
||||
*/
|
||||
protected function getGaps(array $slotRanges)
|
||||
{
|
||||
if (empty($slotRanges)) {
|
||||
return [
|
||||
new NullSlotRange(0, SlotRange::MAX_SLOTS),
|
||||
];
|
||||
}
|
||||
$gaps = [];
|
||||
$count = count($slotRanges);
|
||||
$i = 0;
|
||||
foreach ($slotRanges as $key => $slotRange) {
|
||||
$start = $slotRange->getStart();
|
||||
$end = $slotRange->getEnd();
|
||||
if (static::isValidRange($i, $start - 1)) {
|
||||
$gaps[] = new NullSlotRange($i, $start - 1);
|
||||
}
|
||||
|
||||
$i = $end + 1;
|
||||
|
||||
if ($key === $count - 1) {
|
||||
if (static::isValidRange($i, SlotRange::MAX_SLOTS)) {
|
||||
$gaps[] = new NullSlotRange($i, SlotRange::MAX_SLOTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $gaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort slot ranges by start index.
|
||||
*
|
||||
* @param SlotRange[] $slotRanges
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function sortSlotRanges(array &$slotRanges)
|
||||
{
|
||||
usort(
|
||||
$slotRanges,
|
||||
function (SlotRange $a, SlotRange $b) {
|
||||
if ($a->getStart() == $b->getStart()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $a->getStart() < $b->getStart() ? -1 : 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact adjacent slot ranges with the same connection.
|
||||
*
|
||||
* @param SlotRange[] $slotRanges
|
||||
*
|
||||
* @return SlotRange[]
|
||||
*/
|
||||
protected function compactSlotRanges(array $slotRanges)
|
||||
{
|
||||
if (empty($slotRanges)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$compacted = [];
|
||||
$count = count($slotRanges);
|
||||
$i = 0;
|
||||
$carry = $slotRanges[0];
|
||||
while ($i < $count) {
|
||||
$next = $slotRanges[$i + 1] ?? null;
|
||||
if (
|
||||
!is_null($next)
|
||||
&& ($carry->getEnd() + 1) === $next->getStart()
|
||||
&& $carry->getConnection() === $next->getConnection()
|
||||
) {
|
||||
$carry = new SlotRange($carry->getStart(), $next->getEnd(), $carry->getConnection());
|
||||
} else {
|
||||
$compacted[] = $carry;
|
||||
$carry = $next;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
return array_values($compacted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
<?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\Cluster;
|
||||
|
||||
use Countable;
|
||||
use OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Represents a range of slots in a Redis cluster.
|
||||
*/
|
||||
class SlotRange implements Countable
|
||||
{
|
||||
/**
|
||||
* Maximum number of slots in a Redis cluster is 16384.
|
||||
*/
|
||||
public const MAX_SLOTS = 0x3FFF;
|
||||
|
||||
/**
|
||||
* Starting slot of the range.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $start;
|
||||
|
||||
/**
|
||||
* Ending slot of the range.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $end;
|
||||
|
||||
/**
|
||||
* Connection to the server hosting this slot range.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
public function __construct(int $start, int $end, string $connection)
|
||||
{
|
||||
if (!static::isValidRange($start, $end)) {
|
||||
throw new OutOfBoundsException("Invalid slot range $start-$end for `$connection`");
|
||||
}
|
||||
$this->start = $start;
|
||||
$this->end = $end;
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a slot range is valid.
|
||||
*
|
||||
* @param int $first
|
||||
* @param int $last
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidRange($first, $last)
|
||||
{
|
||||
return $first >= 0 && $first <= self::MAX_SLOTS && $last >= 0x0000 && $last <= self::MAX_SLOTS && $first <= $last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start slot index of this range.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getStart()
|
||||
{
|
||||
return $this->start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end slot index of this range.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getEnd()
|
||||
{
|
||||
return $this->end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection to the server hosting this slot range.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specific slot is contained in this range.
|
||||
*
|
||||
* @param int $slot
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasSlot(int $slot)
|
||||
{
|
||||
return $this->start <= $slot && $this->end >= $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of connection strings for each slot in this range.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return array_fill($this->start, $this->end - $this->start + 1, $this->connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of slots in this range.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return $this->end - $this->start + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this range has an intersection with the given slot range.
|
||||
*
|
||||
* @param SlotRange $slotRange
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasIntersectionWith(SlotRange $slotRange): bool
|
||||
{
|
||||
return $this->start <= $slotRange->getEnd() && $this->end >= $slotRange->getStart();
|
||||
}
|
||||
}
|
||||
@@ -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\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
class BITFIELD_RO extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'BITFIELD_RO';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $arguments
|
||||
* @return void
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$processedArguments = [$arguments[0]];
|
||||
|
||||
if (array_key_exists(1, $arguments) && is_array($arguments[1])) {
|
||||
// Convert encoding => offset, into GET, encoding, offset
|
||||
array_walk($arguments[1], function ($value, $key) use (&$processedArguments) {
|
||||
array_push($processedArguments, 'GET', $key, $value);
|
||||
});
|
||||
}
|
||||
|
||||
parent::setArguments($processedArguments);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ namespace Predis\Command\Redis\Container;
|
||||
use Predis\Response\Status;
|
||||
|
||||
/**
|
||||
* @method array cat(string $category = null)
|
||||
* @method Status dryRun(string $username, string $command, ...$arguments)
|
||||
* @method array getUser(string $username)
|
||||
* @method Status setUser(string $username, string ...$rules)
|
||||
|
||||
@@ -13,8 +13,12 @@
|
||||
namespace Predis\Command\Redis\Search;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
use Predis\Command\Redis\CONFIG;
|
||||
|
||||
/**
|
||||
* @deprecated FT.CONFIG GET and SET is deprecated since Redis 8.0.
|
||||
* @see CONFIG if you want to manipulate search configuration
|
||||
*
|
||||
* @see https://redis.io/commands/ft.config-get/
|
||||
* @see https://redis.io/commands/ft.config-set/
|
||||
*
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?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\Search;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
|
||||
class FT_LIST extends RedisCommand
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return 'FT._LIST';
|
||||
}
|
||||
}
|
||||
@@ -165,6 +165,11 @@ class Parameters implements ParametersInterface
|
||||
}
|
||||
}
|
||||
|
||||
public function __set($parameter, $value)
|
||||
{
|
||||
$this->parameters[$parameter] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Predis\Connection;
|
||||
* @property float $timeout Timeout for the connect() operation.
|
||||
* @property float $read_write_timeout Timeout for read() and write() operations.
|
||||
* @property bool $persistent Leaves the connection open after a GC collection.
|
||||
* @property string $conn_uid Unique identifier of connection, needs to create a multiple persistent connections to the same resource.
|
||||
* @property string $password Password to access Redis (see the AUTH command).
|
||||
* @property string $database Database index (see the SELECT command).
|
||||
* @property bool $async_connect Performs the connect() operation asynchronously.
|
||||
|
||||
@@ -35,6 +35,15 @@ use Predis\Response\Status as StatusResponse;
|
||||
*/
|
||||
class StreamConnection extends AbstractConnection
|
||||
{
|
||||
/**
|
||||
* @param ParametersInterface $parameters
|
||||
*/
|
||||
public function __construct(ParametersInterface $parameters)
|
||||
{
|
||||
parent::__construct($parameters);
|
||||
$this->parameters->conn_uid = spl_object_hash($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects from the server and destroys the underlying resource when the
|
||||
* garbage collector kicks in only if the connection has not been marked as
|
||||
@@ -105,6 +114,18 @@ class StreamConnection extends AbstractConnection
|
||||
$timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
|
||||
$context = stream_context_create(['socket' => ['tcp_nodelay' => (bool) $parameters->tcp_nodelay]]);
|
||||
|
||||
if (
|
||||
(isset($parameters->persistent) && $parameters->persistent)
|
||||
&& (isset($parameters->conn_uid) && $parameters->conn_uid)
|
||||
) {
|
||||
$conn_uid = '/' . $parameters->conn_uid;
|
||||
} else {
|
||||
$conn_uid = '';
|
||||
}
|
||||
|
||||
// Needs to create multiple persistent connections to the same resource
|
||||
$address = $address . $conn_uid;
|
||||
|
||||
if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags, $context)) {
|
||||
$this->onConnectionError(trim($errstr), $errno);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ abstract class PredisTestCase extends PHPUnit\Framework\TestCase
|
||||
return [
|
||||
'scheme' => 'tcp',
|
||||
'host' => constant('REDIS_SERVER_HOST'),
|
||||
'port' => ($this->isStackTest()) ? constant('REDIS_STACK_SERVER_PORT') : constant('REDIS_SERVER_PORT'),
|
||||
'port' => ($this->isStackTest()) ? getenv('REDIS_STACK_SERVER_PORT') : constant('REDIS_SERVER_PORT'),
|
||||
'database' => constant('REDIS_SERVER_DBNUM'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1283,6 +1283,44 @@ class ClientTest extends PredisTestCase
|
||||
$this->assertSame(Client::VERSION, $libVer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
*/
|
||||
public function testClientsCreateDifferentPersistentConnections(): void
|
||||
{
|
||||
$client1 = new Client($this->getParameters(['database' => 14, 'persistent' => true]));
|
||||
$client2 = new Client($this->getParameters(['database' => 15, 'persistent' => true]));
|
||||
|
||||
$client1->set('foo', 'bar');
|
||||
$client2->set('foo', 'baz');
|
||||
|
||||
$this->assertSame('bar', $client1->get('foo'));
|
||||
$this->assertSame('baz', $client2->get('foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group cluster
|
||||
* @requiresRedisVersion >= 2.0.0
|
||||
*/
|
||||
public function testClusterClientsCreateDifferentPersistentConnections(): void
|
||||
{
|
||||
$client1 = new Client(
|
||||
$this->getDefaultParametersArray(),
|
||||
['cluster' => 'redis', 'parameters' => ['persistent' => true]]
|
||||
);
|
||||
$client2 = new Client(
|
||||
$this->getDefaultParametersArray(),
|
||||
['cluster' => 'redis', 'parameters' => ['persistent' => true]]
|
||||
);
|
||||
|
||||
$client1->set('{shard1}foo', 'bar');
|
||||
$client2->set('{shard2}foo', 'baz');
|
||||
|
||||
$this->assertSame('bar', $client1->get('{shard1}foo'));
|
||||
$this->assertSame('baz', $client2->get('{shard2}foo'));
|
||||
}
|
||||
|
||||
// ******************************************************************** //
|
||||
// ---- HELPER METHODS ------------------------------------------------ //
|
||||
// ******************************************************************** //
|
||||
|
||||
@@ -0,0 +1,473 @@
|
||||
<?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\Cluster;
|
||||
|
||||
use Predis\Cluster\SimpleSlotMap as SlotMap;
|
||||
use PredisTestCase;
|
||||
|
||||
class SimpleSlotMapTest extends PredisTestCase
|
||||
{
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidReturnsTrueOnValidSlot(): void
|
||||
{
|
||||
$this->assertTrue(SlotMap::isValid(0));
|
||||
$this->assertTrue(SlotMap::isValid(16383));
|
||||
|
||||
$this->assertTrue(SlotMap::isValid(5000));
|
||||
$this->assertTrue(SlotMap::isValid('5000'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidReturnsFalseOnInvalidSlot(): void
|
||||
{
|
||||
$this->assertFalse(SlotMap::isValid(-1));
|
||||
$this->assertFalse(SlotMap::isValid(16384));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsTrueOnValidSlotRange(): void
|
||||
{
|
||||
$this->assertTrue(SlotMap::isValidRange(0, 16383));
|
||||
$this->assertTrue(SlotMap::isValidRange(2000, 2999));
|
||||
$this->assertTrue(SlotMap::isValidRange(3000, 3000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsFalseOnInvalidSlotRange(): void
|
||||
{
|
||||
$this->assertFalse(SlotMap::isValidRange(0, 16384));
|
||||
$this->assertFalse(SlotMap::isValidRange(-1, 16383));
|
||||
$this->assertFalse(SlotMap::isValidRange(-1, 16384));
|
||||
$this->assertFalse(SlotMap::isValidRange(2999, 2000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testToArrayReturnsEmptyArrayOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertEmpty($slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsAssignsSpecifiedNodeToSlotRange(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$expectedMap = array_merge(
|
||||
array_fill(0, 5461, '127.0.0.1:6379'),
|
||||
array_fill(5461, 5462, '127.0.0.1:6380'),
|
||||
array_fill(10923, 5461, '127.0.0.1:6381')
|
||||
);
|
||||
|
||||
$this->assertSame($expectedMap, $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsOverwritesSlotRange(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(1000, 2000, '127.0.0.1:6380');
|
||||
|
||||
$expectedMap =
|
||||
array_fill(0, 5461, '127.0.0.1:6379') +
|
||||
array_fill(1000, 2000, '127.0.0.1:6380');
|
||||
|
||||
$this->assertSame($expectedMap, $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsAssignsSingleSlotWhenFirstAndLastSlotMatch(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(10, 10, '127.0.0.1:6379');
|
||||
|
||||
$this->assertSame([10 => '127.0.0.1:6379'], $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsCastsValueToString(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$connection = $this->getMockConnection();
|
||||
$connection
|
||||
->expects($this->once())
|
||||
->method('__toString')
|
||||
->willReturn('127.0.0.1:6379');
|
||||
|
||||
$slotmap->setSlots(10, 10, $connection);
|
||||
|
||||
$this->assertSame([10 => '127.0.0.1:6379'], $slotmap->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testSetSlotsThrowsExceptionOnInvalidSlotRange(): void
|
||||
{
|
||||
$this->expectException('OutOfBoundsException');
|
||||
$this->expectExceptionMessage('Invalid slot range 0-16384 for `127.0.0.1:6379`');
|
||||
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 16384, '127.0.0.1:6379');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsReturnsEmptyArrayOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertEmpty($slotmap->getSlots(3, 11));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsReturnsDictionaryOfSlotsWithAssignedNodes(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(10, 13, '127.0.0.1:6380');
|
||||
|
||||
$expectedMap = [
|
||||
3 => '127.0.0.1:6379',
|
||||
4 => '127.0.0.1:6379',
|
||||
5 => '127.0.0.1:6379',
|
||||
10 => '127.0.0.1:6380',
|
||||
11 => '127.0.0.1:6380',
|
||||
];
|
||||
|
||||
$this->assertSame($expectedMap, $slotmap->getSlots(3, 11));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsReturnsEmptyArrayOnEmptySlotRange(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(10, 13, '127.0.0.1:6380');
|
||||
|
||||
$this->assertEmpty($slotmap->getSlots(100, 200));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetSlotsThrowsExceptionOnInvalidSlotRange(): void
|
||||
{
|
||||
$this->expectException('OutOfBoundsException');
|
||||
$this->expectExceptionMessage('Invalid slot range 0-16384');
|
||||
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->getSlots(0, 16384);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsEmptyReturnsTrueOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertTrue($slotmap->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsEmptyReturnsFalseOnNonEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse($slotmap->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCountReturnsZeroOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertCount(0, $slotmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCountReturnsAssignedSlotsInSlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$this->assertCount(5461, $slotmap);
|
||||
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$this->assertCount(10923, $slotmap);
|
||||
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
$this->assertCount(16384, $slotmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testResetEmptiesSlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$this->assertFalse($slotmap->isEmpty());
|
||||
|
||||
$slotmap->reset();
|
||||
|
||||
$this->assertTrue($slotmap->isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetNodesReturnsEmptyArrayOnEmptySlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$this->assertEmpty($slotmap->getNodes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetNodesReturnsArrayOfNodesInSlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$this->assertSame(['127.0.0.1:6379', '127.0.0.1:6380', '127.0.0.1:6381'], $slotmap->getNodes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetExistsReturnsTrueOnAssignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertTrue(isset($slotmap[0]));
|
||||
$this->assertTrue(isset($slotmap[2000]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetExistsReturnsFalseOnAssignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse(isset($slotmap[6000]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetExistsReturnsFalseOnInvalidSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse(isset($slotmap[-100]));
|
||||
$this->assertFalse(isset($slotmap[16384]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetGetReturnsNodeOfAssignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$this->assertSame('127.0.0.1:6379', $slotmap[0]);
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[5461]);
|
||||
$this->assertSame('127.0.0.1:6381', $slotmap[10923]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetGetReturnsNullOnUnassignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertNull($slotmap[5461]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetGetReturnsNullOnInvalidSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertNull($slotmap[-100]);
|
||||
$this->assertNull($slotmap[16384]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetUnsetRemovesSlotAssignment(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertTrue(isset($slotmap[100]));
|
||||
unset($slotmap[100]);
|
||||
$this->assertFalse(isset($slotmap[100]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetUnsetDoesNotDoAnythingOnUnassignedSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertFalse(isset($slotmap[5461]));
|
||||
unset($slotmap[5461]);
|
||||
$this->assertFalse(isset($slotmap[5461]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetSetAssignsNodeToSlot(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[100] = '127.0.0.1:6380');
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[100]);
|
||||
|
||||
$this->assertNull($slotmap[5461]);
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[5461] = '127.0.0.1:6380');
|
||||
$this->assertSame('127.0.0.1:6380', $slotmap[5461]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetSetCastsValueToString(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$connection = $this->getMockConnection();
|
||||
$connection
|
||||
->expects($this->once())
|
||||
->method('__toString')
|
||||
->willReturn('127.0.0.1:6379');
|
||||
|
||||
$this->assertSame($connection, $slotmap[0] = $connection);
|
||||
$this->assertSame('127.0.0.1:6379', $slotmap[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testOffsetSetThrowsExceptionOnInvalidSlot(): void
|
||||
{
|
||||
$this->expectException('OutOfBoundsException');
|
||||
$this->expectExceptionMessage('Invalid slot 16384 for `127.0.0.1:6379`');
|
||||
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap[16384] = '127.0.0.1:6379';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetIteratorReturnsIteratorOverSlotMap(): void
|
||||
{
|
||||
$slotmap = new SlotMap();
|
||||
|
||||
$slotmap->setSlots(0, 5460, '127.0.0.1:6379');
|
||||
$slotmap->setSlots(5461, 10922, '127.0.0.1:6380');
|
||||
$slotmap->setSlots(10923, 16383, '127.0.0.1:6381');
|
||||
|
||||
$expectedMap = array_merge(
|
||||
array_fill(0, 5461, '127.0.0.1:6379'),
|
||||
array_fill(5461, 5462, '127.0.0.1:6380'),
|
||||
array_fill(10923, 5461, '127.0.0.1:6381')
|
||||
);
|
||||
|
||||
$this->assertSame($expectedMap, iterator_to_array($slotmap));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?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\Cluster;
|
||||
|
||||
use PredisTestCase;
|
||||
|
||||
class SlotRangeTest extends PredisTestCase
|
||||
{
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testMaxSlotsEquals16383()
|
||||
{
|
||||
$this->assertEquals(SlotRange::MAX_SLOTS, 16383);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testConstructorThrowExceptionOnInvalidSlotRange(): void
|
||||
{
|
||||
$this->expectException('OutOfBoundsException');
|
||||
$this->expectExceptionMessage('Invalid slot range 600-300 for `c1`');
|
||||
|
||||
new SlotRange(600, 300, 'c1');
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsTrueOnValidSlotRange()
|
||||
{
|
||||
$this->assertTrue(SlotRange::isValidRange(0, SlotRange::MAX_SLOTS));
|
||||
$this->assertTrue(SlotRange::isValidRange(2000, 2999));
|
||||
$this->assertTrue(SlotRange::isValidRange(3000, 3000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testIsValidRangeReturnsFalseOnInvalidSlotRange()
|
||||
{
|
||||
$this->assertFalse(SlotRange::isValidRange(0, 16384));
|
||||
$this->assertFalse(SlotRange::isValidRange(-1, 16383));
|
||||
$this->assertFalse(SlotRange::isValidRange(-1, 16384));
|
||||
$this->assertFalse(SlotRange::isValidRange(2999, 2000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPropertySetters()
|
||||
{
|
||||
$range = new SlotRange(2000, 5000, 'c1');
|
||||
|
||||
$this->assertEquals(2000, $range->getStart());
|
||||
$this->assertEquals(5000, $range->getEnd());
|
||||
$this->assertEquals('c1', $range->getConnection());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testHasSlot()
|
||||
{
|
||||
$range = new SlotRange(2000, 4000, 'c1');
|
||||
|
||||
$this->assertTrue($range->hasSlot(2000));
|
||||
$this->assertTrue($range->hasSlot(4000));
|
||||
$this->assertTrue($range->hasSlot(3000));
|
||||
|
||||
$this->assertFalse($range->hasSlot(1000));
|
||||
$this->assertFalse($range->hasSlot(5000));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testToArray()
|
||||
{
|
||||
$range = new SlotRange(2000, 2005, 'c1');
|
||||
$array = $range->toArray();
|
||||
$this->assertSame([
|
||||
2000 => 'c1',
|
||||
2001 => 'c1',
|
||||
2002 => 'c1',
|
||||
2003 => 'c1',
|
||||
2004 => 'c1',
|
||||
2005 => 'c1',
|
||||
], $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testCount()
|
||||
{
|
||||
$range = new SlotRange(2000, 3000, 'c1');
|
||||
$this->assertEquals(1001, $range->count());
|
||||
$this->assertEquals(1001, count($range));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testHasIntersectionWith()
|
||||
{
|
||||
$original = new SlotRange(2000, 3000, 'c1');
|
||||
|
||||
$this->assertFalse($original->hasIntersectionWith(new SlotRange(1000, 1500, 'c1')));
|
||||
$this->assertFalse($original->hasIntersectionWith(new SlotRange(3001, 5000, 'c1')));
|
||||
$this->assertFalse($original->hasIntersectionWith(new SlotRange(1999, 1999, 'c1')));
|
||||
$this->assertFalse($original->hasIntersectionWith(new SlotRange(3001, 3001, 'c1')));
|
||||
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(1500, 6000, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(2500, 2999, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(2000, 2999, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(2500, 3000, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(1500, 2000, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(3000, 3500, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(2000, 2000, 'c1')));
|
||||
$this->assertTrue($original->hasIntersectionWith(new SlotRange(3000, 3000, 'c1')));
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
use Predis\Response\ServerException;
|
||||
|
||||
class ACL_Test extends PredisCommandTestCase
|
||||
@@ -132,6 +133,174 @@ class ACL_Test extends PredisCommandTestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 7.9.0
|
||||
*/
|
||||
public function testModuleCategoriesAppearsInListOfAllCategories(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$allCategories = $redis->acl->cat();
|
||||
|
||||
foreach (['bloom', 'cuckoo', 'cms', 'topk', 'tdigest', 'search', 'timeseries', 'json'] as $category) {
|
||||
$this->assertContains($category, $allCategories);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group relay-incompatible
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 7.9.0
|
||||
*/
|
||||
public function testSetModuleCommandPrivileges(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'reset',
|
||||
'nopass',
|
||||
'on'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals('OK', $redis->auth('testUser', ''));
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$redis->ftcreate('test', [new TextField('foo')]);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'+ft.create',
|
||||
'+ft.search'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals('OK', $redis->ftcreate('test', [new TextField('foo')]));
|
||||
$this->assertEmpty($redis->ftsearch('test', '*'));
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$redis->jsonset('test', '$', '{"key":"value"}');
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'+json.set'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals('OK', $redis->jsonset('test', '$', '{"key":"value"}'));
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$redis->bfadd('test', 'value');
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'+bf.add'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals(1, $redis->bfadd('test', 'value'));
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$redis->tsadd('test', time(), 0.01);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'+ts.add'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals(1, $redis->tsadd('test', time(), 0.01));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group relay-incompatible
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 7.9.0
|
||||
*/
|
||||
public function testSetModuleCategoryPrivileges(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'reset',
|
||||
'nopass',
|
||||
'on'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals('OK', $redis->auth('testUser', ''));
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$redis->ftcreate('test', [new TextField('foo')]);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'+@search'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals('OK', $redis->ftcreate('test', [new TextField('foo')]));
|
||||
$this->assertEmpty($redis->ftsearch('test', '*'));
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$redis->jsonset('test', '$', '{"key":"value"}');
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'+@json'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals('OK', $redis->jsonset('test', '$', '{"key":"value"}'));
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$redis->bfadd('test', 'value');
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'+@bloom'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals(1, $redis->bfadd('test', 'value'));
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$redis->tsadd('test', time(), 0.01);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->acl->setUser(
|
||||
'testUser',
|
||||
'+@timeseries'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals(1, $redis->tsadd('test', time(), 0.01));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
<?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;
|
||||
|
||||
class BITFIELD_RO_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return BITFIELD_RO::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'BITFIELD_RO';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
* @dataProvider argumentsProvider
|
||||
*/
|
||||
public function testFilterArguments(array $actualArguments, array $expectedArguments): void
|
||||
{
|
||||
$command = $this->getCommand();
|
||||
$command->setArguments($actualArguments);
|
||||
|
||||
$this->assertSame($expectedArguments, $command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 6.2.0
|
||||
*/
|
||||
public function testReturnBitsOfSpecificString()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$redis->set('foo', 'bar');
|
||||
|
||||
$this->assertSame([6, 98], $redis->bitfield_ro('foo', ['u4' => 0, 'i8' => 0]));
|
||||
}
|
||||
|
||||
public function argumentsProvider(): array
|
||||
{
|
||||
return [
|
||||
'with default arguments' => [
|
||||
['key'],
|
||||
['key'],
|
||||
],
|
||||
'with single encoding-offset entry' => [
|
||||
['key', ['encoding' => 'offset']],
|
||||
['key', 'GET', 'encoding', 'offset'],
|
||||
],
|
||||
'with multiple encoding-offset entry' => [
|
||||
['key', ['encoding' => 'offset', 'encoding1' => 'offset1', 'encoding2' => 'offset2']],
|
||||
['key', 'GET', 'encoding', 'offset', 'GET', 'encoding1', 'offset1', 'GET', 'encoding2', 'offset2'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Response\ServerException;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-server
|
||||
@@ -137,6 +139,48 @@ class CONFIG_Test extends PredisCommandTestCase
|
||||
$redis->config('SET', 'loglevel', $previous['loglevel']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group relay-incompatible
|
||||
* @requiresRedisVersion >= 7.9.0
|
||||
*/
|
||||
public function testOverrideDefaultDialectWithConfigCommand()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
$default_dialect = (int) $redis
|
||||
->config('GET', 'search-default-dialect')['search-default-dialect'];
|
||||
|
||||
$this->assertEquals('OK', $redis->config('SET', 'search-default-dialect', 2));
|
||||
$this->assertEquals(2, (int) $redis->ftconfig->get('DEFAULT_DIALECT')[0][1]);
|
||||
$this->assertEquals(2,
|
||||
(int) $redis->config('GET', 'search-default-dialect')['search-default-dialect']);
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$redis->config('SET', 'search-default-dialect', $default_dialect)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 7.9.0
|
||||
*/
|
||||
public function testSetGetSearchConfiguration()
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertGreaterThan(0,
|
||||
(int) $redis->config('GET', 'search-timeout')['search-timeout']);
|
||||
$this->assertGreaterThanOrEqual(0,
|
||||
(int) $redis->config('GET', 'ts-retention-policy')['ts-retention-policy']);
|
||||
$this->assertGreaterThanOrEqual(0,
|
||||
(int) $redis->config('GET', 'bf-error-rate')['bf-error-rate']);
|
||||
$this->assertGreaterThan(0,
|
||||
(int) $redis->config('GET', 'cf-initial-size')['cf-initial-size']);
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$redis->config('SET', 'search-max-doctablesize', 10000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 2.0.0
|
||||
|
||||
@@ -313,6 +313,19 @@ BUFFER;
|
||||
$this->assertArrayHasKey('redis_version', $info['Server'] ?? $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion >= 7.9.0
|
||||
*/
|
||||
public function testExposeSearchInformation(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertArrayHasKey('search', $redis->info('modules')['Modules']);
|
||||
$this->assertNotEmpty($redis->info('search'));
|
||||
$this->assertArrayHasKey('search', $redis->info('everything')['Modules']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requiresRedisVersion < 2.6.0
|
||||
|
||||
@@ -124,7 +124,6 @@ class FTAGGREGATE_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('index: no such index');
|
||||
|
||||
$redis->ftaggregate('index', 'query');
|
||||
}
|
||||
|
||||
@@ -94,7 +94,6 @@ class FTALIASADD_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Unknown index name (or name is an alias itself)');
|
||||
|
||||
$redis->ftaliasadd('alias', 'index');
|
||||
}
|
||||
|
||||
@@ -109,7 +109,6 @@ class FTALIASUPDATE_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Unknown index name (or name is an alias itself)');
|
||||
|
||||
$redis->ftaliasupdate('alias', 'index');
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ class FTALTER_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Unknown index name');
|
||||
|
||||
$redis->ftalter('alias', [new TextField('field_name')]);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class FTCONFIG_Test extends PredisCommandTestCase
|
||||
* @group connected
|
||||
* @group relay-resp3
|
||||
* @return void
|
||||
* @requiresRedisVersion <= 7.3.0
|
||||
* @requiresRedisVersion <= 7.0.0
|
||||
*/
|
||||
public function testSetGivenRediSearchConfigurationParameter(): void
|
||||
{
|
||||
@@ -104,7 +104,7 @@ class FTCONFIG_Test extends PredisCommandTestCase
|
||||
* @group connected
|
||||
* @group relay-resp3
|
||||
* @return void
|
||||
* @requiresRedisVersion <= 7.3.0
|
||||
* @requiresRedisVersion <= 7.0.0
|
||||
*/
|
||||
public function testGetReturnsGivenRediSearchConfigurationParameter(): void
|
||||
{
|
||||
@@ -118,7 +118,7 @@ class FTCONFIG_Test extends PredisCommandTestCase
|
||||
* @group connected
|
||||
* @group relay-resp3
|
||||
* @return void
|
||||
* @requiresRedisVersion <= 7.3.0
|
||||
* @requiresRedisVersion <= 7.0.0
|
||||
*/
|
||||
public function testHelpReturnsGivenRediSearchConfigurationDescription(): void
|
||||
{
|
||||
@@ -141,14 +141,13 @@ class FTCONFIG_Test extends PredisCommandTestCase
|
||||
* @group connected
|
||||
* @group relay-resp3
|
||||
* @return void
|
||||
* @requiresRedisVersion <= 7.3.0
|
||||
* @requiresRedisVersion <= 7.0.0
|
||||
*/
|
||||
public function testSetThrowsExceptionOnNonExistingOption(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Invalid option');
|
||||
|
||||
$redis->ftconfig->set('foobar', 'value');
|
||||
}
|
||||
|
||||
@@ -188,7 +188,6 @@ class FTCURSOR_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Cursor not found');
|
||||
|
||||
$redis->ftcursor->read('idx', 21412412);
|
||||
}
|
||||
@@ -204,7 +203,6 @@ class FTCURSOR_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Cursor does not exist');
|
||||
|
||||
$redis->ftcursor->del('idx', 21412412);
|
||||
}
|
||||
|
||||
@@ -78,13 +78,13 @@ class FTDICTDUMP_Test extends PredisCommandTestCase
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRediSearchVersion >= 1.4.0
|
||||
* @requiresRedisVersion <= 7.9.0
|
||||
*/
|
||||
public function testThrowsExceptionOnNonExistingDictionary(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('could not open dict key');
|
||||
|
||||
$redis->ftdictdump('dict');
|
||||
}
|
||||
|
||||
@@ -85,7 +85,6 @@ class FTDROPINDEX_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Unknown Index name');
|
||||
|
||||
$redis->ftdropindex('index');
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ class FTEXPLAIN_Test extends PredisCommandTestCase
|
||||
* @group relay-resp3
|
||||
* @return void
|
||||
* @requiresRediSearchVersion >= 1.0.0
|
||||
* @requiresRedisVersion <= 7.9.0
|
||||
*/
|
||||
public function testExplainReturnsExecutionPlanForGivenQuery(): void
|
||||
{
|
||||
@@ -119,7 +120,6 @@ EOT;
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('index: no such index');
|
||||
|
||||
$redis->ftexplain('index', 'query');
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ class FTPROFILE_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('index: no such index');
|
||||
|
||||
$redis->ftprofile('index', (new ProfileArguments())->search()->query('query'));
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ use Predis\Command\Argument\Search\SchemaFields\GeoShapeField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\NumericField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TagField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\VectorField;
|
||||
use Predis\Command\Argument\Search\SearchArguments;
|
||||
use Predis\Command\Redis\PredisCommandTestCase;
|
||||
use Predis\Response\ServerException;
|
||||
@@ -409,6 +410,80 @@ class FTSEARCH_Test extends PredisCommandTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group relay-resp3
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 7.9.0
|
||||
*/
|
||||
public function testVectorSearchWithInt8Types(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertEquals('OK', $redis->ftcreate('test', [
|
||||
new VectorField(
|
||||
'v', 'HNSW',
|
||||
['TYPE', 'INT8', 'DIM', 2, 'DISTANCE_METRIC', 'L2']
|
||||
),
|
||||
]));
|
||||
|
||||
$this->sleep(0.1);
|
||||
|
||||
$a = [1.5, 10];
|
||||
$b = [123, 100];
|
||||
$c = [1, 1];
|
||||
|
||||
$redis->hset('a', 'v', pack('c*', ...$a));
|
||||
$redis->hset('b', 'v', pack('c*', ...$b));
|
||||
$redis->hset('c', 'v', pack('c*', ...$c));
|
||||
|
||||
$searchArguments = new SearchArguments();
|
||||
$searchArguments->params(['vec', pack('c*', ...$a)]);
|
||||
$searchArguments->dialect(2);
|
||||
|
||||
$this->assertEquals(
|
||||
2,
|
||||
$redis->ftsearch('test', '*=>[KNN 2 @v $vec]', $searchArguments)[0]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @group relay-resp3
|
||||
* @return void
|
||||
* @requiresRedisVersion >= 7.9.0
|
||||
*/
|
||||
public function testVectorSearchWithUInt8Types(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertEquals('OK', $redis->ftcreate('test', [
|
||||
new VectorField(
|
||||
'v', 'HNSW',
|
||||
['TYPE', 'UINT8', 'DIM', 2, 'DISTANCE_METRIC', 'L2']
|
||||
),
|
||||
]));
|
||||
|
||||
$this->sleep(0.1);
|
||||
|
||||
$a = [1.5, 10];
|
||||
$b = [123, 100];
|
||||
$c = [1, 1];
|
||||
|
||||
$redis->hset('a', 'v', pack('C*', ...$a));
|
||||
$redis->hset('b', 'v', pack('C*', ...$b));
|
||||
$redis->hset('c', 'v', pack('C*', ...$c));
|
||||
|
||||
$searchArguments = new SearchArguments();
|
||||
$searchArguments->params(['vec', pack('C*', ...$a)]);
|
||||
$searchArguments->dialect(2);
|
||||
|
||||
$this->assertEquals(
|
||||
2,
|
||||
$redis->ftsearch('test', '*=>[KNN 2 @v $vec]', $searchArguments)[0]
|
||||
);
|
||||
}
|
||||
|
||||
public function argumentsProvider(): array
|
||||
{
|
||||
return [
|
||||
|
||||
@@ -116,7 +116,6 @@ class FTSPELLCHECK_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Unknown Index name');
|
||||
|
||||
$redis->ftspellcheck(
|
||||
'index',
|
||||
|
||||
@@ -105,11 +105,11 @@ class FTSUGGET_Test extends PredisCommandTestCase
|
||||
* @return void
|
||||
* @requiresRediSearchVersion >= 1.0.0
|
||||
*/
|
||||
public function testGetReturnsNullOnNonExistingKey(): void
|
||||
public function testGetReturnsEmptyArrayOnNonExistingKey(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertNull($redis->ftsugget('key', 'hel'));
|
||||
$this->assertEmpty($redis->ftsugget('key', 'hel'));
|
||||
}
|
||||
|
||||
public function argumentsProvider(): array
|
||||
|
||||
@@ -94,7 +94,6 @@ class FTSYNDUMP_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Unknown index name');
|
||||
|
||||
$redis->ftsyndump('index');
|
||||
}
|
||||
|
||||
@@ -116,7 +116,6 @@ class FTSYNUPDATE_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Unknown index name');
|
||||
|
||||
$redis->ftsynupdate(
|
||||
'index',
|
||||
|
||||
@@ -104,7 +104,6 @@ class FTTAGVALS_Test extends PredisCommandTestCase
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->expectException(ServerException::class);
|
||||
$this->expectExceptionMessage('Unknown Index name');
|
||||
|
||||
$redis->fttagvals('index', 'fieldName');
|
||||
}
|
||||
|
||||
@@ -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\Redis\Search;
|
||||
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
use Predis\Command\Redis\PredisCommandTestCase;
|
||||
|
||||
class FT_LIST_Test extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return FT_LIST::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return 'FT_LIST';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments(): void
|
||||
{
|
||||
$this->assertEmpty($this->getCommand()->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @return void
|
||||
* @requiresRediSearchVersion >= 2.0.0
|
||||
*/
|
||||
public function testReturnListOfExistingIndices(): void
|
||||
{
|
||||
$redis = $this->getClient();
|
||||
|
||||
$this->assertEquals('OK', $redis->ftcreate('idx1', [new TextField('text')]));
|
||||
$this->assertEquals('OK', $redis->ftcreate('idx2', [new TextField('text')]));
|
||||
|
||||
$this->sleep(0.1);
|
||||
|
||||
$this->assertSameValues(['idx1', 'idx2'], $redis->ft_list());
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,7 @@ class TSINFO_Test extends PredisCommandTestCase
|
||||
* @group relay-resp3
|
||||
* @return void
|
||||
* @requiresRedisTimeSeriesVersion <= 1.10.13
|
||||
* @requiresRedisVersion > 6.3.0
|
||||
*/
|
||||
public function testReturnsInformationAboutGivenTimeSeries(): void
|
||||
{
|
||||
|
||||
@@ -123,23 +123,6 @@ class StreamConnectionTest extends PredisConnectionTestCase
|
||||
$connection1->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requires PHP 5.4
|
||||
*/
|
||||
public function testPersistentConnectionsToSameNodeShareResource(): void
|
||||
{
|
||||
$connection1 = $this->createConnectionWithParams(['persistent' => true]);
|
||||
$connection2 = $this->createConnectionWithParams(['persistent' => true]);
|
||||
|
||||
$this->assertPersistentConnection($connection1);
|
||||
$this->assertPersistentConnection($connection2);
|
||||
|
||||
$this->assertSame($connection1->getResource(), $connection2->getResource());
|
||||
|
||||
$connection1->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @group connected
|
||||
* @requires PHP 5.4
|
||||
|
||||
Reference in New Issue
Block a user