diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..cb5099cc04 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,118 @@ +# GitHub Copilot Instructions for PhpRedis + +> Repository-wide instructions for GitHub Copilot (agent, chat, and review). +> Keep changes small, reversible, and aligned with PhpRedis practices. + +## What this project is + +PhpRedis is a PHP extension (C + PHP test suites) for Redis/Valkey/KeyDB. +Project homepage: https://github.com/phpredis/phpredis + +## Golden rules for Copilot + +1. **Never commit build outputs** or local environment files. + - Do **not** add or modify: + - `redis.la`, `*.lo`, `*.o`, `*.a`, `*.so`, `*.dylib`, `*.dll` + - `modules/`, `modules/*` + - `autom4te.cache/`, `build/`, `Release/`, `Debug/` + - generated `configure`, `config.log`, `config.status`, `libtool` + - `Makefile*`, `install-sh`, `ltmain.sh`, `aclocal.m4` + - OS/editor temp files (`.DS_Store`, swap files, etc.) + - any files under `tests/nodes/*` created locally (e.g. `*.conf`) +2. **Do not run `phpize && ./configure && make` and commit results.** + - You may build **locally** to validate, but **never** commit outputs. +3. **Do not “fix” `.gitignore`** to hide accidental commits. Revert them. +4. **Do not edit generated headers** (e.g., `*_arginfo.h`) by hand. Edit + the corresponding `*.stub.php` and regenerate locally when needed. +5. **Prefer minimal, surgical PRs** with focused scope and tests. +6. **Follow project style** and avoid mass reformatting. + +## Build (local validation only; do not commit outputs) + +```sh +phpize +./configure --enable-redis +make -j"$(nproc 2>/dev/null || sysctl -n hw.ncpu || echo 2)" +# Optional quick load check (path depends on OS/build): +php -dextension="$(pwd)/modules/redis.so" --ri redis +``` + +## Stub files + +Whenever any of our `*.stub.php` files are modified, they'll need to be +regenerated with PHP's `gen_stub.php` script. When they are regenerated +both the `*.stub.php` and corresponding `*_arginfo` files *should* be committed. + +`gen_stub.php` can be downloaded from public locations but is also included in +any non-eol system wide PHP version. + +On a system with `php` and `php-config` one can typically run it like so: + +```sh +# Run inside of the `phpredis` source directory: +# On Linux it's typically here +php $(php-config --extension-dir)/build/gen_stub.php *.stub.php + +# Run inside of the `phpredis` source directory: +# On macOS it's typically here +php $(php-config --prefix)/lib/php/build/gen_stub.php *.stub.php +``` + +## Optional PhpRedis features + +PhpRedis has several optional features including `lz4`, `lzf`, `zstd`, +`igbinary`, and `msgpack` support. Each requires their respective dependencies +although liblzf is included as a submodule so can just be pulled that way. + +Build PhpRedis with every option like so: + +```sh +phpize +./configure \ + --enable-redis-zstd \ + --enable-redis-lzf \ + --enable-redis-lz4 \ + --enable-redis-msgpack \ + --enable-redis-igbinary + # --with-liblzf can be used to use system-wide liblzf +make -j"$(nproc 2>/dev/null || sysctl -n hw.ncpu || echo 2)" +php -dextension="$(pwd)/modules/redis.so" --ri redis +``` + +## Running tests + +PhpRedis uses a bespoke test harness. Redis just needs to be running locally. +Running the tests is simple and described below. + +```sh +# For standalone Redis +php tests/TestRedis.php --class Redis [--port ] +php tests/TestRedis.php --class RedisCluster [--port ] +php tests/TestRedis.php --class RedisSentinel [--port ] +``` + +### Running specific tests + +With any of the above test invocations you can limit to a specific test with the +`--test ` option. This is a substring match so for example + +```sh +# Will run any test with "get" in the name +php tests/TestRedis.php --class Redis --test get +``` + +### Regression tests + +Where appropriate adding a regression test and a comment with the issue number +is preferred. In most cases you only need to add the test to `RedisTest` and the +cluster tests will by default also run it. + +Remember that if the command takes multiple keys they all have to hash to the +same slot in cluster mode, so you can do this by picking key names like +`{foo}1`, `{foo}2`, etc. + +## Existing PhpRedis CI workflows + +Building and running the tests can be useful for any PR but PhpRedis already has +a very extensive CI setup which will be triggered on PRs. It is not necessary to +replicate this with extensive local tests unless there is a good reason. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9ba6cbef7..3288d59d63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,84 @@ on: [push, pull_request] jobs: - ubuntu: + configured-deps: runs-on: ubuntu-latest continue-on-error: false + strategy: + fail-fast: true + matrix: + php: ['8.3'] + steps: + - name: Checkout PhpRedis + uses: actions/checkout@v4 + + - name: Install liblzf + run: | + git clone --depth=1 https://github.com/nemequ/liblzf.git + cd liblzf + autoreconf -vi + CFLAGS=-fPIC ./configure --prefix="$GITHUB_WORKSPACE/liblzf" + make install + + - name: Install liblz4 + run: | + git clone -b v1.9.4 --depth=1 https://github.com/lz4/lz4 + cd lz4/lib + PREFIX="$GITHUB_WORKSPACE/liblz4" make install + + - name: Install libzstd + run: | + git clone -b v1.5.5 --depth=1 https://github.com/facebook/zstd + cd zstd + PREFIX="$GITHUB_WORKSPACE/libzstd" make install + + - name: Install PHP ${{ matrix.php }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: json, igbinary, msgpack, :redis + coverage: none + tools: none + + - name: Configure and build PhpRedis with distinct dep paths + run: | + phpize + ./configure \ + --enable-redis-lz4 \ + --with-liblz4="$GITHUB_WORKSPACE/liblz4" \ + --enable-redis-lzf \ + --with-liblzf="$GITHUB_WORKSPACE/liblzf" \ + --enable-redis-zstd \ + --with-libzstd="$GITHUB_WORKSPACE/libzstd" + sudo make -j"$(nproc)" + + - name: Make sure we're linking against specific liblz4 + run: | + grep "INCLUDES.*$GITHUB_WORKSPACE/liblz4" Makefile + grep "REDIS_SHARED_LIBADD.*-L$GITHUB_WORKSPACE/liblz4" Makefile + + - name: Make sure we're linking against specific liblzf + run: | + grep "INCLUDES.*$GITHUB_WORKSPACE/liblzf" Makefile + grep "REDIS_SHARED_LIBADD.*-L$GITHUB_WORKSPACE/liblzf" Makefile + + - name: Make sure we're linking against specific libzstd + run: | + grep "INCLUDES.*$GITHUB_WORKSPACE/libzstd" Makefile + grep "REDIS_SHARED_LIBADD.*-L$GITHUB_WORKSPACE/libzstd" Makefile + + ubuntu: + runs-on: ubuntu-22.04 + continue-on-error: false strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] + server: ['redis', 'keydb', 'valkey'] + steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true - name: Install PHP ${{ matrix.php }} @@ -20,43 +88,132 @@ jobs: extensions: json, igbinary, msgpack, :redis coverage: none tools: none - - name: Install dependencies + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install valgrind libzstd-dev liblz4-dev libssl-dev + + - name: Install Redis + if: matrix.server == 'redis' + env: + REDIS_PPA_URI: "packages.redis.io/deb" + REDIS_PPA_KEY: "packages.redis.io/gpg" + run: | + echo "deb https://$REDIS_PPA_URI $(lsb_release -cs) main" | \ + sudo tee /etc/apt/sources.list.d/redis.list + curl -fsSL "https://$REDIS_PPA_KEY" | \ + sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/redis.gpg + sudo apt-get update + sudo apt-get install redis + + - name: Install KeyDB + if: matrix.server == 'keydb' + env: + KEYDB_PPA_URI: "download.keydb.dev/open-source-dist" + KEYDB_PPA_KEY: "download.keydb.dev/open-source-dist/keyring.gpg" run: | - curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg - echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list + echo "deb https://$KEYDB_PPA_URI $(lsb_release -sc) main" | \ + sudo tee /etc/apt/sources.list.d/keydb.list + sudo wget -O /etc/apt/trusted.gpg.d/keydb.gpg "https://$KEYDB_PPA_KEY" sudo apt-get update - sudo apt --fix-broken install - sudo apt-get install redis valgrind libzstd-dev liblz4-dev + sudo apt-get install keydb + + - name: Install ValKey + if: matrix.server == 'valkey' + run: | + git clone --depth 1 --branch 8.1.3 https://github.com/valkey-io/valkey.git + cd valkey && BUILD_TLS=yes sudo make install + - name: Build phpredis run: | phpize - ./configure --enable-redis-lzf --enable-redis-zstd --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lz4 --with-liblz4 + ./configure \ + --enable-redis-lzf \ + --enable-redis-zstd \ + --enable-redis-igbinary \ + --enable-redis-msgpack \ + --enable-redis-lz4 \ + --with-liblz4 sudo make -j"$(nproc)" install - echo 'extension = redis.so' | sudo tee -a "$(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')"/90-redis.ini - - name: Start redis + echo 'extension = redis.so' | sudo tee -a "$(php-config --ini-dir)"/90-redis.ini + + - name: Attempt to shutdown default server + run: ${{ matrix.server }}-cli SHUTDOWN NOSAVE || true + + - name: Start ${{ matrix.server }}-server run: | - redis-cli SHUTDOWN NOSAVE - for PORT in $(seq 6379 6382) $(seq 32767 32769); do - redis-server --port "$PORT" --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels + for PORT in {6379..6382} {32767..32769}; do + ${{ matrix.server }}-server \ + --port "$PORT" \ + --daemonize yes \ + --aclfile tests/users.acl \ + --acl-pubsub-default allchannels done - redis-server --port 0 --unixsocket /tmp/redis.sock --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels - - name: Start redis cluster + ${{ matrix.server }}-server \ + --port 0 \ + --unixsocket /tmp/redis.sock \ + --daemonize yes \ + --aclfile tests/users.acl \ + --acl-pubsub-default allchannels + + - name: Start ${{ matrix.server }} cluster run: | mkdir -p tests/nodes echo -n > tests/nodes/nodemap - for PORT in $(seq 7000 7005); do - redis-server --port "$PORT" --cluster-enabled yes --cluster-config-file "$PORT".conf --daemonize yes --aclfile tests/users.acl --acl-pubsub-default allchannels + for PORT in {7000..7005}; do + ${{ matrix.server }}-server \ + --port "$PORT" \ + --cluster-enabled yes \ + --cluster-config-file "$PORT".conf \ + --daemonize yes \ + --aclfile tests/users.acl \ + --acl-pubsub-default allchannels echo 127.0.0.1:"$PORT" >> tests/nodes/nodemap done - echo yes | redis-cli --cluster create $(seq -f 127.0.0.1:%g 7000 7005) --cluster-replicas 1 --user phpredis -a phpredis - - name: Start redis sentinel + + - name: Start ${{ matrix.server }} sentinel run: | wget raw.githubusercontent.com/redis/redis/7.0/sentinel.conf - for PORT in $(seq 26379 26380); do + for PORT in {26379..26380}; do cp sentinel.conf "$PORT.conf" sed -i '/^sentinel/Id' "$PORT.conf" - redis-server "$PORT.conf" --port "$PORT" --daemonize yes --sentinel monitor mymaster 127.0.0.1 6379 1 --sentinel auth-pass mymaster phpredis + ${{ matrix.server }}-server "$PORT.conf" \ + --port "$PORT" \ + --daemonize yes \ + --sentinel monitor mymaster 127.0.0.1 6379 1 \ + --sentinel auth-pass mymaster phpredis + done + + - name: Wait for ${{ matrix.server }} instances + run: | + WAIT_TIMEOUT_SECONDS=30 + + for PORT in {6379..6382} {7000..7005} {32767..32768} {26379..26380}; do + START_TIME=$SECONDS + until echo PING | ${{ matrix.server }}-cli -p "$PORT" 2>&1 | grep -qE 'PONG|NOAUTH'; do + if (( SECONDS - START_TIME >= WAIT_TIMEOUT_SECONDS )); then + echo "Timed out waiting for ${{ matrix.server }} on port $PORT after ${WAIT_TIMEOUT_SECONDS}s" + exit 1 + fi + echo "Still waiting for ${{ matrix.server }} on port $PORT" + sleep .5 + done + done + START_TIME=$SECONDS + until echo PING | ${{ matrix.server }}-cli -s /tmp/redis.sock 2>&1 | grep -qE 'PONG|NOAUTH'; do + if (( SECONDS - START_TIME >= WAIT_TIMEOUT_SECONDS )); then + echo "Timed out waiting for ${{ matrix.server }} at /tmp/redis.sock after ${WAIT_TIMEOUT_SECONDS}s" + exit 1 + fi + echo "Still waiting for ${{ matrix.server }} at /tmp/redis.sock" + sleep .5 done + + - name: Initialize ${{ matrix.server }} cluster + run: | + echo yes | ${{ matrix.server }}-cli --cluster create 127.0.0.1:{7000..7005} \ + --cluster-replicas 1 --user phpredis -a phpredis + - name: Run tests run: | php tests/TestRedis.php --class Redis --user phpredis --auth phpredis @@ -68,10 +225,14 @@ jobs: - name: Run tests using valgrind continue-on-error: true run: | - valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class Redis --user phpredis --auth phpredis - valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis - valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis - valgrind --suppressions=tests/vg.supp --error-exitcode=1 php tests/TestRedis.php --class RedisSentinel --auth phpredis + valgrind --suppressions=tests/vg.supp --error-exitcode=1 \ + php tests/TestRedis.php --class Redis --user phpredis --auth phpredis + valgrind --suppressions=tests/vg.supp --error-exitcode=1 \ + php tests/TestRedis.php --class RedisArray --user phpredis --auth phpredis + valgrind --suppressions=tests/vg.supp --error-exitcode=1 \ + php tests/TestRedis.php --class RedisCluster --user phpredis --auth phpredis + valgrind --suppressions=tests/vg.supp --error-exitcode=1 \ + php tests/TestRedis.php --class RedisSentinel --auth phpredis env: TEST_PHP_ARGS: -e USE_ZEND_ALLOC: 0 @@ -82,10 +243,10 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: true - name: Install PHP ${{ matrix.php }} @@ -104,45 +265,59 @@ jobs: phpize ./configure --enable-redis-lzf --enable-redis-zstd --enable-redis-igbinary --enable-redis-msgpack --enable-redis-lz4 --with-liblz4 sudo make install - echo 'extension = redis.so' | sudo tee -a "$(php --ini | grep 'Scan for additional .ini files' | awk '{print $7}')/90-redis.ini" + sudo mkdir -p "$(php-config --ini-dir)" + echo 'extension = redis.so' | sudo tee -a "$(php-config --ini-dir)/90-redis.ini" windows: - runs-on: windows-latest + runs-on: windows-2022 continue-on-error: false strategy: fail-fast: false matrix: - php: ['7.3', '7.4', '8.0', '8.1', '8.2'] + php: ['8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] ts: [nts, ts] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: submodules: true - - name: Install PHP ${{ matrix.php }} - uses: cmb69/setup-php-sdk@v0.6 - id: setup-php-sdk + - name: Build phpredis + uses: php/php-windows-builder/extension@v1 with: - version: ${{ matrix.php }} arch: x64 - ts: ${{matrix.ts}} - - name: Install dependencies - uses: ilammy/msvc-dev-cmd@v1 + php-version: ${{ matrix.php }} + ts: ${{ matrix.ts }} + run-tests: false + + pecl: + runs-on: ubuntu-latest + container: php:8.3-cli-alpine + steps: + - name: Install required system packages + run: apk add --update $PHPIZE_DEPS zstd-libs zstd-dev git + - name: Checkout + uses: actions/checkout@v4 with: - arch: x64 - toolset: ${{steps.setup-php-sdk.outputs.toolset}} - - name: Build phpredis + submodules: true + - name: Create temporary directory + id: temp-dir + run: printf "path=%s\n" "$(mktemp -d)" >>"$GITHUB_OUTPUT" + - name: Create package run: | - phpize - ./configure --enable-redis --with-prefix=${{steps.setup-php-sdk.outputs.prefix}} - nmake - - name: package - run: | - md binaries - copy LICENSE binaries - Get-ChildItem -Recurse -Filter "php_redis.dll" | ForEach-Object {Copy-Item -Path $_.FullName -Destination "binaries"} - - name: Upload artifacts - uses: actions/upload-artifact@v2 - with: - name: redis-${{matrix.php}}-x64-${{matrix.ts}} - path: binaries + cd "${{ steps.temp-dir.outputs.path }}" + pecl package "$GITHUB_WORKSPACE/package.xml" + - name: Compile package + run: printf '' | pecl install ${{ steps.temp-dir.outputs.path }}/redis-*.tgz + - name: Enable extension + run: docker-php-ext-enable redis + - name: Check for PHP startup warnings + run: | + php -d display_errors=stderr -d display_startup_errors=1 -d error_reporting=-1 -r ';' 2>/tmp/php-startup-warnings + if [ -s /tmp/php-startup-warnings ]; then + echo 'The PHP extension was successfully installed, but PHP raised these warnings:' >&2 + cat /tmp/php-startup-warnings >&2 + exit 1 + fi + echo "PHP didn't raise any warnings at startup." + - name: Inspect extension + run: php --ri redis diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7b7c2cc310..6116b7b761 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -6,9 +6,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: cpp queries: +security-and-quality @@ -16,6 +16,6 @@ jobs: run: | phpize - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.gitignore b/.gitignore index f858a89c41..fa1d633688 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,13 @@ -/.github /.idea /.vscode +.cache /docs/.cache .cquery +*.dep *.deps *.libs *.o +*.la *.lo Makefile* configure* @@ -21,4 +23,5 @@ mkinstalldirs tags compile_commands.json doctum.phar -run-tests.php \ No newline at end of file +run-tests.php +vendor/ diff --git a/CHANGELOG.md b/CHANGELOG.md index c345854e68..1815dd32b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,1467 @@ All changes to phpredis will be documented in this file. We're basing this format on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +# [6.3.0RC1] - 2025-10-15 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/6.3.0RC1), [PECL](https://pecl.php.net/package/redis/6.3.0RC1)) + +This release introduces support for dozens of new commands, including hash field expiration, Valkey’s DELIFEQ, and Redis vector set operations. It also delivers numerous bug fixes and performance improvements. + +### Sponsors :sparkling_heart: + +- [A-VISION](https://github.com/A-VISION-BV) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Geoffrey Hoffman](https://github.com/phpguru) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Open LMS](https://openlms.net/) +- [Relay](https://relay.so) +- [Salvatore Sanfilippo](https://github.com/antirez) +- [Ty Karok](https://github.com/karock) + +## Fixed + +- Fix possible hash field name truncation + [834d2b37](https://github.com/phpredis/phpredis/commit/834d2b37) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix a possible segfault during failover + [5ebb853e](https://github.com/phpredis/phpredis/commit/5ebb853e) + ([rlerdorf](https://github.com/rlerdorf)) +- Bump version and fix XGROUP test + [f5db01b7](https://github.com/phpredis/phpredis/commit/f5db01b7), + [4517d5f9](https://github.com/phpredis/phpredis/commit/4517d5f9) + ([remicollet](https://github.com/remicollet)) +- Fix an overflow bug in ZADD on Windows + [35df8ad7](https://github.com/phpredis/phpredis/commit/35df8ad7c2fc54fbf2a58d486cce49e712344bb2) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix errors and a warning + [b8de91c9](https://github.com/phpredis/phpredis/commit/b8de91c9e09dfaf6850273619b5ed92ce5d88db9) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix `RedisCluster` segfault + [f61e8cd7](https://github.com/phpredis/phpredis/commit/f61e8cd7bae16a761fd5c6c4b62e1e52707e0841) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix passing NULL for hash expiry argument + [ca80ee0e](https://github.com/phpredis/phpredis/commit/ca80ee0e6781bad4b4e453a10e968491b12db579) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix an off-by-one length calculation error + [340f23b0](https://github.com/phpredis/phpredis/commit/340f23b0827b31ae32638730110323d607a29412) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix hset fields handling + [6b2f088d](https://github.com/phpredis/phpredis/commit/6b2f088d49ae295c7a787e228a7f51d62beddec0) + ([yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix HSetEx expiry argument handling + [7805da75](https://github.com/phpredis/phpredis/commit/7805da7542b4f33fd2bd0300f3666d2325307e5b) + ([yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix the echo liveness check when in sentinel mode + [2acab399](https://github.com/phpredis/phpredis/commit/2acab399cb667475569e313bd688f5f19665c7e4) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix double -> int truncation warning + [152fdda9](https://github.com/phpredis/phpredis/commit/152fdda9b15fe5e60914f43fa34f64fd6e19d90d) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix `SIGABRT` in PHP 8.4 with RedisArray + [3c64b33f](https://github.com/phpredis/phpredis/commit/3c64b33ffe06a8929d61dd2b71ae5ea08014a455) + ([Novynn](https://github.com/Novynn)) +- Fixing segfault in cluster_update_slot + [b0ba827b](https://github.com/phpredis/phpredis/commit/b0ba827be243eae701239fa4068c05249ada6b49) + ([JacobBrownAustin](https://github.com/JacobBrownAustin)) +- Fix typo + [d0b0c5cf](https://github.com/phpredis/phpredis/commit/d0b0c5cfdde9d49a265ca4bf7184e3998863aed0) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix compiling with PHP 8.5.0 alpha3 and newer + [1e6f5477](https://github.com/phpredis/phpredis/commit/1e6f5477cb3728724eb26cd9bb1150701e68a31e) + ([wyattoday](https://github.com/wyattoday)) +- Fix error length calculation + UB sanity check + [e73130fe](https://github.com/phpredis/phpredis/commit/e73130fee0c22a20e11ce1596579df3f6f826974) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix arguments order for `SET` command + [f73f5fcc](https://github.com/phpredis/phpredis/commit/f73f5fcce55ab9268c4eb40bf93cccdae418c1d2) + ([yatsukhnenko](https://github.com/yatsukhnenko)) + +## Added + +- Add `hgetwithmeta` method + [7d3b2e4d](https://github.com/phpredis/phpredis/commit/7d3b2e4d) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add `maxRetries` to `redis_sock_configure` + [e9e9e495](https://github.com/phpredis/phpredis/commit/e9e9e4950946cd92aab8d0aa14d9d84adce4a646) + ([michael-grunder](https://github.com/michael-grunder)) +- Implement vectorset commands + [92dd256f](https://github.com/phpredis/phpredis/commit/92dd256f981be863fb3811c17aad60e5b270c862), + [d80b7258](https://github.com/phpredis/phpredis/commit/d80b725824d5cb56a44910b761c82acaec265713), + [7f9b1f41](https://github.com/phpredis/phpredis/commit/7f9b1f416ec96e3c34fb2b18a674cb28debcf8c5), + [92716ed0](https://github.com/phpredis/phpredis/commit/92716ed0c5ccd199fe20575989d84c582832d01c), + [dc91631b](https://github.com/phpredis/phpredis/commit/dc91631b3f6fe689fbf2de63da96f7a5ff350048), + [1deca628](https://github.com/phpredis/phpredis/commit/1deca62841ab0b8df0699733166bf9394d5143e0), + [96378b70](https://github.com/phpredis/phpredis/commit/96378b70fd03b571d1952022ec07f20307bf9fe6), + [0fda9f29](https://github.com/phpredis/phpredis/commit/0fda9f293b81f271b0f33e36303cf4df3e9dcefe), + [0ed0fc05](https://github.com/phpredis/phpredis/commit/0ed0fc0562eccbfd53b30d466c4076bab35bc39b), + [d1d69005](https://github.com/phpredis/phpredis/commit/d1d690053f8a468a64628da1d53cfd3ad6155344), + [286fa630](https://github.com/phpredis/phpredis/commit/286fa630649af95c5530f5df54cc74927bdf2383), + [38115dec](https://github.com/phpredis/phpredis/commit/38115decb97ccdd96088077fcb447db9251667f0), + [c4b2ea6c](https://github.com/phpredis/phpredis/commit/c4b2ea6ca532d4ef2c836f9b9c0701846c3bb202), + [6ce3bd53](https://github.com/phpredis/phpredis/commit/6ce3bd53) + ([michael-grunder](https://github.com/michael-grunder)) +- Implement `GEOSEARCH[STORE]` `BYPOLYGON` + [8d369f4d](https://github.com/phpredis/phpredis/commit/8d369f4d6263c8fe549d6eb89c52dc148843963d), + [f24814a4](https://github.com/phpredis/phpredis/commit/f24814a42349e6bb1b2aff8fdae4422be1cbd732) + ([michael-grunder](https://github.com/michael-grunder)) +- Implement Valkey's `DELIFEQ` command + [b1b0c191](https://github.com/phpredis/phpredis/commit/b1b0c1914232b2b8d94cd2147b12df4429ba10bc) + ([michael-grunder](https://github.com/michael-grunder)) +- Add an INI setting returning 5.x legacy behavior -- readonly session on lock failure + [8dada174](https://github.com/phpredis/phpredis/commit/8dada174c4c0daf7de38d7be3ccbb5f71a94ad74) + ([arokettu](https://github.com/arokettu)) +- Implement several hash expiration commands + [7350768c](https://github.com/phpredis/phpredis/commit/7350768cd9285b7d0c5c28742eabe52cfb1b326a) + ([michael-grunder](https://github.com/michael-grunder)) +- Introduce `Redis::serverName` and `Redis::serverVersion` methods + [056c2dbe](https://github.com/phpredis/phpredis/commit/056c2dbee7f6379a9f546e46584ace59449847c7) + [cbaf095f](https://github.com/phpredis/phpredis/commit/cbaf095ff708caf2728541bd627399a4058d0f19) + [fa3eb006](https://github.com/phpredis/phpredis/commit/fa3eb00683a2c8d539b52c0738db6821c74fef54) + ([yatsukhnenko](https://github.com/yatsukhnenko)) + ([michael-grunder](https://github.com/michael-grunder)) +- New option 'database' for Redis class constructor + [4f6a3ed1](https://github.com/phpredis/phpredis/commit/4f6a3ed1e71c70f80b631a9f53749e6a9fdb457a) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Implement `HGETEX`, `HSETEX`, `HGETDEL`, and refactor `HMGET` + [ce5b0fac](https://github.com/phpredis/phpredis/commit/ce5b0facc2520457f52a29d90b84ee1fa62936d1) + ([michael-grunder](https://github.com/michael-grunder)) +- Implement `GETDEL` for `RedisCluster` + [d342e4ac](https://github.com/phpredis/phpredis/commit/d342e4ac18723607b001deb593c8d45e40bbc4c8) + ([michael-grunder](https://github.com/michael-grunder)) + +## Changed + +- Check for `dragonfly_version` in `HELLO` response + [593ba012](https://github.com/phpredis/phpredis/commit/593ba012ac49065343f6bbf10adca5047414ce85) + ([michael-grunder](https://github.com/michael-grunder)) +- Simplify redis_unpack method calling + [0a85bd82](https://github.com/phpredis/phpredis/commit/0a85bd824a1506d54abe3c48a3ad12c34429b00d) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Refactor `getWithMeta` logic + [0445e683](https://github.com/phpredis/phpredis/commit/0445e683e7552d60dbc82f21e0ee845911844651) + ([michael-grunder](https://github.com/michael-grunder)) +- cleanup session temp file + [3828c929](https://github.com/phpredis/phpredis/commit/3828c9293b8fcd473e3c0b6d72c8db740b32bed8) + ([remicollet](https://github.com/remicollet)) +- Make execHello protected + [300c5fb2](https://github.com/phpredis/phpredis/commit/300c5fb218ebb55fb6eca4de91756a91e57912ea) + ([michael-grunder](https://github.com/michael-grunder)) + +## Documentation + +- Update Relay sponsorship + [7a69d73](https://github.com/phpredis/phpredis/commit/7a69d73) + ([tillkruss](https://github.com/tillkruss)) +- Fix markdown headlines + [1d662f56](https://github.com/phpredis/phpredis/commit/1d662f56aaac82e9f4a480265666e49eb592d9f6) + ([tillkruss](https://github.com/tillkruss)) +- document PECL configure options + [2066cfad](https://github.com/phpredis/phpredis/commit/2066cfad26db9e72295697485a0756df66dfe87c) + ([tillkruss](https://github.com/tillkruss)) +- Improve wording in README about project support + [0ac01476](https://github.com/phpredis/phpredis/commit/0ac014764dfa6529ba9778c243f787d10ffc4506) + ([tillkruss](https://github.com/tillkruss)) +- Improve install instructions + [c0076036](https://github.com/phpredis/phpredis/commit/c0076036d99d604d3a0772a5c6314847ad0d09e5) + ([remicollet](https://github.com/remicollet)) +- Clean up README.md: fix typos, spelling, and grammar errors + [09cd4c54](https://github.com/phpredis/phpredis/commit/09cd4c54b97941cd0f2becd5cc3ca31a6cbc6b76) + ([Copilot](https://github.com/apps/copilot-swe-agent)) +- Refine parameter descriptions and examples in README + [f9f609e1](https://github.com/phpredis/phpredis/commit/f9f609e1ebd5c0054e50d9f9e254b21f46a2c41d) + ([joshtrichards](https://github.com/joshtrichards)) +- Link to the correct header + [d18db84c](https://github.com/phpredis/phpredis/commit/d18db84c68d4db907fe04e6531a5129c1fe97391) + ([wyattoday](https://github.com/wyattoday)) +- Update README.md about supported PHP versions. + [b7a97e5e](https://github.com/phpredis/phpredis/commit/b7a97e5ec37ade2481f875295e45a2e1b6dd5366) + ([AkameOuO](https://github.com/AkameOuO)) +- Broaden return type for Redis::hGetAll + [bfbab892](https://github.com/phpredis/phpredis/commit/bfbab8925878409d0f6614c17a597e74c30574a8) + ([mgiuffrida](https://github.com/mgiuffrida)) + +## Tests/CI + +- Add a regression test for `EVAL[SHA]` + [22a2914b](https://github.com/phpredis/phpredis/commit/22a2914b09e927c0641da846b01fe54ebef50334) + ([michael-grunder](https://github.com/michael-grunder)) +- Rework `HEXPIRE` test inclusion + bump Valkey + [b83981aa](https://github.com/phpredis/phpredis/commit/b83981aaeb6614754664353d8b4db22f5a631543) + ([michael-grunder](https://github.com/michael-grunder)) +- Add PHP 8.5 to CI + [334937cb](https://github.com/phpredis/phpredis/commit/334937cb98adb557c31830e77e0c7ad36d8bdbac) + ([yatsukhnenko](https://github.com/yatsukhnenko)) +- Attempt to fix flaky GitHub CI tests + [80140003](https://github.com/phpredis/phpredis/commit/801400036946676e48f975468f2e9c28d2c17027) + ([michael-grunder](https://github.com/michael-grunder)) +- Reorganize tests + [807f806f](https://github.com/phpredis/phpredis/commit/807f806fe8a4df77691c869289db24358a684f7f) + ([yatsukhnenko](https://github.com/yatsukhnenko)) + +## Internal/Performance + +- Introduce `[[nodiscard]]` type attribute where supported. + [2d963e79](https://github.com/phpredis/phpredis/commit/2d963e79) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix typo (s/sees/seeds/) + [25e6d5fc](https://github.com/phpredis/phpredis/commit/25e6d5fcc2b7599907ec9571a12c1d5c8f9c0dcb) + ([xabbuh](https://github.com/xabbuh)) +- Fix an unused variable warning + [b48aa0d4](https://github.com/phpredis/phpredis/commit/b48aa0d471bf7280a1365fb5b4cb7595b5920498) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix several issues surfaced by `gcc -fanalyze` + [8be2306e](https://github.com/phpredis/phpredis/commit/8be2306e4f9192aaa167a5338673f3c348a2caf6) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix dead assignment + [d564e8cf](https://github.com/phpredis/phpredis/commit/d564e8cf3ce33c65898bc098173358b697fa459a) ([michael-grunder](https://github.com/michael-grunder)) +- Refactor `redis_replicaof_cmd` + [659dc763](https://github.com/phpredis/phpredis/commit/659dc763e0f69f526ff0a3404c1a113c9c55e9ea) + ([michael-grunder](https://github.com/michael-grunder)) +- Refactor several command handling macros into typesafe static functions + [9802fc0e](https://github.com/phpredis/phpredis/commit/9802fc0e46a3746e7913807029c6edcf03be3ec5), + [8f0931bb](https://github.com/phpredis/phpredis/commit/8f0931bbed9c68a9d9316ec2dbe36852235bbb04), + [7c953d45](https://github.com/phpredis/phpredis/commit/7c953d458b8b30bc6bb6e7877a9b07ff5607300a), + [950d2bc7](https://github.com/phpredis/phpredis/commit/950d2bc79dd328ae4d9b55a019b9d51a312813f5), + [601ebbff](https://github.com/phpredis/phpredis/commit/601ebbff2b654d7f73d58a5cea275f08009fd4ee), + [c3a71631](https://github.com/phpredis/phpredis/commit/c3a71631080d0e40a66b1362bc9113f4048a9aca), + [b0040514](https://github.com/phpredis/phpredis/commit/b004051499ffd78cd4ca678e0a061cf279dedb6c), + [fae89fa9](https://github.com/phpredis/phpredis/commit/fae89fa9927d5d1074da45ffe019822af215969e), + [f880e1f7](https://github.com/phpredis/phpredis/commit/f880e1f7273a230bd462824533e03c35e49a57f4), + [b90e27f2](https://github.com/phpredis/phpredis/commit/b90e27f2858276908803b6ac65103aeb89c254f8), + [1db39089](https://github.com/phpredis/phpredis/commit/1db390891455e2faeb5a9fef4f3da9e18a2ba754) + ([michael-grunder](https://github.com/michael-grunder)) +- Refactor `EVAL[SHA]` command and add a regression test + [6e5faf42](https://github.com/phpredis/phpredis/commit/6e5faf4226b6a0a33ce782ae04d5d395fe233f55) + ([michael-grunder](https://github.com/michael-grunder)) +- New macros REDIS_RESPONSE_ERROR and REDIS_RETURN_ZVAL + [614b86e4](https://github.com/phpredis/phpredis/commit/614b86e457532f0cc3c6f41322740e6125949721) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Remove wrapper macro which hides branching logic + [58e1a04f](https://github.com/phpredis/phpredis/commit/58e1a04f76340b7add977b586d18bb1f8cb23bac) + ([michael-grunder](https://github.com/michael-grunder)) +- Use continue not break if we get a NULL node + [8685c49c](https://github.com/phpredis/phpredis/commit/8685c49c7055d538f695cb3af1bb85b4145efad5) + ([michael-grunder](https://github.com/michael-grunder)) +- Remove pointless casts + [03837f02](https://github.com/phpredis/phpredis/commit/03837f0230a0f00d88194e373ec89e77ec1c7bbe) + ([michael-grunder](https://github.com/michael-grunder)) +- Change `int` flags to `zend_bool` since we only use them as booleans + [77691947](https://github.com/phpredis/phpredis/commit/77691947571921ced7170abcbdc0643ad7cf7c47) + ([michael-grunder](https://github.com/michael-grunder)) +- Remove unused macros + simplify some logic + [75acbb09](https://github.com/phpredis/phpredis/commit/75acbb0984704c0e66919153d9485ac156d382be) + ([michael-grunder](https://github.com/michael-grunder)) + +# [6.2.0] - 2025-03-24 ([Github](https://github.com/phpredis/phpredis/releases/6.2.0), [PECL](https://pecl.php.net/package/redis/6.2.0)) + +### Sponsors :sparkling_heart: + +- [A-VISION](https://github.com/A-VISION-BV) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Geoffrey Hoffman](https://github.com/phpguru) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Open LMS](https://openlms.net/) +- [Salvatore Sanfilippo](https://github.com/antirez) +- [Ty Karok](https://github.com/karock) +- [Vanessa Santana](https://github.com/vanessa-dev) + + Special thanks to [Jakub Onderka](https://github.com/jakubonderka) for nearly two dozen performance improvements in this release! + +## Fixed + +- Fix arguments order for `SET` command + [f73f5fc](https://github.com/phpredis/phpredis/commit/f73f5fcce55ab9268c4eb40bf93cccdae418c1d2) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix error length calculation and UB sanity check + [e73130fe](https://github.com/phpredis/phpredis/commit/e73130fee0c22a20e11ce1596579df3f6f826974) + ([michael-grunder](https://github.com/michael-grunder)) +- Invalidate slot cache on failed cluster connections + [c7b87843](https://github.com/phpredis/phpredis/commit/c7b878431014789f35d2fb1834b95257ca6cbba5) + ([James Kennedy](https://github.com/jkenn99)) +- Don't cast a uint64_t to a long + [faa4bc20](https://github.com/phpredis/phpredis/commit/faa4bc20868c76be4ecc4265015104a8adafccc4) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix potential NULL dereference + [43e6cab8](https://github.com/phpredis/phpredis/commit/43e6cab8792dc01580894d85600add9b68c27a42) + ([peter15914](https://github.com/peter15914)) +- Print cursor as unsigned 64 bit integer + [138d07b6](https://github.com/phpredis/phpredis/commit/138d07b67c5537373834f1cae99804e092db1631) + ([Bentley O'Kane-Chase](https://github.com/bentleyo)) +- Fix XAUTOCLAIM argc when sending COUNT + [0fe45d24](https://github.com/phpredis/phpredis/commit/0fe45d24d4d8c115a5b52846be072ecb9bb43329) + ([michael-grunder](https://github.com/michael-grunder)) + +### Added + +- Added `serverName()` and `serverVersion()` introspection methods + [056c2dbe](https://github.com/phpredis/phpredis/commit/056c2dbee7f6379a9f546e46584ace59449847c7) + [cbaf095f](https://github.com/phpredis/phpredis/commit/cbaf095ff708caf2728541bd627399a4058d0f19) + [fa3eb006](https://github.com/phpredis/phpredis/commit/fa3eb00683a2c8d539b52c0738db6821c74fef54) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + ([michael-grunder](https://github.com/michael-grunder)) +- Added `getWithMeta` method + [9036ffca](https://github.com/phpredis/phpredis/commit/9036ffca) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Implement `GETDEL` command for RedisCluster + [d342e4ac](https://github.com/phpredis/phpredis/commit/d342e4ac18723607b001deb593c8d45e40bbc4c8) + ([michael-grunder](https://github.com/michael-grunder)) +- Introduce `Redis::OPT_PACK_IGNORE_NUMBERS` option + [f9ce9429](https://github.com/phpredis/phpredis/commit/f9ce9429ef9f14a3de2c3fe1d68d02fb7440093d) + [29e5cf0d](https://github.com/phpredis/phpredis/commit/29e5cf0d8c03069aa34c2a63322951fdf2c268c2) + ([michael-grunder](https://github.com/michael-grunder)) +- Implement Valkey >= 8.1 `IFEQ` `SET` option + [a2eef77f](https://github.com/phpredis/phpredis/commit/a2eef77f4419cda815052e75def3af81b0ccd80f) + ([michael-grunder](https://github.com/michael-grunder)) +- Implement KeyDB's EXPIREMEMBER[AT] commands + [4cd3f593](https://github.com/phpredis/phpredis/commit/4cd3f59356582a65aec1cceed44741bd5d161d9e) + ([michael-grunder](https://github.com/michael-grunder)) +- Set priority to 60 (for PIE installations) + [9e504ede](https://github.com/phpredis/phpredis/commit/9e504ede34749326a39f997db6cc5c4201f6a9bc) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Documentation + +- Fix phpdoc type of `$pattern` + [5cad2076](https://github.com/phpredis/phpredis/commit/5cad20763710d44f8efb8e537f8f84a812935604) + ([OHZEKI Naoki](https://github.com/zeek0x)) +- Better documentation for the `$tlsOptions` parameter of RedisCluster + [8144db37](https://github.com/phpredis/phpredis/commit/8144db374338006a316beb11549f37926bd40c5d) + ([Jacob Brown](https://github.com/JacobBrownAustin)) + +### Tests/CI + +- Reorganize tests + [807f806f](https://github.com/phpredis/phpredis/commit/807f806f) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add details to the option doc block + [abb0f6cc](https://github.com/phpredis/phpredis/commit/abb0f6ccc827f240a1de53633225abbc2848fc3a) + ([michael-grunder](https://github.com/michael-grunder)) +- Update CodeQL to v3 + [41e11417](https://github.com/phpredis/phpredis/commit/41e114177a20a03e3013db2a3b90980a1f4f1635) + [a10bca35](https://github.com/phpredis/phpredis/commit/a10bca35bba32bb969cc1e473564695d3f8a8811) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add PHP 8.4 to CI + [6097e7ba](https://github.com/phpredis/phpredis/commit/6097e7ba50c0a300bc4f420f84c5d2665ef99d90) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Pin ubuntu version for KeyDB + [eb66fc9e](https://github.com/phpredis/phpredis/commit/eb66fc9e2fe60f13e5980ea2ecbe9457ca5ae8b4) + [985b0313](https://github.com/phpredis/phpredis/commit/985b0313fb664c9776c3d2c84e778ddd6733728e) + ([michael-grunder](https://github.com/michael-grunder)) +- Windows CI: update setup-php-sdk to v0.10 and enable caching + [f89d4d8f](https://github.com/phpredis/phpredis/commit/f89d4d8f6eecbe223e158651ffffd77ffa27449b) + ([Christoph M. Becker](https://github.com/cmb69)) + +### Internal/Performance + +- Reduce buffer size for signed integer + [044b3038](https://github.com/phpredis/phpredis/commit/044b30386f0418e9ed2a2bbc3b79582520d008d8) + [35c59880](https://github.com/phpredis/phpredis/commit/35c5988027eda663167a64decde4512957cae738) + ([Bentley O'Kane-Chase](https://github.com/bentleyo)) +- Create a strncmp wrapper + [085d61ec](https://github.com/phpredis/phpredis/commit/085d61ecfb0d484832547b46343a2e4b275a372e) + ([michael-grunder](https://github.com/michael-grunder)) +- Refactor and avoid allocation in rawcommand method + [f68544f7](https://github.com/phpredis/phpredis/commit/f68544f70385e1d431fb0245fafe30b39ee7479a) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Switch from linked list to growing array for reply callbacks + [a551fdc9](https://github.com/phpredis/phpredis/commit/a551fdc94c14d7974f2303cd558f7bd3e0fd91d6) + [42a42769](https://github.com/phpredis/phpredis/commit/42a427695e89577a1f1a554dba268527f3995708) + ([JakubOnderka](https://github.com/JakubOnderka)) + ([michael-grunder](https://github.com/michael-grunder)) +- Reuse redis_sock_append_auth method + [be388562](https://github.com/phpredis/phpredis/commit/be388562058a75ed8fd31926bb0e6a60e2d8cb08) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Switch pipeline_cmd from smart_str to smart_string + [571ffbc8](https://github.com/phpredis/phpredis/commit/571ffbc8e0a5da807a6cc4a2cc5aa90af72e23b0) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Remove unused redis_debug_response method from library.c + [7895636a](https://github.com/phpredis/phpredis/commit/7895636a3a7cd3cad396a83ebe3aa5fe0208f42d) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Optimise HMGET method + [2434ba29](https://github.com/phpredis/phpredis/commit/2434ba294cbb3b2f5b4ee581c37056906902d0d9) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Avoid unnecessary allocation in redis_hset_cmd + [aba09933](https://github.com/phpredis/phpredis/commit/aba09933db05a1a36e947c6fa9dca9889c6a77ff) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Avoid unnecessary allocation in redis_hdel_cmd + [4082dd07](https://github.com/phpredis/phpredis/commit/4082dd07f714fd2f6a0918b1845eb46c403a9edd) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Avoid unnecessary allocation in redis_key_varval_cmd + [99650e15](https://github.com/phpredis/phpredis/commit/99650e15453f03b5dd99284548514551fde4c812) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Use zval_get_tmp_string method that is faster when provided zval is string + [f6906470](https://github.com/phpredis/phpredis/commit/f6906470a52e2d24b1e1b9f2574726643edd7a64) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Optimise constructing Redis command string + [2a2f908f](https://github.com/phpredis/phpredis/commit/2a2f908f2b6b695a0e6705200160e592802f0e41) + ([JakubOnderka](https://github.com/JakubOnderka)) +- If no command is issued in multi mode, return immutable empty array + [5156e032](https://github.com/phpredis/phpredis/commit/5156e0320242ff05f327a3801667140069688c0e) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Test for empty pipeline and multi + [426de2bb](https://github.com/phpredis/phpredis/commit/426de2bb71372f665f5a5bb5a779a7b9c586892d) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Optimise method array_zip_values_and_scores + [400503b8](https://github.com/phpredis/phpredis/commit/400503b8718104b766ceb4a0b84e4a446dbee09b) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Faster parameter parsing in redis_key_cmd and redis_key_long_val_cmd + [83a19656](https://github.com/phpredis/phpredis/commit/83a19656f49aec8f354596099dbf97ba7375d7af) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Use immutable empty array in Redis::hKeys + [3a2f3f45](https://github.com/phpredis/phpredis/commit/3a2f3f45fc7bb01d1be2b9d97cf9d8bff0b0e818) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Use immutable empty array in Redis::exec + [60b5a886](https://github.com/phpredis/phpredis/commit/60b5a8860ae3ff2d02d7f06cc6f86b59cb53b2cf) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Do not allocate empty string or string with one character + [64da891e](https://github.com/phpredis/phpredis/commit/64da891e6fe5810b1aa2a47bc0632a2cd346659d) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Initialize arrays with known size + [99beb922](https://github.com/phpredis/phpredis/commit/99beb9221c815018f1d076654b033cafac22a6ce) + ([JakubOnderka](https://github.com/JakubOnderka)) +- Use smart str for constructing pipeline cmd + [b665925e](https://github.com/phpredis/phpredis/commit/b665925eeddfdf6a6fc1de471c0789ffb60cd067) + ([JakubOnderka](https://github.com/JakubOnderka)) + +## [6.1.0] - 2024-10-04 ([Github](https://github.com/phpredis/phpredis/releases/6.1.0), [PECL](https://pecl.php.net/package/redis/6.1.0)) + +**NOTE**: There were no changes to C code between 6.1.0RC2 and 6.1.0. + +### Documentation + +- Update package.xml to make it clearer that we support many key-value stores + [52e69ede](https://github.com/phpredis/phpredis/commit/52e69ede) + ([Remi Collet](https://github.com/remicollet)) +- Fix redis.io urls + [0bae4bb0](https://github.com/phpredis/phpredis/commit/0bae4bb0) + ([Vincent Langlet](https://github.com/VincentLanglet)) + +### Tests/CI + +- Fix 2 tests with redis 6.2 + [cc1be322](https://github.com/phpredis/phpredis/commit/cc1be322) + ([Remi Collet](https://github.com/remicollet)) + +### Sponsors :sparkling_heart: + +- [A-VISION](https://github.com/A-VISION-BV) +- [Open LMS](https://openlms.net/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Ty Karok](https://github.com/karock) +- [Object Cache Pro for WordPress](https://objectcache.pro/) + +### Contributors to this release :sparkling_heart: + + [@michael-grunder](https://github.com/michael-grunder), + [@yatsukhnenko](https://github.com/yatsukhnenko), + [@bitactive](https://github.com/bitactive), + [@OrangeJuiced](https://github.com/OrangeJuiced), + [@crocodele](https://github.com/crocodele), + [@kalifg](https://github.com/kalifg), + [@divinity76](https://github.com/divinity76), + [@PlavorSeol](https://github.com/PlavorSeol), + [@kjoe](https://github.com/kjoe), + [@tstarling](https://github.com/tstarling), + [@acorncom](https://github.com/acorncom), + [@tuxmartin](https://github.com/tuxmartin), + [@BenMorel](https://github.com/BenMorel), + [@szepeviktor](https://github.com/szepeviktor), + [@SplotyCode](https://github.com/SplotyCode), + [@taka-oyama](https://github.com/taka-oyama), + [@PROFeNoM](https://github.com/PROFeNoM), + [@woodongwong](https://github.com/woodongwong), + [@RobiNN1](https://github.com/RobiNN1), + [@vtsykun](https://github.com/vtsykun), + [@solracsf](https://github.com/solracsf), + [@tillkruss](https://github.com/tillkruss), + [@deiga](https://github.com/deiga), + [@tutuna](https://github.com/tutuna) + [@VincentLanglet](https://github.com/VincentLanglet) + + +## [6.1.0RC2] - 2024-09-23 ([Github](https://github.com/phpredis/phpredis/releases/6.1.0RC2), [PECL](https://pecl.php.net/package/redis/6.1.0RC2)) + +### Fixed + +- Fixed a `SIGABRT` error in PHP 8.4 + [a75a7e5a](https://github.com/phpredis/phpredis/commit/a75a7e5a) + ([Michael Grunder](https://github.com/michael-grunder)) +- Clean up code for unsupported versions of PHP + [37cebdd7](https://github.com/phpredis/phpredis/commit/37cebdd7) + ([Remi Collet](https://github.com/remicollet)) +- Add `SessionHelpers.php` to `package.xml` + [e9474b80](https://github.com/phpredis/phpredis/commit/e9474b80) + ([Remi Collet](https://github.com/remicollet)) +- 8.4 implicit null fix, bump version + [bff3a22e](https://github.com/phpredis/phpredis/commit/bff3a22e) + [30c8f90c](https://github.com/phpredis/phpredis/commit/30c8f90c) + ([Remi Collet](https://github.com/remicollet)) + +### Changed + +- Raised minimum supported PHP version to 7.4 + [8b519423](https://github.com/phpredis/phpredis/commit/8b519423) + ([Michael Grunder](https://github.com/michael-grunder)) + +### Removed + +- Removed erroneously duplicated changelog entries + [40c89736](https://github.com/phpredis/phpredis/commit/40c89736) + ([Michael Grunder](https://github.com/michael-grunder)) + +### Tests/CI + +- Move to upload artifacts v4 + [9d3805009](https://github.com/phpredis/phpredis/commit/9d3805009) + ([Michael Grunder](https://github.com/michael-grunder)) + +### Added + +- Added `composer.json` to support [PIE](https://github.com/php/pie) (PHP Installer for Extensions) + [b59e35a6](https://github.com/phpredis/phpredis/commit/b59e35a6) + ([James Titcumb](https://github.com/asgrim)) + +## [6.1.0RC1] - 2024-08-04 ([GitHub](https://github.com/phpredis/phpredis/releases/6.1.0RC1), [PECL](https://pecl.php.net/package/redis/6.1.0RC1)) + +### Fixed + +- Fix random connection timeouts with Redis Cluster. + [eb7f31e7](https://github.com/phpredis/phpredis/commit/eb7f31e7) + ([Jozsef Koszo](https://github.com/kjoe)) + [#1142](https://github.com/phpredis/phpredis/pull/1142) + [#1385](https://github.com/phpredis/phpredis/pull/1385) + [#1633](https://github.com/phpredis/phpredis/pull/1633) + [#1707](https://github.com/phpredis/phpredis/pull/1707) + [#1811](https://github.com/phpredis/phpredis/pull/1811) + [#2407](https://github.com/phpredis/phpredis/pull/2407) +- Fix argument count issue in HSET with associative array + [6ea5b3e0](https://github.com/phpredis/phpredis/commit/6ea5b3e0) + ([Viktor Djupsjöbacka](https://github.com/crocodele)) +- SRANDMEMBER can return any type because of serialization. + [6673b5b2](https://github.com/phpredis/phpredis/commit/6673b5b2) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix HRANDFIELD command when WITHVALUES is used. + [99f9fd83](https://github.com/phpredis/phpredis/commit/99f9fd83) + ([Michael Grunder](https://github.com/michael-grunder)) + [#2524](https://github.com/phpredis/phpredis/pull/2524) +- Allow context array to be nullable + [50529f56](https://github.com/phpredis/phpredis/commit/50529f56) + ([michael-grunder](https://github.com/michael-grunder)) + [#2521](https://github.com/phpredis/phpredis/pull/2521) +- Fix a macOS (M1) compiler warning. + [7de29d57](https://github.com/phpredis/phpredis/commit/7de29d57) + ([michael-grunder](https://github.com/michael-grunder)) +- `GETEX` documentation/updates and implentation in `RedisCluster` + [981c6931](https://github.com/phpredis/phpredis/commit/981c6931) + ([michael-grunder](https://github.com/michael-grunder)) + [#2512](https://github.com/phpredis/phpredis/pull/2512) +- Refactor redis_script_cmd and fix to `flush` subcommand. + [7c551424](https://github.com/phpredis/phpredis/commit/7c551424) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Update liveness check and fix PHP 8.4 compilation error. + [c139de3a](https://github.com/phpredis/phpredis/commit/c139de3a) + ([michael-grunder](https://github.com/michael-grunder)) +- Rework how we declare ZSTD min/max constants. + [34b5bd81](https://github.com/phpredis/phpredis/commit/34b5bd81) + ([michael-grunder](https://github.com/michael-grunder)) + [#2487](https://github.com/phpredis/phpredis/pull/2487) +- Fix memory leak if we fail in ps_open_redis. + [0e926165](https://github.com/phpredis/phpredis/commit/0e926165) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix segfault and remove redundant macros + [a9e53fd1](https://github.com/phpredis/phpredis/commit/a9e53fd1) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix PHP 8.4 includes + [a51215ce](https://github.com/phpredis/phpredis/commit/a51215ce) + [#2463](https://github.com/phpredis/phpredis/pull/2463) + ([michael-grunder](https://github.com/michael-grunder)) +- Handle arbitrarily large `SCAN` cursors properly. + [2612d444](https://github.com/phpredis/phpredis/commit/2612d444) + [e52f0afa](https://github.com/phpredis/phpredis/commit/e52f0afa) + [#2454](https://github.com/phpredis/phpredis/pull/2454) + [#2458](https://github.com/phpredis/phpredis/pull/2458) + ([michael-grunder](https://github.com/michael-grunder)) +- Improve warning when we encounter an invalid EXPIRY in SET + [732e466a](https://github.com/phpredis/phpredis/commit/732e466a) + [#2448](https://github.com/phpredis/phpredis/pull/2448) + ([michael-grunder](https://github.com/michael-grunder)) +- Fix Arginfo / zpp mismatch for DUMP command + [50e5405c](https://github.com/phpredis/phpredis/commit/50e5405c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- RedisCluster::publish returns a cluster_long_resp + [14f93339](https://github.com/phpredis/phpredis/commit/14f93339) + ([Alexandre Choura](https://github.com/PROFeNoM)) +- Fix segfault when passing just false to auth. + [6dc0a0be](https://github.com/phpredis/phpredis/commit/6dc0a0be) + [#2430](https://github.com/phpredis/phpredis/pull/2430) + ([michael-grunder](https://github.com/michael-grunder)) +- the VALUE argument type for hSetNx must be the same as for hSet + [df074dbe](https://github.com/phpredis/phpredis/commit/df074dbe) + ([Uładzimir Tsykun](https://github.com/vtsykun)) +- Other fixes + [e18f6c6d](https://github.com/phpredis/phpredis/commit/e18f6c6d) + [3d7be358](https://github.com/phpredis/phpredis/commit/3d7be358) + [2b555c89](https://github.com/phpredis/phpredis/commit/2b555c89) + [fa1a283a](https://github.com/phpredis/phpredis/commit/fa1a283a) + ([michael-grunder](https://github.com/michael-grunder)) + [37c5f8d4](https://github.com/phpredis/phpredis/commit/37c5f8d4) + ([Viktor Szépe](https://github.com/szepeviktor)) + +### Added + +- Compression support for PHP sessions. + [da4ab0a7](https://github.com/phpredis/phpredis/commit/da4ab0a7) + [#2473](https://github.com/phpredis/phpredis/pull/2473) + ([bitactive](https://github.com/bitactive)) +- Support for early_refresh in Redis sessions to match cluster behavior + [b6989018](https://github.com/phpredis/phpredis/commit/b6989018) + ([Bitactive](https://github.com/bitactive)) +- Implement WAITAOF command. + [ed7c9f6f](https://github.com/phpredis/phpredis/commit/ed7c9f6f) + ([michael-grunder](https://github.com/michael-grunder)) + +### Removed + +- PHP 7.1, 7.2, and 7.3 CI jobs + [d68c30f8](https://github.com/phpredis/phpredis/commit/d68c30f8) + [dc39bd55](https://github.com/phpredis/phpredis/commit/dc39bd55) + [#2478](https://github.com/phpredis/phpredis/pull/2478) + ([Michael Grunder](https://github.com/michael-grunder)) + +### Changed + +- Fix the time unit of retry_interval + [3fdd52b4](https://github.com/phpredis/phpredis/commit/3fdd52b4) + ([woodong](https://github.com/woodongwong)) + +### Documentation + +- Many documentation fixes. + [eeb51099](https://github.com/phpredis/phpredis/commit/eeb51099) + ([Michael Dwyer](https://github.com/kalifg)) + [#2523](https://github.com/phpredis/phpredis/pull/2523) +- fix missing \ tags + [f865d5b9](https://github.com/phpredis/phpredis/commit/f865d5b9) + ([divinity76](https://github.com/divinity76)) +- Mention Valkey support + [5f1eecfb](https://github.com/phpredis/phpredis/commit/5f1eecfb) + ([PlavorSeol](https://github.com/PlavorSeol)) +- Mention KeyDB support in README.md + [37fa3592](https://github.com/phpredis/phpredis/commit/37fa3592) + ([Tim Starling](https://github.com/tstarling)) +- Remove mention of pickle + [c7a73abb](https://github.com/phpredis/phpredis/commit/c7a73abb) + ([David Baker](https://github.com/acorncom)) +- Add session.save_path examples + [8a39caeb](https://github.com/phpredis/phpredis/commit/8a39caeb) + ([Martin Vancl](https://github.com/tuxmartin)) +- Tighter return types for Redis::(keys|hKeys|hVals|hGetAll)() + [77ab62bc](https://github.com/phpredis/phpredis/commit/77ab62bc) + ([Benjamin Morel](https://github.com/BenMorel)) +- Update stubs + [4d233977](https://github.com/phpredis/phpredis/commit/4d233977) + [ff305349](https://github.com/phpredis/phpredis/commit/ff305349) + [12966a74](https://github.com/phpredis/phpredis/commit/12966a74) + [a4a283ab](https://github.com/phpredis/phpredis/commit/a4a283ab) + ([michael-grunder](https://github.com/michael-grunder)) + [8f8ff72a](https://github.com/phpredis/phpredis/commit/8f8ff72a) + ([Takayasu Oyama](https://github.com/taka-oyama)) + [5d293245](https://github.com/phpredis/phpredis/commit/5d293245) +- Fix config.m4 when using custom dep paths + [ece3f7be](https://github.com/phpredis/phpredis/commit/ece3f7be) + ([Michael Grunder](https://github.com/michael-grunder)) + [#2453](https://github.com/phpredis/phpredis/pull/2453) + [#2452](https://github.com/phpredis/phpredis/pull/2452) +- Fix retry_internal documentation + [142c1f4a](https://github.com/phpredis/phpredis/commit/142c1f4a) + ([SplotyCode](https://github.com/SplotyCode)) +- Fix anchor link + [9b5cad31](https://github.com/phpredis/phpredis/commit/9b5cad31) + ([Git'Fellow](https://github.com/solracsf)) +- Fix typo in link + [bfd379f0](https://github.com/phpredis/phpredis/commit/bfd379f0) + [#2349](https://github.com/phpredis/phpredis/pull/2349) + ([deiga](https://github.com/deiga)) +- Fix Fedora package url + [60b1ba14](https://github.com/phpredis/phpredis/commit/60b1ba14) + [717713e1](https://github.com/phpredis/phpredis/commit/717713e1) + ([Dmitrii Kotov](https://github.com/tutunak)) +- Update Redis Sentinel documentation to reflect changes to constructor in 6.0 release + [dc05d65c](https://github.com/phpredis/phpredis/commit/dc05d65c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + [#2381](https://github.com/phpredis/phpredis/pull/2381) + +### Tests/CI + +- Avoid fatal error in test execution. + [57304970](https://github.com/phpredis/phpredis/commit/57304970) + ([Michael Grunder](https://github.com/michael-grunder)) + [#2510](https://github.com/phpredis/phpredis/pull/2510) +- Refactor unit test framework. + [b1771def](https://github.com/phpredis/phpredis/commit/b1771def) + ([Michael Grunder](https://github.com/michael-grunder)) + [#2509](https://github.com/phpredis/phpredis/pull/2509) +- Get unit tests working in `php-cgi`. + [b808cc60](https://github.com/phpredis/phpredis/commit/b808cc60) + ([michael-grunder](https://github.com/michael-grunder)) + [#2507](https://github.com/phpredis/phpredis/pull/2507) +- Switch to `ZEND_STRL` in more places. + [7050c989](https://github.com/phpredis/phpredis/commit/7050c989) + [f8c762e7](https://github.com/phpredis/phpredis/commit/f8c762e7) + ([Michael Grunder](https://github.com/michael-grunder)) + [#2505](https://github.com/phpredis/phpredis/pull/2505) +- Workaround weird PHP compiler crash. + [d3b2d87b](https://github.com/phpredis/phpredis/commit/d3b2d87b) + ([michael-grunder](https://github.com/michael-grunder)) +- Refactor tests (formatting, modernization, etc). + [dab6a62d](https://github.com/phpredis/phpredis/commit/dab6a62d) + [c6cd665b](https://github.com/phpredis/phpredis/commit/c6cd665b) + [78b70ca8](https://github.com/phpredis/phpredis/commit/78b70ca8) + [3c125b09](https://github.com/phpredis/phpredis/commit/3c125b09) + [18b0da72](https://github.com/phpredis/phpredis/commit/18b0da72) + [b88e72b1](https://github.com/phpredis/phpredis/commit/b88e72b1) + [#2492](https://github.com/phpredis/phpredis/pull/2492) + [0f94d9c1](https://github.com/phpredis/phpredis/commit/0f94d9c1) + [59965971](https://github.com/phpredis/phpredis/commit/59965971) + [3dbc2bd8](https://github.com/phpredis/phpredis/commit/3dbc2bd8) + [9b90c03b](https://github.com/phpredis/phpredis/commit/9b90c03b) + [c0d6f042](https://github.com/phpredis/phpredis/commit/c0d6f042) + ([michael-grunder](https://github.com/michael-grunder)) +- Spelling fixes + [0d89e928](https://github.com/phpredis/phpredis/commit/0d89e928) + ([michael-grunder](https://github.com/michael-grunder)) +- Added Valkey support. + [f350dc34](https://github.com/phpredis/phpredis/commit/f350dc34) + ([michael-grunder](https://github.com/michael-grunder)) +- Add a test for session compression. + [9f3ca98c](https://github.com/phpredis/phpredis/commit/9f3ca98c) + ([michael-grunder](https://github.com/michael-grunder)) + [#2473](https://github.com/phpredis/phpredis/pull/2473) + [#2480](https://github.com/phpredis/phpredis/pull/2480) +- Test against valkey + [a819a44b](https://github.com/phpredis/phpredis/commit/a819a44b) + ([michael-grunder](https://github.com/michael-grunder)) +- sessionSaveHandler injection. + [9f8f80ca](https://github.com/phpredis/phpredis/commit/9f8f80ca) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- KeyDB addiions + [54d62c72](https://github.com/phpredis/phpredis/commit/54d62c72) + [d9c48b78](https://github.com/phpredis/phpredis/commit/d9c48b78) + [#2466](https://github.com/phpredis/phpredis/pull/2466) + ([michael-grunder](https://github.com/michael-grunder)) +- Add PHP 8.3 to CI + [78d15140](https://github.com/phpredis/phpredis/commit/78d15140) + ([Róbert Kelčák](https://github.com/RobiNN1)) + [e051a5db](https://github.com/phpredis/phpredis/commit/e051a5db) + [#2427](https://github.com/phpredis/phpredis/pull/2427) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use newInstance in RedisClusterTest + [954fbab8](https://github.com/phpredis/phpredis/commit/954fbab8) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use actions/checkout@v4 + [f4c2ac26](https://github.com/phpredis/phpredis/commit/f4c2ac26) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Cluster nodes from ENV + [eda39958](https://github.com/phpredis/phpredis/commit/eda39958) + [0672703b](https://github.com/phpredis/phpredis/commit/0672703b) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Ensure we're talking to redis-server in our high ports test. + [7825efbc](https://github.com/phpredis/phpredis/commit/7825efbc) + ([michael-grunder](https://github.com/michael-grunder)) +- Add missing option to installation example + [2bddd84f](https://github.com/phpredis/phpredis/commit/2bddd84f) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + [#2378](https://github.com/phpredis/phpredis/pull/2378) +- Fix typo in link + [8f6bc98f](https://github.com/phpredis/phpredis/commit/8f6bc98f) + ([Timo Sand](https://github.com/deiga)) +- Update tests to allow users to use a custom class. + [5f6ce414](https://github.com/phpredis/phpredis/commit/5f6ce414) + ([michael-grunder](https://github.com/michael-grunder)) + + +## [6.0.2] - 2023-10-22 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.2), [PECL](https://pecl.php.net/package/redis/6.0.2)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed +- Fix deprecation error when passing null to match_type parameter. + [b835aaa3](https://github.com/phpredis/phpredis/commit/b835aaa3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix flaky test and OBJECT in a pipeline. + [a7f51f70](https://github.com/phpredis/phpredis/commit/a7f51f70) + ([Michael Grunder](https://github.com/michael-grunder)) +- Find our callback by pattern with PSUBSCRIBE + [2f276dcd](https://github.com/phpredis/phpredis/commit/2f276dcd) + ([Michael Grunder](https://github.com/michael-grunder)) + +## [6.0.1] - 2023-09-23 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.1), [PECL](https://pecl.php.net/package/redis/6.0.1)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed +- Fix memory leak and segfault in Redis::exec + [362e1141](https://github.com/phpredis/phpredis/commit/362e1141) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), + ([Markus Podar](https://github.com/mfn)) +- Fix unknown expiration modifier + [264c0c7e](https://github.com/phpredis/phpredis/commit/264c0c7e), + [95bd184b](https://github.com/phpredis/phpredis/commit/95bd184b) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Added +- Add missing option to exampleA + [3674d663](https://github.com/phpredis/phpredis/commit/3674d663) + ([Till Krüss](https://github.com/tillkruss)) +- Update sentinel documentation + [849bedb6](https://github.com/phpredis/phpredis/commit/849bedb6), + [1ad95b63](https://github.com/phpredis/phpredis/commit/1ad95b63) + ([Joost OrangeJuiced](https://github.com/OrangeJuiced)) + +## [6.0.0] - 2023-09-09 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0), [PECL](https://pecl.php.net/package/redis/6.0.0)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +*There were no changes between 6.0.0 and 6.0.0RC2* + +## [6.0.0RC2] - 2023-08-20 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0RC2), [PECL](https://pecl.php.net/package/redis/6.0.0RC2)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fix arginfo for arguments that default to null + [8d99b7d1](https://github.com/phpredis/phpredis/commit/8d99b7d1) + ([Nicolas Grekas](https://github.com/nicolas-grekas)) +- Fix C99 usages + [54d9ca45](https://github.com/phpredis/phpredis/commit/54d9ca45) + ([Remi Collet](https://github.com/remicollet)) +- Raise minimal supported version to 7.2 + [e10b9a85](https://github.com/phpredis/phpredis/commit/e10b9a85) + ([Remi Collet](https://github.com/remicollet)) + +## [6.0.0RC1] - 2023-08-01 ([GitHub](https://github.com/phpredis/phpredis/releases/6.0.0RC1), [PECL](https://pecl.php.net/package/redis/6.0.0RC1)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fix restoring keys when using compression + [82e08723](https://github.com/phpredis/phpredis/commit/82e08723) + ([Till Krüss](https://github.com/tillkruss)) +- Fix missing auth in RedisSentinel stub + [5db85561](https://github.com/phpredis/phpredis/commit/5db85561) + ([Lu Fei](https://github.com/sy-records)) +- Fix RedisSentinel pconnect check + [42cbd88a](https://github.com/phpredis/phpredis/commit/42cbd88a) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix NULL-pointer dereferences and handle possible UB + [36457555](https://github.com/phpredis/phpredis/commit/36457555) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix security alerts + [ee210f86](https://github.com/phpredis/phpredis/commit/ee210f86), + [fb6a297c](https://github.com/phpredis/phpredis/commit/fb6a297c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix segfault + [55bf0202](https://github.com/phpredis/phpredis/commit/55bf0202) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix default host length + [c40f9d6c](https://github.com/phpredis/phpredis/commit/c40f9d6c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix redis session standalone stream ssl context + [ed10f365](https://github.com/phpredis/phpredis/commit/ed10f365), + [d1bc6727](https://github.com/phpredis/phpredis/commit/d1bc6727), + [2ff11df5](https://github.com/phpredis/phpredis/commit/2ff11df5) + ([patricio.dorantes](https://github.com/patricio.dorantes)) +- Fix segfault with session+tls + [a471c87a](https://github.com/phpredis/phpredis/commit/a471c87a) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix non standards conforming prototypes. + [b3ce0486](https://github.com/phpredis/phpredis/commit/b3ce0486) + ([Michael Grunder](https://github.com/michael-grunder)) +- Avoid registering the same replicas multiple times + [f2bfd723](https://github.com/phpredis/phpredis/commit/f2bfd723) + ([Marius Adam](https://github.com/mariusadam)) +- Better unix:// or file:// detection. + [d05d301b](https://github.com/phpredis/phpredis/commit/d05d301b) + ([Michael Grunder](https://github.com/michael-grunder)) +- Future proof our igbinary header check + [69355faa](https://github.com/phpredis/phpredis/commit/69355faa) + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix BITOP cross-slot bug + [af13f951](https://github.com/phpredis/phpredis/commit/af13f951) + ([Michael Grunder](https://github.com/michael-grunder)) +- SENTINEL RESET returns a long. + [0243dd9d](https://github.com/phpredis/phpredis/commit/0243dd9d) + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix redis_sock_read_multibulk_multi_reply_loop logic + [d9cb5946](https://github.com/phpredis/phpredis/commit/d9cb5946), + [5a643b62](https://github.com/phpredis/phpredis/commit/5a643b62) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix RPOP to unserialize/decompress data. + [02c91d59](https://github.com/phpredis/phpredis/commit/02c91d59) + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix testObject for redis 7.2 + [fea19b52](https://github.com/phpredis/phpredis/commit/fea19b52), + [dcb95a3f](https://github.com/phpredis/phpredis/commit/dcb95a3f) + ([Remi Collet](https://github.com/remicollet)) +- Fix bug: the pipeline mode socket return an unexpected result after reconnecting + [a3327d9d](https://github.com/phpredis/phpredis/commit/a3327d9d) + ([thomaston](https://github.com/omigafu)) +- Fix stub files + [9aa5f387](https://github.com/phpredis/phpredis/commit/9aa5f387), + [74cf49f5](https://github.com/phpredis/phpredis/commit/74cf49f5), + [8b1eafe8](https://github.com/phpredis/phpredis/commit/8b1eafe8), + [e392dd88](https://github.com/phpredis/phpredis/commit/e392dd88), + [b5ea5fd7](https://github.com/phpredis/phpredis/commit/b5ea5fd7), + [71758b09](https://github.com/phpredis/phpredis/commit/71758b09), + [2a6dee5d](https://github.com/phpredis/phpredis/commit/2a6dee5d) + ([Nicolas Grekas](https://github.com/nicolas-grekas)), + ([Michael Grunder](https://github.com/michael-grunder)) +- Update documentation + [b64d93e1](https://github.com/phpredis/phpredis/commit/b64d93e1), + [703d71b5](https://github.com/phpredis/phpredis/commit/703d71b5), + [eba1c6d2](https://github.com/phpredis/phpredis/commit/eba1c6d2), + [0f502c9e](https://github.com/phpredis/phpredis/commit/0f502c9e), + [130b5d0b](https://github.com/phpredis/phpredis/commit/130b5d0b), + [21c3ef94](https://github.com/phpredis/phpredis/commit/21c3ef94), + [b7bf22d4](https://github.com/phpredis/phpredis/commit/b7bf22d4), + [50151e7a](https://github.com/phpredis/phpredis/commit/50151e7a), + [b9950727](https://github.com/phpredis/phpredis/commit/b9950727), + [ab4ce4ab](https://github.com/phpredis/phpredis/commit/ab4ce4ab), + [8d80ca5b](https://github.com/phpredis/phpredis/commit/8d80ca5b), + [c4de8667](https://github.com/phpredis/phpredis/commit/c4de8667), + [6982941b](https://github.com/phpredis/phpredis/commit/6982941b), + [375d093d](https://github.com/phpredis/phpredis/commit/375d093d), + [43da8dd9](https://github.com/phpredis/phpredis/commit/43da8dd9), + [71344612](https://github.com/phpredis/phpredis/commit/71344612), + [b9de0b97](https://github.com/phpredis/phpredis/commit/b9de0b97), + [2d8a8a44](https://github.com/phpredis/phpredis/commit/2d8a8a44), + [a2b0c86f](https://github.com/phpredis/phpredis/commit/a2b0c86f), + [e0b24be1](https://github.com/phpredis/phpredis/commit/e0b24be1), + [e609fbe8](https://github.com/phpredis/phpredis/commit/e609fbe8), + [c4aef956](https://github.com/phpredis/phpredis/commit/c4aef956), + [df50b2ad](https://github.com/phpredis/phpredis/commit/df50b2ad), + [cc2383f0](https://github.com/phpredis/phpredis/commit/cc2383f0), + [0dd2836f](https://github.com/phpredis/phpredis/commit/0dd2836f), + [7d5db510](https://github.com/phpredis/phpredis/commit/7d5db510), + [99340889](https://github.com/phpredis/phpredis/commit/99340889), + [70a55f3e](https://github.com/phpredis/phpredis/commit/70a55f3e), + [b04684d4](https://github.com/phpredis/phpredis/commit/b04684d4), + [980ea6b1](https://github.com/phpredis/phpredis/commit/980ea6b1), + [bb06ffa3](https://github.com/phpredis/phpredis/commit/bb06ffa3), + [b8679d7a](https://github.com/phpredis/phpredis/commit/b8679d7a), + [854f3aa4](https://github.com/phpredis/phpredis/commit/854f3aa4), + [a5c47901](https://github.com/phpredis/phpredis/commit/a5c47901), + [cf63e96e](https://github.com/phpredis/phpredis/commit/cf63e96e), + [f05ba819](https://github.com/phpredis/phpredis/commit/f05ba819), + [17db2328](https://github.com/phpredis/phpredis/commit/17db2328), + [450904f7](https://github.com/phpredis/phpredis/commit/450904f7), + [114f4d60](https://github.com/phpredis/phpredis/commit/114f4d60), + [142bddf0](https://github.com/phpredis/phpredis/commit/142bddf0), + [87fa36d6](https://github.com/phpredis/phpredis/commit/87fa36d6), + [531177d4](https://github.com/phpredis/phpredis/commit/531177d4), + [ecf65144](https://github.com/phpredis/phpredis/commit/ecf65144), + [53d142d9](https://github.com/phpredis/phpredis/commit/53d142d9), + [c14a9e3a](https://github.com/phpredis/phpredis/commit/c14a9e3a), + [72f8eb25](https://github.com/phpredis/phpredis/commit/72f8eb25), + [872b6931](https://github.com/phpredis/phpredis/commit/872b6931) + ([Karina Kwiatek](https://github.com/raccube)), + ([Nicolas Grekas](https://github.com/nicolas-grekas)), + ([Muhammad Dyas Yaskur](https://github.com/dyaskur)), + ([sergkash7](https://github.com/sergkash7)), + ([Dawid Polak](https://github.com/DeyV)), + ([Michael Grunder](https://github.com/michael-grunder)), + ([Yurun](https://github.com/Yurunsoft)), + ([twosee](https://github.com/twose)), + ([Juha](https://github.com/ejuhjav)), + ([Till Krüss](https://github.com/tillkruss)) + +### Changed + +- Allow to pass null as iterator + [14d121bb](https://github.com/phpredis/phpredis/commit/14d121bb) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add NOMKSTREAM option to XADD command. + [f9436e25](https://github.com/phpredis/phpredis/commit/f9436e25) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Don't allow reconnect on read response + [5a269ab6](https://github.com/phpredis/phpredis/commit/5a269ab6) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Reset multi/pipline transaction on pconnect close + [0879770a](https://github.com/phpredis/phpredis/commit/0879770a) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use read_mbulk_header helper where possible + [ca8b4c93](https://github.com/phpredis/phpredis/commit/ca8b4c93) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow to pass null as auth argument + [41517753](https://github.com/phpredis/phpredis/commit/41517753) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor redis_parse_client_list_response + [68136a29](https://github.com/phpredis/phpredis/commit/68136a29), + [aaa4c91a](https://github.com/phpredis/phpredis/commit/aaa4c91a), + [1fb2935b](https://github.com/phpredis/phpredis/commit/1fb2935b), + [cf2c052c](https://github.com/phpredis/phpredis/commit/cf2c052c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor subscribe/unsubscribe + [3c9e159c](https://github.com/phpredis/phpredis/commit/3c9e159c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Change PHPREDIS_CTX_PTR type + [de3635da](https://github.com/phpredis/phpredis/commit/de3635da) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor redis_parse_info_response + [982bd13b](https://github.com/phpredis/phpredis/commit/982bd13b) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow IPv6 address within square brackets + [c28ad7bb](https://github.com/phpredis/phpredis/commit/c28ad7bb) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow multiple field-value pairs for hmset command. + [e858e8e3](https://github.com/phpredis/phpredis/commit/e858e8e3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor MINIT and use @generate-class-entries in stub files + [3675f442](https://github.com/phpredis/phpredis/commit/3675f442) + ([Remi Collet](https://github.com/remicollet)) +- Use spl_ce_RuntimeException + [3cd5ac1e](https://github.com/phpredis/phpredis/commit/3cd5ac1e), + [a7e5ea64](https://github.com/phpredis/phpredis/commit/a7e5ea64) + ([Remi Collet](https://github.com/remicollet)) +- Regenerate arginfo using 8.2.0 + [a38e08da](https://github.com/phpredis/phpredis/commit/a38e08da) + ([Remi Collet](https://github.com/remicollet)) +- Refactor client command + [a8d10291](https://github.com/phpredis/phpredis/commit/a8d10291) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Pull COUNT/ANY parsing into a helper function + [d67b2020](https://github.com/phpredis/phpredis/commit/d67b2020) + ([Michael Grunder](https://github.com/michael-grunder)) +- Return false or NULL on empty lpos response + [39a01ac7](https://github.com/phpredis/phpredis/commit/39a01ac7) + ([Michael Grunder](https://github.com/michael-grunder)) +- BLPOP with a float timeout + [a98605f2](https://github.com/phpredis/phpredis/commit/a98605f2), + [dc9af529](https://github.com/phpredis/phpredis/commit/dc9af529) + ([Michael Grunder](https://github.com/michael-grunder)) +- Make sure we set an error for key based scans + [98fda1b8](https://github.com/phpredis/phpredis/commit/98fda1b8) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add back a default switch case for setoption handler + [87464932](https://github.com/phpredis/phpredis/commit/87464932) + ([Michael Grunder](https://github.com/michael-grunder)) +- Update stubs so the tests pass in strict mode + [bebd398c](https://github.com/phpredis/phpredis/commit/bebd398c) + ([Michael Grunder](https://github.com/michael-grunder)) +- Move where we generate our salt + [d2044c9f](https://github.com/phpredis/phpredis/commit/d2044c9f) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor XINFO handler + [3b0d8b77](https://github.com/phpredis/phpredis/commit/3b0d8b77) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor and fix XPENDING handler + [457953f4](https://github.com/phpredis/phpredis/commit/457953f4) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor FLUSHDB and update docs. + [54a084e5](https://github.com/phpredis/phpredis/commit/54a084e5) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add missing directed node command to docs and refactor stubs. + [5ac92d25](https://github.com/phpredis/phpredis/commit/5ac92d25) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor BITPOS and implement BIT/BYTE option. + [4d8afd38](https://github.com/phpredis/phpredis/commit/4d8afd38) + ([Michael Grunder](https://github.com/michael-grunder)) +- INFO with multiple sections + [44d03ca0](https://github.com/phpredis/phpredis/commit/44d03ca0) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SLOWLOG command + [d87f1428](https://github.com/phpredis/phpredis/commit/d87f1428) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SORT and add SORT_RO command + [8c7c5a3a](https://github.com/phpredis/phpredis/commit/8c7c5a3a) + ([Michael Grunder](https://github.com/michael-grunder)) +- Use ZEND_STRL in redis_commands.c + [78de25a3](https://github.com/phpredis/phpredis/commit/78de25a3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor PubSub command + [2a0d1c1e](https://github.com/phpredis/phpredis/commit/2a0d1c1e) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor SLAVEOF handler + [f2cef8be](https://github.com/phpredis/phpredis/commit/f2cef8be) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor ACL command + [504810a5](https://github.com/phpredis/phpredis/commit/504810a5) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use fast_zpp API + [376d4d27](https://github.com/phpredis/phpredis/commit/376d4d27) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix XAUTOCLAIM response handler + [0b7bd83f](https://github.com/phpredis/phpredis/commit/0b7bd83f) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor `command` command + [ff863f3f](https://github.com/phpredis/phpredis/commit/ff863f3f) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor rawCommand and WAIT + [79c9d224](https://github.com/phpredis/phpredis/commit/79c9d224) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SELECT command + [86f15cca](https://github.com/phpredis/phpredis/commit/86f15cca) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SRANDMEMBER command. + [f62363c2](https://github.com/phpredis/phpredis/commit/f62363c2) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor OBJECT command. + [acb5db76](https://github.com/phpredis/phpredis/commit/acb5db76) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor gen_varkey_cmd + [3efa59cb](https://github.com/phpredis/phpredis/commit/3efa59cb) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor MGET command. + [8cb6dd17](https://github.com/phpredis/phpredis/commit/8cb6dd17) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor INFO and SCRIPT commands. + [3574ef08](https://github.com/phpredis/phpredis/commit/3574ef08) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor MSET and MSETNX commands. + [6d104481](https://github.com/phpredis/phpredis/commit/6d104481) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor HMSET command. + [90eb0470](https://github.com/phpredis/phpredis/commit/90eb0470) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor PFCOUNT command. + [19fd7e0c](https://github.com/phpredis/phpredis/commit/19fd7e0c) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor SMOVE command. + [204a02c5](https://github.com/phpredis/phpredis/commit/204a02c5) + ([Michael Grunder](https://github.com/michael-grunder)) +- Rework ZRANGE argument handling. + [aa0938a4](https://github.com/phpredis/phpredis/commit/aa0938a4) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor a couple more command methods. + [5b560ccf](https://github.com/phpredis/phpredis/commit/5b560ccf), + [c8224b93](https://github.com/phpredis/phpredis/commit/c8224b93), + [40e1b1bf](https://github.com/phpredis/phpredis/commit/40e1b1bf), + [ccd419a4](https://github.com/phpredis/phpredis/commit/ccd419a4) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor HMGET command + [bb66a547](https://github.com/phpredis/phpredis/commit/bb66a547) + ([Michael Grunder](https://github.com/michael-grunder)) +- Refactor CLIENT command + [77c4f7a3](https://github.com/phpredis/phpredis/commit/77c4f7a3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Refactor redis_long_response + [f14a80db](https://github.com/phpredis/phpredis/commit/f14a80db) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Synchronize Redis and RedisSentinel constructors + [ebb2386e](https://github.com/phpredis/phpredis/commit/ebb2386e) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use redis_sock_connect on connect + [f6c8b9c6](https://github.com/phpredis/phpredis/commit/f6c8b9c6) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Auto-select db in redis_sock_server_open + [6930a81c](https://github.com/phpredis/phpredis/commit/6930a81c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Use on-stack allocated valiables + [7a055cad](https://github.com/phpredis/phpredis/commit/7a055cad) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Added + +- Add XAUTOCLAIM command + [01f3342c](https://github.com/phpredis/phpredis/commit/01f3342c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH + [750b6cf3](https://github.com/phpredis/phpredis/commit/750b6cf3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add reset command + [947a2d38](https://github.com/phpredis/phpredis/commit/947a2d38) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add hRandField command + [fe397371](https://github.com/phpredis/phpredis/commit/fe397371) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add PXAT/EXAT arguments to SET command. + [0a160685](https://github.com/phpredis/phpredis/commit/0a160685) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add GETEX, GETDEL commands. + [11861d95](https://github.com/phpredis/phpredis/commit/11861d95) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add FAILOVER command. + [4b767be7](https://github.com/phpredis/phpredis/commit/4b767be7) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Backoff settings in constructor + [e6b3fe54](https://github.com/phpredis/phpredis/commit/e6b3fe54) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add the COUNT argument to LPOP and RPOP + [df97cc35](https://github.com/phpredis/phpredis/commit/df97cc35) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Unsubscribe from all channels + [0f1ca0cc](https://github.com/phpredis/phpredis/commit/0f1ca0cc) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add lPos command. + [687a5c78](https://github.com/phpredis/phpredis/commit/687a5c78) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add the ANY argument to GEOSEARCH and GEORADIUS + [bf6f31e3](https://github.com/phpredis/phpredis/commit/bf6f31e3) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add 'BIT'/'BYTE' modifier to BITCOUNT + tests + [a3d2f131](https://github.com/phpredis/phpredis/commit/a3d2f131) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add missing configureoption entries in package.xml + [59053f10](https://github.com/phpredis/phpredis/commit/59053f10) + ([Michele Locati](https://github.com/mlocati)) +- Implement CONFIG RESETSTAT + [239678a0](https://github.com/phpredis/phpredis/commit/239678a0) + ([Michael Grunder](https://github.com/michael-grunder)) +- SINTERCARD and ZINTERCARD commands + [64300508](https://github.com/phpredis/phpredis/commit/64300508) + ([Michael Grunder](https://github.com/michael-grunder)) +- LCS command + [c0e839f6](https://github.com/phpredis/phpredis/commit/c0e839f6) + ([Michael Grunder](https://github.com/michael-grunder)) +- EXPIRETIME and PEXPIRETIME + [f5b2a09b](https://github.com/phpredis/phpredis/commit/f5b2a09b) + ([Michael Grunder](https://github.com/michael-grunder)) +- [B]LMPOP and [B]ZMPOP commands + [6ea978eb](https://github.com/phpredis/phpredis/commit/6ea978eb) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement new RESTORE options + [9a3fe401](https://github.com/phpredis/phpredis/commit/9a3fe401) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add new Redis 6.2.0 XTRIM options + [6b34d17f](https://github.com/phpredis/phpredis/commit/6b34d17f) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement AUTH/AUTH2 arguments for MIGRATE + [114d79d1](https://github.com/phpredis/phpredis/commit/114d79d1) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement CONFIG REWRITE + [525958ea](https://github.com/phpredis/phpredis/commit/525958ea) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement Redis 7.0.0 [P]EXPIRE[AT] options + [872ae107](https://github.com/phpredis/phpredis/commit/872ae107) + ([Michael Grunder](https://github.com/michael-grunder)) +- Variadic CONFIG GET/SET + [36ef4bd8](https://github.com/phpredis/phpredis/commit/36ef4bd8), + [a176f586](https://github.com/phpredis/phpredis/commit/a176f586) + ([Michael Grunder](https://github.com/michael-grunder)) +- EVAL_RO and EVALSHA_RO + [f3a40830](https://github.com/phpredis/phpredis/commit/f3a40830) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement ZRANGESTORE and add ZRANGE options + [71bcbcb9](https://github.com/phpredis/phpredis/commit/71bcbcb9) + ([Michael Grunder](https://github.com/michael-grunder)) +- XGROUP DELCONSUMER and ENTRIESREAD + [1343f500](https://github.com/phpredis/phpredis/commit/1343f500) + ([Michael Grunder](https://github.com/michael-grunder)) +- Expose the transferred number of bytes + [e0a88b7b](https://github.com/phpredis/phpredis/commit/e0a88b7b), + [90828019](https://github.com/phpredis/phpredis/commit/90828019), + [7a4cee2d](https://github.com/phpredis/phpredis/commit/7a4cee2d) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), + ([Michael Grunder](https://github.com/michael-grunder)) +- TOUCH command + [dc1f2398](https://github.com/phpredis/phpredis/commit/dc1f2398) + ([Michael Grunder](https://github.com/michael-grunder)) +- Redis Sentinel TLS support + [f2bb2cdb](https://github.com/phpredis/phpredis/commit/f2bb2cdb) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add the CH, NX, XX arguments to GEOADD + [2bb64038](https://github.com/phpredis/phpredis/commit/2bb64038), + [e8f5b517](https://github.com/phpredis/phpredis/commit/e8f5b517) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Implement SMISMEMBER for RedisCluster + [abfac47b](https://github.com/phpredis/phpredis/commit/abfac47b) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement ssubscribe/sunsubscribe + [7644736e](https://github.com/phpredis/phpredis/commit/7644736e) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Implement BLMOVE and add LMOVE/BLMOVE to cluster. + [121e9d9c](https://github.com/phpredis/phpredis/commit/121e9d9c) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement LPOS for RedisCluster + [7121aaae](https://github.com/phpredis/phpredis/commit/7121aaae) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement GEOSEARCH and GEOSEARCHSTORE for RedisCluster. + [fa5d1af9](https://github.com/phpredis/phpredis/commit/fa5d1af9) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement HRANDFIELD for RedisCluster + [e222b85e](https://github.com/phpredis/phpredis/commit/e222b85e) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement COPY for RedisCluster + [40a2c254](https://github.com/phpredis/phpredis/commit/40a2c254) + ([Michael Grunder](https://github.com/michael-grunder)) +- Implement new ZSET commands for cluster + [27900f39](https://github.com/phpredis/phpredis/commit/27900f39) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add cluster support for strict sessions and lazy write + [b6cf6361](https://github.com/phpredis/phpredis/commit/b6cf6361) + ([Michael Grunder](https://github.com/michael-grunder)) +- Add function command + [90a0e9cc](https://github.com/phpredis/phpredis/commit/90a0e9cc) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Add FCALL/FCALL_RO commands + [7c46ad2c](https://github.com/phpredis/phpredis/commit/7c46ad2c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +### Removed + +- Remove unused macros + [831d6118](https://github.com/phpredis/phpredis/commit/831d6118) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +## [5.3.7] - 2021-02-15 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.7), [PECL](https://pecl.php.net/package/redis/5.3.7)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +*There were no changes between 5.3.7 and 5.3.7RC2* + +## [5.3.7RC2] - 2021-02-12 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.7RC2), [PECL](https://pecl.php.net/package/redis/5.3.7RC2)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +*There were no changes between 5.3.7RC2 and 5.3.7RC1* + +## [5.3.7RC1] - 2021-02-02 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.7RC1), [PECL](https://pecl.php.net/package/redis/5.3.7RC1)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fix RedisArray::[hsz]scan and tests + [08a9d5db](https://github.com/phpredis/phpredis/commit/08a9d5db), + [0264de18](https://github.com/phpredis/phpredis/commit/0264de18), + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)), + ([Michael Grunder](https://github.com/michael-grunder)) +- Fix RedisArray::scan + [8689ab1c](https://github.com/phpredis/phpredis/commit/8689ab1c) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Fix LZF decompression logic + [0719c1ec](https://github.com/phpredis/phpredis/commit/0719c1ec) + ([Michael Grunder](https://github.com/michael-grunder)) + +## [5.3.6] - 2021-01-17 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.6), [PECL](https://pecl.php.net/package/redis/5.3.6)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fix a segfault in RedisArray::del + [d2f2a7d9](https://github.com/phpredis/phpredis/commit/d2f2a7d9) + ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) + +## [5.3.5] - 2021-12-18 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.5), [PECL](https://pecl.php.net/package/redis/5.3.5)) + +### Sponsors :sparkling_heart: + +- [Audiomack](https://audiomack.com) +- [Open LMS](https://openlms.net/) +- [BlueHost](https://bluehost.com) +- [Object Cache Pro for WordPress](https://objectcache.pro/) +- [Avtandil Kikabidze](https://github.com/akalongman) +- [Zaher Ghaibeh](https://github.com/zaherg) +- [BatchLabs](https://batch.com) +- [Stackhero](https://github.com/stackhero-io) +- [Florian Levis](https://github.com/Gounlaf) +- [Luis Zárate](https://github.com/jlzaratec) + +### Fixed + +- Fixed typo in cluster_scan_resp + [44affad2](https://github.com/phpredis/phpredis/commit/44affad2) ## [5.3.5RC1] - 2021-11-16 ([GitHub](https://github.com/phpredis/phpredis/releases/5.3.5RC1), [PECL](https://pecl.php.net/package/redis/5.3.5RC1)) @@ -118,7 +1578,7 @@ and PhpRedis adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm [05129c3a3](https://github.com/phpredis/phpredis/commit/05129c3a3) [5bba6a7fc](https://github.com/phpredis/phpredis/commit/5bba6a7fc) ([Nathaniel Braun](https://github.com/nbraun-amazon)) -- Added experimental support for detecting a dirty connection by +- Added experimental support for detecting a dirty connection by trying to determine if the underlying stream is readable. [d68579562](https://github.com/phpredis/phpredis/commit/d68579562) [#2013](https://github.com/phpredis/phpredis/issues/2013) @@ -797,7 +2257,7 @@ serializers, soft deprecation of non-Redis commands. ## [4.3.0] - 2019-03-13 ([GitHub](https://github.com/phpredis/phpredis/releases/tag/4.3.0), [PECL](https://pecl.php.net/package/redis/4.3.0)) -This is probably the last release with PHP 5 suport!!! +This is probably the last release with PHP 5 support!!! ### Added @@ -868,7 +2328,7 @@ The main feature of this release is new Streams API implemented by ### Changed - Optimize close method [2a1ef961](https://www.github.com/phpredis/phpredis/commit/2a1ef961) ([yulonghu](https://github.com/yulonghu)) -- Use a ZSET insted of SET for EVAL tests [2e412373](https://www.github.com/phpredis/phpredis/commit/2e412373) ([Michael Grunder](https://github.com/michael-grunder)) +- Use a ZSET instead of SET for EVAL tests [2e412373](https://www.github.com/phpredis/phpredis/commit/2e412373) ([Michael Grunder](https://github.com/michael-grunder)) - Modify session testing logic [bfd27471](https://www.github.com/phpredis/phpredis/commit/bfd27471) ([Michael Grunder](https://github.com/michael-grunder)) - Documentation improvements ([@michael-grunder](https://github.com/michael-grunder), [@elcheco](https://github.com/elcheco), [@lucascourot](https://github.com/lucascourot), [@nolimitdev](https://github.com/nolimitdev), [Michael Grunder](https://github.com/michael-grunder)) @@ -920,7 +2380,7 @@ The main feature of this release is new Streams API implemented by - Add tcp_keepalive option to redis sock [68c58513](https://www.github.com/phpredis/phpredis/commit/68c58513), [5101172a](https://www.github.com/phpredis/phpredis/commit/5101172a), [010336d5](https://www.github.com/phpredis/phpredis/commit/010336d5), [51e48729](https://www.github.com/phpredis/phpredis/commit/51e48729) ([@git-hulk](https://github.com/git-hulk), [Michael Grunder](https://github.com/michael-grunder)) - More robust GEORADIUS COUNT validation [f7edee5d](https://www.github.com/phpredis/phpredis/commit/f7edee5d) ([Michael Grunder](https://github.com/michael-grunder)) -- Allow to use empty string as persistant_id [ec4fd1bd](https://www.github.com/phpredis/phpredis/commit/ec4fd1bd) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow to use empty string as persistent_id [ec4fd1bd](https://www.github.com/phpredis/phpredis/commit/ec4fd1bd) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Documentation improvements ([Michael Grunder](https://github.com/michael-grunder), [@TomA-R](https://github.com/TomA-R)) ### Fixed @@ -943,7 +2403,7 @@ This is interim release which contains only bug fixes. - Fix segfault when extending Redis class in PHP 5 [d23eff](https://www.github.com/phpredis/phpredis/commit/d23eff) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Fix RedisCluster constructor with PHP 7 strict scalar type [5c21d7](https://www.github.com/phpredis/phpredis/commit/5c21d7) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) -- Allow to use empty string as persistant_id [344de5](https://www.github.com/phpredis/phpredis/commit/344de5) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) +- Allow to use empty string as persistent_id [344de5](https://www.github.com/phpredis/phpredis/commit/344de5) ([Pavlo Yatsukhnenko](https://github.com/yatsukhnenko)) - Fix cluster_init_seeds. [db1347](https://www.github.com/phpredis/phpredis/commit/db1347) ([@adlagares](https://github.com/adlagares)) - Fix z_seeds may be a reference [42581a](https://www.github.com/phpredis/phpredis/commit/42581a) ([@janic716](https://github.com/janic716)) - PHP >=7.3 uses zend_string for php_url elements [b566fb](https://www.github.com/phpredis/phpredis/commit/b566fb) ([@fmk](https://github.com/fmk)) @@ -1003,9 +2463,9 @@ to the api, listed below. This release contains two big improvements: -1. Adding a new printf like command construction function with additionaly +1. Adding a new printf like command construction function with additionally format specifiers specific to phpredis. -2. Implementation of custom objects for Redis and RedisArray wich eliminates +2. Implementation of custom objects for Redis and RedisArray which eliminates double hash lookup. Also many small improvements and bug fixes were made. @@ -1085,7 +2545,7 @@ the php 5 and 7 codebase into a single branch. - wrong size. ([@remicollet](https://github.com/remicollet)) - - Added php session unit test ([@yatsukhnenko](https://github.com/weltling)) -- Added explicit module dependancy for igbinary ([@remicollet](https://github.com/remicollet)) +- Added explicit module dependency for igbinary ([@remicollet](https://github.com/remicollet)) - Added phpinfo serialization information ([@remicollet](https://github.com/remicollet)) --- @@ -1188,7 +2648,7 @@ than 7. - Fixed memory leak in discard function [17b1f427](https://www.github.com/phpredis/phpredis/commit/17b1f427) - Sanity check for igbinary unserialization [3266b222](https://www.github.com/phpredis/phpredis/commit/3266b222), [528297a](https://www.github.com/phpredis/phpredis/commit/528297a) ([Maurus Cuelenaere](https://github.com/mcuelenaere)). -- Fix segfault occuring from unclosed socket connection for Redis Cluster +- Fix segfault occurring from unclosed socket connection for Redis Cluster [04196aee](https://www.github.com/phpredis/phpredis/commit/04196aee) ([CatKang](https://github.com/CatKang)) - Case insensitive zRangeByScore options - Fixed dreaded size_t vs long long compiler warning diff --git a/INSTALL.md b/INSTALL.md index bd3fbd985a..449275f1db 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,13 +1,24 @@ -# Installation from pecl/pickle +# Installation with PIE from packagist -To pull latest stable released version, from [pecl](https://pecl.php.net/package/redis) / [pickle](https://wiki.php.net/rfc/deprecate-pear-include-composer): +To pull latest stable released version, from [packagist](https://packagist.org/packages/phpredis/phpredis) ~~~ -pecl install redis +pie install phpredis/phpredis +~~~ + +# Installation from pecl + +To pull latest stable released version, from [pecl](https://pecl.php.net/package/redis) -// If using PHP >= 7.3 -pickle install redis ~~~ +pecl install redis +~~~ + +Configure options can be passed as well: + +```bash +pecl install --configureoptions="enable-redis-msgpack='yes' enable-redis-igbinary='yes'" redis +``` # Installation from sources @@ -17,7 +28,7 @@ To build this extension for the sources tree: git clone https://github.com/phpredis/phpredis.git cd phpredis phpize -./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd] +./configure [--enable-redis-igbinary] [--enable-redis-msgpack] [--enable-redis-lzf [--with-liblzf[=DIR]]] [--enable-redis-zstd] [--enable-redis-lz4] make && make install ~~~ @@ -43,20 +54,20 @@ Follow the DLL link on the [https://pecl.php.net/package/redis](https://pecl.php Fedora users can install the package from the official repository. -### Fedora ≥ 29, Version 5 +### Fedora ≥ 40, Version 6 -Installation of the [php-pecl-redis5](https://apps.fedoraproject.org/packages/php-pecl-redis5) package: +Installation of the [php-pecl-redis6](https://src.fedoraproject.org/rpms/php-pecl-redis6/) package: ~~~ -dnf install php-pecl-redis5 +dnf install php-redis ~~~ -## RHEL / CentOS +## CentOS / RHEL and clones -Installation of the [php-pecl-redis](https://apps.fedoraproject.org/packages/php-pecl-redis) package, from the [EPEL repository](https://fedoraproject.org/wiki/EPEL): +Installation of the php-pecl-redis6 package, available for PHP ≥ 8.3: ~~~ -yum install php-pecl-redis +dnf install php-redis ~~~ ### openSUSE ≥ 15.1 diff --git a/README.md b/README.md index f630c71356..b56b7f539a 100644 --- a/README.md +++ b/README.md @@ -2,40 +2,46 @@ [![Build Status](https://github.com/phpredis/phpredis/actions/workflows/ci.yml/badge.svg)](https://github.com/phpredis/phpredis/actions/workflows/ci.yml) [![Coverity Scan Build Status](https://scan.coverity.com/projects/13205/badge.svg)](https://scan.coverity.com/projects/phpredis-phpredis) -[![PHP version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg)](https://github.com/phpredis/phpredis) +[![PHP version](https://img.shields.io/badge/php-%3E%3D%207.4-8892BF.svg)](https://github.com/phpredis/phpredis) -The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). -This code has been developed and maintained by Owlient from November 2009 to March 2011. +The phpredis extension provides an API for communicating with the [Redis](http://redis.io/) key-value store. [Valkey](https://valkey.io/) and [KeyDB](https://docs.keydb.dev/) are supported as well. -You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to michael.grunder@gmail.com ([Twitter](https://twitter.com/grumi78), Mastodon), p.yatsukhnenko@gmail.com ([@yatsukhnenko](https://twitter.com/yatsukhnenko)), or n.favrefelix@gmail.com ([@yowgi](https://twitter.com/yowgi)). +It is released under the [PHP License, version 3.01](http://www.php.net/license/3_01.txt). +You can send comments, patches, questions [here on github](https://github.com/phpredis/phpredis/issues), to [Michael](mailto:michael.grunder@gmail.com) ([Twitter](https://twitter.com/grumi78), Mastodon), [Pavlo](mailto:p.yatsukhnenko@gmail.com) ([@yatsukhnenko](https://twitter.com/yatsukhnenko)), or [Nicolas](mailto:n.favrefelix@gmail.com) ([@yowgi](https://twitter.com/yowgi)). -## [API Documentation](https://phpredis.github.io/phpredis) -These are a work in progress, but will eventually replace our **ONE README TO RULE THEM ALL** docs. +## Sponsors -## Supporting the project -PhpRedis will always be free and open source software, but if you or your company has found it useful please consider supporting the project. Developing a large, complex, and performant library like PhpRedis takes a great deal of time and effort, and support would be appreciated! :heart: +
+
+ + The next-generation caching layer for PHP. + +


+ Object Cache Pro  + Audiomack.com  + Bluehost.com + OpenLMS.net +

+
-The best way to support the project is through [GitHub sponsors](https://github.com/sponsors/michael-grunder). Many of the reward tiers grant access to our [slack channel](https://phpredis.slack.com) where [myself](https://github.com/michael-grunder) and [Pavlo](https://github.com/yatsukhnenko) are regularly available to answer questions. Additionally this will allow you to provide feedback on which fixes and new features to prioritize. +## Become a Sponsor +PhpRedis will always be free and open source software and if you or your company has found it useful please consider supporting the project. Developing a large, complex, and performant library like PhpRedis takes a great deal of time and effort, and support is greatly appreciated! :heart: -You can also make a one-time contribution with [![PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/michaelgrunder/5) +The ongoing development of PhpRedis is made possible thanks to the generous support of [Relay](https://relay.so), which funds the vast majority of work on the project. Relay is a high-performance in-memory cache and drop-in replacement for PhpRedis, which handles millions of requests per second without breaking a sweat. -## Sponsors -Audiomack.com -Bluehost.com -Object Cache Pro -OpenLMS.net +The best way to support the project is through [GitHub Sponsors](https://github.com/sponsors/michael-grunder). Many of the reward tiers grant access to our [Slack](https://phpredis.slack.com) where [myself](https://github.com/michael-grunder) and [Pavlo](https://github.com/yatsukhnenko) are regularly available to answer questions. Additionally this will allow you to provide feedback on which fixes and features to prioritize. You can also make a one-time contribution with [PayPal](https://www.paypal.me/michaelgrunder/5). -# Table of contents ------ +## Table of contents 1. [Installing/Configuring](#installingconfiguring) * [Installation](#installation) * [PHP Session handler](#php-session-handler) - * [Distributed Redis Array](./array.md#readme) + * [Distributed Redis Array](./arrays.md#readme) * [Redis Cluster support](./cluster.md#readme) * [Redis Sentinel support](./sentinel.md#readme) * [Running the unit tests](#running-the-unit-tests) -1. [Classes and methods](#classes-and-methods) +2. [API Documentation](#api-documentation) +3. [Classes and methods](#classes-and-methods) * [Usage](#usage) * [Connection](#connection) * [Retry and backoff](#retry-and-backoff) @@ -51,31 +57,26 @@ You can also make a one-time contribution with [![PayPal](https://img.shields.io * [Pub/sub](#pubsub) * [Transactions](#transactions) * [Scripting](#scripting) + * [Local Helper Methods](#local-helper-methods) * [Introspection](#introspection) ------ - -# Installing/Configuring ------ +## Installing/Configuring -## Installation +### Installation For everything you should need to install PhpRedis on your system, see the [INSTALL.md](./INSTALL.md) page. -## PHP Session handler +### PHP Session handler -phpredis can be used to store PHP sessions. To do this, configure `session.save_handler` and `session.save_path` in your php.ini to tell phpredis where to store the sessions: -~~~ -session.save_handler = redis -session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2&read_timeout=2.5" -~~~ +phpredis can be used to store PHP sessions. To do this, configure `session.save_handler` and `session.save_path` in your php.ini to tell phpredis where to store the sessions. `session.save_path` can have a simple `host:port` format too, but you need to provide the `tcp://` scheme if you want to use the parameters. The following parameters are available: -* weight (integer): the weight of a host is used in comparison with the others in order to customize the session distribution on several hosts. If host A has twice the weight of host B, it will get twice the amount of sessions. In the example, *host1* stores 20% of all the sessions (1/(1+2+2)) while *host2* and *host3* each store 40% (2/(1+2+2)). The target host is determined once and for all at the start of the session, and doesn't change. The default weight is 1. +* weight (integer): the weight of a host is used in comparison with the others to customize the session distribution on several hosts. If host A has twice the weight of host B, it will get twice the amount of sessions. In the example, *host1* stores 20% of all the sessions (1/(1+2+2)) while *host2* and *host3* each store 40% (2/(1+2+2)). The target host is determined once and for all at the start of the session, and doesn't change. The default weight is 1. * timeout (float): the connection timeout to a redis host, expressed in seconds. If the host is unreachable in that amount of time, the session storage will be unavailable for the client. The default timeout is very high (86400 seconds). -* persistent (integer, should be 1 or 0): defines if a persistent connection should be used. +* read_timeout (float): the timeout for read operations on an established connection, expressed in seconds. If a Redis response is not received within this time, the session operation fails. The default is `0` (no timeout), meaning PHP can hang indefinitely if Redis becomes unresponsive or a connection goes stale. Setting this to a low value (e.g. `2.5`) is strongly recommended in production environments. +* persistent (integer, should be 1 or 0): defines if a persistent connection should be used. The default value is `0` (persistent connection is not used). * prefix (string, defaults to "PHPREDIS_SESSION:"): used as a prefix to the Redis key in which the session is stored. The key is composed of the prefix followed by the session ID. * auth (string, or an array with one or two elements): used to authenticate with the server prior to sending commands. * database (integer): selects a different database. @@ -84,12 +85,32 @@ Sessions have a lifetime expressed in seconds and stored in the INI variable "se The session handler requires a version of Redis supporting `EX` and `NX` options of `SET` command (at least 2.6.12). phpredis can also connect to a unix domain socket: `session.save_path = "unix:///var/run/redis/redis.sock?persistent=1&weight=1&database=0"`. -### Session locking +#### Examples + +Multiple Redis servers: +~~~ +session.save_handler = redis +session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2&read_timeout=2.5" +~~~ + +Login to Redis using username and password: +~~~ +session.save_handler = redis +session.save_path = "tcp://127.0.0.1:6379?auth[]=user&auth[]=password" +~~~ + +Login to Redis using username, password, and set prefix: +~~~ +session.save_handler = redis +session.save_path = "tcp://127.0.0.1:6379?auth[]=user&auth[]=password&prefix=user_PHPREDIS_SESSION:" +~~~ + +#### Session locking **Support**: Locking feature is currently only supported for Redis setup with single master instance (e.g. classic master/slave Sentinel environment). So locking may not work properly in RedisArray or RedisCluster environments. -Following INI variables can be used to configure session locking: +The following INI variables can be used to configure session locking: ~~~ ; Should the locking be enabled? Defaults to: 0. redis.session.locking_enabled = 1 @@ -101,7 +122,17 @@ redis.session.lock_wait_time = 50000 redis.session.lock_retries = 2000 ~~~ -## Running the unit tests +#### Session compression + +The following INI variables can be used to configure session compression: +~~~ +; Should session compression be enabled? Possible values are zstd, lzf, lz4, none. Defaults to: none +redis.session.compression = zstd +; What compression level should be used? Compression level depends on used library. For most deployments range 1-9 should be fine. Defaults to: 3 +redis.session.compression_level = 3 +~~~ + +### Running the unit tests phpredis uses a small custom unit test suite for testing functionality of the various classes. To run tests, simply do the following: @@ -130,29 +161,32 @@ Note that it is possible to run only tests which match a substring of the test i php tests/TestRedis.php --class Redis --test echo ~~~ -# Classes and methods ------ +## API Documentation + +The [API Documentation](https://phpredis.github.io/phpredis) are a work in progress, but will eventually replace our **ONE README TO RULE THEM ALL** docs. -## Usage +## Classes and methods + +### Usage 1. [Class Redis](#class-redis) 1. [Class RedisException](#class-redisexception) 1. [Predefined constants](#predefined-constants) -### Class Redis +#### Class Redis ----- _**Description**_: Creates a Redis client -##### *Example* +###### *Example* ~~~php $redis = new Redis(); ~~~ Starting from version 6.0.0 it's possible to specify configuration options. -This allows to connect lazily to the server without explicitly invoking `connect` command. +This allows to connect lazily to the server without explicitly invoking `connect` / `pconnect`. -##### *Example* +###### *Example* ~~~php $redis = new Redis([ @@ -160,6 +194,7 @@ $redis = new Redis([ 'port' => 6379, 'connectTimeout' => 2.5, 'auth' => ['phpredis', 'phpredis'], + 'database' => 2, 'ssl' => ['verify_peer' => false], 'backoff' => [ 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, @@ -169,24 +204,25 @@ $redis = new Redis([ ]); ~~~ -##### *Parameters* +###### *Parameters* (optional) -*host*: string. can be a host, or the path to a unix domain socket. -*port*: int (default is 6379, should be -1 for unix domain socket) -*connectTimeout*: float, value in seconds (default is 0 meaning unlimited) -*retryInterval*: int, value in milliseconds (optional) -*readTimeout*: float, value in seconds (default is 0 meaning unlimited) -*persistent*: mixed, if value is string then it used as persistend id, else value casts to boolean -*auth*: mixed, authentication information -*ssl*: array, SSL context options +*host*: string, the hostname/FQDN/IP address or the path to a unix domain socket; since v5.0.0 it is possible to specify schema +*port*: int, actual port (e.g. `6379`) or `-1` (or `0`) for unix domain socket; setting to < `0` with non-UDS will assume default of `6379` +*connectTimeout*: float, value in seconds (default is 0 meaning unlimited) +*retryInterval*: int, value in milliseconds (optional) +*readTimeout*: float, value in seconds (default is 0 meaning unlimited) +*persistent*: mixed, If the value is a string it is used as a persistent ID. Non-strings are cast to boolean. +*auth*: mixed, authentication information +*database*: int, database number +*ssl*: array, SSL context options -### Class RedisException +#### Class RedisException ----- phpredis throws a [RedisException](#class-redisexception) object if it can't reach the Redis server. That can happen in case of connectivity issues, -if the Redis service is down, or if the redis host is overloaded. In any other problematic case that does not involve an +if the Redis service is down, or if the Redis host is overloaded. In any other problematic case that does not involve an unreachable server (such as a key not existing, an invalid command, etc), phpredis will return `FALSE`. -### Predefined constants +#### Predefined constants ----- _**Description**_: Available Redis Constants @@ -197,12 +233,12 @@ Redis::REDIS_SET - Set Redis::REDIS_LIST - List Redis::REDIS_ZSET - Sorted set Redis::REDIS_HASH - Hash +Redis::REDIS_STREAM - Stream +Redis::REDIS_VECTORSET - Vector set Redis::REDIS_NOT_FOUND - Not found / other ~~~ -@TODO: OPT_SERIALIZER, AFTER, BEFORE,... - -## Connection +### Connection 1. [connect, open](#connect-open) - Connect to a server 1. [pconnect, popen](#pconnect-popen) - Connect to a server (persistent) @@ -215,25 +251,25 @@ Redis::REDIS_NOT_FOUND - Not found / other 1. [ping](#ping) - Ping the server 1. [echo](#echo) - Echo the given string -### connect, open +#### connect, open ----- -_**Description**_: Connects to a Redis instance. +_**Description**_: Connects to a Redis instance. If phpredis cannot reach the server it will usually throw a [`RedisException`](#class-redisexception); only rarely will `FALSE` be returned without an exception. -##### *Parameters* +###### *Parameters* -*host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema -*port*: int, optional -*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) -*reserved*: should be '' if retry_interval is specified -*retry_interval*: int, value in milliseconds (optional) -*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) -*others*: array, with PhpRedis >= 5.3.0, it allows setting _auth_ and [_stream_](https://www.php.net/manual/en/context.ssl.php) configuration. +*host*: string, *host*: string, the hostname/FQDN/IP address or the path to a unix domain socket; since v5.0.0 it is possible to specify schema +*port*: int, actual port (e.g. `6379`) or `-1` (or `0`) for unix domain socket; setting to < `0` with non-UDS will assume default of `6379`, optional +*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) +*reserved*: should be '' if retry_interval is specified +*retry_interval*: int, value in milliseconds, optional +*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) +*others*: array, with PhpRedis >= 5.3.0, it allows setting _auth_ and [_stream_](https://www.php.net/manual/en/context.ssl.php) configuration. -##### *Return value* +###### *Return value* -*BOOL*: `TRUE` on success, `FALSE` on error. +*BOOL*: `TRUE` on success. Failures typically raise a `RedisException`, though in some edge cases `FALSE` may be returned instead. -##### *Example* +###### *Example* ~~~php $redis->connect('127.0.0.1', 6379); @@ -247,11 +283,46 @@ $redis->connect('/tmp/redis.sock', 0, 1.5, NULL, 0, 1.5); // Unix socket with 1. /* With PhpRedis >= 5.3.0 you can specify authentication and stream information on connect */ $redis->connect('127.0.0.1', 6379, 1, '', 0, 0, ['auth' => ['phpredis', 'phpredis']]); + +/* TLS connections can customise the underlying PHP stream context */ +$redis->connect('tls://redis.example.com', 6380, 1.5, null, 0, 0, [ + 'auth' => ['app-user', 'strong-password'], + 'stream' => [ + 'verify_peer' => true, // validate the server certificate against cafile/capath + 'verify_peer_name' => true, // require the certificate common/SAN name to match peer_name + 'peer_name' => 'redis.example.com', // expected hostname presented by the server certificate + 'cafile' => '/etc/ssl/redis-ca.pem', // CA or bundle used to trust the server certificate + 'capath' => '/etc/ssl/certs', // directory alternative to cafile + 'allow_self_signed' => false, // set to true if you rely on a self-signed certificate + 'local_cert' => '/etc/ssl/client.crt', // client certificate for mutual TLS (optional) + 'local_pk' => '/etc/ssl/client.key', // private key that matches local_cert (optional) + 'passphrase' => 'secret', // passphrase for local_pk if it is encrypted (optional) + 'ciphers' => 'HIGH:!aNULL:!MD5', // TLS cipher list provided to OpenSSL (optional) + ], +]); + +try { + $redis->connect('redis.invalid', 6379); +} catch (RedisException $ex) { + echo "Connection failed: {$ex->getMessage()}"; +} ~~~ +When you pass a `stream` key PhpRedis forwards the options to [`stream_socket_client`](https://www.php.net/manual/en/context.ssl.php). +Commonly used options include: + +- `verify_peer`, `verify_peer_name`, `peer_name`: control server certificate validation behaviour. +- `cafile`/`capath`: provide the trusted certificate authority bundle when the default store is insufficient. +- `allow_self_signed`: permits self-signed certificates when set to `true`. +- `local_cert`, `local_pk`, `passphrase`: configure client-side certificates for mutual TLS. +- `ciphers`: restrict the negotiated TLS cipher suites. +- Any other SSL context option supported by PHP (e.g. `SNI_enabled`, `disable_compression`) can also be supplied. + +The same array format can be used with `pconnect`/`popen`. + **Note:** `open` is an alias for `connect` and will be removed in future versions of phpredis. -### pconnect, popen +#### pconnect, popen ----- _**Description**_: Connects to a Redis instance or reuse a connection already established with `pconnect`/`popen`. @@ -259,28 +330,28 @@ The connection will not be closed on end of request until the php process ends. So be prepared for too many open FD's errors (specially on redis server side) when using persistent connections on many servers connecting to one redis server. -Also more than one persistent connection can be made identified by either host + port + timeout +Also more than one persistent connection can be made, identified by either host + port + timeout or host + persistent_id or unix socket + timeout. -Starting from version 4.2.1, it became possible to use connection pooling by setting INI variable `redis.pconnect.pooling_enabled` to 1. +Since v4.2.1, it became possible to use connection pooling by setting INI variable `redis.pconnect.pooling_enabled` to 1. -This feature is not available in threaded versions. `pconnect` and `popen` then working like their non +This feature is not available in threaded versions. `pconnect` and `popen` then work like their non persistent equivalents. -##### *Parameters* +###### *Parameters* -*host*: string. can be a host, or the path to a unix domain socket. Starting from version 5.0.0 it is possible to specify schema -*port*: int, optional -*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) -*persistent_id*: string. identity for the requested persistent connection -*retry_interval*: int, value in milliseconds (optional) -*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) +*host*: string, *host*: string, the hostname/FQDN/IP address or the path to a unix domain socket; since v5.0.0 it is possible to specify schema +*port*: int, actual port (e.g. `6379`) or `-1` (or `0`) for unix domain socket; setting to < `0` with non-UDS will assume default of `6379`, optional +*timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) +*persistent_id*: string. identity for the requested persistent connection +*retry_interval*: int, value in milliseconds (optional) +*read_timeout*: float, value in seconds (optional, default is 0 meaning it will use default_socket_timeout) -##### *Return value* +###### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. -##### *Example* +###### *Example* ~~~php $redis->pconnect('127.0.0.1', 6379); @@ -294,20 +365,20 @@ $redis->pconnect('/tmp/redis.sock'); // unix domain socket - would be another co **Note:** `popen` is an alias for `pconnect` and will be removed in future versions of phpredis. -### auth +#### auth ----- _**Description**_: Authenticate the connection using a password or a username and password. *Warning*: The password is sent in plain-text over the network. -##### *Parameters* +###### *Parameters* *MIXED*: password -##### *Return value* +###### *Return value* *BOOL*: `TRUE` if the connection is authenticated, `FALSE` otherwise. *Note*: In order to authenticate with a username and password you need Redis >= 6.0. -##### *Example* +###### *Example* ~~~php /* Authenticate with the password 'foobared' */ $redis->auth('foobared'); @@ -323,60 +394,60 @@ $redis->auth(['user' => 'phpredis', 'pass' => 'phpredis']); $redis->auth(['pass' => 'phpredis']); ~~~ -### select +#### select ----- _**Description**_: Change the selected database for the current connection. -##### *Parameters* +###### *Parameters* *INTEGER*: dbindex, the database number to switch to. -##### *Return value* +###### *Return value* `TRUE` in case of success, `FALSE` in case of failure. -##### *Example* +###### *Example* See method for example: [move](#move) -### swapdb +#### swapdb ----- -_**Description**_: Swap one Redis database with another atomically +_**Description**_: Swap one Redis database with another atomically -##### *Parameters* -*INTEGER*: db1 -*INTEGER*: db2 +###### *Parameters* +*INTEGER*: db1 +*INTEGER*: db2 -##### *Return value* +###### *Return value* `TRUE` on success and `FALSE` on failure. *Note*: Requires Redis >= 4.0.0 -##### *Example* +###### *Example* ~~~php $redis->swapdb(0, 1); /* Swaps DB 0 with DB 1 atomically */ ~~~ -### close +#### close ----- _**Description**_: Disconnects from the Redis instance. *Note*: Closing a persistent connection requires PhpRedis >= 4.2.0. -##### *Parameters* +###### *Parameters* None. -##### *Return value* +###### *Return value* *BOOL*: `TRUE` on success, `FALSE` on failure. -### setOption +#### setOption ----- _**Description**_: Set client option. -##### *Parameters* -*parameter name* +###### *Parameters* +*parameter name* *parameter value* -##### *Return value* +###### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. -##### *Example* +###### *Example* ~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // Don't serialize data $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // Use built-in serialize/unserialize @@ -387,9 +458,9 @@ $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON); // Use JSON t $redis->setOption(Redis::OPT_PREFIX, 'myAppName:'); // use custom prefix on all keys /* Options for the SCAN family of commands, indicating whether to abstract - empty results from the user. If set to SCAN_NORETRY (the default), phpredis + empty results from the user. If set to SCAN_NORETRY (the default), phpredis will just issue one SCAN command at a time, sometimes returning an empty - array of results. If set to SCAN_RETRY, phpredis will retry the scan command + array of results. If set to SCAN_RETRY, phpredis will retry the scan command until keys come back OR Redis returns an iterator of zero */ $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); @@ -402,36 +473,36 @@ $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX); ~~~ -### getOption +#### getOption ----- _**Description**_: Get client option. -##### *Parameters* +###### *Parameters* *parameter name* -##### *Return value* +###### *Return value* Parameter value. -##### *Example* +###### *Example* ~~~php -// return Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, +// return Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP, // Redis::SERIALIZER_IGBINARY, Redis::SERIALIZER_MSGPACK or Redis::SERIALIZER_JSON $redis->getOption(Redis::OPT_SERIALIZER); ~~~ -### ping +#### ping ----- _**Description**_: Check the current connection status. -##### *Prototype* +###### *Prototype* ~~~php $redis->ping([string $message]); ~~~ -##### *Return value* +###### *Return value* *Mixed*: This method returns `TRUE` on success, or the passed string if called with an argument. -##### *Example* +###### *Example* ~~~php /* When called without an argument, PING returns `TRUE` */ $redis->ping(); @@ -442,34 +513,34 @@ $redis->ping('hello'); *Note*: Prior to PhpRedis 5.0.0 this command simply returned the string `+PONG`. -### echo +#### echo ----- _**Description**_: Sends a string to Redis, which replies with the same string -##### *Parameters* +###### *Parameters* *STRING*: The message to send. -##### *Return value* +###### *Return value* *STRING*: the same message. -## Retry and backoff +### Retry and backoff 1. [Maximum retries](#maximum-retries) 1. [Backoff algorithms](#backoff-algorithms) -### Maximum retries -You can set and get the maximum retries upon connection issues using the `OPT_MAX_RETRIES` option. Note that this is the number of _retries_, meaning if you set this option to _n_, there will be a maximum _n+1_ attemps overall. Defaults to 10. +#### Maximum retries +You can set and get the maximum retries upon connection issues using the `OPT_MAX_RETRIES` option. Note that this is the number of _retries_, meaning if you set this option to _n_, there will be a maximum _n+1_ attempts overall. Defaults to 10. -##### *Example* +###### *Example* ~~~php $redis->setOption(Redis::OPT_MAX_RETRIES, 5); $redis->getOption(Redis::OPT_MAX_RETRIES); ~~~ -### Backoff algorithms +#### Backoff algorithms You can set the backoff algorithm using the `Redis::OPT_BACKOFF_ALGORITHM` option and choose among the following algorithms described in this blog post by Marc Brooker from AWS: [Exponential Backoff And Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter): * Default: `Redis::BACKOFF_ALGORITHM_DEFAULT` @@ -482,7 +553,7 @@ You can set the backoff algorithm using the `Redis::OPT_BACKOFF_ALGORITHM` optio These algorithms depend on the _base_ and _cap_ parameters, both in milliseconds, which you can set using the `Redis::OPT_BACKOFF_BASE` and `Redis::OPT_BACKOFF_CAP` options, respectively. -##### *Example* +###### *Example* ~~~php $redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER); @@ -490,7 +561,7 @@ $redis->setOption(Redis::OPT_BACKOFF_BASE, 500); // base for backoff computation $redis->setOption(Redis::OPT_BACKOFF_CAP, 750); // backoff time capped at 750ms ~~~ -## Server +### Server 1. [acl](#acl) - Manage Redis ACLs 1. [bgRewriteAOF](#bgrewriteaof) - Asynchronously rewrite the append-only file @@ -502,71 +573,73 @@ $redis->setOption(Redis::OPT_BACKOFF_CAP, 750); // backoff time capped at 750ms 1. [info](#info) - Get information and statistics about the server 1. [lastSave](#lastsave) - Get the timestamp of the last disk save 1. [save](#save) - Synchronously save the dataset to disk (wait to complete) +1. [wait](#wait) - Block until write acknowledgements from replicas +1. [waitaof](#waitaof) - Wait for local AOF fsync and replica acknowledgements 1. [slaveOf](#slaveof) - Make the server a slave of another instance, or promote it to master 1. [time](#time) - Return the current server time 1. [slowLog](#slowlog) - Access the Redis slowLog entries -### acl +#### acl ----- _**Description**_: Execute the Redis ACL command. -##### *Parameters* -_variable_: Minumum of one argument for `Redis` and two for `RedisCluster`. +###### *Parameters* +_variable_: Minimum of one argument for `Redis` and two for `RedisCluster`. -##### *Example* +###### *Example* ~~~php $redis->acl('USERS'); /* Get a list of users */ $redis->acl('LOG'); /* See log of Redis' ACL subsystem */ ~~~ -*Note*: In order to user the `ACL` command you must be communicating with Redis >= 6.0 and be logged into an account that has access to administration commands such as ACL. Please reference [this tutorial](https://redis.io/topics/acl) for an overview of Redis 6 ACLs and [the redis command reference](https://redis.io/commands) for every ACL subcommand. +*Note*: In order to use the `ACL` command you must be communicating with Redis >= 6.0 and be logged into an account that has access to administration commands such as ACL. Please reference [this tutorial](https://redis.io/topics/acl) for an overview of Redis 6 ACLs and [the redis command reference](https://redis.io/commands) for every ACL subcommand. *Note*: If you are connecting to Redis server >= 4.0.0 you can remove a key with the `unlink` method in the exact same way you would use `del`. The Redis [unlink](https://redis.io/commands/unlink) command is non-blocking and will perform the actual deletion asynchronously. -### bgRewriteAOF +#### bgRewriteAOF ----- _**Description**_: Start the background rewrite of AOF (Append-Only File) -##### *Parameters* +###### *Parameters* None. -##### *Return value* +###### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. -##### *Example* +###### *Example* ~~~php $redis->bgRewriteAOF(); ~~~ -### bgSave +#### bgSave ----- _**Description**_: Asynchronously save the dataset to disk (in background) -##### *Parameters* +###### *Parameters* None. -##### *Return value* +###### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. -##### *Example* +###### *Example* ~~~php $redis->bgSave(); ~~~ -### config +#### config ----- _**Description**_: Get or Set the Redis server configuration parameters. -##### *Prototype* +###### *Prototype* ~~~php $redis->config(string $operation, string|array|null $key = NULL, ?string $value = NULL): mixed; ~~~ -##### *Return value* -*Associative array* for `GET`, key(s) -> value(s) +###### *Return value* +*Associative array* for `GET`, key(s) -> value(s) *bool* for `SET`, `RESETSTAT`, and `REWRITE` -##### *Examples* +###### *Examples* ~~~php $redis->config("GET", "*max-*-entries*"); $redis->config("SET", ['timeout', 'loglevel']); @@ -575,55 +648,55 @@ $redis->config("SET", ['timeout' => 128, 'loglevel' => 'warning']); $redis->config('RESETSTAT'); ~~~ -### dbSize +#### dbSize ----- _**Description**_: Return the number of keys in selected database. -##### *Parameters* +###### *Parameters* None. -##### *Return value* +###### *Return value* *INTEGER*: DB size, in number of keys. -##### *Example* +###### *Example* ~~~php $count = $redis->dbSize(); echo "Redis has $count keys\n"; ~~~ -### flushAll +#### flushAll ----- _**Description**_: Remove all keys from all databases. -##### *Parameters* +###### *Parameters* *async* (bool) requires server version 4.0.0 or greater -##### *Return value* +###### *Return value* *BOOL*: Always `TRUE`. -##### *Example* +###### *Example* ~~~php $redis->flushAll(); ~~~ -### flushDb +#### flushDb ----- _**Description**_: Remove all keys from the current database. -##### *Prototype* +###### *Prototype* ~~~php $redis->flushdb(?bool $sync = NULL): Redis|bool; ~~~ -##### *Return value* +###### *Return value* *BOOL*: This command returns true on success and false on failure. -##### *Example* +###### *Example* ~~~php $redis->flushDb(); ~~~ -### info +#### info ----- _**Description**_: Get information and statistics about the server @@ -647,96 +720,138 @@ INFO will call the standard REDIS INFO command, which returns information such a You can pass a variety of options to INFO ([per the Redis documentation](http://redis.io/commands/info)), which will modify what is returned. -##### *Parameters* +###### *Parameters* *option*: The option to provide redis (e.g. "COMMANDSTATS", "CPU") -##### *Example* +###### *Example* ~~~php $redis->info(); /* standard redis INFO command */ $redis->info("COMMANDSTATS"); /* Information on the commands that have been run (>=2.6 only) $redis->info("CPU"); /* just CPU information from Redis INFO */ ~~~ -### lastSave +#### lastSave ----- _**Description**_: Returns the timestamp of the last disk save. -##### *Parameters* +###### *Parameters* None. -##### *Return value* +###### *Return value* *INT*: timestamp. -##### *Example* +###### *Example* ~~~php $redis->lastSave(); ~~~ -### save +#### save ----- _**Description**_: Synchronously save the dataset to disk (wait to complete) -##### *Parameters* +###### *Parameters* None. -##### *Return value* +###### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. If a save is already running, this command will fail and return `FALSE`. -##### *Example* +###### *Example* ~~~php $redis->save(); ~~~ -### slaveOf +#### wait +----- +_**Description**_: Block the client until a specified number of replicas acknowledge the most recent write or until the timeout elapses. + +###### *Parameters* +*numreplicas* (int): The number of replicas that must confirm the write. +*timeout* (int): Maximum time to wait in milliseconds (`0` to wait indefinitely). + +###### *Return value* +*INT*: The number of replicas that acknowledged the write before timing out, or `FALSE` on error. + +###### *Example* +~~~php +$redis->set('user:1', 'marco'); +$confirmed = $redis->wait(1, 500); + +if ($confirmed < 1) { + // Handle the case where the write was not replicated in time. +} +~~~ + +#### waitaof +----- +_**Description**_: Block until Redis has durably written recent changes to the local AOF file the requested number of times and received acknowledgements from the requested number of replicas (Redis >= 7.2). + +###### *Parameters* +*numlocal* (int): Minimum number of local AOF fsync operations to wait for. +*numreplicas* (int): Number of replicas that must acknowledge the write. +*timeout* (int): Maximum time to wait in milliseconds (`0` to wait indefinitely). + +###### *Return value* +*ARRAY*: Two integers, where element `0` is the number of local fsync operations performed and element `1` is the number of replica acknowledgements, or `FALSE` on error. + +###### *Example* +~~~php +[$local, $replicas] = $redis->waitaof(1, 1, 1000); + +if ($local < 1 || $replicas < 1) { + // The write was not persisted or replicated as requested. +} +~~~ + +#### slaveOf ----- _**Description**_: Changes the slave status -##### *Parameters* +###### *Parameters* Either host (string) and port (int), or no parameter to stop being a slave. -##### *Return value* +###### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. -##### *Example* +###### *Example* ~~~php $redis->slaveOf('10.0.1.7', 6379); /* ... */ $redis->slaveOf(); ~~~ -### time +#### time ----- _**Description**_: Return the current server time. -##### *Parameters* +###### *Parameters* (none) -##### *Return value* +###### *Return value* If successful, the time will come back as an associative array with element zero being the unix timestamp, and element one being microseconds. -##### *Examples* +###### *Examples* ~~~php $redis->time(); ~~~ -### slowLog +#### slowLog ----- _**Description**_: Access the Redis slowLog -##### *Parameters* -*Operation* (string): This can be either `GET`, `LEN`, or `RESET` +###### *Parameters* +*Operation* (string): This can be either `GET`, `LEN`, or `RESET` *Length* (integer), optional: If executing a `SLOWLOG GET` command, you can pass an optional length. ##### -##### *Return value* +###### *Return value* The return value of SLOWLOG will depend on which operation was performed. SLOWLOG GET: Array of slowLog entries, as provided by Redis SLOGLOG LEN: Integer, the length of the slowLog SLOWLOG RESET: Boolean, depending on success ##### -##### *Examples* +###### *Examples* ~~~php // Get ten slowLog entries $redis->slowLog('get', 10); @@ -750,9 +865,9 @@ $redis->slowLog('reset'); $redis->slowLog('len'); ~~~ -## Keys and Strings +### Keys and Strings -### Strings +#### Strings ----- * [append](#append) - Append a value to a key @@ -760,12 +875,13 @@ $redis->slowLog('len'); * [bitOp](#bitop) - Perform bitwise operations between strings * [decr, decrBy](#decr-decrby) - Decrement the value of a key * [get](#get) - Get the value of a key +* [getEx](#getex) - Get the value of a key and set its expiration * [getBit](#getbit) - Returns the bit value at offset in the string value stored at key * [getRange](#getrange) - Get a substring of the string stored at a key * [getSet](#getset) - Set the string value of a key and return its old value * [incr, incrBy](#incr-incrby) - Increment the value of a key * [incrByFloat](#incrbyfloat) - Increment the float value of a key by the given amount -* [mGet, getMultiple](#mget-getmultiple) - Get the values of all the given keys +* [mGet](#mget) - Get the values of all the given keys * [mSet, mSetNX](#mset-msetnx) - Set multiple keys to multiple values * [set](#set) - Set the string value of a key * [setBit](#setbit) - Sets or clears the bit at offset in the string value stored at key @@ -774,22 +890,22 @@ $redis->slowLog('len'); * [setRange](#setrange) - Overwrite part of a string at key starting at the specified offset * [strLen](#strlen) - Get the length of the value stored in a key -### Keys +#### Keys ----- * [del, delete, unlink](#del-delete-unlink) - Delete a key * [dump](#dump) - Return a serialized version of the value stored at the specified key. * [exists](#exists) - Determine if a key exists -* [expire, setTimeout, pexpire](#expire-settimeout-pexpire) - Set a key's time to live in seconds +* [expire, pexpire](#expire-pexpire) - Set a key's time to live in seconds * [expireAt, pexpireAt](#expireat-pexpireat) - Set the expiration for a key as a UNIX timestamp -* [keys, getKeys](#keys-getkeys) - Find all keys matching the given pattern +* [keys](#keys-1) - Find all keys matching the given pattern * [scan](#scan) - Scan for keys in the keyspace (Redis >= 2.8.0) * [migrate](#migrate) - Atomically transfer a key from a Redis instance to another one * [move](#move) - Move a key to another database * [object](#object) - Inspect the internals of Redis objects * [persist](#persist) - Remove the expiration from a key * [randomKey](#randomkey) - Return a random key from the keyspace -* [rename, renameKey](#rename-renamekey) - Rename a key +* [rename](#rename) - Rename a key * [renameNx](#renamenx) - Rename a key, only if the new key does not exist * [type](#type) - Determine the type stored at key * [sort](#sort) - Sort the elements in a list, set or sorted set @@ -798,35 +914,57 @@ $redis->slowLog('len'); ----- -### get +#### get ----- _**Description**_: Get the value related to the specified key -##### *Parameters* +###### *Parameters* *key* -##### *Return value* -*String* or *Bool*: If key didn't exist, `FALSE` is returned. Otherwise, the value related to this key is returned. +###### *Return value* +*String* or *Bool*: If key doesn't exist, `FALSE` is returned. Otherwise, the value related to this key is returned. -##### *Examples* +###### *Examples* ~~~php $redis->get('key'); ~~~ -### set +#### getEx +----- +_**Description**_: Get the value related to the specified key and set its expiration + +###### *Parameters* +*key* +*options array* (optional) with the following keys: + * `EX` - expire time in seconds + * `PX` - expire time in milliseconds + * `EXAT` - expire time in seconds since UNIX epoch + * `PXAT` - expire time in milliseconds since UNIX epoch + * `PERSIST` - remove the expiration from the key + +###### *Return value* +*String* or *Bool*: If key doesn't exist, `FALSE` is returned. Otherwise, the value related to this key is returned. + +###### *Examples* + +~~~php +$redis->getEx('key', ['EX' => 10]); // get key and set its expiration to 10 seconds +~~~ + +#### set ----- -_**Description**_: Set the string value in argument as value of the key. If you're using Redis >= 2.6.12, you can pass extended options as explained below +_**Description**_: Set the string value in argument as value of the key. If you're using Redis >= 2.6.12, you can pass extended options as explained below -##### *Parameters* -*Key* -*Value* +###### *Parameters* +*Key* +*Value* *Timeout or Options Array* (optional). If you pass an integer, phpredis will redirect to SETEX, and will try to use Redis >= 2.6.12 extended options if you pass an array with valid values -##### *Return value* +###### *Return value* *Bool* `TRUE` if the command is successful. -##### *Examples* +###### *Examples* ~~~php // Simple key -> value set $redis->set('key', 'value'); @@ -842,55 +980,55 @@ $redis->set('key', 'value', ['xx', 'px'=>1000]); ~~~ -### setEx, pSetEx +#### setEx, pSetEx ----- _**Description**_: Set the string value in argument as value of the key, with a time to live. PSETEX uses a TTL in milliseconds. -##### *Parameters* +###### *Parameters* *Key* *TTL* *Value* -##### *Return value* -*Bool* `TRUE` if the command is successful. +###### *Return value* +*Bool* `TRUE` if the command is successful, `FALSE` in case of failure. -##### *Examples* +###### *Examples* ~~~php $redis->setEx('key', 3600, 'value'); // sets key → value, with 1h TTL. $redis->pSetEx('key', 100, 'value'); // sets key → value, with 0.1 sec TTL. ~~~ -### setNx +#### setNx ----- _**Description**_: Set the string value in argument as value of the key if the key doesn't already exist in the database. -##### *Parameters* -*key* -*value* +###### *Parameters* +*Key* +*Value* -##### *Return value* -*Bool* `TRUE` in case of success, `FALSE` in case of failure. +###### *Return value* +*Bool* `TRUE` if the command is successful, `FALSE` in case of failure. -##### *Examples* +###### *Examples* ~~~php $redis->setNx('key', 'value'); /* return TRUE */ $redis->setNx('key', 'value'); /* return FALSE */ ~~~ -### del, delete, unlink +#### del, delete, unlink ----- _**Description**_: Remove specified keys. -##### *Parameters* +###### *Parameters* An array of keys, or an undefined number of parameters, each a key: *key1* *key2* *key3* ... *keyN* *Note*: If you are connecting to Redis server >= 4.0.0 you can remove a key with the `unlink` method in the exact same way you would use `del`. The Redis [unlink](https://redis.io/commands/unlink) command is non-blocking and will perform the actual deletion asynchronously. -##### *Return value* -*Long* Number of keys deleted. +###### *Return value* +*Long* Number of keys deleted (`0` if none existed to start with), `FALSE` in case of unexpected failure (i.e. connection, etc.) -##### *Examples* +###### *Examples* ~~~php $redis->set('key1', 'val1'); $redis->set('key2', 'val2'); @@ -907,17 +1045,17 @@ $redis->unlink(['key1', 'key2']); **Note:** `delete` is an alias for `del` and will be removed in future versions of phpredis. -### exists +#### exists ----- _**Description**_: Verify if the specified key exists. -##### *Parameters* +###### *Parameters* *key* -##### *Return value* -*long*: The number of keys tested that do exist. +###### *Return value* +*Long* Number of keys tested that exist (`0` if none do), `FALSE` in case of failure. -##### *Examples* +###### *Examples* ~~~php $redis->set('key', 'value'); $redis->exists('key'); /* 1 */ @@ -928,20 +1066,20 @@ $redis->exists(['foo', 'bar', 'baz']); /* 3 */ $redis->exists('foo', 'bar', 'baz'); /* 3 */ ~~~ -**Note**: This function took a single argument and returned TRUE or FALSE in phpredis versions < 4.0.0. +**Note**: This function took a single argument and returned TRUE or FALSE incr('key1'); /* key1 didn't exists, set to 0 before the increment */ /* and now has the value 1 */ @@ -956,18 +1094,18 @@ $redis->incr('key1', 10); /* 14 */ $redis->incrBy('key1', 10); /* 24 */ ~~~ -### incrByFloat +#### incrByFloat ----- _**Description**_: Increment the key with floating point precision. -##### *Parameters* -*key* +###### *Parameters* +*key* *value*: (float) value that will be added to the key -##### *Return value* +###### *Return value* *FLOAT* the new value -##### *Examples* +###### *Examples* ~~~php $redis->incrByFloat('key1', 1.5); /* key1 didn't exist, so it will now be 1.5 */ @@ -977,18 +1115,18 @@ $redis->incrByFloat('key1', -1.5); /* 1.5 */ $redis->incrByFloat('key1', 2.5); /* 4 */ ~~~ -### decr, decrBy +#### decr, decrBy ----- _**Description**_: Decrement the number stored at key by one. If the second argument is filled, it will be used as the integer value of the decrement. -##### *Parameters* -*key* +###### *Parameters* +*key* *value*: value that will be subtracted to key (only for decrBy) -##### *Return value* +###### *Return value* *INT* the new value -##### *Examples* +###### *Examples* ~~~php $redis->decr('key1'); /* key1 didn't exists, set to 0 before the increment */ /* and now has the value -1 */ @@ -1002,17 +1140,17 @@ $redis->decr('key1', 10); /* -13 */ $redis->decrBy('key1', 10); /* -23 */ ~~~ -### mGet, getMultiple +#### mGet ----- _**Description**_: Get the values of all the specified keys. If one or more keys don't exist, the array will contain `FALSE` at the position of the key. -##### *Parameters* +###### *Parameters* *Array*: Array containing the list of the keys -##### *Return value* +###### *Return value* *Array*: Array containing the values related to keys in argument -##### *Examples* +###### *Examples* ~~~php $redis->set('key1', 'value1'); $redis->set('key2', 'value2'); @@ -1021,52 +1159,50 @@ $redis->mGet(['key1', 'key2', 'key3']); /* ['value1', 'value2', 'value3']; $redis->mGet(['key0', 'key1', 'key5']); /* [`FALSE`, 'value1', `FALSE`]; ~~~ -**Note:** `getMultiple` is an alias for `mGet` and will be removed in future versions of phpredis. - -### getSet +#### getSet ----- _**Description**_: Sets a value and returns the previous entry at that key. -##### *Parameters* +###### *Parameters* *Key*: key *STRING*: value -##### *Return value* +###### *Return value* A string, the previous value located at this key. -##### *Example* +###### *Example* ~~~php $redis->set('x', '42'); $exValue = $redis->getSet('x', 'lol'); // return '42', replaces x by 'lol' -$newValue = $redis->get('x')' // return 'lol' +$newValue = $redis->get('x'); // return 'lol' ~~~ -### randomKey +#### randomKey ----- _**Description**_: Returns a random key. -##### *Parameters* +###### *Parameters* None. -##### *Return value* +###### *Return value* *STRING*: an existing key in redis. -##### *Example* +###### *Example* ~~~php $key = $redis->randomKey(); $surprise = $redis->get($key); // who knows what's in there. ~~~ -### move +#### move ----- _**Description**_: Moves a key to a different database. -##### *Parameters* +###### *Parameters* *Key*: key, the key to move. *INTEGER*: dbindex, the database number to move the key to. -##### *Return value* +###### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. -##### *Example* +###### *Example* ~~~php $redis->select(0); // switch to DB 0 @@ -1076,17 +1212,17 @@ $redis->select(1); // switch to DB 1 $redis->get('x'); // will return 42 ~~~ -### rename, renameKey +#### rename ----- _**Description**_: Renames a key. -##### *Parameters* +###### *Parameters* *STRING*: srckey, the key to rename. *STRING*: dstkey, the new name for the key. -##### *Return value* +###### *Return value* *BOOL*: `TRUE` in case of success, `FALSE` in case of failure. -##### *Example* +###### *Example* ~~~php $redis->set('x', '42'); $redis->rename('x', 'y'); @@ -1094,25 +1230,23 @@ $redis->get('y'); // → 42 $redis->get('x'); // → `FALSE` ~~~ -**Note:** `renameKey` is an alias for `rename` and will be removed in future versions of phpredis. - -### renameNx +#### renameNx ----- _**Description**_: Same as rename, but will not replace a key if the destination already exists. This is the same behaviour as setNx. -### expire, pexpire +#### expire, pexpire ----- _**Description**_: Sets an expiration on a key in either seconds or milliseconds. -##### *Prototype* +###### *Prototype* ~~~php public function expire(string $key, int $seconds, ?string $mode = NULL): Redis|bool; public function pexpire(string $key, int $milliseconds, ?string $mode = NULL): Redis|bool; ~~~ -##### *Return value* +###### *Return value* *BOOL*: `TRUE` if an expiration was set, and `FALSE` on failure or if one was not set. You can distinguish between an error and an expiration not being set by checking `getLastError()`. -##### *Example* +###### *Example* ~~~php $redis->set('x', '42'); $redis->expire('x', 3); // x will disappear in 3 seconds. @@ -1120,22 +1254,20 @@ sleep(5); // wait 5 seconds $redis->get('x'); // will return `FALSE`, as 'x' has expired. ~~~ -**Note:** `setTimeout` is an alias for `expire` and will be removed in future versions of phpredis. - -### expireAt, pexpireAt +#### expireAt, pexpireAt ----- -_**Description**_: Seta specific timestamp for a key to expire in seconds or milliseconds. +_**Description**_: Set a specific timestamp for a key to expire in seconds or milliseconds. -##### *Prototype* +###### *Prototype* ~~~php public function expireat(string $key, int $unix_timestamp, ?string $mode = NULL): Redis|bool; public function pexpireat(string $key, int $unix_timestamp_millis, ?string $mode = NULL): Redis|bool; ~~~ -##### *Return value* -*BOOL*: `TRUE` if an expiration was set and `FALSE` if one was not set or in the event on an error. You can detect an actual error by checking `getLastError()`. +###### *Return value* +*BOOL*: `TRUE` if an expiration was set and `FALSE` if one was not set or in the event of an error. You can detect an actual error by checking `getLastError()`. -##### *Example* +###### *Example* ~~~php $redis->set('x', '42'); $redis->expireAt('x', time(NULL) + 3); // x will disappear in 3 seconds. @@ -1143,39 +1275,37 @@ sleep(5); // wait 5 seconds $redis->get('x'); // will return `FALSE`, as 'x' has expired. ~~~ -### keys, getKeys +#### keys ----- _**Description**_: Returns the keys that match a certain pattern. -##### *Parameters* +###### *Parameters* *STRING*: pattern, using '*' as a wildcard. -##### *Return value* +###### *Return value* *Array of STRING*: The keys that match a certain pattern. -##### *Example* +###### *Example* ~~~php $allKeys = $redis->keys('*'); // all keys will match this. $keyWithUserPrefix = $redis->keys('user*'); ~~~ -**Note:** `getKeys` is an alias for `keys` and will be removed in future versions of phpredis. - -### scan +#### scan ----- _**Description**_: Scan the keyspace for keys -##### *Parameters* +###### *Parameters* *LONG (reference)*: Iterator, initialized to NULL *STRING, Optional*: Pattern to match *LONG, Optional*: Count of keys per iteration (only a suggestion to Redis) -##### *Return value* -*Array, boolean*: This function will return an array of keys or FALSE if Redis returned zero keys +###### *Return value* +*Array, boolean*: This function will return an array of keys or FALSE if Redis returns zero keys *Note*: SCAN is a "directed node" command in [RedisCluster](cluster.md#directed-node-commands) -##### *Example* +###### *Example* ~~~php /* Without enabling Redis::SCAN_RETRY (default condition) */ @@ -1207,154 +1337,152 @@ while ($arr_keys = $redis->scan($it)) { echo "No more keys to scan!\n"; ~~~ -### object +#### object ----- _**Description**_: Describes the object pointed to by a key. -##### *Parameters* +###### *Parameters* The information to retrieve (string) and the key (string). Info can be one of the following: * "encoding" * "refcount" * "idletime" -##### *Return value* +###### *Return value* *STRING* for "encoding", *LONG* for "refcount" and "idletime", `FALSE` if the key doesn't exist. -##### *Example* +###### *Example* ~~~php $redis->object("encoding", "l"); // → ziplist $redis->object("refcount", "l"); // → 1 $redis->object("idletime", "l"); // → 400 (in seconds, with a precision of 10 seconds). ~~~ -### type +#### type ----- _**Description**_: Returns the type of data pointed by a given key. -##### *Parameters* +###### *Parameters* *Key*: key -##### *Return value* +###### *Return value* -Depending on the type of the data pointed by the key, this method will return the following value: -string: Redis::REDIS_STRING -set: Redis::REDIS_SET -list: Redis::REDIS_LIST -zset: Redis::REDIS_ZSET -hash: Redis::REDIS_HASH +Depending on the type of the data pointed by the key, this method will return the following value: +string: Redis::REDIS_STRING +set: Redis::REDIS_SET +list: Redis::REDIS_LIST +zset: Redis::REDIS_ZSET +hash: Redis::REDIS_HASH other: Redis::REDIS_NOT_FOUND -##### *Example* +###### *Example* ~~~php $redis->type('key'); ~~~ -### append +#### append ----- _**Description**_: Append specified string to the string stored in specified key. -##### *Parameters* +###### *Parameters* *Key* *Value* -##### *Return value* +###### *Return value* *INTEGER*: Size of the value after the append -##### *Example* +###### *Example* ~~~php $redis->set('key', 'value1'); $redis->append('key', 'value2'); /* 12 */ $redis->get('key'); /* 'value1value2' */ ~~~ -### getRange +#### getRange ----- _**Description**_: Return a substring of a larger string -##### *Parameters* -*key* -*start* +###### *Parameters* +*key* +*start* *end* -##### *Return value* +###### *Return value* *STRING*: the substring -##### *Example* +###### *Example* ~~~php $redis->set('key', 'string value'); $redis->getRange('key', 0, 5); /* 'string' */ $redis->getRange('key', -5, -1); /* 'value' */ ~~~ -**Note**: `substr` is an alias for `getRange` and will be removed in future versions of phpredis. - -### setRange +#### setRange ----- _**Description**_: Changes a substring of a larger string. -##### *Parameters* +###### *Parameters* *key* *offset* *value* -##### *Return value* +###### *Return value* *STRING*: the length of the string after it was modified. -##### *Example* +###### *Example* ~~~php $redis->set('key', 'Hello world'); $redis->setRange('key', 6, "redis"); /* returns 11 */ $redis->get('key'); /* "Hello redis" */ ~~~ -### strLen +#### strLen ----- _**Description**_: Get the length of a string value. -##### *Parameters* +###### *Parameters* *key* -##### *Return value* +###### *Return value* *INTEGER* -##### *Example* +###### *Example* ~~~php $redis->set('key', 'value'); $redis->strlen('key'); /* 5 */ ~~~ -### getBit +#### getBit ----- _**Description**_: Return a single bit out of a larger string -##### *Parameters* -*key* +###### *Parameters* +*key* *offset* -##### *Return value* +###### *Return value* *LONG*: the bit value (0 or 1) -##### *Example* +###### *Example* ~~~php $redis->set('key', "\x7f"); // this is 0111 1111 $redis->getBit('key', 0); /* 0 */ $redis->getBit('key', 1); /* 1 */ ~~~ -### setBit +#### setBit ----- _**Description**_: Changes a single bit of a string. -##### *Parameters* -*key* -*offset* +###### *Parameters* +*key* +*offset* *value*: bool or int (1 or 0) -##### *Return value* +###### *Return value* *LONG*: 0 or 1, the value of the bit before it was set. -##### *Example* +###### *Example* ~~~php $redis->set('key', "*"); // ord("*") = 42 = 0x2f = "0010 1010" $redis->setBit('key', 5, 1); /* returns 0 */ @@ -1362,34 +1490,34 @@ $redis->setBit('key', 7, 1); /* returns 0 */ $redis->get('key'); /* chr(0x2f) = "/" = b("0010 1111") */ ~~~ -### bitOp +#### bitOp ----- _**Description**_: Bitwise operation on multiple keys. -##### *Parameters* -*operation*: either "AND", "OR", "NOT", "XOR" -*ret_key*: return key -*key1* +###### *Parameters* +*operation*: either "AND", "OR", "NOT", "XOR" +*ret_key*: return key +*key1* *key2...* -##### *Return value* +###### *Return value* *LONG*: The size of the string stored in the destination key. -### bitCount +#### bitCount ----- _**Description**_: Count bits in a string. -##### *Parameters* +###### *Parameters* *key* -##### *Return value* +###### *Return value* *LONG*: The number of bits set to 1 in the value behind the input key. -### sort +#### sort ----- _**Description**_: Sort the elements in a list, set or sorted set. -##### *Parameters* +###### *Parameters* *Key*: key *Options*: [key => value, ...] - optional, with the following keys and values: ~~~ @@ -1400,10 +1528,10 @@ _**Description**_: Sort the elements in a list, set or sorted set. 'alpha' => TRUE, 'store' => 'external-key' ~~~ -##### *Return value* +###### *Return value* An array of values, or a number corresponding to the number of elements stored if that was used. -##### *Example* +###### *Example* ~~~php $redis->del('s'); $redis->sAdd('s', 5); @@ -1420,47 +1548,47 @@ var_dump($redis->sort('s', ['sort' => 'desc', 'store' => 'out'])); // (int)5 -### ttl, pttl +#### ttl, pttl ----- _**Description**_: Returns the time to live left for a given key in seconds (ttl), or milliseconds (pttl). -##### *Parameters* +###### *Parameters* *Key*: key -##### *Return value* -*LONG*: The time to live in seconds. If the key has no ttl, `-1` will be returned, and `-2` if the key doesn't exist. +###### *Return value* +*LONG*: The time to live in seconds. If the key has no ttl, `-1` will be returned, and `-2` if the key doesn't exist. -##### *Example* +###### *Example* ~~~php $redis->ttl('key'); ~~~ -### persist +#### persist ----- _**Description**_: Remove the expiration timer from a key. -##### *Parameters* +###### *Parameters* *Key*: key -##### *Return value* +###### *Return value* *BOOL*: `TRUE` if a timeout was removed, `FALSE` if the key didn’t exist or didn’t have an expiration timer. -##### *Example* +###### *Example* ~~~php $redis->persist('key'); ~~~ -### mSet, mSetNx +#### mSet, mSetNx ----- _**Description**_: Sets multiple key-value pairs in one atomic command. MSETNX only returns TRUE if all the keys were set (see SETNX). -##### *Parameters* +###### *Parameters* *Pairs*: [key => value, ...] -##### *Return value* +###### *Return value* *Bool* `TRUE` in case of success, `FALSE` in case of failure. -##### *Example* +###### *Example* ~~~php $redis->mSet(['key0' => 'value0', 'key1' => 'value1']); @@ -1476,50 +1604,50 @@ string(6) "value1" -### dump +#### dump ----- _**Description**_: Dump a key out of a redis database, the value of which can later be passed into redis using the RESTORE command. The data that comes out of DUMP is a binary representation of the key as Redis stores it. -##### *Parameters* +###### *Parameters* *key* string -##### *Return value* +###### *Return value* The Redis encoded value of the key, or FALSE if the key doesn't exist -##### *Examples* +###### *Examples* ~~~php $redis->set('foo', 'bar'); $val = $redis->dump('foo'); // $val will be the Redis encoded key value ~~~ -### restore +#### restore ----- _**Description**_: Restore a key from the result of a DUMP operation. -##### *Parameters* -*key* string. The key name -*ttl* integer. How long the key should live (if zero, no expire will be set on the key) +###### *Parameters* +*key* string. The key name +*ttl* integer. How long the key should live (if zero, no expire will be set on the key) *value* string (binary). The Redis encoded key value (from DUMP) -##### *Examples* +###### *Examples* ~~~php $redis->set('foo', 'bar'); $val = $redis->dump('foo'); $redis->restore('bar', 0, $val); // The key 'bar', will now be equal to the key 'foo' ~~~ -### migrate +#### migrate ----- _**Description**_: Migrates a key to a different Redis instance. **Note:**: Redis introduced migrating multiple keys in 3.0.6, so you must have at least -that version in order to call `migrate` with an array of keys. - -##### *Parameters* -*host* string. The destination host -*port* integer. The TCP port to connect to. -*key(s)* string or array. -*destination-db* integer. The target DB. -*timeout* integer. The maximum amount of time given to this transfer. -*copy* boolean, optional. Should we send the COPY flag to redis. -*replace* boolean, optional. Should we send the REPLACE flag to redis -##### *Examples* +that version to call `migrate` with an array of keys. + +###### *Parameters* +*host* string. The destination host +*port* integer. The TCP port to connect to. +*key(s)* string or array. +*destination-db* integer. The target DB. +*timeout* integer. The maximum amount of time given to this transfer. +*copy* boolean, optional. Should we send the COPY flag to redis. +*replace* boolean, optional. Should we send the REPLACE flag to redis +###### *Examples* ~~~php $redis->migrate('backup', 6379, 'foo', 0, 3600); $redis->migrate('backup', 6379, 'foo', 0, 3600, true, true); /* copy and replace */ @@ -1531,7 +1659,7 @@ $redis->migrate('backup', 6379, ['key1', 'key2', 'key3'], 0, 3600); -## Hashes +### Hashes * [hDel](#hdel) - Delete one or more hash fields * [hExists](#hexists) - Determine if a hash field exists @@ -1549,17 +1677,17 @@ $redis->migrate('backup', 6379, ['key1', 'key2', 'key3'], 0, 3600); * [hScan](#hscan) - Scan a hash key for members * [hStrLen](#hstrlen) - Get the string length of the value associated with field in the hash -### hSet +#### hSet ----- _**Description**_: Adds a value to the hash stored at key. -##### *Parameters* -*key* -*hashKey* +###### *Parameters* +*key* +*hashKey* *value* -##### *Return value* +###### *Return value* *LONG* `1` if value didn't exist and was added successfully, `0` if the value was already present and was replaced, `FALSE` if there was an error. -##### *Example* +###### *Example* ~~~php $redis->del('h') $redis->hSet('h', 'key1', 'hello'); /* 1, 'key1' => 'hello' in the hash at "h" */ @@ -1569,14 +1697,14 @@ $redis->hSet('h', 'key1', 'plop'); /* 0, value was replaced. */ $redis->hGet('h', 'key1'); /* returns "plop" */ ~~~ -### hSetNx +#### hSetNx ----- _**Description**_: Adds a value to the hash stored at key only if this field isn't already in the hash. -##### *Return value* +###### *Return value* *BOOL* `TRUE` if the field was set, `FALSE` if it was already present. -##### *Example* +###### *Example* ~~~php $redis->del('h') $redis->hSetNx('h', 'key1', 'hello'); /* TRUE, 'key1' => 'hello' in the hash at "h" */ @@ -1584,27 +1712,27 @@ $redis->hSetNx('h', 'key1', 'world'); /* FALSE, 'key1' => 'hello' in the hash at ~~~ -### hGet +#### hGet ----- _**Description**_: Gets a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. -##### *Parameters* -*key* +###### *Parameters* +*key* *hashKey* -##### *Return value* -*STRING* The value, if the command executed successfully +###### *Return value* +*STRING* The value, if the command executed successfully *BOOL* `FALSE` in case of failure -### hLen +#### hLen ----- _**Description**_: Returns the length of a hash, in number of items -##### *Parameters* +###### *Parameters* *key* -##### *Return value* +###### *Return value* *LONG* the number of items in a hash, `FALSE` if the key doesn't exist or isn't a hash. -##### *Example* +###### *Example* ~~~php $redis->del('h') $redis->hSet('h', 'key1', 'hello'); @@ -1612,30 +1740,30 @@ $redis->hSet('h', 'key2', 'plop'); $redis->hLen('h'); /* returns 2 */ ~~~ -### hDel +#### hDel ----- _**Description**_: Removes a value from the hash stored at key. If the hash table doesn't exist, or the key doesn't exist, `FALSE` is returned. -##### *Parameters* -*key* -*hashKey1* -*hashKey2* +###### *Parameters* +*key* +*hashKey1* +*hashKey2* ... -##### *Return value* +###### *Return value* *LONG* the number of deleted keys, 0 if the key doesn't exist, `FALSE` if the key isn't a hash. -### hKeys +#### hKeys ----- _**Description**_: Returns the keys in a hash, as an array of strings. -##### *Parameters* +###### *Parameters* *Key*: key -##### *Return value* +###### *Return value* An array of elements, the keys of the hash. This works like PHP's array_keys(). -##### *Example* +###### *Example* ~~~php $redis->del('h'); $redis->hSet('h', 'a', 'x'); @@ -1660,17 +1788,17 @@ array(4) { ~~~ The order is random and corresponds to redis' own internal representation of the set structure. -### hVals +#### hVals ----- _**Description**_: Returns the values in a hash, as an array of strings. -##### *Parameters* +###### *Parameters* *Key*: key -##### *Return value* +###### *Return value* An array of elements, the values of the hash. This works like PHP's array_values(). -##### *Example* +###### *Example* ~~~php $redis->del('h'); $redis->hSet('h', 'a', 'x'); @@ -1695,17 +1823,17 @@ array(4) { ~~~ The order is random and corresponds to redis' own internal representation of the set structure. -### hGetAll +#### hGetAll ----- _**Description**_: Returns the whole hash, as an array of strings indexed by strings. -##### *Parameters* +###### *Parameters* *Key*: key -##### *Return value* +###### *Return value* An array of elements, the contents of the hash. -##### *Example* +###### *Example* ~~~php $redis->del('h'); $redis->hSet('h', 'a', 'x'); @@ -1730,47 +1858,47 @@ array(4) { ~~~ The order is random and corresponds to redis' own internal representation of the set structure. -### hExists +#### hExists ----- _**Description**_: Verify if the specified member exists in a key. -##### *Parameters* -*key* +###### *Parameters* +*key* *memberKey* -##### *Return value* +###### *Return value* *BOOL*: If the member exists in the hash table, return `TRUE`, otherwise return `FALSE`. -##### *Examples* +###### *Examples* ~~~php $redis->hSet('h', 'a', 'x'); $redis->hExists('h', 'a'); /* TRUE */ $redis->hExists('h', 'NonExistingKey'); /* FALSE */ ~~~ -### hIncrBy +#### hIncrBy ----- _**Description**_: Increments the value of a member from a hash by a given amount. -##### *Parameters* -*key* -*member* +###### *Parameters* +*key* +*member* *value*: (integer) value that will be added to the member's value -##### *Return value* +###### *Return value* *LONG* the new value -##### *Examples* +###### *Examples* ~~~php $redis->del('h'); $redis->hIncrBy('h', 'x', 2); /* returns 2: h[x] = 2 now. */ $redis->hIncrBy('h', 'x', 1); /* h[x] ← 2 + 1. Returns 3 */ ~~~ -### hIncrByFloat +#### hIncrByFloat ----- _**Description**_: Increments the value of a hash member by the provided float value -##### *Parameters* -*key* -*member* +###### *Parameters* +*key* +*member* *value*: (float) value that will be added to the member's value -##### *Return value* +###### *Return value* *FLOAT* the new value -##### *Examples* +###### *Examples* ~~~php $redis->del('h'); $redis->hIncrByFloat('h','x', 1.5); /* returns 1.5: h[x] = 1.5 now */ @@ -1778,30 +1906,30 @@ $redis->hIncrByFloat('h', 'x', 1.5); /* returns 3.0: h[x] = 3.0 now */ $redis->hIncrByFloat('h', 'x', -3.0); /* returns 0.0: h[x] = 0.0 now */ ~~~ -### hMSet +#### hMSet ----- _**Description**_: Fills in a whole hash. Non-string values are converted to string, using the standard `(string)` cast. NULL values are stored as empty strings. -##### *Parameters* -*key* +###### *Parameters* +*key* *members*: key → value array -##### *Return value* +###### *Return value* *BOOL* -##### *Examples* +###### *Examples* ~~~php $redis->del('user:1'); $redis->hMSet('user:1', ['name' => 'Joe', 'salary' => 2000]); $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now. ~~~ -### hMGet +#### hMGet ----- _**Description**_: Retrieve the values associated to the specified fields in the hash. -##### *Parameters* -*key* +###### *Parameters* +*key* *memberKeys* Array -##### *Return value* +###### *Return value* *Array* An array of elements, the values of the specified fields in the hash, with the hash keys as array keys. -##### *Examples* +###### *Examples* ~~~php $redis->del('h'); $redis->hSet('h', 'field1', 'value1'); @@ -1809,18 +1937,18 @@ $redis->hSet('h', 'field2', 'value2'); $redis->hMGet('h', ['field1', 'field2']); /* returns ['field1' => 'value1', 'field2' => 'value2'] */ ~~~ -### hScan +#### hScan ----- _**Description**_: Scan a HASH value for members, with an optional pattern and count -##### *Parameters* -*key*: String -*iterator*: Long (reference) -*pattern*: Optional pattern to match against +###### *Parameters* +*key*: String +*iterator*: Long (reference) +*pattern*: Optional pattern to match against *count*: How many keys to return in a go (only a suggestion to Redis) -##### *Return value* +###### *Return value* *Array* An array of members that match our pattern -##### *Examples* +###### *Examples* ~~~php $it = NULL; /* Don't ever return an empty array until we're done iterating */ @@ -1832,54 +1960,54 @@ while($arr_keys = $redis->hScan('hash', $it)) { } ~~~ -### hStrLen +#### hStrLen ----- _**Description**_: Get the string length of the value associated with field in the hash stored at key. -##### *Parameters* -*key*: String +###### *Parameters* +*key*: String *field*: String -##### *Return value* +###### *Return value* *LONG* the string length of the value associated with field, or zero when field is not present in the hash or key does not exist at all. -## Lists +### Lists * [blPop, brPop](#blpop-brpop) - Remove and get the first/last element in a list * [bRPopLPush](#brpoplpush) - Pop a value from a list, push it to another list and return it -* [lIndex, lGet](#lindex-lget) - Get an element from a list by its index +* [lIndex](#lindex) - Get an element from a list by its index * [lInsert](#linsert) - Insert an element before or after another element in a list -* [lLen, lSize](#llen-lsize) - Get the length/size of a list +* [lLen](#llen) - Get the length/size of a list * [lPop](#lpop) - Remove and get the first element in a list * [lPush](#lpush) - Prepend one or multiple values to a list * [lPushx](#lpushx) - Prepend a value to a list, only if the list exists -* [lRange, lGetRange](#lrange-lgetrange) - Get a range of elements from a list -* [lRem, lRemove](#lrem-lremove) - Remove elements from a list +* [lRange](#lrange) - Get a range of elements from a list +* [lRem](#lrem) - Remove elements from a list * [lSet](#lset) - Set the value of an element in a list by its index -* [lTrim, listTrim](#ltrim-listtrim) - Trim a list to the specified range +* [lTrim](#ltrim) - Trim a list to the specified range * [rPop](#rpop) - Remove and get the last element in a list * [rPopLPush](#rpoplpush) - Remove the last element in a list, append it to another list and return it (redis >= 1.1) * [rPush](#rpush) - Append one or multiple values to a list * [rPushX](#rpushx) - Append a value to a list, only if the list exists -### blPop, brPop +#### blPop, brPop ----- _**Description**_: Is a blocking lPop(rPop) primitive. If at least one of the lists contains at least one element, the element will be popped from the head of the list and returned to the caller. If all the list identified by the keys passed in arguments are empty, blPop will block during the specified timeout until an element is pushed to one of those lists. This element will be popped. -##### *Parameters* -*ARRAY* Array containing the keys of the lists -*INTEGER* Timeout -Or -*STRING* Key1 -*STRING* Key2 -*STRING* Key3 -... -*STRING* Keyn +###### *Parameters* +*ARRAY* Array containing the keys of the lists +*INTEGER* Timeout +Or +*STRING* Key1 +*STRING* Key2 +*STRING* Key3 +... +*STRING* Keyn *INTEGER* Timeout -##### *Return value* +###### *Return value* *ARRAY* ['listName', 'element'] -##### *Example* +###### *Example* ~~~php /* Non blocking feature */ $redis->lPush('key1', 'A'); @@ -1907,36 +2035,36 @@ $redis->lPush('key1', 'A'); /* ['key1', 'A'] is returned*/ ~~~ -### bRPopLPush +#### bRPopLPush ----- _**Description**_: A blocking version of `rPopLPush`, with an integral timeout in the third parameter. -##### *Parameters* -*Key*: srckey -*Key*: dstkey +###### *Parameters* +*Key*: srckey +*Key*: dstkey *Long*: timeout -##### *Return value* +###### *Return value* *STRING* The element that was moved in case of success, `FALSE` in case of timeout. -### lIndex, lGet +#### lIndex ----- _**Description**_: Return the specified element of the list stored at the specified key. -0 the first element, 1 the second ... +0 the first element, 1 the second ... -1 the last element, -2 the penultimate ... Return `FALSE` in case of a bad index or a key that doesn't point to a list. -##### *Parameters* -*key* +###### *Parameters* +*key* *index* -##### *Return value* -*String* the element at this index +###### *Return value* +*String* the element at this index *Bool* `FALSE` if the key identifies a non-string data type, or no value corresponds to this index in the list `Key`. -##### *Example* +###### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); @@ -1946,25 +2074,23 @@ $redis->lindex('key1', -1); /* 'C' */ $redis->lindex('key1', 10); /* `FALSE` */ ~~~ -**Note:** `lGet` is an alias for `lIndex` and will be removed in future versions of phpredis. - -### lInsert +#### lInsert ----- _**Description**_: Insert value in the list before or after the pivot value. The parameter options specify the position of the insert (before or after). If the list didn't exists, or the pivot didn't exists, the value is not inserted. -##### *Parameters* -*key* -*position* Redis::BEFORE | Redis::AFTER -*pivot* +###### *Parameters* +*key* +*position* Redis::BEFORE | Redis::AFTER +*pivot* *value* -##### *Return value* +###### *Return value* The number of the elements in the list, -1 if the pivot didn't exists. -##### *Example* +###### *Example* ~~~php $redis->del('key1'); $redis->lInsert('key1', Redis::AFTER, 'A', 'X'); /* 0 */ @@ -1982,18 +2108,18 @@ $redis->lRange('key1', 0, -1); /* ['A', 'B', 'X', 'C', 'Y'] */ $redis->lInsert('key1', Redis::AFTER, 'W', 'value'); /* -1 */ ~~~ -### lPop +#### lPop ----- _**Description**_: Return and remove the first element of the list. -##### *Parameters* +###### *Parameters* *key* -##### *Return value* -*STRING* if command executed successfully +###### *Return value* +*STRING* if command executed successfully *BOOL* `FALSE` in case of failure (empty list) -##### *Example* +###### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); @@ -2001,19 +2127,19 @@ $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->lPop('key1'); /* key1 => [ 'B', 'C' ] */ ~~~ -### lPush +#### lPush ----- _**Description**_: Adds one or more values to the head of a LIST. Creates the list if the key didn't exist. If the key exists and is not a list, `FALSE` is returned. -##### *Prototype* +###### *Prototype* ~~~php $redis->lPush($key, $entry [, $entry, $entry]); ~~~ -##### *Return value* +###### *Return value* *LONG* The new length of the list in case of success, `FALSE` in case of Failure. -##### *Examples* +###### *Examples* ~~~php $redis->del('key1'); $redis->lPush('key1', 'F'); // returns 1 @@ -2025,18 +2151,18 @@ $redis->lPush('key1', 'C', 'B', 'A'); // Returns 6 /* key1 now contains: [ 'A', 'B', 'C', 'D', 'E', 'F' ] ~~~ -### lPushx +#### lPushx ----- _**Description**_: Adds the string value to the head (left) of the list if the list exists. -##### *Parameters* -*key* +###### *Parameters* +*key* *value* String, value to push in key -##### *Return value* +###### *Return value* *LONG* The new length of the list in case of success, `FALSE` in case of Failure. -##### *Examples* +###### *Examples* ~~~php $redis->del('key1'); $redis->lPushx('key1', 'A'); // returns 0 @@ -2046,21 +2172,21 @@ $redis->lPushx('key1', 'C'); // returns 3 /* key1 now points to the following list: [ 'A', 'B', 'C' ] */ ~~~ -### lRange, lGetRange +#### lRange ----- -_**Description**_: Returns the specified elements of the list stored at the specified key in the range [start, end]. start and stop are interpreted as indices: -0 the first element, 1 the second ... +_**Description**_: Returns the specified elements of the list stored at the specified key in the range [start, end]. start and stop are interpreted as indices: +0 the first element, 1 the second ... -1 the last element, -2 the penultimate ... -##### *Parameters* -*key* -*start* +###### *Parameters* +*key* +*start* *end* -##### *Return value* +###### *Return value* *Array* containing the values in specified range. -##### *Example* +###### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); @@ -2068,24 +2194,22 @@ $redis->rPush('key1', 'C'); $redis->lRange('key1', 0, -1); /* ['A', 'B', 'C'] */ ~~~ -**Note:** `lGetRange` is an alias for `lRange` and will be removed in future versions of phpredis. - -### lRem, lRemove +#### lRem ----- _**Description**_: Removes the first `count` occurrences of the value element from the list. If count is zero, all the matching elements are removed. If count is negative, elements are removed from tail to head. **Note**: The argument order is not the same as in the Redis documentation. This difference is kept for compatibility reasons. -##### *Parameters* -*key* -*value* +###### *Parameters* +*key* +*value* *count* -##### *Return value* -*LONG* the number of elements to remove +###### *Return value* +*LONG* the number of elements to remove *BOOL* `FALSE` if the value identified by key is not a list. -##### *Example* +###### *Example* ~~~php $redis->lPush('key1', 'A'); $redis->lPush('key1', 'B'); @@ -2098,21 +2222,19 @@ $redis->lRem('key1', 'A', 2); /* 2 */ $redis->lRange('key1', 0, -1); /* ['C', 'B', 'A'] */ ~~~ -**Note:** `lRemove` is an alias for `lRem` and will be removed in future versions of phpredis. - -### lSet +#### lSet ----- _**Description**_: Set the list at index with the new value. -##### *Parameters* -*key* -*index* +###### *Parameters* +*key* +*index* *value* -##### *Return value* +###### *Return value* *BOOL* `TRUE` if the new value was set. `FALSE` if the index is out of range, or data type identified by key is not a list. -##### *Example* +###### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); @@ -2122,20 +2244,20 @@ $redis->lSet('key1', 0, 'X'); $redis->lindex('key1', 0); /* 'X' */ ~~~ -### lTrim, listTrim +#### lTrim ----- _**Description**_: Trims an existing list so that it will contain only a specified range of elements. -##### *Parameters* -*key* -*start* +###### *Parameters* +*key* +*start* *stop* -##### *Return value* -*Array* +###### *Return value* +*Array* *Bool* return `FALSE` if the key identify a non-list value. -##### *Example* +###### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); @@ -2145,20 +2267,18 @@ $redis->lTrim('key1', 0, 1); $redis->lRange('key1', 0, -1); /* ['A', 'B'] */ ~~~ -**Note:** `listTrim` is an alias for `lTrim` and will be removed in future versions of phpredis. - -### rPop +#### rPop ----- _**Description**_: Returns and removes the last element of the list. -##### *Parameters* +###### *Parameters* *key* -##### *Return value* -*STRING* if command executed successfully +###### *Return value* +*STRING* if command executed successfully *BOOL* `FALSE` in case of failure (empty list) -##### *Example* +###### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); @@ -2166,18 +2286,18 @@ $redis->rPush('key1', 'C'); /* key1 => [ 'A', 'B', 'C' ] */ $redis->rPop('key1'); /* key1 => [ 'A', 'B' ] */ ~~~ -### rPopLPush +#### rPopLPush ----- _**Description**_: Pops a value from the tail of a list, and pushes it to the front of another list. Also return this value. (redis >= 1.1) -##### *Parameters* -*Key*: srckey +###### *Parameters* +*Key*: srckey *Key*: dstkey -##### *Return value* +###### *Return value* *STRING* The element that was moved in case of success, `FALSE` in case of failure. -##### *Example* +###### *Example* ~~~php $redis->del('x', 'y'); @@ -2209,19 +2329,19 @@ array(3) { } ~~~ -### rPush +#### rPush ----- _**Description**_: Adds one or more entries to the tail of a LIST. Redis will create the list if it doesn't exist. -##### *Prototype* +###### *Prototype* ~~~php $redis->rPush($key, $entry [, $entry, $entry]); ~~~ -##### *Return value* +###### *Return value* *LONG* The new length of the list in case of success, `FALSE` in case of Failure. -##### *Examples* +###### *Examples* ~~~php $redis->del('key1'); $redis->rPush('key1', 'A'); // returns 1 @@ -2231,18 +2351,18 @@ $redis->rPush('key1', 'D', 'E', 'F'); // returns 6 /* key1 now contains: [ 'A', 'B', 'C', 'D', 'E', 'F' ] */ ~~~ -### rPushX +#### rPushX ----- _**Description**_: Adds the string value to the tail (right) of the list if the list exists. `FALSE` in case of Failure. -##### *Parameters* -*key* +###### *Parameters* +*key* *value* String, value to push in key -##### *Return value* +###### *Return value* *LONG* The new length of the list in case of success, `FALSE` in case of Failure. -##### *Examples* +###### *Examples* ~~~php $redis->del('key1'); $redis->rPushX('key1', 'A'); // returns 0 @@ -2252,20 +2372,20 @@ $redis->rPushX('key1', 'C'); // returns 3 /* key1 now points to the following list: [ 'A', 'B', 'C' ] */ ~~~ -### lLen, lSize +#### lLen ----- _**Description**_: Returns the size of a list identified by Key. If the list didn't exist or is empty, the command returns 0. If the data type identified by Key is not a list, the command return `FALSE`. -##### *Parameters* +###### *Parameters* *Key* -##### *Return value* -*LONG* The size of the list identified by Key exists. +###### *Return value* +*LONG* The size of the list identified by Key exists. *BOOL* `FALSE` if the data type identified by Key is not list -##### *Example* +###### *Example* ~~~php $redis->rPush('key1', 'A'); $redis->rPush('key1', 'B'); @@ -2275,51 +2395,49 @@ $redis->rPop('key1'); $redis->lLen('key1');/* 2 */ ~~~ -**Note:** `lSize` is an alias for `lLen` and will be removed in future versions of phpredis. - -## Sets +### Sets * [sAdd](#sadd) - Add one or more members to a set -* [sCard, sSize](#scard-ssize) - Get the number of members in a set +* [sCard](#scard) - Get the number of members in a set * [sDiff](#sdiff) - Subtract multiple sets * [sDiffStore](#sdiffstore) - Subtract multiple sets and store the resulting set in a key * [sInter](#sinter) - Intersect multiple sets * [sInterStore](#sinterstore) - Intersect multiple sets and store the resulting set in a key -* [sIsMember, sContains](#sismember-scontains) - Determine if a given value is a member of a set -* [sMembers, sGetMembers](#smembers-sgetmembers) - Get all the members in a set +* [sIsMember](#sismember) - Determine if a given value is a member of a set +* [sMembers](#smembers) - Get all the members in a set * [sMove](#smove) - Move a member from one set to another * [sPop](#spop) - Remove and return one or more members of a set at random * [sRandMember](#srandmember) - Get one or multiple random members from a set -* [sRem, sRemove](#srem-sremove) - Remove one or more members from a set +* [sRem](#srem) - Remove one or more members from a set * [sUnion](#sunion) - Add multiple sets * [sUnionStore](#sunionstore) - Add multiple sets and store the resulting set in a key * [sScan](#sscan) - Scan a set for members -### sAdd +#### sAdd ----- _**Description**_: Adds a value to the set value stored at key. -##### *Parameters* -*key* +###### *Parameters* +*key* *value* -##### *Return value* +###### *Return value* *LONG* the number of elements added to the set. -##### *Example* +###### *Example* ~~~php $redis->sAdd('key1' , 'member1'); /* 1, 'key1' => {'member1'} */ $redis->sAdd('key1' , 'member2', 'member3'); /* 2, 'key1' => {'member1', 'member2', 'member3'}*/ $redis->sAdd('key1' , 'member2'); /* 0, 'key1' => {'member1', 'member2', 'member3'}*/ ~~~ -### sCard, sSize +#### sCard ----- _**Description**_: Returns the cardinality of the set identified by key. -##### *Parameters* +###### *Parameters* *key* -##### *Return value* +###### *Return value* *LONG* the cardinality of the set identified by key, 0 if the set doesn't exist. -##### *Example* +###### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); @@ -2328,19 +2446,17 @@ $redis->sCard('key1'); /* 3 */ $redis->sCard('keyX'); /* 0 */ ~~~ -**Note:** `sSize` is an alias for `sCard` and will be removed in future versions of phpredis. - -### sDiff +#### sDiff ----- _**Description**_: Performs the difference between N sets and returns it. -##### *Parameters* +###### *Parameters* *Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. -##### *Return value* +###### *Return value* *Array of strings*: The difference of the first set will all the others. -##### *Example* +###### *Example* ~~~php $redis->del('s0', 's1', 's2'); @@ -2364,17 +2480,17 @@ array(2) { } ~~~ -### sDiffStore +#### sDiffStore ----- _**Description**_: Performs the same action as sDiff, but stores the result in the first key -##### *Parameters* +###### *Parameters* *Key*: dstkey, the key to store the diff into. *Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis -##### *Return value* +###### *Return value* *INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. -##### *Example* +###### *Example* ~~~php $redis->del('s0', 's1', 's2'); @@ -2400,22 +2516,22 @@ array(2) { } ~~~ -### sInter +#### sInter ----- _**Description**_: Returns the members of a set resulting from the intersection of all the sets held at the specified keys. If just a single key is specified, then this command produces the members of this set. If one of the keys is missing, `FALSE` is returned. -##### *Parameters* +###### *Parameters* key1, key2, keyN: keys identifying the different sets on which we will apply the intersection. -##### *Return value* +###### *Return value* -Array, contain the result of the intersection between those keys. If the intersection between the different sets is empty, the return value will be empty array. +Array, containing the result of the intersection between those keys. If the intersection between the different sets is empty, the return value will be an empty array. -##### *Examples* +###### *Examples* ~~~php $redis->sAdd('key1', 'val1'); $redis->sAdd('key1', 'val2'); @@ -2442,18 +2558,18 @@ array(2) { } ~~~ -### sInterStore +#### sInterStore ----- _**Description**_: Performs a sInter command and stores the result in a new set. -##### *Parameters* +###### *Parameters* *Key*: dstkey, the key to store the diff into. *Keys*: key1, key2... keyN. key1..keyN are intersected as in sInter. -##### *Return value* +###### *Return value* *INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. -##### *Example* +###### *Example* ~~~php $redis->sAdd('key1', 'val1'); $redis->sAdd('key1', 'val2'); @@ -2483,16 +2599,16 @@ array(2) { } ~~~ -### sIsMember, sContains +#### sIsMember ----- _**Description**_: Checks if `value` is a member of the set stored at the key `key`. -##### *Parameters* -*key* +###### *Parameters* +*key* *value* -##### *Return value* +###### *Return value* *BOOL* `TRUE` if `value` is a member of the set at key `key`, `FALSE` otherwise. -##### *Example* +###### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); @@ -2502,19 +2618,17 @@ $redis->sIsMember('key1', 'member1'); /* TRUE */ $redis->sIsMember('key1', 'memberX'); /* FALSE */ ~~~ -**Note:** `sContains` is an alias for `sIsMember` and will be removed in future versions of phpredis. - -### sMembers, sGetMembers +#### sMembers ----- _**Description**_: Returns the contents of a set. -##### *Parameters* +###### *Parameters* *Key*: key -##### *Return value* +###### *Return value* An array of elements, the contents of the set. -##### *Example* +###### *Example* ~~~php $redis->del('s'); $redis->sAdd('s', 'a'); @@ -2537,18 +2651,16 @@ array(3) { ~~~ The order is random and corresponds to redis' own internal representation of the set structure. -**Note:** `sGetMembers` is an alias for `sMembers` and will be removed in future versions of phpredis. - -### sMove +#### sMove ----- _**Description**_: Moves the specified member from the set at srcKey to the set at dstKey. -##### *Parameters* -*srcKey* -*dstKey* +###### *Parameters* +*srcKey* +*dstKey* *member* -##### *Return value* +###### *Return value* *BOOL* If the operation is successful, return `TRUE`. If the srcKey and/or dstKey didn't exist, and/or the member didn't exist in srcKey, `FALSE` is returned. -##### *Example* +###### *Example* ~~~php $redis->sAdd('key1' , 'member11'); $redis->sAdd('key1' , 'member12'); @@ -2560,19 +2672,19 @@ $redis->sMove('key1', 'key2', 'member13'); /* 'key1' => {'member11', 'member12' ~~~ -### sPop +#### sPop ----- _**Description**_: Removes and returns a random element from the set value at Key. -##### *Parameters* -*key* +###### *Parameters* +*key* *count*: Integer, optional -##### *Return value (without count argument)* -*String* "popped" value +###### *Return value (without count argument)* +*String* "popped" value *Bool* `FALSE` if set identified by key is empty or doesn't exist. -##### *Return value (with count argument)* -*Array*: Member(s) returned or an empty array if the set doesn't exist +###### *Return value (with count argument)* +*Array*: Member(s) returned or an empty array if the set doesn't exist *Bool*: `FALSE` on error if the key is not a set -##### *Example* +###### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); @@ -2585,18 +2697,18 @@ $redis->sAdd('key2', 'member1', 'member2', 'member3'); $redis->sPop('key2', 3); /* Will return all members but in no particular order */ ~~~ -### sRandMember +#### sRandMember ----- _**Description**_: Returns a random element from the set value at Key, without removing it. -##### *Parameters* -*key* +###### *Parameters* +*key* *count* (Integer, optional) -##### *Return value* -If no count is provided, a random *String* value from the set will be returned. If a count +###### *Return value* +If no count is provided, a random *String* value from the set will be returned. If a count is provided, an array of values from the set will be returned. Read about the different ways to use the count here: [SRANDMEMBER](http://redis.io/commands/srandmember) *Bool* `FALSE` if set identified by key is empty or doesn't exist. -##### *Example* +###### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); @@ -2614,15 +2726,15 @@ $redis->sRandMember('empty-set', 100); // Will return an empty array $redis->sRandMember('not-a-set', 100); // Will return FALSE ~~~ -### sRem, sRemove +#### sRem ----- _**Description**_: Removes the specified member from the set value stored at key. -##### *Parameters* -*key* +###### *Parameters* +*key* *member* -##### *Return value* +###### *Return value* *LONG* The number of elements removed from the set. -##### *Example* +###### *Example* ~~~php $redis->sAdd('key1' , 'member1'); $redis->sAdd('key1' , 'member2'); @@ -2630,21 +2742,19 @@ $redis->sAdd('key1' , 'member3'); /* 'key1' => {'member1', 'member2', 'member3'} $redis->sRem('key1', 'member2', 'member3'); /*return 2. 'key1' => {'member1'} */ ~~~ -**Note:** `sRemove` is an alias for `sRem` and will be removed in future versions of phpredis. - -### sUnion +#### sUnion ----- _**Description**_: Performs the union between N sets and returns it. -##### *Parameters* +###### *Parameters* *Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. -##### *Return value* +###### *Return value* *Array of strings*: The union of all these sets. **Note:** `sUnion` can also take a single array with keys (see example below). -##### *Example* +###### *Example* ~~~php $redis->del('s0', 's1', 's2'); @@ -2676,19 +2786,19 @@ array(4) { } ~~~ -### sUnionStore +#### sUnionStore ----- _**Description**_: Performs the same action as sUnion, but stores the result in the first key -##### *Parameters* +###### *Parameters* *Key*: dstkey, the key to store the diff into. *Keys*: key1, key2, ... , keyN: Any number of keys corresponding to sets in redis. -##### *Return value* +###### *Return value* *INTEGER*: The cardinality of the resulting set, or `FALSE` in case of a missing key. -##### *Example* +###### *Example* ~~~php $redis->del('s0', 's1', 's2'); @@ -2717,20 +2827,20 @@ array(4) { } ~~~ -### sScan +#### sScan ----- _**Description**_: Scan a set for members -##### *Parameters* -*Key*: The set to search -*iterator*: LONG (reference) to the iterator as we go -*pattern*: String, optional pattern to match against +###### *Parameters* +*Key*: The set to search +*iterator*: LONG (reference) to the iterator as we go +*pattern*: String, optional pattern to match against *count*: How many members to return at a time (Redis might return a different amount) -##### *Return value* +###### *Return value* *Array, boolean*: PHPRedis will return an array of keys or FALSE when we're done iterating -##### *Example* +###### *Example* ~~~php $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* don't return empty results until we're done */ @@ -2753,32 +2863,37 @@ while(($arr_mems = $redis->sScan('set', $it, "*pattern*"))!==FALSE) { } ~~~ -## Sorted sets +### Sorted sets * [bzPop](#bzpop) - Block until Redis can pop the highest or lowest scoring member from one or more ZSETs. * [zAdd](#zadd) - Add one or more members to a sorted set or update its score if it already exists -* [zCard, zSize](#zcard-zsize) - Get the number of members in a sorted set +* [zCard](#zcard) - Get the number of members in a sorted set * [zCount](#zcount) - Count the members in a sorted set with scores within the given values +* [zDiff](#zdiff) - Computes the difference between the first and all successive input sorted sets and return the resulting sorted set +* [zdiffstore](#zdiffstore) - Computes the difference between the first and all successive input sorted sets and stores the result in a new key * [zIncrBy](#zincrby) - Increment the score of a member in a sorted set -* [zinterstore, zInter](#zinterstore-zinter) - Intersect multiple sorted sets and store the resulting sorted set in a new key +* [zInter](#zinter) - Intersect multiple sorted sets and return the resulting sorted set +* [zinterstore](#zinterstore) - Intersect multiple sorted sets and store the resulting sorted set in a new key +* [zMscore](#zmscore) - Get the scores associated with the given members in a sorted set * [zPop](#zpop) - Redis can pop the highest or lowest scoring member from one a ZSET. * [zRange](#zrange) - Return a range of members in a sorted set, by index * [zRangeByScore, zRevRangeByScore](#zrangebyscore-zrevrangebyscore) - Return a range of members in a sorted set, by score * [zRangeByLex](#zrangebylex) - Return a lexicographical range from members that share the same score * [zRank, zRevRank](#zrank-zrevrank) - Determine the index of a member in a sorted set -* [zRem, zDelete, zRemove](#zrem-zdelete-zremove) - Remove one or more members from a sorted set -* [zRemRangeByRank, zDeleteRangeByRank](#zremrangebyrank-zdeleterangebyrank) - Remove all members in a sorted set within the given indexes -* [zRemRangeByScore, zDeleteRangeByScore, zRemoveRangeByScore](#zremrangebyscore-zdeleterangebyscore-zremoverangebyscore) - Remove all members in a sorted set within the given scores +* [zRem](#zrem) - Remove one or more members from a sorted set +* [zRemRangeByRank](#zremrangebyrank) - Remove all members in a sorted set within the given indexes +* [zRemRangeByScore](#zremrangebyscore) - Remove all members in a sorted set within the given scores * [zRevRange](#zrevrange) - Return a range of members in a sorted set, by index, with scores ordered from high to low * [zScore](#zscore) - Get the score associated with the given member in a sorted set -* [zunionstore, zUnion](#zunionstore-zunion) - Add multiple sorted sets and store the resulting sorted set in a new key +* [zUnion](#zunion) - Add multiple sorted sets and return the resulting sorted set +* [zunionstore](#zunionstore) - Add multiple sorted sets and store the resulting sorted set in a new key * [zScan](#zscan) - Scan a sorted set for members -### bzPop +#### bzPop ----- _**Description**_: Block until Redis can pop the highest or lowest scoring members from one or more ZSETs. There are two commands (`BZPOPMIN` and `BZPOPMAX` for popping the lowest and highest scoring elements respectively.) -##### *Prototype* +###### *Prototype* ~~~php $redis->bzPopMin(array $keys, int $timeout): array $redis->bzPopMax(array $keys, int $timeout): array @@ -2787,10 +2902,10 @@ $redis->bzPopMin(string $key1, string $key2, ... int $timeout): array $redis->bzPopMax(string $key1, string $key2, ... int $timeout): array ~~~ -##### *Return value* +###### *Return value* *ARRAY:* Either an array with the key member and score of the highest or lowest element or an empty array if the timeout was reached without an element to pop. -##### *Example* +###### *Example* ~~~php /* Wait up to 5 seconds to pop the *lowest* scoring member from sets `zs1` and `zs2`. */ $redis->bzPopMin(['zs1', 'zs2'], 5); @@ -2803,28 +2918,28 @@ $redis->bzPopMax('zs1', 'zs2', 5); **Note:** Calling these functions with an array of keys or with a variable number of arguments is functionally identical. -### zAdd +#### zAdd ----- _**Description**_: Add one or more members to a sorted set or update its score if it already exists -##### *Prototype* +###### *Prototype* ~~~php $redis->zAdd($key, [ $options ,] $score, $value [, $score1, $value1, ...]); ~~~ -##### *Parameters* +###### *Parameters* *key*: string *options*: array (optional) -*score*: double +*score*: double *value*: string *score1*: double *value1*: string -##### *Return value* +###### *Return value* *Long* 1 if the element is added. 0 otherwise. -##### *Example* +###### *Example* ~~~php $redis->zAdd('key', 1, 'val1'); $redis->zAdd('key', 0, 'val0'); @@ -2835,17 +2950,17 @@ $redis->zRange('key', 0, -1); // [val0, val1, val5] $redis->zAdd('key', ['CH'], 5, 'val5', 10, 'val10', 15, 'val15'); ~~~ -### zCard, zSize +#### zCard ----- _**Description**_: Returns the cardinality of an ordered set. -##### *Parameters* +###### *Parameters* *key* -##### *Return value* +###### *Return value* *Long*, the set's cardinality -##### *Example* +###### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); @@ -2853,21 +2968,19 @@ $redis->zAdd('key', 10, 'val10'); $redis->zCard('key'); /* 3 */ ~~~ -**Note**: `zSize` is an alias for `zCard` and will be removed in future versions of phpredis. - -### zCount +#### zCount ----- _**Description**_: Returns the *number* of elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. -##### *Parameters* -*key* -*start*: string +###### *Parameters* +*key* +*start*: string *end*: string -##### *Return value* +###### *Return value* *LONG* the size of a corresponding zRangeByScore. -##### *Example* +###### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); @@ -2875,19 +2988,88 @@ $redis->zAdd('key', 10, 'val10'); $redis->zCount('key', 0, 3); /* 2, corresponding to ['val0', 'val2'] */ ~~~ -### zIncrBy +#### zDiff +----- +_**Description**_: Computes the difference between the first and all successive input sorted sets in the first argument. The result of the difference will be returned. + +The second argument is a set of options. It can define `WITHSCORES` so that the scores are returned as well. + +###### *Parameters* +*arrayZSetKeys* +*arrayOptions* One option is available: `withscores => TRUE`. + +###### *Return value* +*ARRAY* The result of the difference of sets. + +###### *Example* +~~~php +$redis->del('k1'); +$redis->del('k2'); +$redis->del('k3'); + +$redis->zAdd('k1', 0, 'val0'); +$redis->zAdd('k1', 1, 'val1'); +$redis->zAdd('k1', 3, 'val3'); + +$redis->zAdd('k2', 5, 'val1'); + +$redis->zAdd('k3', 5, 'val0'); +$redis->zAdd('k3', 3, 'val4'); + +$redis->zDiff(['k1', 'k2']); /* ['val0', 'val3'] */ +$redis->zDiff(['k2', 'k1']); /* [] */ +$redis->zDiff(['k1', 'k2'], ['withscores' => true]); /* ['val0' => 0.0, 'val3' => 3.0] */ + +$redis->zDiff(['k1', 'k2', 'k3']); /* ['val3'] */ +$redis->zDiff(['k3', 'k2', 'k1']); /* ['val4'] */ +~~~ + +#### zdiffstore +----- +_**Description**_: Computes the difference between the first and all successive input sorted sets in the second argument. The result of the difference will be stored in the sorted set defined by the first argument. + +###### *Parameters* +*keyOutput* +*arrayZSetKeys* + +###### *Return value* +*LONG* The number of values in the new sorted set. + +###### *Example* +~~~php +$redis->del('k1'); +$redis->del('k2'); +$redis->del('k3'); + +$redis->zAdd('k1', 0, 'val0'); +$redis->zAdd('k1', 1, 'val1'); +$redis->zAdd('k1', 3, 'val3'); + +$redis->zAdd('k2', 5, 'val1'); + +$redis->zAdd('k3', 5, 'val0'); +$redis->zAdd('k3', 3, 'val4'); + +$redis->zdiffstore('ko1', ['k1', 'k2']); /* 2, 'ko1' => ['val0', 'val3'] */ +$redis->zdiffstore('ko2', ['k2', 'k1']); /* 0, 'ko2' => [] */ + +$redis->zdiffstore('ko3', ['k1', 'k2', 'k3']); /* 1, 'ko3' => ['val3'] */ +$redis->zdiffstore('ko4', ['k3', 'k2', 'k1']); /* 1, 'k04' => ['val4'] */ +~~~ + +#### zIncrBy ----- _**Description**_: Increments the score of a member from a sorted set by a given amount. -##### *Parameters* -*key* -*value*: (double) value that will be added to the member's score +###### *Parameters* +*key* +*value*: (double) value that will be added to the member's score *member* -##### *Return value* +###### *Return value* *DOUBLE* the new value -##### *Examples* +###### *Examples* ~~~php $redis->del('key'); $redis->zIncrBy('key', 2.5, 'member1'); /* key or member1 didn't exist, so member1's score is to 0 before the increment */ @@ -2895,23 +3077,59 @@ $redis->zIncrBy('key', 2.5, 'member1'); /* key or member1 didn't exist, so membe $redis->zIncrBy('key', 1, 'member1'); /* 3.5 */ ~~~ -### zinterstore, zInter +#### zInter ----- -_**Description**_: Creates an intersection of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. +_**Description**_: Creates an intersection of sorted sets given in first argument. The result of the intersection will be returned. + +The second optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. +The third argument is a set of options. It can define the `AGGREGATE` option which specify how the results of the intersection are aggregated. It can also define `WITHSCORES` so that the scores are returned as well. + +###### *Parameters* +*arrayZSetKeys* +*arrayWeights* +*arrayOptions* Two options are available: `withscores => TRUE`, and `aggregate => $behaviour`. Either "SUM", "MIN", or "MAX" defines the behaviour to use on duplicate entries during the zinter. + +###### *Return value* +*ARRAY* The result of the intersection of sets. + +###### *Example* +~~~php +$redis->del('k1'); +$redis->del('k2'); +$redis->del('k3'); + +$redis->zAdd('k1', 0, 'val0'); +$redis->zAdd('k1', 1, 'val1'); +$redis->zAdd('k1', 3, 'val3'); + +$redis->zAdd('k2', 5, 'val1'); +$redis->zAdd('k2', 3, 'val3'); + +$redis->zinter(['k1', 'k2']); /* ['val1', 'val3'] */ +$redis->zinter(['k1', 'k2'], [1, 1]); /* ['val1', 'val3'] */ + +/* Weighted zinter */ +$redis->zinter(['k1', 'k2'], [1, 5], 'min'); /* ['val1', 'val3'] */ +$redis->zinter(['k1', 'k2'], [1, 5], 'max'); /* ['val3', 'val1'] */ +~~~ + +#### zinterstore +----- +_**Description**_: Creates an intersection of sorted sets given in second argument. The result of the intersection will be stored in the sorted set defined by the first argument. The third optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. -The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. +The forth argument defines the `AGGREGATE` option which specify how the results of the intersection are aggregated. -##### *Parameters* -*keyOutput* -*arrayZSetKeys* -*arrayWeights* +###### *Parameters* +*keyOutput* +*arrayZSetKeys* +*arrayWeights* *aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zinterstore. -##### *Return value* +###### *Return value* *LONG* The number of values in the new sorted set. -##### *Example* +###### *Example* ~~~php $redis->del('k1'); $redis->del('k2'); @@ -2926,24 +3144,41 @@ $redis->zAdd('k1', 0, 'val0'); $redis->zAdd('k1', 1, 'val1'); $redis->zAdd('k1', 3, 'val3'); -$redis->zAdd('k2', 2, 'val1'); +$redis->zAdd('k2', 5, 'val1'); $redis->zAdd('k2', 3, 'val3'); $redis->zinterstore('ko1', ['k1', 'k2']); /* 2, 'ko1' => ['val1', 'val3'] */ -$redis->zinterstore('ko2', ['k1', 'k2'], [1, 1]); /* 2, 'ko2' => ['val1', 'val3'] */ +$redis->zinterstore('ko2', ['k1', 'k2'], [1, 1]); /* 2, 'ko2' => ['val1', 'val3'] */ /* Weighted zinterstore */ $redis->zinterstore('ko3', ['k1', 'k2'], [1, 5], 'min'); /* 2, 'ko3' => ['val1', 'val3'] */ $redis->zinterstore('ko4', ['k1', 'k2'], [1, 5], 'max'); /* 2, 'ko4' => ['val3', 'val1'] */ ~~~ -**Note:** `zInter` is an alias for `zinterstore` and will be removed in future versions of phpredis. +#### zMscore +----- +_**Description**_: Returns the scores of the given members in the specified sorted set. + +###### *Parameters* +*key* +*members*: member1, member2, ... , memberN: Any number of members in the specified sorted set. + +###### *Return value* +*ARRAY* or *FALSE* when the key is not found. Array entries corresponding to members that do not exist will be `false`. + +###### *Example* +~~~php +$redis->zAdd('key', 2.5, 'val2'); +$redis->zAdd('key', 4.5, 'val4'); + +$redis->zMscore('key', 'val2', 'val3', 'val4'); /* [2.5, false, 4.5] */ +~~~ -### zPop +#### zPop ----- _**Description**_: Can pop the highest or lowest scoring members from one ZSETs. There are two commands (`ZPOPMIN` and `ZPOPMAX` for popping the lowest and highest scoring elements respectively.) -##### *Prototype* +###### *Prototype* ~~~php $redis->zPopMin(string $key, int $count): array $redis->zPopMax(string $key, int $count): array @@ -2952,10 +3187,10 @@ $redis->zPopMin(string $key, int $count): array $redis->zPopMax(string $key, int $count): array ~~~ -##### *Return value* +###### *Return value* *ARRAY:* Either an array with the key member and score of the highest or lowest element or an empty array if there is no element available. -##### *Example* +###### *Example* ~~~php /* Pop the *lowest* scoring member from set `zs1`. */ $redis->zPopMin('zs1', 5); @@ -2964,24 +3199,24 @@ $redis->zPopMin('zs1', 5); $redis->zPopMax('zs1', 5); ~~~ -### zRange +#### zRange ----- _**Description**_: Returns a range of elements from the ordered set stored at the specified key, with values in the range [start, end]. -Start and stop are interpreted as zero-based indices: -`0` the first element, `1` the second ... -`-1` the last element, `-2` the penultimate ... +Start and stop are interpreted as zero-based indices: +`0` the first element, `1` the second ... +`-1` the last element, `-2` the penultimate ... -##### *Parameters* +###### *Parameters* *key* -*start*: long -*end*: long +*start*: long +*end*: long *withscores*: bool = false -##### *Return value* +###### *Return value* *Array* containing the values in specified range. -##### *Example* +###### *Example* ~~~php $redis->zAdd('key1', 0, 'val0'); $redis->zAdd('key1', 2, 'val2'); @@ -2992,22 +3227,22 @@ $redis->zRange('key1', 0, -1); /* ['val0', 'val2', 'val10'] */ $redis->zRange('key1', 0, -1, true); /* ['val0' => 0, 'val2' => 2, 'val10' => 10] */ ~~~ -### zRangeByScore, zRevRangeByScore +#### zRangeByScore, zRevRangeByScore ----- _**Description**_: Returns the elements of the sorted set stored at the specified key which have scores in the range [start,end]. Adding a parenthesis before `start` or `end` excludes it from the range. +inf and -inf are also valid limits. zRevRangeByScore returns the same items in reverse order, when the `start` and `end` parameters are swapped. -##### *Parameters* -*key* -*start*: string -*end*: string +###### *Parameters* +*key* +*start*: string +*end*: string *options*: array Two options are available: `withscores => TRUE`, and `limit => [$offset, $count]` -##### *Return value* +###### *Return value* *Array* containing the values in specified range. -##### *Example* +###### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); @@ -3019,21 +3254,21 @@ $redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE, 'limit' => [1, 1]]); / $redis->zRangeByScore('key', '-inf', '+inf', ['withscores' => TRUE]); /* ['val0' => 0, 'val2' => 2, 'val10' => 10] */ ~~~ -### zRangeByLex +#### zRangeByLex ----- _**Description**_: Returns a lexicographical range of members in a sorted set, assuming the members have the same score. The min and max values are required to start with '(' (exclusive), '[' (inclusive), or be exactly the values '-' (negative inf) or '+' (positive inf). The command must be called with either three *or* five arguments or will return FALSE. -##### *Parameters* -*key*: The ZSET you wish to run against -*min*: The minimum alphanumeric value you wish to get -*max*: The maximum alphanumeric value you wish to get -*offset*: Optional argument if you wish to start somewhere other than the first element. +###### *Parameters* +*key*: The ZSET you wish to run against +*min*: The minimum alphanumeric value you wish to get +*max*: The maximum alphanumeric value you wish to get +*offset*: Optional argument if you wish to start somewhere other than the first element. *limit*: Optional argument if you wish to limit the number of elements returned. -##### *Return value* +###### *Return value* *Array* containing the values in the specified range. -##### *Example* +###### *Example* ~~~php foreach(['a','b','c','d','e','f','g'] as $c) $redis->zAdd('key',0,$c); @@ -3043,18 +3278,18 @@ $redis->zRangeByLex('key','-','(c') /* ['a','b'] */ $redis->zRangeByLex('key','-','[c',1,2) /* ['b','c'] */ ~~~ -### zRank, zRevRank +#### zRank, zRevRank ----- _**Description**_: Returns the rank of a given member in the specified sorted set, starting at 0 for the item with the smallest score. zRevRank starts at 0 for the item with the *largest* score. -##### *Parameters* -*key* +###### *Parameters* +*key* *member* -##### *Return value* +###### *Return value* *Long*, the item's rank. -##### *Example* +###### *Example* ~~~php $redis->del('z'); $redis->zAdd('key', 1, 'one'); @@ -3065,39 +3300,37 @@ $redis->zRevRank('key', 'one'); /* 1 */ $redis->zRevRank('key', 'two'); /* 0 */ ~~~ -### zRem, zDelete, zRemove +#### zRem ----- _**Description**_: Delete one or more members from a sorted set. -##### *Prototype* +###### *Prototype* ~~~php $redis->zRem($key, $member [, $member ...]); ~~~ -##### *Return value* +###### *Return value* *LONG:* The number of members deleted. -##### *Example* +###### *Example* ~~~php $redis->zAdd('key', 0, 'val0', 1, 'val1', 2, 'val2'); $redis->zRem('key', 'val0', 'val1', 'val2'); // Returns: 3 ~~~ -**Note:** `zDelete` and `zRemove` are an alias for `zRem` and will be removed in future versions of phpredis. - -### zRemRangeByRank, zDeleteRangeByRank +#### zRemRangeByRank ----- _**Description**_: Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end]. -##### *Parameters* -*key* -*start*: LONG +###### *Parameters* +*key* +*start*: LONG *end*: LONG -##### *Return value* +###### *Return value* *LONG* The number of values deleted from the sorted set -##### *Example* +###### *Example* ~~~php $redis->zAdd('key', 1, 'one'); $redis->zAdd('key', 2, 'two'); @@ -3106,21 +3339,19 @@ $redis->zRemRangeByRank('key', 0, 1); /* 2 */ $redis->zRange('key', 0, -1, ['withscores' => TRUE]); /* ['three' => 3] */ ~~~ -**Note:** `zDeleteRangeByRank` is an alias for `zRemRangeByRank` and will be removed in future versions of phpredis. - -### zRemRangeByScore, zDeleteRangeByScore, zRemoveRangeByScore +#### zRemRangeByScore ----- _**Description**_: Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end]. -##### *Parameters* -*key* -*start*: double or "+inf" or "-inf" string +###### *Parameters* +*key* +*start*: double or "+inf" or "-inf" string *end*: double or "+inf" or "-inf" string -##### *Return value* +###### *Return value* *LONG* The number of values deleted from the sorted set -##### *Example* +###### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); @@ -3128,24 +3359,22 @@ $redis->zAdd('key', 10, 'val10'); $redis->zRemRangeByScore('key', 0, 3); /* 2 */ ~~~ -**Note:** `zDeleteRangeByScore` and `zRemoveRangeByScore` are an alias for `zRemRangeByScore` and will be removed in future versions of phpredis. - -### zRevRange +#### zRevRange ----- -_**Description**_: Returns the elements of the sorted set stored at the specified key in the range [start, end] in reverse order. start and stop are interpreted as zero-based indices: -`0` the first element, `1` the second ... +_**Description**_: Returns the elements of the sorted set stored at the specified key in the range [start, end] in reverse order. start and stop are interpreted as zero-based indices: +`0` the first element, `1` the second ... `-1` the last element, `-2` the penultimate ... -##### *Parameters* -*key* -*start*: long -*end*: long +###### *Parameters* +*key* +*start*: long +*end*: long *withscores*: bool = false -##### *Return value* +###### *Return value* *Array* containing the values in specified range. -##### *Example* +###### *Example* ~~~php $redis->zAdd('key', 0, 'val0'); $redis->zAdd('key', 2, 'val2'); @@ -3156,40 +3385,74 @@ $redis->zRevRange('key', 0, -1); /* ['val10', 'val2', 'val0'] */ $redis->zRevRange('key', 0, -1, true); /* ['val10' => 10, 'val2' => 2, 'val0' => 0] */ ~~~ -### zScore +#### zScore ----- _**Description**_: Returns the score of a given member in the specified sorted set. -##### *Parameters* -*key* +###### *Parameters* +*key* *member* -##### *Return value* +###### *Return value* *Double* or *FALSE* when the value is not found -##### *Example* +###### *Example* ~~~php $redis->zAdd('key', 2.5, 'val2'); $redis->zScore('key', 'val2'); /* 2.5 */ ~~~ -### zunionstore, zUnion +#### zUnion +----- +_**Description**_: Creates an union of sorted sets given in first argument. The result of the union will be returned. + +The second optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. +The third argument is a set of options. It can define the `AGGREGATE` option which specify how the results of the intersection are aggregated. It can also define `WITHSCORES` so that the scores are returned as well. + +###### *Parameters* +*arrayZSetKeys* +*arrayWeights* +*arrayOptions* Two options are available: `withscores => TRUE`, and `aggregate => $behaviour`. Either "SUM", "MIN", or "MAX" defines the behaviour to use on duplicate entries during the zunion. + +###### *Return value* +*ARRAY* The result of the union of sets. + +###### *Example* +~~~php +$redis->del('k1'); +$redis->del('k2'); +$redis->del('k3'); + +$redis->zAdd('k1', 0, 'val0'); +$redis->zAdd('k1', 1, 'val1'); + +$redis->zAdd('k2', 2, 'val2'); +$redis->zAdd('k2', 3, 'val3'); + +$redis->zunion(['k1', 'k2']); /* ['val0', 'val1', 'val2', 'val3'] */ + +/* Weighted zunion */ +$redis->zunion(['k1', 'k2'], [1, 1]); /* ['val0', 'val1', 'val2', 'val3'] */ +$redis->zunion(['k1', 'k2'], [5, 1]); /* ['val0', 'val2', 'val3', 'val1'] */ +~~~ + +#### zunionstore ----- _**Description**_: Creates an union of sorted sets given in second argument. The result of the union will be stored in the sorted set defined by the first argument. The third optional argument defines `weights` to apply to the sorted sets in input. In this case, the `weights` will be multiplied by the score of each element in the sorted set before applying the aggregation. The forth argument defines the `AGGREGATE` option which specify how the results of the union are aggregated. -##### *Parameters* -*keyOutput* -*arrayZSetKeys* -*arrayWeights* +###### *Parameters* +*keyOutput* +*arrayZSetKeys* +*arrayWeights* *aggregateFunction* Either "SUM", "MIN", or "MAX": defines the behaviour to use on duplicate entries during the zunionstore. -##### *Return value* +###### *Return value* *LONG* The number of values in the new sorted set. -##### *Example* +###### *Example* ~~~php $redis->del('k1'); $redis->del('k2'); @@ -3211,22 +3474,20 @@ $redis->zunionstore('ko2', ['k1', 'k2'], [1, 1]); /* 4, 'ko2' => ['val0', 'val1' $redis->zunionstore('ko3', ['k1', 'k2'], [5, 1]); /* 4, 'ko3' => ['val0', 'val2', 'val3', 'val1'] */ ~~~ -**Note:** `zUnion` is an alias for `zunionstore` and will be removed in future versions of phpredis. - -### zScan +#### zScan ----- _**Description**_: Scan a sorted set for members, with optional pattern and count -##### *Parameters* -*key*: String, the set to scan -*iterator*: Long (reference), initialized to NULL -*pattern*: String (optional), the pattern to match +###### *Parameters* +*key*: String, the set to scan +*iterator*: Long (reference), initialized to NULL +*pattern*: String (optional), the pattern to match *count*: How many keys to return per iteration (Redis might return a different number) -##### *Return value* +###### *Return value* *Array, boolean* PHPRedis will return matching keys from Redis, or FALSE when iteration is complete -##### *Example* +###### *Example* ~~~php $it = NULL; $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); @@ -3237,49 +3498,49 @@ while($arr_matches = $redis->zScan('zset', $it, '*pattern*')) { } ~~~ -## HyperLogLogs +### HyperLogLogs -### pfAdd +#### pfAdd ----- _**Description**_: Adds the specified elements to the specified HyperLogLog. -##### *Prototype* +###### *Prototype* ~~~php $redis->pfAdd($key, Array $elements); ~~~ -##### *Parameters* -_Key_ -_Array of values_ +###### *Parameters* +_Key_ +_Array of values_ -##### *Return value* +###### *Return value* *Integer*: 1 if at least 1 HyperLogLog internal register was altered. 0 otherwise. -##### *Example* +###### *Example* ~~~php $redis->pfAdd('hll', ['a', 'b', 'c']); // (int) 1 $redis->pfAdd('hll', ['a', 'b']); // (int) 0 ~~~ -### pfCount +#### pfCount ----- _**Description**_: Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s). -##### *Prototype* +###### *Prototype* ~~~php $redis->pfCount($key); $redis->pfCount(Array $keys); ~~~ -##### *Parameters* -_Key_ or _Array of keys_ +###### *Parameters* +_Key_ or _Array of keys_ -##### *Return value* +###### *Return value* *Integer*: The approximated number of unique elements observed via [pfAdd](#pfAdd). -##### *Example* +###### *Example* ~~~php $redis->pfAdd('hll1', ['a', 'b', 'c']); // (int) 1 $redis->pfCount('hll1'); // (int) 3 @@ -3290,24 +3551,24 @@ $redis->pfCount('hll2'); // (int) 3 $redis->pfCount(['hll1', 'hll2']); // (int) 5 ~~~ -### pfMerge +#### pfMerge ----- _**Description**_: Merge N different HyperLogLogs into a single one. -##### *Prototype* +###### *Prototype* ~~~php $redis->pfMerge($destkey, Array $sourceKeys); ~~~ -##### *Parameters* -_Destination Key_ -_Array of Source Keys_ +###### *Parameters* +_Destination Key_ +_Array of Source Keys_ -##### *Return value* +###### *Return value* *BOOL*: `TRUE` on success, `FALSE` on error. -##### *Example* +###### *Example* ~~~php $redis->pfAdd('hll1', ['a', 'b', 'c']); // (int) 1 $redis->pfAdd('hll2', ['d', 'e', 'a']); // (int) 1 @@ -3317,22 +3578,22 @@ $redis->pfMerge('hll3', ['hll1', 'hll2']); // true $redis->pfCount('hll3'); // (int) 5 ~~~ -## Geocoding +### Geocoding -### geoAdd +#### geoAdd ----- -##### *Prototype* +###### *Prototype* ~~~php $redis->geoAdd($key, $longitude, $latitude, $member [, $longitude, $latitude, $member, ...]); ~~~ _**Description**_: Add one or more geospatial items to the specified key. This function must be called with at least one _longitude, latitude, member_ triplet. -##### *Return value* +###### *Return value* *Integer*: The number of elements added to the geospatial key. -##### *Example* +###### *Example* ~~~php $redis->del("myplaces"); @@ -3342,29 +3603,29 @@ $result = $redis->geoAdd( -122.431, 37.773, "San Francisco", -157.858, 21.315, "Honolulu" ); -~~~ +~~~ -### geoHash +#### geoHash ----- -##### *Prototype* +###### *Prototype* ~~~php $redis->geoHash($key, $member [, $member, $member, ...]); ~~~ -_**Description**_: Retrieve Geohash strings for one or more elements of a geospatial index. +_**Description**_: Retrieve Geohash strings for one or more elements of a geospatial index. -##### *Return value* -*Array*: One or more Redis Geohash encoded strings. +###### *Return value* +*Array*: One or more Redis Geohash encoded strings. -##### *Example* +###### *Example* ~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); $hashes = $redis->geoHash("hawaii", "Honolulu", "Maui"); var_dump($hashes); ~~~ -##### *Output* +###### *Output* ~~~ array(2) { [0]=> @@ -3374,27 +3635,27 @@ array(2) { } ~~~ -### geoPos +#### geoPos ----- -##### *Prototype* +###### *Prototype* ~~~php $redis->geoPos($key, $member [, $member, $member, ...]); ~~~ _**Description**_: Return longitude, latitude positions for each requested member. -##### *Return value* +###### *Return value* *Array*: One or more longitude/latitude positions -##### *Example* +###### *Example* ~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); $positions = $redis->geoPos("hawaii", "Honolulu", "Maui"); var_dump($positions); ~~~ -##### *Output* +###### *Output* ~~~ array(2) { [0]=> @@ -3414,10 +3675,10 @@ array(2) { } ~~~ -### GeoDist +#### GeoDist ----- -##### *Prototype* +###### *Prototype* ~~~php $redis->geoDist($key, $member1, $member2 [, $unit]); ~~~ @@ -3430,10 +3691,10 @@ _**Description**_: Return the distance between two members in a geospatial set. * 'mi' => Miles * 'ft' => Feet -##### *Return value* -*Double*: The distance between the two passed members in the units requested (meters by default). +###### *Return value* +*Double*: The distance between the two passed members in the units requested (meters by default). -##### *Example* +###### *Example* ~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); @@ -3452,9 +3713,9 @@ echo " feet : $feet\n"; $inches = $redis->geoDist("hawaii", "Honolulu", "Maui", 'in'); echo "Invalid unit returned:\n"; var_dump($inches); -~~~ +~~~ -##### *Output* +###### *Output* ~~~ Distance between Honolulu and Maui: meters : 168275.204 @@ -3465,18 +3726,18 @@ Invalid unit returned: bool(false) ~~~ -### geoRadius +#### geoRadius ----- -##### *Prototype* +###### *Prototype* ~~~php $redis->geoRadius($key, $longitude, $latitude, $radius, $unit [, Array $options]); ~~~ -_**Description**_: Return members of a set with geospatial information that are within the radius specified by the caller. +_**Description**_: Return members of a set with geospatial information that are within the radius specified by the caller. -##### *Options Array* -The georadius command can be called with various options that control how Redis returns results. The following table describes the options phpredis supports. All options are case insensitive. +###### *Options Array* +The georadius command can be called with various options that control how Redis returns results. The following table describes the options phpredis supports. All options are case insensitive. | Key | Value | Description | :--- | :--- | :---- | @@ -3489,13 +3750,13 @@ The georadius command can be called with various options that control how Redis | STORE | _key_ | Store results in _key_ | STOREDIST | _key_ | Store the results as distances in _key_ - *Note*: It doesn't make sense to pass both `ASC` and `DESC` options but if both are passed the last one passed will be used. + *Note*: It doesn't make sense to pass both `ASC` and `DESC` options but if both are passed the last one passed will be used. *Note*: When using `STORE[DIST]` in Redis Cluster, the store key must has to the same slot as the query key or you will get a `CROSSLOT` error. -##### *Return value* +###### *Return value* *Mixed*: When no `STORE` option is passed, this function returns an array of results. If it is passed this function returns the number of stored entries. - -##### *Example* + +###### *Example* ~~~php /* Add some cities */ $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); @@ -3516,7 +3777,7 @@ $options[] = 'DESC'; var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options)); ~~~ -##### *Output* +###### *Output* ~~~ Within 300 miles of Honolulu: array(2) { @@ -3567,22 +3828,22 @@ array(1) { } ~~~ -### geoRadiusByMember +#### geoRadiusByMember -##### *Prototype* +###### *Prototype* ~~~php $redis->geoRadiusByMember($key, $member, $radius, $units [, Array $options]); ~~~ _**Description**_: This method is identical to [geoRadius](#georadius) except that instead of passing a longitude and latitude as the "source" you pass an existing member in the geospatial set. -##### *Options Array* +###### *Options Array* See [geoRadius](#georadius) command for options array. -##### *Return value* -*Array*: The zero or more entries that are close enough to the member given the distance and radius specified. +###### *Return value* +*Array*: The zero or more entries that are close enough to the member given the distance and radius specified. -##### *Example* +###### *Example* ~~~php $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui"); @@ -3593,7 +3854,7 @@ echo "\nFirst match within 300 miles of Honolulu:\n"; var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi', ['count' => 1])); ~~~ -##### *Output* +###### *Output* ~~~ Within 300 miles of Honolulu: array(2) { @@ -3610,7 +3871,7 @@ array(1) { } ~~~ -## Streams +### Streams * [xAck](#xack) - Acknowledge one or more pending messages * [xAdd](#xadd) - Add a message to a stream @@ -3626,55 +3887,55 @@ array(1) { * [xRevRange](#xrevrange) - Query one or more messages from end to start * [xTrim](#xtrim) - Trim a stream's size -### xAck +#### xAck ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xAck($stream, $group, $arr_messages); ~~~ _**Description**_: Acknowledge one or more messages on behalf of a consumer group. -##### *Return value* +###### *Return value* *long*: The number of messages Redis reports as acknowledged. -##### *Example* +###### *Example* ~~~php $obj_redis->xAck('stream', 'group1', ['1530063064286-0', '1530063064286-1']); ~~~ -### xAdd +#### xAdd ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xAdd($str_key, $str_id, $arr_message[, $i_maxlen, $boo_approximate]); ~~~ _**Description**_: Add a message to a stream -##### *Return value* +###### *Return value* *String*: The added message ID -##### *Example* +###### *Example* ~~~php $obj_redis->xAdd('mystream', "*", ['field' => 'value']); $obj_redis->xAdd('mystream', "*", ['field' => 'value'], 1000); // set max length of stream to 1000 $obj_redis->xAdd('mystream', "*", ['field' => 'value'], 1000, true); // set max length of stream to ~1000 ~~~ -### xClaim +#### xClaim ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xClaim($str_key, $str_group, $str_consumer, $min_idle_time, $arr_ids, [$arr_options]); ~~~ _**Description**_: Claim ownership of one or more pending messages. -#### *Options Array* +##### *Options Array* ~~~php $options = [ /* Note: 'TIME', and 'IDLE' are mutually exclusive */ @@ -3686,10 +3947,10 @@ $options = [ ]; ~~~ -##### *Return value* +###### *Return value* *Array*: Either an array of message IDs along with corresponding data, or just an array of IDs (if the 'JUSTID' option was passed). -##### *Example* +###### *Example* ~~~php $ids = ['1530113681011-0', '1530113681011-1', '1530113681011-2']; @@ -3710,28 +3971,28 @@ $obj_redis->xClaim( ); ~~~ -### xDel +#### xDel ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xDel($str_key, $arr_ids); ~~~ _**Description**_: Delete one or more messages from a stream. -##### *Return value* +###### *Return value* *long*: The number of messages removed -##### *Example* +###### *Example* ~~~php $obj_redis->xDel('mystream', ['1530115304877-0', '1530115305731-0']); ~~~ -### xGroup +#### xGroup ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xGroup('HELP'); $obj_redis->xGroup('CREATE', $str_key, $str_group, $str_msg_id, [$boo_mkstream]); @@ -3740,22 +4001,22 @@ $obj_redis->xGroup('DESTROY', $str_key, $str_group); $obj_redis->xGroup('DELCONSUMER', $str_key, $str_group, $str_consumer_name); ~~~ -_**Description**_: This command is used in order to create, destroy, or manage consumer groups. +_**Description**_: This command is used to create, destroy, or manage consumer groups. -##### *Return value* +###### *Return value* *Mixed*: This command returns different types depending on the specific XGROUP command executed. -##### *Example* +###### *Example* ~~~php $obj_redis->xGroup('CREATE', 'mystream', 'mygroup', '0'); $obj_redis->xGroup('CREATE', 'mystream', 'mygroup2', '0', true); /* Create stream if non-existent. */ $obj_redis->xGroup('DESTROY', 'mystream', 'mygroup'); ~~~ -### xInfo +#### xInfo ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xInfo('CONSUMERS', $str_stream, $str_group); $obj_redis->xInfo('GROUPS', $str_stream); @@ -3765,66 +4026,66 @@ $obj_redis->xInfo('HELP'); _**Description**_: Get information about a stream or consumer groups. -##### *Return value* +###### *Return value* *Mixed*: This command returns different types depending on which subcommand is used. -##### *Example* +###### *Example* ~~~php $obj_redis->xInfo('STREAM', 'mystream'); $obj_redis->xInfo('STREAM', 'mystream', 'FULL', 10); ~~~ -### xLen +#### xLen ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xLen($str_stream); ~~~ _**Description**_: Get the length of a given stream -##### *Return value* +###### *Return value* *Long*: The number of messages in the stream. -##### *Example* +###### *Example* ~~~php $obj_redis->xLen('mystream'); ~~~ -### xPending +#### xPending ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xPending($str_stream, $str_group [, $str_start, $str_end, $i_count, $str_consumer]); ~~~ _**Description**_: Get information about pending messages in a given stream. -##### *Return value* +###### *Return value* *Array*: Information about the pending messages, in various forms depending on the specific invocation of XPENDING. -##### *Examples* +###### *Examples* ~~~php $obj_redis->xPending('mystream', 'mygroup'); $obj_redis->xPending('mystream', 'mygroup', '-', '+', 1, 'consumer-1'); ~~~ -### xRange +#### xRange ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xRange($str_stream, $str_start, $str_end [, $i_count]); ~~~ _**Description**_: Get a range of messages from a given stream. -##### *Return value* +###### *Return value* *Array*: The messages in the stream within the requested range. -##### *Example* +###### *Example* ~~~php /* Get everything in this stream */ $obj_redis->xRange('mystream', '-', '+'); @@ -3833,20 +4094,20 @@ $obj_redis->xRange('mystream', '-', '+'); $obj_redis->xRange('mystream', '-', '+', 2); ~~~ -### xRead +#### xRead ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xRead($arr_streams [, $i_count, $i_block]); ~~~ _**Description**_: Read data from one or more streams and only return IDs greater than sent in the command. -##### *Return value* +###### *Return value* *Array*: The messages in the stream newer than the IDs passed to Redis (if any). -##### *Example* +###### *Example* ~~~php $obj_redis->xRead(['stream1' => '1535222584555-0', 'stream2' => '1535222584555-0']); @@ -3878,20 +4139,20 @@ Array $obj_redis->xRead(['stream1' => '$'], 1, 0); ~~~ -### xReadGroup +#### xReadGroup ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xReadGroup($str_group, $str_consumer, $arr_streams [, $i_count, $i_block]); ~~~ _**Description**_: This method is similar to xRead except that it supports reading messages for a specific consumer group. -##### *Return value* +###### *Return value* *Array*: The messages delivered to this consumer group (if any). -##### *Examples* +###### *Examples* ~~~php /* Consume messages for 'mygroup', 'consumer1' */ $obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => 0, 's2' => 0]); @@ -3903,38 +4164,38 @@ $obj_redis->xReadGroup('mygroup', 'consumer1', ['s1' => '>', 's2' => '>']); $obj_redis->xReadGroup('mygroup', 'consumer2', ['s1' => 0, 's2' => 0], 1, 1000); ~~~ -### xRevRange +#### xRevRange ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xRevRange($str_stream, $str_end, $str_start [, $i_count]); ~~~ _**Description**_: This is identical to xRange except the results come back in reverse order. Also note that Redis reverses the order of "start" and "end". -##### *Return value* +###### *Return value* *Array*: The messages in the range specified. -##### *Example* +###### *Example* ~~~php $obj_redis->xRevRange('mystream', '+', '-'); ~~~ -### xTrim +#### xTrim ----- -##### *Prototype* +###### *Prototype* ~~~php $obj_redis->xTrim($str_stream, $i_max_len [, $boo_approximate]); ~~~ -_**Description**_: Trim the stream length to a given maximum. If the "approximate" flag is pasesed, Redis will use your size as a hint but only trim trees in whole nodes (this is more efficient). +_**Description**_: Trim the stream length to a given maximum. If the "approximate" flag is passed, Redis will use your size as a hint but only trim trees in whole nodes (this is more efficient). -##### *Return value* +###### *Return value* *long*: The number of messages trimmed from the stream. -##### *Example* +###### *Example* ~~~php /* Trim to exactly 100 messages */ $obj_redis->xTrim('mystream', 100); @@ -3943,86 +4204,124 @@ $obj_redis->xTrim('mystream', 100); $obj_redis->xTrim('mystream', 100, true); ~~~ -## Pub/sub +### Pub/sub +* [subscribe](#subscribe) - Subscribe to channels +* [unsubscribe](#unsubscribe) - Stop listening to channels * [pSubscribe](#psubscribe) - Subscribe to channels by pattern +* [punsubscribe](#punsubscribe) - Stop listening to pattern subscriptions * [publish](#publish) - Post a message to a channel -* [subscribe](#subscribe) - Subscribe to channels * [pubSub](#pubsub) - Introspection into the pub/sub subsystem -### pSubscribe +#### subscribe ----- -_**Description**_: Subscribe to channels by pattern +_**Description**_: Subscribe to channels. Warning: this function will probably change in the future. + +###### *Parameters* +*channels*: an array of channels to subscribe to +*callback*: either a string or [$instance, 'method_name']. The callback function receives 3 parameters: the redis instance, the channel name, and the message. +*return value*: Mixed. Any non-null return value in the callback will be returned to the caller. + +Calling `$redis->unsubscribe()` inside the callback removes the provided channels. Once all channel subscriptions have been removed (for example by omitting the parameter so every channel is removed) PhpRedis leaves pub/sub mode and returns control to your script, which is the safest way to exit a `subscribe()` loop from within the callback. -##### *Parameters* -*patterns*: An array of patterns to match -*callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message) -*return value*: Mixed. Any non-null return value in the callback will be returned to the caller. -##### *Example* +###### *Example* ~~~php -function pSubscribe($redis, $pattern, $chan, $msg) { - echo "Pattern: $pattern\n"; - echo "Channel: $chan\n"; - echo "Payload: $msg\n"; -} +$r = new Redis; +$r->connect('localhost', 6379); + +$r->subscribe(['channel'], function ($redis, $chan, $message) { + echo "Received {$chan}: {$message}\n"; + $redis->unsubscribe(['channel']); +}); + +echo "Done\n"; ~~~ -### publish +#### unsubscribe ----- -_**Description**_: Publish messages to channels. Warning: this function will probably change in the future. +_**Description**_: Remove one or more channel subscriptions. When all active channel subscriptions are removed PhpRedis exits pub/sub mode. -##### *Parameters* -*channel*: a channel to publish to -*message*: string +###### *Parameters* +*channels*: an array of channels to unsubscribe from. Passing an empty array, or omitting the parameter while inside an active subscription loop, unsubscribes from every channel. -##### *Example* +###### *Return value* +*Redis|array|bool*: Outside of a subscription loop the method returns `true` on success. When invoked from within a subscription callback the method returns the server response array (`['unsubscribe', $channel, $remaining]`) until all subscriptions have been removed. + +###### *Example* ~~~php -$redis->publish('chan-1', 'hello, world!'); // send message. +$redis->unsubscribe(['first', 'second']); ~~~ -### subscribe +#### pSubscribe ----- -_**Description**_: Subscribe to channels. Warning: this function will probably change in the future. +_**Description**_: Subscribe to channels by pattern. Warning: this function will probably change in the future. -##### *Parameters* -*channels*: an array of channels to subscribe to -*callback*: either a string or an Array($instance, 'method_name'). The callback function receives 3 parameters: the redis instance, the channel name, and the message. -*return value*: Mixed. Any non-null return value in the callback will be returned to the caller. -##### *Example* -~~~php -function f($redis, $chan, $msg) { - switch($chan) { - case 'chan-1': - ... - break; - - case 'chan-2': - ... - break; - - case 'chan-2': - ... - break; - } -} +###### *Parameters* +*patterns*: An array of patterns to match +*callback*: Either a string or an array with an object and method. The callback will get four arguments ($redis, $pattern, $channel, $message) +*return value*: Mixed. Any non-null return value in the callback will be returned to the caller. + +When you no longer need a pattern subscription call `$redis->punsubscribe()` from inside the callback. PhpRedis tracks the exact patterns you subscribed to and exits pub/sub mode once no patterns remain (including when the parameter is omitted to remove every pattern). -$redis->subscribe(['chan-1', 'chan-2', 'chan-3'], 'f'); // subscribe to 3 chans +###### *Example* +~~~php +$r = new Redis; +$r->connect('localhost', 6379); + +$r->psubscribe(['*foo*', '*bar*'], function ($redis, $pattern, $channel, $message) { + echo "Pattern {$pattern} matched {$channel}: {$message}\n"; + $redis->punsubscribe(['*foo*', '*bar*']); +}); + +echo "Done\n"; ~~~ -### pubSub +#### punsubscribe +----- +_**Description**_: Remove one or more pattern subscriptions that were registered with `pSubscribe()`. + +###### *Parameters* +*patterns*: An array of patterns to unsubscribe from. + +###### *Return value* +*Redis|array|bool*: The channels that were removed. + +###### *Example* +~~~php +$redis->punsubscribe(['orders.*']); +$redis->punsubscribe(['foo', 'bar']); +$redis->punsubscribe(['*']); +~~~ + +_**Note**_: PhpRedis does not interpret glob-style requests that differ from the patterns you originally supplied to `pSubscribe()`. For example, subscribing with `['foo']` and later calling `$redis->punsubscribe(['*'])` will send the command to Redis, but PhpRedis will still think `foo` is active and will keep invoking the callback. Always pass the same concrete patterns that were used to subscribe. + +#### publish +----- +_**Description**_: Publish messages to channels. Warning: this function will probably change in the future. + +###### *Parameters* +*channel*: a channel to publish to +*message*: string + +###### *Example* +~~~php +$redis->publish('chan-1', 'hello, world!'); // send message. +~~~ + +#### pubSub ----- _**Description**_: A command allowing you to get information on the Redis pub/sub system. -##### *Parameters* -*keyword*: String, which can be: "channels", "numsub", or "numpat" +###### *Parameters* +*keyword*: String, which can be: "channels", "numsub", or "numpat" *argument*: Optional, variant. For the "channels" subcommand, you can pass a string pattern. For "numsub" an array of channel names. -##### *Return value* -*CHANNELS*: Returns an array where the members are the matching channels. -*NUMSUB*: Returns a key/value array where the keys are channel names and values are their counts. +###### *Return value* +*CHANNELS*: Returns an array where the members are the matching channels. +*NUMSUB*: Returns a key/value array where the keys are channel names and values are their counts. *NUMPAT*: Integer return containing the number active pattern subscriptions -##### *Example* +###### *Example* ~~~php $redis->pubSub("channels"); /*All channels */ $redis->pubSub("channels", "*pattern*"); /* Just channels matching your pattern */ @@ -4032,20 +4331,20 @@ $redis->pubSub("numpat"); /* Get the number of pattern subscribers */ ~~~ -## Generic +### Generic 1. [rawCommand](#rawcommand) - Execute any generic command against the server. -### rawCommand +#### rawCommand ----- _**Description**_: A method to execute any arbitrary command against the a Redis server -##### *Parameters* +###### *Parameters* This method is variadic and takes a dynamic number of arguments of various types (string, long, double), but must be passed at least one argument (the command keyword itself). -##### *Return value* -The return value can be various types depending on what the server itself returns. No post processing is done to the returned value and must be handled by the client code. +###### *Return value* +The return value can be various types depending on what the server itself returns. No post processing is done to the returned value and it must be handled by the client code. -##### *Example* +###### *Example* ```php /* Returns: true */ $redis->rawCommand("set", "foo", "bar"); @@ -4060,22 +4359,22 @@ $redis->rawCommand("rpush", "mylist", "one", 2, 3.5); $redis->rawCommand("lrange", "mylist", 0, -1); ``` -## Transactions +### Transactions 1. [multi, exec, discard](#multi-exec-discard) - Enter and exit transactional mode 2. [watch, unwatch](#watch-unwatch) - Watches a key for modifications by another client. -### multi, exec, discard. +#### multi, exec, discard. ----- _**Description**_: Enter and exit transactional mode. -##### *Parameters* +###### *Parameters* (optional) `Redis::MULTI` or `Redis::PIPELINE`. Defaults to `Redis::MULTI`. A `Redis::MULTI` block of commands runs as a single transaction; a `Redis::PIPELINE` block is simply transmitted faster to the server, but without any guarantee of atomicity. `discard` cancels a transaction. -##### *Return value* +###### *Return value* `multi()` returns the Redis instance and enters multi-mode. Once in multi-mode, all subsequent method calls return the same object until `exec()` is called. -##### *Example* +###### *Example* ~~~php $ret = $redis->multi() ->set('key1', 'val1') @@ -4085,20 +4384,20 @@ $ret = $redis->multi() ->exec(); /* -$ret == Array(0 => TRUE, 1 => 'val1', 2 => TRUE, 3 => 'val2'); +$ret == [0 => TRUE, 1 => 'val1', 2 => TRUE, 3 => 'val2']; */ ~~~ -### watch, unwatch +#### watch, unwatch ----- _**Description**_: Watches a key for modifications by another client. If the key is modified between `WATCH` and `EXEC`, the MULTI/EXEC transaction will fail (return `FALSE`). `unwatch` cancels all the watching of all keys by this client. -##### *Parameters* +###### *Parameters* *keys*: string for one key or array for a list of keys -##### *Example* +###### *Example* ~~~php $redis->watch('x'); // or for a list of keys: $redis->watch(['x','another key']); /* long code here during the execution of which other clients could well modify `x` */ @@ -4112,32 +4411,29 @@ $ret = FALSE if x has been modified between the call to WATCH and the call to EX -## Scripting +### Scripting * [eval](#eval) - Evaluate a LUA script serverside * [evalSha](#evalsha) - Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself * [script](#script) - Execute the Redis SCRIPT command to perform various operations on the scripting subsystem * [getLastError](#getlasterror) - The last error message (if any) * [clearLastError](#clearlasterror) - Clear the last error message -* [_prefix](#_prefix) - A utility method to prefix the value with the prefix setting for phpredis -* [_unserialize](#_unserialize) - A utility method to unserialize data with whatever serializer is set up -* [_serialize](#_serialize) - A utility method to serialize data with whatever serializer is set up -### eval +#### eval ----- _**Description**_: Evaluate a LUA script serverside -##### *Parameters* -*script* string. -*args* array, optional. +###### *Parameters* +*script* string. +*args* array, optional. *num_keys* int, optional. -##### *Return value* +###### *Return value* Mixed. What is returned depends on what the LUA script itself returns, which could be a scalar value (int/string), or an array. Arrays that are returned can also contain other arrays, if that's how it was set up in your LUA script. If there is an error executing the LUA script, the getLastError() function can tell you the message that came back from Redis (e.g. compile error). -##### *Examples* +###### *Examples* ~~~php $redis->eval("return 1"); // Returns an integer: 1 $redis->eval("return {1,2,3}"); // Returns [1,2,3] @@ -4149,33 +4445,33 @@ $redis->rpush('mylist','c'); $redis->eval("return {1,2,3,redis.call('lrange','mylist',0,-1)}"); ~~~ -### evalSha +#### evalSha ----- _**Description**_: Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself. In order to run this command Redis will have to have already loaded the script, either by running it or via the SCRIPT LOAD command. -##### *Parameters* -*script_sha* string. The sha1 encoded hash of the script you want to run. -*args* array, optional. Arguments to pass to the LUA script. +###### *Parameters* +*script_sha* string. The sha1 encoded hash of the script you want to run. +*args* array, optional. Arguments to pass to the LUA script. *num_keys* int, optional. The number of arguments that should go into the KEYS array, vs. the ARGV array when Redis spins the script -##### *Return value* +###### *Return value* Mixed. See EVAL -##### *Examples* +###### *Examples* ~~~php $script = 'return 1'; $sha = $redis->script('load', $script); $redis->evalSha($sha); // Returns 1 ~~~ -### script +#### script ----- _**Description**_: Execute the Redis SCRIPT command to perform various operations on the scripting subsystem. -##### *Usage* +###### *Usage* ~~~php $redis->script('load', $script); $redis->script('flush'); @@ -4183,13 +4479,13 @@ $redis->script('kill'); $redis->script('exists', $script1, [$script2, $script3, ...]); ~~~ -##### *Return value* +###### *Return value* * SCRIPT LOAD will return the SHA1 hash of the passed script on success, and FALSE on failure. * SCRIPT FLUSH should always return TRUE -* SCRIPT KILL will return true if a script was able to be killed and false if not +* SCRIPT KILL will return true if a script was killed and false if not * SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script -### client +#### client ----- _**Description**_: Issue the CLIENT command with various arguments. @@ -4199,7 +4495,7 @@ The Redis CLIENT command can be used in four ways. * CLIENT SETNAME [name] * CLIENT KILL [ip:port] -##### *Usage* +###### *Usage* ~~~php $redis->client('list'); // Get a list of clients $redis->client('getname'); // Get the name of the current connection @@ -4207,7 +4503,7 @@ $redis->client('setname', 'somename'); // Set the name of the current connection $redis->client('kill', ); // Kill the process at ip:port ~~~ -##### *Return value* +###### *Return value* This will vary depending on which client command was executed. * CLIENT LIST will return an array of arrays with client information. @@ -4217,34 +4513,34 @@ This will vary depending on which client command was executed. Note: phpredis will attempt to reconnect so you can actually kill your own connection but may not notice losing it! -### getLastError +#### getLastError ----- _**Description**_: The last error message (if any) -##### *Parameters* +###### *Parameters* *none* -##### *Return value* +###### *Return value* A string with the last returned script based error message, or NULL if there is no error -##### *Examples* +###### *Examples* ~~~php $redis->eval('this-is-not-lua'); $err = $redis->getLastError(); // "ERR Error compiling script (new function): user_script:1: '=' expected near '-'" ~~~ -### clearLastError +#### clearLastError ----- _**Description**_: Clear the last error message -##### *Parameters* +###### *Parameters* *none* -##### *Return value* +###### *Return value* *BOOL* TRUE -##### *Examples* +###### *Examples* ~~~php $redis->set('x', 'a'); $redis->incr('x'); @@ -4255,35 +4551,66 @@ $err = $redis->getLastError(); // NULL ~~~ -### _prefix +### Local Helper Methods + +These helpers expose the exact logic PhpRedis uses locally for serialization, compression, and prefixing. + +* [_compress](#_compress) - Compress a string with the configured compression option +* [_uncompress](#_uncompress) - Uncompress a string with the configured compression option +* [_serialize](#_serialize) - Serialize a value with the configured serializer +* [_unserialize](#_unserialize) - Unserialize a string with the configured serializer +* [_pack](#_pack) - Serialize and compress a value exactly as PhpRedis would before sending it to Redis +* [_unpack](#_unpack) - Reverse `_pack` to retrieve the original PHP value +* [_prefix](#_prefix) - Apply the configured key prefix locally +* [_digest](#_digest) - Compute the XXH3 digest of a packed value just like Redis' DIGEST command + +#### _compress ----- -_**Description**_: A utility method to prefix the value with the prefix setting for phpredis. +_**Description**_: Compress a string using whatever `Redis::OPT_COMPRESSION` is currently set to. This is useful when you need to mimic PhpRedis' compression logic without sending data to Redis. -##### *Parameters* -*value* string. The value you wish to prefix +###### *Parameters* +*value* string. The string to compress. -##### *Return value* -If a prefix is set up, the value now prefixed. If there is no prefix, the value will be returned unchanged. +###### *Return value* +*string* Returns the compressed string, or the original value if compression is disabled. -##### *Examples* +###### *Examples* ~~~php -$redis->setOption(Redis::OPT_PREFIX, 'my-prefix:'); -$redis->_prefix('my-value'); // Will return 'my-prefix:my-value' +$redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_LZF); +$payload = $redis->_compress('large-payload'); +~~~ + +#### _uncompress +----- +_**Description**_: Reverse `_compress` by uncompressing a string with the compression option currently in use. + +###### *Parameters* +*value* string. The raw compressed value to uncompress. + +###### *Return value* +*string* Returns the uncompressed value. Throws an exception if the payload is invalid for the configured compressor. + +###### *Examples* +~~~php +$redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_LZ4); +$original = $redis->_uncompress($compressedPayload); ~~~ -### _serialize +#### _serialize ----- -_**Description**_: A utility method to serialize values manually. +_**Description**_: Serialize a value manually using whatever serializer is configured. -This method allows you to serialize a value with whatever serializer is configured, manually. This can be useful for serialization/unserialization of data going in and out of EVAL commands -as phpredis can't automatically do this itself. Note that if no serializer is set, phpredis +as phpredis can't automatically do this itself. Note that if no serializer is set, phpredis will change Array values to 'Array', and Objects to 'Object'. -##### *Parameters* -*value*: Mixed. The value to be serialized +###### *Parameters* +*value* mixed. The value to be serialized. -##### *Examples* +###### *Return value* +*string* The serialized representation of the value. + +###### *Examples* ~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); $redis->_serialize("foo"); // returns "foo" @@ -4294,104 +4621,167 @@ $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); $redis->_serialize("foo"); // Returns 's:3:"foo";' ~~~ -### _unserialize +#### _unserialize ----- -_**Description**_: A utility method to unserialize data with whatever serializer is set up. +_**Description**_: Unserialize a string using whatever serializer is configured. + +If there is no serializer set, the value will be returned unchanged. If there is a serializer set up, +and the data passed in is malformed, an exception will be thrown. -If there is no serializer set, the value will be returned unchanged. If there is a serializer set up, -and the data passed in is malformed, an exception will be thrown. This can be useful if phpredis is -serializing values, and you return something from redis in a LUA script that is serialized. +###### *Parameters* +*value* string. The value to be unserialized. -##### *Parameters* -*value* string. The value to be unserialized +###### *Return value* +*mixed* The PHP value represented by the serialized string. -##### *Examples* +###### *Examples* ~~~php $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); $redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return [1,2,3] ~~~ +#### _pack +----- +_**Description**_: Pack a value the same way PhpRedis does internally by first serializing (if configured) and then compressing (if configured). This mirrors the exact bytes Redis receives. + +###### *Parameters* +*value* mixed. The PHP value to pack. + +###### *Return value* +*string* The packed payload that would be sent to Redis. + +###### *Examples* +~~~php +$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); +$redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_LZF); +$payload = $redis->_pack(['a' => 1]); +~~~ + +#### _unpack +----- +_**Description**_: Reverse `_pack` by uncompressing and then unserializing a payload using the current PhpRedis options. + +###### *Parameters* +*value* string. The packed payload to unpack. +###### *Return value* +*mixed* The unpacked PHP value. + +###### *Examples* +~~~php +$value = $redis->_unpack($payload); +~~~ + +#### _prefix +----- +_**Description**_: A utility method to prefix the value with the prefix setting for PhpRedis. + +###### *Parameters* +*value* string. The value you wish to prefix. + +###### *Return value* +*string* Returns the prefixed value when a prefix is configured, or the original string otherwise. + +###### *Examples* +~~~php +$redis->setOption(Redis::OPT_PREFIX, 'my-prefix:'); +$redis->_prefix('my-value'); // Will return 'my-prefix:my-value' +~~~ + +#### _digest +----- +_**Description**_: Compute the XXH3 digest of a PHP value after it has been `_pack`ed. This produces the same output as Redis' `DIGEST` command, making it easy to verify values locally. + +###### *Parameters* +*value* mixed. The PHP value to digest. + +###### *Return value* +*string* A 16 character hex string containing the XXH3 digest. + +###### *Examples* +~~~php +$digest = $redis->_digest(['foo' => 'bar']); +~~~ -## Introspection +### Introspection -### isConnected +#### isConnected ----- _**Description**_: A method to determine if a phpredis object thinks it's connected to a server -##### *Parameters* +###### *Parameters* None -##### *Return value* +###### *Return value* *Boolean* Returns TRUE if phpredis thinks it's connected and FALSE if not -### getHost +#### getHost ----- _**Description**_: Retrieve our host or unix socket that we're connected to -##### *Parameters* +###### *Parameters* None -##### *Return value* +###### *Return value* *Mixed* The host or unix socket we're connected to or FALSE if we're not connected -### getPort +#### getPort ----- _**Description**_: Get the port we're connected to -##### *Parameters* +###### *Parameters* None -##### *Return value* +###### *Return value* *Mixed* Returns the port we're connected to or FALSE if we're not connected -### getDbNum +#### getDbNum ----- _**Description**_: Get the database number phpredis is pointed to -##### *Parameters* +###### *Parameters* None -##### *Return value* +###### *Return value* *Mixed* Returns the database number (LONG) phpredis thinks it's pointing to or FALSE if we're not connected -### getTimeout +#### getTimeout ----- _**Description**_: Get the (write) timeout in use for phpredis -##### *Parameters* +###### *Parameters* None -##### *Return value* +###### *Return value* *Mixed* The timeout (DOUBLE) specified in our connect call or FALSE if we're not connected -### getReadTimeout +#### getReadTimeout _**Description**_: Get the read timeout specified to phpredis or FALSE if we're not connected -##### *Parameters* +###### *Parameters* None -##### *Return value* +###### *Return value* *Mixed* Returns the read timeout (which can be set using setOption and Redis::OPT_READ_TIMEOUT) or FALSE if we're not connected -### getPersistentID +#### getPersistentID ----- _**Description**_: Gets the persistent ID that phpredis is using -##### *Parameters* +###### *Parameters* None -##### *Return value* +###### *Return value* *Mixed* Returns the persistent id phpredis is using (which will only be set if connected with pconnect), NULL if we're not using a persistent ID, and FALSE if we're not connected -### getAuth +#### getAuth ----- _**Description**_: Get the password (or username and password if using Redis 6 ACLs) used to authenticate the connection. -### *Parameters* +#### *Parameters* None -### *Return value* +#### *Return value* *Mixed* Returns NULL if no username/password are set, the password string if a password is set, and a `[username, password]` array if authenticated with a username and password. diff --git a/array.md b/arrays.md similarity index 97% rename from array.md rename to arrays.md index c084f70f29..b5ba3738cf 100644 --- a/array.md +++ b/arrays.md @@ -40,7 +40,7 @@ $ra = new RedisArray(array("host1", "host2", "host3"), array("previous" => array #### Specifying the "retry_interval" parameter The retry_interval is used to specify a delay in milliseconds between reconnection attempts in case the client loses connection with a server
-$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("retry_timeout" => 100));
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("retry_interval" => 100));
 
#### Specifying the "lazy_connect" parameter @@ -121,7 +121,7 @@ For instance, the keys “{user:1}:name” and “{user:1}:email” will be stor ## Custom key distribution function In order to control the distribution of keys by hand, you can provide a custom function or closure that returns the server number, which is the index in the array of servers that you created the RedisArray object with. -For instance, instantiate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server. +For instance, instantiate a RedisArray object with `new RedisArray(["us-host", "uk-host", "de-host"], ["distributor" => "dist"]);` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server. ### Example
diff --git a/backoff.c b/backoff.c
index d0961fcfaf..e795cb9405 100644
--- a/backoff.c
+++ b/backoff.c
@@ -1,15 +1,9 @@
 #include "common.h"
 
+#if PHP_VERSION_ID < 80400
 #include 
-
-#if PHP_VERSION_ID >= 70100
-#include 
 #else
-static zend_long php_mt_rand_range(zend_long min, zend_long max) {
-    zend_long number = php_rand();
-    RAND_RANGE(number, min, max, PHP_RAND_MAX);
-    return number;
-}
+#include 
 #endif
 
 #include "backoff.h"
diff --git a/cluster.md b/cluster.md
index 484bc76a84..3949f88fae 100644
--- a/cluster.md
+++ b/cluster.md
@@ -10,21 +10,23 @@ To maintain consistency with the RedisArray class, one can create and connect to
 #### Declaring a cluster with an array of seeds
 ```php
 // Create a cluster setting three nodes as seeds
-$obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003'));
+$obj_cluster = new RedisCluster(NULL, ['host:7000', 'host:7001', 'host:7003']);
 
 // Connect and specify timeout and read_timeout
-$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5);
+$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5);
 
 // Connect with read/write timeout as well as specify that phpredis should use
 // persistent connections to each node.
-$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true);
+$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true);
 
 // Connect with cluster using password.
-$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, "password");
+$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, "password");
 
-// Connect with cluster using SSL/TLS
-// last argument is an array with [SSL context](https://www.php.net/manual/en/context.ssl.php) options
-$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true, NULL, Array("verify_peer" => false));
+// Connect with cluster using TLS
+// last argument is an optional array with [SSL context options](https://www.php.net/manual/en/context.ssl.php) (TLS options)
+// If value is array (even empty), it will connect via TLS.  If not, it will connect without TLS.
+// Note: If the seeds start with "ssl:// or tls://", it will connect to the seeds via TLS, but the subsequent connections will connect without TLS if this value is null.  So, if your nodes require TLS, this value must be an array, even if empty.
+$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, NULL, ["verify_peer" => false]);
 ```
 
 #### Loading a cluster configuration by name
@@ -206,3 +208,13 @@ To enable, set the following INI variable:
 redis.session.early_refresh = 1
 ```
 Note: This is disabled by default since it may significantly reduce the session lifetime for long-running scripts. Redis server version 6.2+ required.
+
+### Session compression
+
+Following INI variables can be used to configure session compression:
+~~~
+; Should session compression be enabled? Possible values are zstd, lzf, lz4, none. Defaults to: none
+redis.session.compression = zstd
+; What compression level should be used? Compression level depends on used library. For most deployments range 1-9 should be fine. Defaults to: 3
+redis.session.compression_level = 3
+~~~
diff --git a/cluster_library.c b/cluster_library.c
index 24c8a9142c..b342bfe3e8 100644
--- a/cluster_library.c
+++ b/cluster_library.c
@@ -87,6 +87,22 @@ static void dump_reply(clusterReply *reply, int indent) {
 }
 */
 
+/* MULTI BULK processing callbacks */
+static int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result,
+    long long count, void *ctx);
+static int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result,
+    long long count, void *ctx);
+static int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
+    long long count, void *ctx);
+static int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result,
+    long long count, void *ctx);
+static int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result,
+    long long count, void *ctx);
+static int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result,
+    long long count, void *ctx);
+
+
+
 
 /* Recursively free our reply object.  If free_data is non-zero we'll also free
  * the payload data (strings) themselves.  If not, we just free the structs */
@@ -450,7 +466,7 @@ void cluster_dist_add_val(redisCluster *c, clusterKeyVal *kv, zval *z_val
     // Serialize our value
     val_free = redis_pack(c->flags, z_val, &val, &val_len);
 
-    // Attach it to the provied keyval entry
+    // Attach it to the provided keyval entry
     kv->val = val;
     kv->val_len = val_len;
     kv->val_free = val_free;
@@ -468,7 +484,7 @@ void cluster_multi_add(clusterMultiCmd *mc, char *data, int data_len) {
     redis_cmd_append_sstr(&(mc->args), data, data_len);
 }
 
-/* Finalize a clusterMutliCmd by constructing the whole thing */
+/* Finalize a clusterMultiCmd by constructing the whole thing */
 void cluster_multi_fini(clusterMultiCmd *mc) {
     mc->cmd.len = 0;
     redis_cmd_init_sstr(&(mc->cmd), mc->argc, mc->kw, mc->kw_len);
@@ -640,8 +656,7 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len,
                                    c->flags->timeout, c->flags->read_timeout,
                                    c->flags->persistent, NULL, 0);
 
-    /* Stream context */
-    node->sock->stream_ctx = c->flags->stream_ctx;
+    redis_sock_set_context(node->sock, c->flags->context);
 
     redis_sock_set_auth(node->sock, c->flags->user, c->flags->pass);
 
@@ -709,7 +724,7 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) {
             master = cluster_node_create(c, host, hlen, port, low, 0);
             zend_hash_str_update_ptr(c->nodes, key, klen, master);
 
-            // Attach slaves first time we encounter a given master in order to avoid regitering the slaves multiple times
+            // Attach slaves first time we encounter a given master in order to avoid registering the slaves multiple times
             for (j = 3; j< r2->elements; j++) {
                 r3 = r2->element[j];
                 if (!VALIDATE_SLOTS_INNER(r3)) {
@@ -833,7 +848,7 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout,
     c->err = NULL;
 
     /* Set up our waitms based on timeout */
-    c->waitms  = (long)(1000 * timeout);
+    c->waitms  = (long)(1000 * (timeout + read_timeout));
 
     /* Allocate our seeds hash table */
     ALLOC_HASHTABLE(c->seeds);
@@ -856,6 +871,8 @@ cluster_free(redisCluster *c, int free_ctx)
     if (c->flags->prefix) zend_string_release(c->flags->prefix);
 
     redis_sock_free_auth(c->flags);
+    redis_sock_free_context(c->flags);
+
     efree(c->flags);
 
     /* Call hash table destructors */
@@ -1003,7 +1020,7 @@ void cluster_init_cache(redisCluster *c, redisCachedCluster *cc) {
                                  NULL, 0);
 
         /* Stream context */
-        sock->stream_ctx = c->flags->stream_ctx;
+        redis_sock_set_context(sock, c->flags->context);
 
         /* Add to seed nodes */
         zend_hash_str_update_ptr(c->seeds, key, keylen, sock);
@@ -1058,11 +1075,9 @@ cluster_init_seeds(redisCluster *c, zend_string **seeds, uint32_t nseeds)
                                  c->flags->timeout, c->flags->read_timeout,
                                  c->flags->persistent, NULL, 0);
 
-        /* Stream context */
-        sock->stream_ctx = c->flags->stream_ctx;
-
-        /* Credentials */
+        /* Credentials and context */
         redis_sock_set_auth(sock, c->flags->user, c->flags->pass);
+        redis_sock_set_context(sock, c->flags->context);
 
         // Index this seed by host/port
         key_len = snprintf(key, sizeof(key), "%s:%u", ZSTR_VAL(sock->host),
@@ -1097,7 +1112,7 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c) {
                 memset(c->master, 0, sizeof(redisClusterNode*)*REDIS_CLUSTER_SLOTS);
             }
         }
-        redis_sock_disconnect(seed, 0);
+        redis_sock_disconnect(seed, 0, 1);
         if (mapped) break;
     } ZEND_HASH_FOREACH_END();
 
@@ -1151,7 +1166,7 @@ static int cluster_set_redirection(redisCluster* c, char *msg, int moved)
  * redirection, parsing out slot host and port so the caller can take
  * appropriate action.
  *
- * In the case of a non MOVED/ASK error, we wlll set our cluster error
+ * In the case of a non MOVED/ASK error, we will set our cluster error
  * condition so GetLastError can be queried by the client.
  *
  * This function will return -1 on a critical error (e.g. parse/communication
@@ -1218,13 +1233,13 @@ PHP_REDIS_API void cluster_disconnect(redisCluster *c, int force) {
         if (node == NULL) continue;
 
         /* Disconnect from the master */
-        redis_sock_disconnect(node->sock, force);
+        redis_sock_disconnect(node->sock, force, 1);
 
         /* We also want to disconnect any slave connections so they will be pooled
          * in the event we are using persistent connections and connection pooling. */
         if (node->slaves) {
             ZEND_HASH_FOREACH_PTR(node->slaves, slave) {
-                redis_sock_disconnect(slave->sock, force);
+                redis_sock_disconnect(slave->sock, force, 1);
             } ZEND_HASH_FOREACH_END();
         }
     } ZEND_HASH_FOREACH_END();
@@ -1381,7 +1396,7 @@ static redisClusterNode *cluster_find_node(redisCluster *c, const char *host,
 
 /* Provided a redisCluster object, the slot where we thought data was and
  * the slot where data was moved, update our node mapping */
-static void cluster_update_slot(redisCluster *c) {
+static int cluster_update_slot(redisCluster *c) {
     redisClusterNode *node;
     char key[1024];
     size_t klen;
@@ -1390,7 +1405,7 @@ static void cluster_update_slot(redisCluster *c) {
     if (c->master[c->redir_slot]) {
         /* No need to do anything if it's the same node */
         if (!CLUSTER_REDIR_CMP(c, SLOT_SOCK(c,c->redir_slot))) {
-            return;
+            return SUCCESS;
         }
 
         /* Check to see if we have this new node mapped */
@@ -1403,6 +1418,10 @@ static void cluster_update_slot(redisCluster *c) {
             /* If the redirected node is a replica of the previous slot owner, a failover has taken place.
             We must then remap the cluster's keyspace in order to update the cluster's topology. */
             redisClusterNode *prev_master = SLOT(c,c->redir_slot);
+            if (NULL == prev_master->slaves) {
+                CLUSTER_THROW_EXCEPTION("Redis Cluster's master data is incomplete.  Cluster is in invalid state.", 0);
+                return FAILURE;
+            }
             redisClusterNode *slave;
             ZEND_HASH_FOREACH_PTR(prev_master->slaves, slave) {
                 if (slave == NULL) {
@@ -1411,8 +1430,11 @@ static void cluster_update_slot(redisCluster *c) {
                 if (!CLUSTER_REDIR_CMP(c, slave->sock)) {
                     // Detected a failover, the redirected node was a replica
                     // Remap the cluster's keyspace
-                    cluster_map_keyspace(c);
-                    return;
+                    if (cluster_map_keyspace(c) == FAILURE) {
+                        CLUSTER_THROW_EXCEPTION("Failed to remap cluster keyspace after failover", 0);
+                        return FAILURE;
+                    }
+                    return SUCCESS;
                 }
             } ZEND_HASH_FOREACH_END();
 
@@ -1445,6 +1467,7 @@ static void cluster_update_slot(redisCluster *c) {
     /* Make sure we unflag this node as a slave, as Redis Cluster will only ever
      * direct us to master nodes. */
     node->slave = 0;
+    return SUCCESS;
 }
 
 /* Abort any transaction in process, by sending DISCARD to any nodes that
@@ -1565,6 +1588,7 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char
          * node until we find one that is available. */
         if (cluster_sock_write(c, cmd, cmd_len, 0) == -1) {
             /* We have to abort, as no nodes are reachable */
+            cluster_cache_clear(c);
             CLUSTER_THROW_EXCEPTION("Can't communicate with any node in the cluster", 0);
             return -1;
         }
@@ -1585,11 +1609,23 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char
 
            if (c->redir_type == REDIR_MOVED) {
                /* For MOVED redirection we want to update our cached mapping */
-               cluster_update_slot(c);
+               if (FAILURE == cluster_update_slot(c)) {
+                   return -1;
+               }
                c->cmd_sock = SLOT_SOCK(c, slot);
+               /* Verify slot is valid after update */
+               if (!c->cmd_sock) {
+                   CLUSTER_THROW_EXCEPTION("Socket for slot is NULL after MOVED redirection", 0);
+                   return -1;
+               }
            } else if (c->redir_type == REDIR_ASK) {
                /* For ASK redirection we want to redirect but not update slot mapping */
                c->cmd_sock = cluster_get_asking_sock(c);
+               /* Verify socket from ASK redirection */
+               if (!c->cmd_sock) {
+                   CLUSTER_THROW_EXCEPTION("Socket is NULL after ASK redirection", 0);
+                   return -1;
+               }
            }
         }
 
@@ -1599,11 +1635,13 @@ PHP_REDIS_API short cluster_send_command(redisCluster *c, short slot, const char
 
     // If we've detected the cluster is down, throw an exception
     if (c->clusterdown) {
+        cluster_cache_clear(c);
         CLUSTER_THROW_EXCEPTION("The Redis Cluster is down (CLUSTERDOWN)", 0);
         return -1;
     } else if (timedout || resp == -1) {
         // Make sure the socket is reconnected, it such that it is in a clean state
-        redis_sock_disconnect(c->cmd_sock, 1);
+        redis_sock_disconnect(c->cmd_sock, 1, 1);
+        cluster_cache_clear(c);
 
         if (timedout) {
             CLUSTER_THROW_EXCEPTION("Timed out attempting to find data in the correct node!", 0);
@@ -1670,32 +1708,55 @@ cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ct
     }
 }
 
-/* BULK response handler */
-PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
-                              void *ctx)
-{
+static int cluster_bulk_resp_to_zval(redisCluster *c, zval *zdst) {
     char *resp;
 
-    // Make sure we can read the response
     if (c->reply_type != TYPE_BULK ||
-       (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL)
+        (resp = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL)
     {
-        CLUSTER_RETURN_FALSE(c);
+        if (c->reply_type != TYPE_BULK)
+            c->reply_len = 0;
+        ZVAL_FALSE(zdst);
+        return FAILURE;
     }
 
+    redis_unpack(c->flags, resp, c->reply_len, zdst);
+
+    efree(resp);
+
+    return SUCCESS;
+}
+
+/* BULK response handler */
+PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+                                     void *ctx)
+{
+    zval zret;
+
+    cluster_bulk_resp_to_zval(c, &zret);
+
     if (CLUSTER_IS_ATOMIC(c)) {
-        if (!redis_unpack(c->flags, resp, c->reply_len, return_value)) {
-            CLUSTER_RETURN_STRING(c, resp, c->reply_len);
-        }
+        RETVAL_ZVAL(&zret, 0, 1);
     } else {
-        zval z_unpacked;
-        if (redis_unpack(c->flags, resp, c->reply_len, &z_unpacked)) {
-            add_next_index_zval(&c->multi_resp, &z_unpacked);
-        } else {
-            add_next_index_stringl(&c->multi_resp, resp, c->reply_len);
-        }
+        add_next_index_zval(&c->multi_resp, &zret);
+    }
+}
+
+PHP_REDIS_API void
+cluster_bulk_withmeta_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+                           void *ctx)
+{
+    zval zbulk, zmeta;
+
+    cluster_bulk_resp_to_zval(c, &zbulk);
+
+    redis_with_metadata(&zmeta, &zbulk, c->reply_len);
+
+    if (CLUSTER_IS_ATOMIC(c)) {
+        RETVAL_ZVAL(&zmeta, 0, 1);
+    } else {
+        add_next_index_zval(&c->multi_resp, &zmeta);
     }
-    efree(resp);
 }
 
 /* Bulk response where we expect a double */
@@ -1845,7 +1906,7 @@ cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ct
 }
 
 PHP_REDIS_API void
-cluster_srandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
+cluster_randmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
     if (ctx == NULL) {
         cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL);
     } else if (ctx == PHPREDIS_CTX_PTR) {
@@ -1900,7 +1961,7 @@ PHP_REDIS_API void cluster_long_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
 
 /* TYPE response handler */
 PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
-                              void *ctx)
+                                     void *ctx)
 {
     // Make sure we got the right kind of response
     if (c->reply_type != TYPE_LINE) {
@@ -1908,19 +1969,22 @@ PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
     }
 
     // Switch on the type
-    if (strncmp (c->line_reply, "string", 6) == 0) {
+    if (redis_strncmp(c->line_reply, ZEND_STRL("string")) == 0) {
         CLUSTER_RETURN_LONG(c, REDIS_STRING);
-    } else if (strncmp(c->line_reply, "set", 3) == 0) {
+    } else if (redis_strncmp(c->line_reply, ZEND_STRL("set")) == 0) {
         CLUSTER_RETURN_LONG(c, REDIS_SET);
-    } else if (strncmp(c->line_reply, "list", 4) == 0) {
+    } else if (redis_strncmp(c->line_reply, ZEND_STRL("list")) == 0) {
         CLUSTER_RETURN_LONG(c, REDIS_LIST);
-    } else if (strncmp(c->line_reply, "hash", 4) == 0) {
+    } else if (redis_strncmp(c->line_reply, ZEND_STRL("hash")) == 0) {
         CLUSTER_RETURN_LONG(c, REDIS_HASH);
-    } else if (strncmp(c->line_reply, "zset", 4) == 0) {
+    } else if (redis_strncmp(c->line_reply, ZEND_STRL("zset")) == 0) {
         CLUSTER_RETURN_LONG(c, REDIS_ZSET);
-    } else if (strncmp(c->line_reply, "stream", 6) == 0) {
+    } else if (redis_strncmp(c->line_reply, ZEND_STRL("stream")) == 0) {
         CLUSTER_RETURN_LONG(c, REDIS_STREAM);
+    } else if (redis_strncmp(c->line_reply, ZEND_STRL("vectorset")) == 0) {
+        CLUSTER_RETURN_LONG(c, REDIS_VECTORSET);
     } else {
+
         CLUSTER_RETURN_LONG(c, REDIS_NOT_FOUND);
     }
 }
@@ -1929,7 +1993,7 @@ PHP_REDIS_API void cluster_type_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
 PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                              void *ctx)
 {
-    subscribeContext *sctx = (subscribeContext*)ctx;
+    subscribeContext *sctx = ctx;
     zval z_tab, *z_tmp;
     int pull = 0;
 
@@ -1946,12 +2010,12 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
         if ((z_tmp = zend_hash_index_find(Z_ARRVAL(z_tab), 0)) == NULL ||
             strcasecmp(Z_STRVAL_P(z_tmp), sctx->kw) != 0
         ) {
-            zval_dtor(&z_tab);
+            zval_ptr_dtor_nogc(&z_tab);
             efree(sctx);
             RETURN_FALSE;
         }
 
-        zval_dtor(&z_tab);
+        zval_ptr_dtor_nogc(&z_tab);
         pull = 1;
     }
 
@@ -1977,12 +2041,12 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
         }
 
         // Make sure we have a message or pmessage
-        if (!strncmp(Z_STRVAL_P(z_type), "message", 7) ||
-            !strncmp(Z_STRVAL_P(z_type), "pmessage", 8)
-        ) {
+        if (zend_string_equals_literal(Z_STR_P(z_type), "message") ||
+            zend_string_equals_literal(Z_STR_P(z_type), "pmessage"))
+        {
             is_pmsg = *Z_STRVAL_P(z_type) == 'p';
         } else {
-            zval_dtor(&z_tab);
+            zval_ptr_dtor_nogc(&z_tab);
             continue;
         }
 
@@ -2021,14 +2085,14 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
         // If we have a return value, free it
         zval_ptr_dtor(&z_ret);
 
-        zval_dtor(&z_tab);
+        zval_ptr_dtor_nogc(&z_tab);
     }
 
     // We're no longer subscribing, due to an error
     c->subscribed_slot = -1;
 
     // Cleanup
-    zval_dtor(&z_tab);
+    zval_ptr_dtor_nogc(&z_tab);
     efree(sctx);
 
     // Failure
@@ -2039,8 +2103,8 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
 PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS,
                                redisCluster *c, void *ctx)
 {
-    subscribeContext *sctx = (subscribeContext*)ctx;
-    zval z_tab, *z_chan, *z_flag;
+    subscribeContext *sctx = ctx;
+    zval z_tab = {0}, *z_chan, *z_flag;
     int pull = 0, argc = sctx->argc;
 
     efree(sctx);
@@ -2052,8 +2116,8 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS,
         if (!cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, pull, mbulk_resp_loop_raw, &z_tab) ||
             (z_chan = zend_hash_index_find(Z_ARRVAL(z_tab), 1)) == NULL
         ) {
-            zval_dtor(&z_tab);
-            zval_dtor(return_value);
+            zval_ptr_dtor_nogc(&z_tab);
+            zval_ptr_dtor_nogc(return_value);
             RETURN_FALSE;
         }
 
@@ -2061,8 +2125,8 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS,
         if ((z_flag = zend_hash_index_find(Z_ARRVAL(z_tab), 2)) == NULL ||
             Z_STRLEN_P(z_flag) != 2
         ) {
-            zval_dtor(&z_tab);
-            zval_dtor(return_value);
+            zval_ptr_dtor_nogc(&z_tab);
+            zval_ptr_dtor_nogc(return_value);
             RETURN_FALSE;
         }
 
@@ -2072,7 +2136,7 @@ PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS,
         // Add result
         add_assoc_bool(return_value, Z_STRVAL_P(z_chan), flag[1] == '1');
 
-        zval_dtor(&z_tab);
+        zval_ptr_dtor_nogc(&z_tab);
         pull = 1;
     }
 }
@@ -2242,10 +2306,11 @@ PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS, re
 }
 
 /* Generic MULTI BULK response processor */
-PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
-                                   redisCluster *c, mbulk_cb cb, void *ctx)
+static void
+cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+                       zend_bool init_array, mbulk_cb cb, void *ctx)
 {
-    zval z_result;
+    zval z_result = {0};
 
     /* Abort if the reply isn't MULTIBULK or has an invalid length */
     if (c->reply_type != TYPE_MULTIBULK || c->reply_len < -1) {
@@ -2254,19 +2319,20 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
 
     if (c->reply_len == -1 && c->flags->null_mbulk_as_null) {
         ZVAL_NULL(&z_result);
-    } else {
+    } else if (c->reply_len <= 0) {
         array_init(&z_result);
+    } else {
+        if (init_array)
+            array_init(&z_result);
 
-        if (c->reply_len > 0) {
-            /* Push serialization settings from the cluster into our socket */
-            c->cmd_sock->serializer = c->flags->serializer;
-            c->cmd_sock->compression = c->flags->compression;
+        /* Push serialization settings from the cluster into our socket */
+        c->cmd_sock->serializer = c->flags->serializer;
+        c->cmd_sock->compression = c->flags->compression;
 
-            /* Call our specified callback */
-            if (cb(c->cmd_sock, &z_result, c->reply_len, ctx) == FAILURE) {
-                zval_dtor(&z_result);
-                CLUSTER_RETURN_FALSE(c);
-            }
+        /* Call our specified callback */
+        if (cb(c->cmd_sock, &z_result, c->reply_len, ctx) == FAILURE) {
+            zval_ptr_dtor_nogc(&z_result);
+            CLUSTER_RETURN_FALSE(c);
         }
     }
 
@@ -2279,8 +2345,9 @@ PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
 }
 
 /* HSCAN, SSCAN, ZSCAN */
-PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
-                              REDIS_SCAN_TYPE type, long *it)
+PHP_REDIS_API int
+cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+                  REDIS_SCAN_TYPE type, uint64_t *cursor)
 {
     char *pit;
 
@@ -2304,12 +2371,11 @@ PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
     }
 
     // Push the new iterator value to our caller
-    *it = atol(pit);
+    *cursor = strtoull(pit, NULL, 10);
     efree(pit);
 
     // We'll need another MULTIBULK response for the payload
-    if (cluster_check_response(c, &c->reply_type) < 0)
-    {
+    if (cluster_check_response(c, &c->reply_type) < 0) {
         return FAILURE;
     }
 
@@ -2395,7 +2461,7 @@ cluster_xrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
     c->cmd_sock->compression = c->flags->compression;
 
     if (redis_read_stream_messages(c->cmd_sock, c->reply_len, &z_messages) < 0) {
-        zval_dtor(&z_messages);
+        zval_ptr_dtor_nogc(&z_messages);
         CLUSTER_RETURN_FALSE(c);
     }
 
@@ -2419,7 +2485,7 @@ cluster_xread_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
     } else {
         array_init(&z_streams);
         if (redis_read_stream_messages_multi(c->cmd_sock, c->reply_len, &z_streams) < 0) {
-            zval_dtor(&z_streams);
+            zval_ptr_dtor_nogc(&z_streams);
             CLUSTER_RETURN_FALSE(c);
         }
     }
@@ -2441,7 +2507,7 @@ cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
     ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR);
 
     if (redis_read_xclaim_reply(c->cmd_sock, c->reply_len, ctx == PHPREDIS_CTX_PTR, &z_msg) < 0) {
-        zval_dtor(&z_msg);
+        zval_ptr_dtor_nogc(&z_msg);
         CLUSTER_RETURN_FALSE(c);
     }
 
@@ -2453,6 +2519,102 @@ cluster_xclaim_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
 
 }
 
+PHP_REDIS_API void
+cluster_vemb_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
+    zval z_ret;
+
+    ZVAL_FALSE(&z_ret);
+
+    if (c->reply_type != TYPE_MULTIBULK || c->reply_len < 0)
+        goto fail;
+
+    array_init(&z_ret);
+
+    if (redis_read_vemb_response(c->cmd_sock, &z_ret, c->reply_len) != SUCCESS)
+        goto fail;
+
+    if (CLUSTER_IS_ATOMIC(c)) {
+        RETURN_ZVAL(&z_ret, 0, 1);
+    } else {
+        add_next_index_zval(&c->multi_resp, &z_ret);
+    }
+
+    return;
+
+fail:
+    zval_ptr_dtor_nogc(&z_ret);
+    CLUSTER_RETURN_FALSE(c);
+}
+
+PHP_REDIS_API void
+cluster_vinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
+    zval z_ret;
+
+    if (c->reply_len < 2 || c->reply_len % 2 != 0) {
+        CLUSTER_RETURN_FALSE(c);
+    }
+
+    array_init_size(&z_ret, c->reply_len / 2);
+    if (redis_read_vinfo_response(c->cmd_sock, &z_ret, c->reply_len) != SUCCESS) {
+        zval_ptr_dtor_nogc(&z_ret);
+        CLUSTER_RETURN_FALSE(c);
+    }
+
+    if (CLUSTER_IS_ATOMIC(c)) {
+        RETURN_ZVAL(&z_ret, 0, 1);
+    }
+
+    add_next_index_zval(&c->multi_resp, &z_ret);
+}
+
+PHP_REDIS_API void
+cluster_vgetattr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
+{
+    zval z_ret;
+    char *str;
+
+    if (c->reply_type != TYPE_BULK || c->reply_len <= 0 ||
+        (str = redis_sock_read_bulk_reply(c->cmd_sock, c->reply_len)) == NULL)
+    {
+        CLUSTER_RETURN_FALSE(c);
+    }
+
+    if (ctx != PHPREDIS_CTX_PTR ||
+        redis_deserialize_vgetattr_reply(&z_ret, str, c->reply_len) != SUCCESS)
+    {
+        ZVAL_STRINGL(&z_ret, str, c->reply_len);
+    }
+
+    efree(str);
+
+    if (CLUSTER_IS_ATOMIC(c)) {
+        RETURN_ZVAL(&z_ret, 0, 1);
+    } else {
+        add_next_index_zval(&c->multi_resp, &z_ret);
+    }
+}
+
+PHP_REDIS_API void
+cluster_vlinks_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) {
+    zval z_ret;
+
+    if (c->reply_len < 0) {
+        CLUSTER_RETURN_FALSE(c);
+    }
+
+    if (redis_read_vlinks_response(c->cmd_sock, &z_ret, c->reply_len, ctx)
+                                   != SUCCESS)
+    {
+        CLUSTER_RETURN_FALSE(c);
+    }
+
+    if (CLUSTER_IS_ATOMIC(c)) {
+        RETURN_ZVAL(&z_ret, 0, 1);
+    } else {
+        add_next_index_zval(&c->multi_resp, &z_ret);
+    }
+}
+
 /* XINFO */
 PHP_REDIS_API void
 cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
@@ -2461,7 +2623,7 @@ cluster_xinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
 
     array_init(&z_ret);
     if (redis_read_xinfo_response(c->cmd_sock, &z_ret, c->reply_len) != SUCCESS) {
-        zval_dtor(&z_ret);
+        zval_ptr_dtor_nogc(&z_ret);
         CLUSTER_RETURN_FALSE(c);
     }
 
@@ -2496,7 +2658,7 @@ cluster_acl_custom_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx
 
     array_init(&z_ret);
     if (cb(c->cmd_sock, &z_ret, c->reply_len) != SUCCESS) {
-        zval_dtor(&z_ret);
+        zval_ptr_dtor_nogc(&z_ret);
         CLUSTER_RETURN_FALSE(c);
     }
 
@@ -2539,7 +2701,7 @@ PHP_REDIS_API zval *cluster_zval_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
 
     // Call our callback
     if (cb(c->cmd_sock, z_ret, c->reply_len, NULL) == FAILURE) {
-        zval_dtor(z_ret);
+        zval_ptr_dtor_nogc(z_ret);
         return NULL;
     }
 
@@ -2551,8 +2713,9 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
                                      redisCluster *c, void *ctx)
 {
     zval *multi_resp = &c->multi_resp;
-    array_init(multi_resp);
+    uint8_t flags = c->flags->flags;
 
+    array_init(multi_resp);
     clusterFoldItem *fi = c->multi_head;
     while (fi) {
         /* Make sure our transaction didn't fail here */
@@ -2564,11 +2727,13 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
             c->cmd_sock = SLOT_SOCK(c, fi->slot);
 
             if (cluster_check_response(c, &c->reply_type) < 0) {
-                zval_dtor(multi_resp);
+                zval_ptr_dtor_nogc(multi_resp);
                 RETURN_FALSE;
             }
 
+            c->flags->flags = fi->flags;
             fi->callback(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, fi->ctx);
+            c->flags->flags = flags;
         } else {
             /* Just add false */
             add_next_index_bool(multi_resp, 0);
@@ -2577,7 +2742,7 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
     }
 
     // Set our return array
-    zval_dtor(return_value);
+    zval_ptr_dtor_nogc(return_value);
     RETVAL_ZVAL(multi_resp, 0, 1);
 }
 
@@ -2585,7 +2750,7 @@ PHP_REDIS_API void cluster_multi_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
 PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS,
                                     redisCluster *c, void *ctx)
 {
-    clusterMultiCtx *mctx = (clusterMultiCtx*)ctx;
+    clusterMultiCtx *mctx = ctx;
 
     /* Protect against an invalid response type, -1 response length, and failure
      * to consume the responses. */
@@ -2621,7 +2786,7 @@ PHP_REDIS_API void cluster_mbulk_mget_resp(INTERNAL_FUNCTION_PARAMETERS,
 PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                                 void *ctx)
 {
-    clusterMultiCtx *mctx = (clusterMultiCtx*)ctx;
+    clusterMultiCtx *mctx = ctx;
     int real_argc = mctx->count/2;
 
     // Protect against an invalid response type
@@ -2657,7 +2822,7 @@ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluste
 PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                              void *ctx)
 {
-    clusterMultiCtx *mctx = (clusterMultiCtx*)ctx;
+    clusterMultiCtx *mctx = ctx;
 
     // If we get an invalid reply, inform the client
     if (c->reply_type != TYPE_INT) {
@@ -2686,14 +2851,14 @@ PHP_REDIS_API void cluster_del_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
 PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                               void *ctx)
 {
-    clusterMultiCtx *mctx = (clusterMultiCtx*)ctx;
+    clusterMultiCtx *mctx = ctx;
 
     // If we get an invalid reply type something very wrong has happened,
     // and we have to abort.
     if (c->reply_type != TYPE_LINE) {
         php_error_docref(0, E_ERROR,
             "Invalid reply type returned for MSET command");
-        zval_dtor(mctx->z_multi);
+        zval_ptr_dtor_nogc(mctx->z_multi);
         efree(mctx->z_multi);
         efree(mctx);
         RETURN_FALSE;
@@ -2702,9 +2867,9 @@ PHP_REDIS_API void cluster_mset_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
     // Set our return if it's the last call
     if (mctx->last) {
         if (CLUSTER_IS_ATOMIC(c)) {
-            ZVAL_BOOL(return_value, zval_is_true(mctx->z_multi));
+            ZVAL_BOOL(return_value, zend_is_true(mctx->z_multi));
         } else {
-            add_next_index_bool(&c->multi_resp, zval_is_true(mctx->z_multi));
+            add_next_index_bool(&c->multi_resp, zend_is_true(mctx->z_multi));
         }
         efree(mctx->z_multi);
     }
@@ -2717,7 +2882,7 @@ PHP_REDIS_API void
 cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
 {
     cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c,
-        mbulk_resp_loop_raw, NULL);
+                           1, mbulk_resp_loop_raw, NULL);
 }
 
 /* Unserialize all the things */
@@ -2725,7 +2890,7 @@ PHP_REDIS_API void
 cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx)
 {
     cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c,
-        mbulk_resp_loop, NULL);
+                           1, mbulk_resp_loop, NULL);
 }
 
 /* For handling responses where we get key, value, key, value that
@@ -2735,7 +2900,7 @@ cluster_mbulk_zipstr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                           void *ctx)
 {
     cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c,
-        mbulk_resp_loop_zipstr, NULL);
+                           1, mbulk_resp_loop_zipstr, NULL);
 }
 
 /* Handling key,value to key=>value where the values are doubles */
@@ -2744,7 +2909,7 @@ cluster_mbulk_zipdbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                           void *ctx)
 {
     cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c,
-        mbulk_resp_loop_zipdbl, NULL);
+                           1, mbulk_resp_loop_zipdbl, NULL);
 }
 
 PHP_REDIS_API void
@@ -2752,7 +2917,7 @@ cluster_mbulk_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                        void *ctx)
 {
     cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c,
-        mbulk_resp_loop_dbl, ctx);
+                           1, mbulk_resp_loop_dbl, ctx);
 }
 
 /* Associate multi bulk response (for HMGET really) */
@@ -2761,15 +2926,15 @@ cluster_mbulk_assoc_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
                          void *ctx)
 {
     cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c,
-        mbulk_resp_loop_assoc, ctx);
+                           0, mbulk_resp_loop_assoc, ctx);
 }
 
 /*
  * Various MULTI BULK reply callback functions
  */
 
-int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result,
-                        long long count, void *ctx)
+static int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result,
+                               long long count, void *ctx)
 {
     char *line;
     int line_len;
@@ -2788,8 +2953,8 @@ int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result,
 }
 
 /* MULTI BULK response where we don't touch the values (e.g. KEYS) */
-int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result,
-                        long long count, void *ctx)
+static int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result,
+                               long long count, void *ctx)
 {
     char *line;
     int line_len;
@@ -2810,8 +2975,8 @@ int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result,
 }
 
 /* MULTI BULK response where we unserialize everything */
-int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result,
-                    long long count, void *ctx)
+static int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result,
+                           long long count, void *ctx)
 {
     char *line;
     int line_len;
@@ -2823,11 +2988,8 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result,
 
         if (line != NULL) {
             zval z_unpacked;
-            if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) {
-                add_next_index_zval(z_result, &z_unpacked);
-            } else {
-                add_next_index_stringl(z_result, line, line_len);
-            }
+            redis_unpack(redis_sock, line, line_len, &z_unpacked);
+            add_next_index_zval(z_result, &z_unpacked);
             efree(line);
         } else {
             add_next_index_bool(z_result, 0);
@@ -2838,12 +3000,12 @@ int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result,
 }
 
 /* MULTI BULK response where we turn key1,value1 into key1=>value1 */
-int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
-                           long long count, void *ctx)
+static int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
+                                  long long count, void *ctx)
 {
     char *line, *key = NULL;
-    int line_len, key_len = 0;
     long long idx = 0;
+    int line_len;
 
     // Our count will need to be divisible by 2
     if (count % 2 != 0) {
@@ -2859,15 +3021,11 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
         if (idx++ % 2 == 0) {
             // Save our key and length
             key = line;
-            key_len = line_len;
         } else {
             /* Attempt unpacking */
             zval z_unpacked;
-            if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) {
-                add_assoc_zval(z_result, key, &z_unpacked);
-            } else {
-                add_assoc_stringl_ex(z_result, key, key_len, line, line_len);
-            }
+            redis_unpack(redis_sock, line, line_len, &z_unpacked);
+            add_assoc_zval(z_result, key, &z_unpacked);
 
             efree(line);
             efree(key);
@@ -2878,8 +3036,8 @@ int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
 }
 
 /* MULTI BULK loop processor where we expect key,score key, score */
-int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result,
-                           long long count, void *ctx)
+static int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result,
+                                  long long count, void *ctx)
 {
     char *line, *key = NULL;
     int line_len, key_len = 0;
@@ -2899,14 +3057,11 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result,
                 key_len = line_len;
             } else {
                 zval zv, *z = &zv;
-                if (redis_unpack(redis_sock,key,key_len, z)) {
-                    zend_string *zstr = zval_get_string(z);
-                    add_assoc_double_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), atof(line));
-                    zend_string_release(zstr);
-                    zval_dtor(z);
-                } else {
-                    add_assoc_double_ex(z_result, key, key_len, atof(line));
-                }
+                redis_unpack(redis_sock,key,key_len, z);
+                zend_string *tmp, *zstr = zval_get_tmp_string(z, &tmp);
+                add_assoc_double_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), atof(line));
+                zend_tmp_string_release(tmp);
+                zval_ptr_dtor_nogc(z);
 
                 /* Free our key and line */
                 efree(key);
@@ -2919,39 +3074,28 @@ int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result,
 }
 
 /* MULTI BULK where we're passed the keys, and we attach vals */
-int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result,
-                          long long count, void *ctx)
+static int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result,
+                                 long long count, void *ctx)
 {
+    zval *zfield, z_unpacked;
+    HashTable *htctx = ctx;
+    int line_len;
     char *line;
-    int line_len, i;
-    zval *z_keys = ctx;
 
-    // Loop while we've got replies
-    for (i = 0; i < count; ++i) {
-        zend_string *zstr = zval_get_string(&z_keys[i]);
+    ZEND_HASH_FOREACH_VAL(htctx, zfield) {
         line = redis_sock_read(redis_sock, &line_len);
-
         if (line != NULL) {
-            zval z_unpacked;
-            if (redis_unpack(redis_sock, line, line_len, &z_unpacked)) {
-                add_assoc_zval_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked);
-            } else {
-                add_assoc_stringl_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), line, line_len);
-            }
+            redis_unpack(redis_sock, line, line_len, &z_unpacked);
             efree(line);
         } else {
-            add_assoc_bool_ex(z_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0);
+            ZVAL_FALSE(&z_unpacked);
         }
 
-        // Clean up key context
-        zend_string_release(zstr);
-        zval_dtor(&z_keys[i]);
-    }
+        ZVAL_COPY_VALUE(zfield, &z_unpacked);
+    } ZEND_HASH_FOREACH_END();
 
-    // Clean up our keys overall
-    efree(z_keys);
+    ZVAL_ARR(z_result, htctx);
 
-    // Success!
     return SUCCESS;
 }
 
@@ -2974,7 +3118,7 @@ static zend_string **get_valid_seeds(HashTable *input, uint32_t *nseeds) {
     zval *z_seed;
     zend_string *zkey, **seeds = NULL;
 
-    /* Short circuit if we don't have any sees */
+    /* Short circuit if we don't have any seeds */
     count = zend_hash_num_elements(input);
     if (count == 0)
         return NULL;
@@ -2995,7 +3139,7 @@ static zend_string **get_valid_seeds(HashTable *input, uint32_t *nseeds) {
         }
 
         /* Add as a key to avoid duplicates */
-        zend_hash_str_update_ptr(valid, Z_STRVAL_P(z_seed), Z_STRLEN_P(z_seed), NULL);
+        zend_hash_str_add_empty_element(valid, Z_STRVAL_P(z_seed), Z_STRLEN_P(z_seed));
     } ZEND_HASH_FOREACH_END();
 
     /* We need at least one valid seed */
@@ -3006,6 +3150,10 @@ static zend_string **get_valid_seeds(HashTable *input, uint32_t *nseeds) {
     /* Populate our return array */
     seeds = ecalloc(count, sizeof(*seeds));
     ZEND_HASH_FOREACH_STR_KEY(valid, zkey) {
+        /* Should not actually be possible but satisfy static analyzer */
+        if (zkey == NULL) {
+            zend_error_noreturn(E_ERROR, "Invalid seed key in valid seeds hash");
+        }
         seeds[idx++] = zend_string_copy(zkey);
     } ZEND_HASH_FOREACH_END();
 
@@ -3112,7 +3260,14 @@ PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash) {
 PHP_REDIS_API void cluster_cache_store(zend_string *hash, HashTable *nodes) {
     redisCachedCluster *cc = cluster_cache_create(hash, nodes);
 
-    redis_register_persistent_resource(cc->hash, cc, le_cluster_slot_cache);
+    zend_register_persistent_resource_ex(cc->hash, cc, le_cluster_slot_cache);
+}
+
+void cluster_cache_clear(redisCluster *c)
+{
+    if (c->cache_key) {
+        zend_hash_del(&EG(persistent_list), c->cache_key);
+    }
 }
 
 
diff --git a/cluster_library.h b/cluster_library.h
index eb2b1531b8..361a1e5dad 100644
--- a/cluster_library.h
+++ b/cluster_library.h
@@ -116,7 +116,7 @@
     mc->args.len = 0; \
     mc->argc     = 0; \
 
-/* Initialzie a clusterMultiCmd with a keyword and length */
+/* Initialize a clusterMultiCmd with a keyword and length */
 #define CLUSTER_MULTI_INIT(mc, keyword, keyword_len) \
     mc.kw     = keyword; \
     mc.kw_len = keyword_len; \
@@ -264,6 +264,8 @@ struct clusterFoldItem {
 
     /* Next item in our list */
     struct clusterFoldItem *next;
+
+    uint8_t flags;
 };
 
 /* Key and value container, with info if they need freeing */
@@ -376,7 +378,7 @@ PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout,
     int failover, int persistent);
 PHP_REDIS_API void cluster_free(redisCluster *c, int free_ctx);
 PHP_REDIS_API void cluster_init_seeds(redisCluster *c, zend_string **seeds, uint32_t nseeds);
-PHP_REDIS_API int cluster_map_keyspace(redisCluster *c);
+REDIS_NODISCARD PHP_REDIS_API int cluster_map_keyspace(redisCluster *c);
 PHP_REDIS_API void cluster_free_node(redisClusterNode *node);
 
 /* Functions for interacting with cached slots maps */
@@ -390,6 +392,7 @@ PHP_REDIS_API char **cluster_sock_read_multibulk_reply(RedisSock *redis_sock, in
 
 PHP_REDIS_API void cluster_cache_store(zend_string *hash, HashTable *nodes);
 PHP_REDIS_API redisCachedCluster *cluster_cache_load(zend_string *hash);
+void cluster_cache_clear(redisCluster *c);
 
 /*
  * Redis Cluster response handlers.  Our response handlers generally take the
@@ -419,7 +422,7 @@ PHP_REDIS_API void cluster_zadd_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster
     void *ctx);
 PHP_REDIS_API void cluster_zrandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
-PHP_REDIS_API void cluster_srandmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+PHP_REDIS_API void cluster_randmember_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
 PHP_REDIS_API void cluster_set_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
@@ -429,6 +432,8 @@ PHP_REDIS_API void cluster_single_line_resp(INTERNAL_FUNCTION_PARAMETERS, redisC
     void *ctx);
 PHP_REDIS_API void cluster_bulk_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
+PHP_REDIS_API void cluster_bulk_withmeta_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+    void *ctx);
 PHP_REDIS_API void cluster_bulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
 PHP_REDIS_API void cluster_dbl_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
@@ -443,6 +448,14 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *
     void *ctx);
 PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
+PHP_REDIS_API void cluster_vemb_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+    void *ctx);
+PHP_REDIS_API void cluster_vinfo_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+    void *ctx);
+PHP_REDIS_API void cluster_vlinks_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+    void *ctx);
+PHP_REDIS_API void cluster_vgetattr_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
+    void *ctx);
 
 PHP_REDIS_API void cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c,
     void *ctx);
@@ -457,8 +470,6 @@ PHP_REDIS_API void cluster_variant_resp_strings(INTERNAL_FUNCTION_PARAMETERS,
     redisCluster *c, void *ctx);
 
 /* MULTI BULK response functions */
-PHP_REDIS_API void cluster_gen_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
-    redisCluster *c, mbulk_cb func, void *ctx);
 PHP_REDIS_API void cluster_mbulk_raw_resp(INTERNAL_FUNCTION_PARAMETERS,
     redisCluster *c, void *ctx);
 PHP_REDIS_API void cluster_mbulk_resp(INTERNAL_FUNCTION_PARAMETERS,
@@ -488,7 +499,7 @@ PHP_REDIS_API void cluster_msetnx_resp(INTERNAL_FUNCTION_PARAMETERS,
 
 /* Response handler for ZSCAN, SSCAN, and HSCAN */
 PHP_REDIS_API int cluster_scan_resp(INTERNAL_FUNCTION_PARAMETERS,
-    redisCluster *c, REDIS_SCAN_TYPE type, long *it);
+    redisCluster *c, REDIS_SCAN_TYPE type, uint64_t *cursor);
 
 /* INFO response handler */
 PHP_REDIS_API void cluster_info_resp(INTERNAL_FUNCTION_PARAMETERS,
@@ -515,20 +526,6 @@ PHP_REDIS_API void cluster_mpop_resp(INTERNAL_FUNCTION_PARAMETERS,
 PHP_REDIS_API void cluster_acl_getuser_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx);
 PHP_REDIS_API void cluster_acl_log_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx);
 
-/* MULTI BULK processing callbacks */
-int mbulk_resp_loop(RedisSock *redis_sock, zval *z_result,
-    long long count, void *ctx);
-int mbulk_resp_loop_raw(RedisSock *redis_sock, zval *z_result,
-    long long count, void *ctx);
-int mbulk_resp_loop_zipstr(RedisSock *redis_sock, zval *z_result,
-    long long count, void *ctx);
-int mbulk_resp_loop_dbl(RedisSock *redis_sock, zval *z_result,
-    long long count, void *ctx);
-int mbulk_resp_loop_zipdbl(RedisSock *redis_sock, zval *z_result,
-    long long count, void *ctx);
-int mbulk_resp_loop_assoc(RedisSock *redis_sock, zval *z_result,
-    long long count, void *ctx);
-
 #endif
 
 /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */
diff --git a/common.h b/common.h
index 8e2ec57ab8..27e79d600d 100644
--- a/common.h
+++ b/common.h
@@ -11,7 +11,7 @@
 #include 
 #include 
 #include 
-#include 
+#include 
 
 #define PHPREDIS_GET_OBJECT(class_entry, o) (class_entry *)((char *)o - XtOffsetOf(class_entry, std))
 #define PHPREDIS_ZVAL_GET_OBJECT(class_entry, z) PHPREDIS_GET_OBJECT(class_entry, Z_OBJ_P(z))
@@ -31,6 +31,43 @@
 #define NULL   ((void *) 0)
 #endif
 
+#if defined(_WIN32) || defined(_WIN64)
+# define PHPREDIS_LITTLE_ENDIAN
+#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+# define PHPREDIS_BIG_ENDIAN
+#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+# define PHPREDIS_LITTLE_ENDIAN
+#else
+# error "Unknown endianness"
+#endif
+
+#if defined(_MSC_VER) && !defined(__clang__)
+#  define REDIS_MSVC 1
+#endif
+
+#ifndef REDIS_MSVC
+#  ifndef REDIS_NODISCARD
+#    if defined(__has_c_attribute)
+#      if __has_c_attribute(nodiscard)
+#        define REDIS_NODISCARD [[nodiscard]]
+#      endif
+#    endif
+#  endif
+#  ifndef REDIS_NODISCARD
+#    if defined(__has_attribute)
+#      if __has_attribute(warn_unused_result)
+#        define REDIS_NODISCARD __attribute__((warn_unused_result))
+#      endif
+#    elif defined(__GNUC__) || defined(__clang__)
+#      define REDIS_NODISCARD __attribute__((warn_unused_result))
+#    endif
+#  endif
+#endif /* ifndef REDIS_MSVC */
+
+#ifndef REDIS_NODISCARD
+#  define REDIS_NODISCARD
+#endif
+
 #include "backoff.h"
 
 typedef enum {
@@ -51,6 +88,7 @@ typedef enum {
 #define REDIS_ZSET      4
 #define REDIS_HASH      5
 #define REDIS_STREAM    6
+#define REDIS_VECTORSET 7
 
 #ifdef PHP_WIN32
 #define PHP_REDIS_API __declspec(dllexport)
@@ -91,20 +129,21 @@ typedef enum _PUBSUB_TYPE {
 #define REDIS_SUBS_BUCKETS   3
 
 /* options */
-#define REDIS_OPT_SERIALIZER         1
-#define REDIS_OPT_PREFIX             2
-#define REDIS_OPT_READ_TIMEOUT       3
-#define REDIS_OPT_SCAN               4
-#define REDIS_OPT_FAILOVER           5
-#define REDIS_OPT_TCP_KEEPALIVE      6
-#define REDIS_OPT_COMPRESSION        7
-#define REDIS_OPT_REPLY_LITERAL      8
-#define REDIS_OPT_COMPRESSION_LEVEL  9
-#define REDIS_OPT_NULL_MBULK_AS_NULL 10
-#define REDIS_OPT_MAX_RETRIES        11
-#define REDIS_OPT_BACKOFF_ALGORITHM  12
-#define REDIS_OPT_BACKOFF_BASE       13
-#define REDIS_OPT_BACKOFF_CAP        14
+#define REDIS_OPT_SERIALIZER          1
+#define REDIS_OPT_PREFIX              2
+#define REDIS_OPT_READ_TIMEOUT        3
+#define REDIS_OPT_SCAN                4
+#define REDIS_OPT_FAILOVER            5
+#define REDIS_OPT_TCP_KEEPALIVE       6
+#define REDIS_OPT_COMPRESSION         7
+#define REDIS_OPT_REPLY_LITERAL       8
+#define REDIS_OPT_COMPRESSION_LEVEL   9
+#define REDIS_OPT_NULL_MBULK_AS_NULL  10
+#define REDIS_OPT_MAX_RETRIES         11
+#define REDIS_OPT_BACKOFF_ALGORITHM   12
+#define REDIS_OPT_BACKOFF_BASE        13
+#define REDIS_OPT_BACKOFF_CAP         14
+#define REDIS_OPT_PACK_IGNORE_NUMBERS 15
 
 /* cluster options */
 #define REDIS_FAILOVER_NONE              0
@@ -176,89 +215,6 @@ typedef enum {
 #define IS_MULTI(redis_sock) (redis_sock->mode & MULTI)
 #define IS_PIPELINE(redis_sock) (redis_sock->mode & PIPELINE)
 
-#define PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len) do { \
-    if (redis_sock->pipeline_cmd == NULL) { \
-        redis_sock->pipeline_cmd = zend_string_init(cmd, cmd_len, 0); \
-    } else { \
-        size_t pipeline_len = ZSTR_LEN(redis_sock->pipeline_cmd); \
-        redis_sock->pipeline_cmd = zend_string_realloc(redis_sock->pipeline_cmd, pipeline_len + cmd_len, 0); \
-        memcpy(&ZSTR_VAL(redis_sock->pipeline_cmd)[pipeline_len], cmd, cmd_len); \
-    } \
-} while (0)
-
-#define SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len) \
-    if(redis_sock_write(redis_sock, cmd, cmd_len) < 0) { \
-    efree(cmd); \
-    RETURN_FALSE; \
-}
-
-#define REDIS_SAVE_CALLBACK(callback, closure_context) do { \
-    fold_item *fi = malloc(sizeof(fold_item)); \
-    fi->fun = callback; \
-    fi->ctx = closure_context; \
-    fi->next = NULL; \
-    if (redis_sock->current) { \
-        redis_sock->current->next = fi; \
-    } \
-    redis_sock->current = fi; \
-    if (NULL == redis_sock->head) { \
-        redis_sock->head = redis_sock->current; \
-    } \
-} while (0)
-
-#define REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len) \
-    if (IS_PIPELINE(redis_sock)) { \
-        PIPELINE_ENQUEUE_COMMAND(cmd, cmd_len); \
-    } else { \
-        SOCKET_WRITE_COMMAND(redis_sock, cmd, cmd_len); \
-    } \
-    efree(cmd);
-
-#define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \
-    if (!IS_PIPELINE(redis_sock)) { \
-        if (redis_response_enqueued(redis_sock) != SUCCESS) { \
-            RETURN_FALSE; \
-        } \
-    } \
-    REDIS_SAVE_CALLBACK(function, closure_context); \
-    RETURN_ZVAL(getThis(), 1, 0); \
-
-#define REDIS_PROCESS_RESPONSE(function) else { \
-    REDIS_PROCESS_RESPONSE_CLOSURE(function, NULL) \
-}
-
-/* Process a command assuming our command where our command building
- * function is redis__cmd */
-#define REDIS_PROCESS_CMD(cmdname, resp_func) \
-    RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \
-    if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL || \
-       redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock, \
-                             &cmd, &cmd_len, NULL, &ctx)==FAILURE) { \
-            RETURN_FALSE; \
-    } \
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \
-    if (IS_ATOMIC(redis_sock)) { \
-        resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \
-    } else { \
-        REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \
-    }
-
-/* Process a command but with a specific command building function
- * and keyword which is passed to us*/
-#define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \
-    RedisSock *redis_sock; char *cmd; int cmd_len; void *ctx=NULL; \
-    if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL || \
-       cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, \
-               &cmd_len, NULL, &ctx)==FAILURE) { \
-            RETURN_FALSE; \
-    } \
-    REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); \
-    if (IS_ATOMIC(redis_sock)) { \
-        resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); \
-    } else { \
-        REDIS_PROCESS_RESPONSE_CLOSURE(resp_func, ctx) \
-    }
-
 /* Case sensitive compare against compile-time static string */
 #define REDIS_STRCMP_STATIC(s, len, sstr) \
     (len == sizeof(sstr) - 1 && !strncmp(s, sstr, len))
@@ -267,6 +223,16 @@ typedef enum {
 #define REDIS_STRICMP_STATIC(s, len, sstr) \
     (len == sizeof(sstr) - 1 && !strncasecmp(s, sstr, len))
 
+/* On some versions of glibc strncmp and strncasecmp can be a macro a macro.
+ * This wrapper allows us to use it in combination with ZEND_STRL in those
+ * cases. */
+static inline int redis_strncmp(const char *s1, const char *s2, size_t n) {
+    return strncmp(s1, s2, n);
+}
+static inline int redis_strncasecmp(const char *s1, const char *s2, size_t n) {
+    return strncasecmp(s1, s2, n);
+}
+
 /* Test if a zval is a string and (case insensitive) matches a static string */
 #define ZVAL_STRICMP_STATIC(zv, sstr) \
     REDIS_STRICMP_STATIC(Z_STRVAL_P(zv), Z_STRLEN_P(zv), sstr)
@@ -275,9 +241,6 @@ typedef enum {
 #define ZSTR_STRICMP_STATIC(zs, sstr) \
     REDIS_STRICMP_STATIC(ZSTR_VAL(zs), ZSTR_LEN(zs), sstr)
 
-#define REDIS_ENABLE_MODE(redis_sock, m) (redis_sock->mode |= m)
-#define REDIS_DISABLE_MODE(redis_sock, m) (redis_sock->mode &= ~m)
-
 /* HOST_NAME_MAX doesn't exist everywhere */
 #ifndef HOST_NAME_MAX
     #if defined(_POSIX_HOST_NAME_MAX)
@@ -294,59 +257,66 @@ typedef enum {
 #define RESP_EXEC_CMD          "*1\r\n$4\r\nEXEC\r\n"
 #define RESP_DISCARD_CMD       "*1\r\n$7\r\nDISCARD\r\n"
 
+typedef struct RedisHello {
+    zend_string *server;
+    zend_string *version;
+} RedisHello;
+
 /* {{{ struct RedisSock */
 typedef struct {
     php_stream          *stream;
-    php_stream_context  *stream_ctx;
     zend_string         *host;
     int                 port;
     zend_string         *user;
     zend_string         *pass;
+    HashTable           *context;
     double              timeout;
     double              read_timeout;
     long                retry_interval;
     int                 max_retries;
     struct RedisBackoff backoff;
     redis_sock_status   status;
-    int                 persistent;
-    int                 watching;
+    zend_bool           persistent;
+    zend_bool           watching;
     zend_string         *persistent_id;
     HashTable           *subs[REDIS_SUBS_BUCKETS];
     redis_serializer    serializer;
+    zend_bool           pack_ignore_numbers;
     int                 compression;
     int                 compression_level;
     long                dbNumber;
-
     zend_string         *prefix;
-
+    struct RedisHello   hello;
     short               mode;
-    struct fold_item    *head;
-    struct fold_item    *current;
-
-    zend_string         *pipeline_cmd;
+    struct fold_item    *reply_callback;
+    size_t              reply_callback_count;
+    size_t              reply_callback_capacity;
+    smart_string        pipeline_cmd;
 
     zend_string         *err;
-
     int                 scan;
 
-    int                 readonly;
-    int                 reply_literal;
-    int                 null_mbulk_as_null;
-    int                 tcp_keepalive;
-    int                 sentinel;
+    zend_bool           readonly;
+    zend_bool           reply_literal;
+    zend_bool           null_mbulk_as_null;
+    zend_bool           tcp_keepalive;
+    zend_bool           sentinel;
     size_t              txBytes;
     size_t              rxBytes;
+    uint8_t             flags;
 } RedisSock;
 /* }}} */
 
 /* Redis response handler function callback prototype */
-typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx);
-typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS, RedisSock*, zval*, void*);
+typedef void (*ResultCallback)(INTERNAL_FUNCTION_PARAMETERS,
+    RedisSock *redis_sock, zval *z_tab, void *ctx);
+typedef int (*FailableResultCallback)(INTERNAL_FUNCTION_PARAMETERS,
+    RedisSock*, zval*, void*);
 
 typedef struct fold_item {
     FailableResultCallback fun;
+    uint8_t flags;
     void *ctx;
-    struct fold_item *next;
 } fold_item;
 
 typedef struct {
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000000..dd4d00af0c
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,63 @@
+
+{
+    "name": "phpredis/phpredis",
+    "type": "php-ext",
+    "license": "PHP-3.01",
+    "description": "A PHP extension for Redis",
+    "require": {
+        "php": ">= 7.4.0"
+    },
+    "php-ext": {
+        "extension-name": "redis",
+        "configure-options": [
+            {
+                "name": "enable-redis",
+                "description": "Enable redis support"
+            },
+            {
+                "name": "disable-redis-session",
+                "description": "Disable session support"
+            },
+            {
+                "name": "disable-redis-json",
+                "description": "Disable json serializer support"
+            },
+            {
+                "name": "enable-redis-igbinary",
+                "description": "Enable igbinary serializer support"
+            },
+            {
+                "name": "enable-redis-msgpack",
+                "description": "Enable msgpack serializer support"
+            },
+            {
+                "name": "enable-redis-lzf",
+                "description": "Enable lzf compression support"
+            },
+            {
+                "name": "with-liblzf",
+                "description": "Use system liblzf",
+                "needs-value": true
+            },
+            {
+                "name": "enable-redis-zstd",
+                "description": "Enable Zstd compression support"
+            },
+            {
+                "name": "with-libzstd",
+                "description": "Use system libzstd",
+                "needs-value": true
+            },
+            {
+                "name": "enable-redis-lz4",
+                "description": "Enable lz4 compression support"
+            },
+            {
+                "name": "with-liblz4",
+                "description": "Use system liblz4",
+                "needs-value": true
+            }
+        ],
+        "priority": 60
+    }
+}
diff --git a/config.m4 b/config.m4
index 2ba4a8b51d..c84ce1e99f 100644
--- a/config.m4
+++ b/config.m4
@@ -221,6 +221,7 @@ if test "$PHP_REDIS" != "no"; then
       PHP_CHECK_LIBRARY(lzf, lzf_compress,
       [
         PHP_ADD_LIBRARY_WITH_PATH(lzf, $LIBLZF_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD)
+        PHP_ADD_INCLUDE($LIBLZF_DIR/include)
       ], [
         AC_MSG_ERROR([could not find usable liblzf])
       ], [
@@ -263,12 +264,12 @@ if test "$PHP_REDIS" != "no"; then
       PHP_CHECK_LIBRARY(lz4, LZ4_compress,
       [
         PHP_ADD_LIBRARY_WITH_PATH(lz4, $LIBLZ4_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD)
+        PHP_ADD_INCLUDE($LIBLZ4_DIR/include)
       ], [
         AC_MSG_ERROR([could not find usable liblz4])
       ], [
         -L$LIBLZ4_DIR/$PHP_LIBDIR
       ])
-      PHP_SUBST(REDIS_SHARED_LIBADD)
     else
       AC_MSG_ERROR([only system liblz4 is supported])
     fi
@@ -307,6 +308,7 @@ if test "$PHP_REDIS" != "no"; then
       PHP_CHECK_LIBRARY(zstd, ZSTD_getFrameContentSize,
       [
         PHP_ADD_LIBRARY_WITH_PATH(zstd, $LIBZSTD_DIR/$PHP_LIBDIR, REDIS_SHARED_LIBADD)
+        PHP_ADD_INCLUDE($LIBZSTD_DIR/include)
       ], [
         AC_MSG_ERROR([could not find usable libzstd, version 1.3.0 required])
       ], [
diff --git a/docs/DOCTUM_VERSION b/docs/DOCTUM_VERSION
index d41f08f1f3..0413736d69 100644
--- a/docs/DOCTUM_VERSION
+++ b/docs/DOCTUM_VERSION
@@ -1 +1 @@
-5.5.1
\ No newline at end of file
+5.5.4
\ No newline at end of file
diff --git a/docs/PROJECT_VERSION b/docs/PROJECT_VERSION
index 6563189c54..ce57f64563 100644
--- a/docs/PROJECT_VERSION
+++ b/docs/PROJECT_VERSION
@@ -1 +1 @@
-develop
+develop
\ No newline at end of file
diff --git a/docs/Redis.html b/docs/Redis.html
index a74aa2d48f..009c813d0a 100644
--- a/docs/Redis.html
+++ b/docs/Redis.html
@@ -5,7 +5,7 @@
     
     Redis | PhpRedis API
 
-            
+                
         
         
         
@@ -16,11 +16,400 @@
         
         
         
+    
+    
+    
+
 
         
+          title="PhpRedis API (develop)" />
     
 
     
@@ -83,7 +472,7 @@ 

Redis

class - Redis (View source) + Redis (View source)

@@ -92,6 +481,511 @@

Redis +

Constants

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ REDIS_NOT_FOUND + +

Returned by \Redis::type() when the key does not exist or has a type +we are not familiar with.

+

+
+ REDIS_STRING + +

Returned by \Redis::type() when the key is a string.

+

+
+ REDIS_SET + +

Returned by \Redis::type() when the key is a set.

+

+
+ REDIS_LIST + +

Returned by \Redis::type() when the key is a list.

+

+
+ REDIS_ZSET + +

Returned by \Redis::type() when the key is a sorted set.

+

+
+ REDIS_HASH + +

Returned by \Redis::type() when the key is a hash.

+

+
+ REDIS_STREAM + +

Returned by \Redis::type() when the key is a stream.

+

+
+ REDIS_VECTORSET + +

Returned by \Redis::type() when the key is a vector set.

+

+
+ ATOMIC + +

Returned from \Redis::getMode() when we're not in a multi or pipeline +transaction.

+

+
+ MULTI + +

Returned from \Redis::getMode() when we're in a multi transaction.

+

+
+ PIPELINE + +

Returned from \Redis::getMode() when we're in a pipeline transaction.

+

+
+ OPT_SERIALIZER + +

Used with \Redis::setOption() to specify the serializer to use

+

+
+ OPT_PREFIX + +

Used to set an automatic prefix for keys used in commands.

+

+
+ OPT_READ_TIMEOUT + +

Used to set the read timeout for the connection.

+

+
+ OPT_TCP_KEEPALIVE + +

Used to enable or disable TCP keepalive on the connection.

+

+
+ OPT_COMPRESSION + +

Used to set the compression algorithm to use for compressing

+

+
+ OPT_REPLY_LITERAL + +

Causes PhpRedis to return the actual string in +OK style responses +from Redis. If disabled those replies are just converted to boolean +true.

+

+
+ OPT_COMPRESSION_LEVEL + +

Used to specify the compression level to use when compressing data.

+

+
+ OPT_NULL_MULTIBULK_AS_NULL + +

Tells PhpRedis to return a NULL multi-bulk (*-1\r\n) response +as null as opposed to an empty array.

+

+
+ OPT_PACK_IGNORE_NUMBERS + +

When enabled, this option tells PhpRedis to ignore purely numeric values +when packing and unpacking data. This does not include numeric strings.

+

If you want numeric strings to be ignored, typecast them to an int or +float.

+

The primary purpose of this option is to make it more ergonomic when +setting keys that will later be incremented or decremented.

+

Note: This option incurs a small performance penalty when reading data +because we have to see if the data is a string representation of an int +or float.

+
+ SERIALIZER_NONE + +

Sets the serializer to none (no serialization).

+

+
+ SERIALIZER_PHP + +

Sets the serializer to PHP's built-in serialize()/unserialize()

+

+
+ SERIALIZER_IGBINARY + +

Sets the serializer to igbinary. Note that phpredis must be compiled +with ighbinary support to use this serializer.

+

+
+ SERIALIZER_MSGPACK + +

Sets the serializer to msgpack. Note that phpredis must be compiled +with msgpack support to use this serializer.

+

+
+ SERIALIZER_JSON + +

Sets the serializer to JSON.

+

+
+ COMPRESSION_NONE + +

Disables compression.

+

+
+ COMPRESSION_LZF + +

Sets the compression algorithm to LZF. PhpRedis must be compiled with +lzf support but this serializer is bundled with the extension.

+

+
+ COMPRESSION_ZSTD + +

Sets the compression algorithm to ZSTD. PhpRedis must be compiled with +zstd support to use this serializer. This is often the best balance +between speed and compression ratio.

+

+
+ COMPRESSION_ZSTD_DEFAULT + +

This constant represents the "default" compression level for ZSTD. If +PhpRedis is compiled against a new enough ZSTD the value comes from the +library, otherwise we just set it to 3.

+

+
+ COMPRESSION_ZSTD_MIN + +

The minimum compression level ZSTD supports, which comes from the +underlying ZSTD library if new enough. Otherwise we just set it to 1.

+

+
+ COMPRESSION_ZSTD_MAX + +

The maximum compression level ZSTD supports, which comes from the +underlying ZSTD library.

+

+
+ COMPRESSION_LZ4 + +

Set the compression algorithm to LZ4. PhpRedis must be compiled with +lz4 support to use this serializer. This algorithm is generally +the fastest but has a lower compression ratio than ZSTD.

+

+
+ OPT_SCAN + +

Used with \Redis::setOption() to specify scan options.

+

+
+ SCAN_RETRY + +

When enabled, this option causes PhpRedis to automatically retry SCAN +commands when Redis returns a non-zero cursor but no keys. This can +happen due to the nature of Redis' scanning algorithm.

+

+
+ SCAN_NORETRY + +

Then enabled, this option tells PhpRedis to not retry SCAN commands +when Redis returns a non-zero cursor but no keys. This means that your +code must handle this case itself.

+

+
+ SCAN_PREFIX + +

Tells PhpRedis to prefix keys returned from SCAN commands with the +currently set key prefix.

+

+
+ SCAN_NOPREFIX + +

Tells PhpRedis to NOT prefix keys returned from SCAN commands with +the currently set key prefix.

+

+
+ BEFORE + +

This is just the string "before" which is used with various list +commands to indicate an insertion point.

+

+
+ AFTER + +

This is just the string "after" which is used with various list commands +to indicate an insertion point.

+

+
+ LEFT + +

This is just the string "left" which is used with various list commands +such as LMOVE.

+

+
+ RIGHT + +

This is just the string "right" which is used with various list commands +such as LMOVE.

+

+
+ OPT_MAX_RETRIES + +

How many times should PhpRedis attempt to reconnect when we are +disconnected.

+

+
+ OPT_BACKOFF_ALGORITHM + +

Used to specify the backoff algorithm to use when reconnecting.

+

+
+ BACKOFF_ALGORITHM_DEFAULT + +

Default backoff - random delay before the first retry, then constant base ms.

+

+
+ BACKOFF_ALGORITHM_CONSTANT + +

Constant backoff - always sleep for exactly base ms (capped by cap).

+

+
+ BACKOFF_ALGORITHM_UNIFORM + +

Uniform backoff - randomly sleep between 0 and base ms for each retry.

+

+
+ BACKOFF_ALGORITHM_EXPONENTIAL + +

Exponential backoff - doubles the delay every retry (up to 2^10) before cap.

+

+
+ BACKOFF_ALGORITHM_FULL_JITTER + +

Full jitter - exponential delay but pick a random value between 0 and that delay.

+

+
+ BACKOFF_ALGORITHM_EQUAL_JITTER + +

Equal jitter - half the exponential delay plus a random amount up to the other half.

+

+
+ BACKOFF_ALGORITHM_DECORRELATED_JITTER + +

Decorrelated jitter - pick a random delay between base and 3x the previous delay.

+

+
+ OPT_BACKOFF_BASE + +

Backoff base - minimum delay in milliseconds that algorithms start from.

+

+
+ OPT_BACKOFF_CAP + +

Backoff cap - maximum delay in milliseconds that any algorithm can reach.

+

+
+

Methods

@@ -102,7 +996,7 @@

Methods

Redis
- __construct(array $options = null) + __construct(array|null $options = null)

Create a new Redis instance. If passed sufficient information in the options array it is also possible to connect to an instance at the same @@ -116,8 +1010,7 @@

Methods

__destruct() -

No description

-
+

Destructor to clean up the Redis object.

@@ -127,8 +1020,8 @@

Methods

_compress(string $value) -

Compress a value with the currently configured compressor as set with -Redis::setOption().

+

Compress a value with the currently configured compressor (Redis::OPT_COMPRESSION) +exactly the same way PhpRedis does before sending data to Redis.

@@ -138,8 +1031,8 @@

Methods

_uncompress(string $value) -

Uncompress the provided argument that has been compressed with the -currently configured compressor as set with Redis::setOption().

+

Uncompress the provided argument using the compressor configured via +Redis::setOption() (Redis::OPT_COMPRESSION).

@@ -182,8 +1075,20 @@

Methods

_pack(mixed $value) -

Pack the provided value with the configured serializer and compressor -as set with Redis::setOption().

+

Pack the provided value by first serializing it (if Redis::OPT_SERIALIZER is set) +and then compressing the serialized payload (if Redis::OPT_COMPRESSION is set), +mirroring exactly what PhpRedis transmits to Redis.

+
+ +
+
+ string +
+
+ _digest(mixed $value) + +

Compute the XXH3 digest of a PHP value after it has been _packed, producing +the same digest Redis' DIGEST command would return for the stored value.

@@ -193,8 +1098,9 @@

Methods

_unpack(string $value) -

Unpack the provided value with the configured compressor and serializer -as set with Redis::setOption().

+

Unpack the provided value by first uncompressing it (if Redis::OPT_COMPRESSION +is set) and then unserializing it (if Redis::OPT_SERIALIZER is set) to recover +the original PHP value.

@@ -204,8 +1110,7 @@

Methods

acl(string $subcmd, string ...$args) -

No description

-
+

Execute Redis ACL subcommands.

@@ -247,6 +1152,17 @@

Methods

Asynchronously rewrite Redis' append-only file

+ +
+
+ Redis|array|false +
+
+ waitaof(int $numlocal, int $numreplicas, int $timeout) + +

No description

+
+
@@ -392,8 +1308,7 @@

Methods

client(string $opt, mixed ...$args) -

No description

-
+

Execute Redis CLIENT subcommands.

@@ -403,8 +1318,7 @@

Methods

close() -

No description

-
+

Closes the connection to Redis

@@ -412,10 +1326,9 @@

Methods

mixed
- command(string $opt = null, string|array $arg) + command(string|null $opt = null, mixed ...$args) -

No description

-
+

Execute Redis COMMAND subcommands.

@@ -423,7 +1336,7 @@

Methods

mixed
- config(string $operation, array|string|null $key_or_settings = NULL, string|null $value = NULL) + config(string $operation, array|string|null $key_or_settings = null, string|null $value = null)

Execute the Redis CONFIG command in a variety of ways.

@@ -433,10 +1346,9 @@

Methods

bool
- connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null) + connect(string $host, int $port = 6379, float $timeout = 0, string|null $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array|null $context = null) -

No description

-
+

Connect to a Redis server

@@ -444,7 +1356,7 @@

Methods

Redis|bool
- copy(string $src, string $dst, array $options = null) + copy(string $src, string $dst, array|null $options = null)

Make a copy of a key.

@@ -466,8 +1378,9 @@

Methods

debug(string $key) -

No description

-
+

Execute the Redis DEBUG command. Note that this is disabled by default +and can be very dangerous, even allowing you to crash the server. Use +with caution

@@ -505,29 +1418,50 @@

Methods

Redis|int|false
- delete(array|string $key, string ...$other_keys) - deprecated -

No description

-
+ delex(string $key, array|null $options = null) + +

Delete a key conditionally based on its value or hash digest

- Redis|bool + Redis|int|false
- discard() + delifeq(string $key, mixed $value) -

Discard a transaction currently in progress.

+

Delete a key if it's equal to the specified value. This command is +specific to Valkey >= 9.0

- Redis|string + Redis|int|false
- dump(string $key) - + delete(array|string $key, string ...$other_keys) + deprecated +

No description

+
+
+
+
+
+ Redis|bool +
+
+ discard() + +

Discard a transaction currently in progress.

+
+
+
+
+ Redis|string|false +
+
+ dump(string $key) +

Dump Redis' internal binary representation of a key.

@@ -609,7 +1543,7 @@

Methods

Redis|bool
- expire(string $key, int $timeout, string|null $mode = NULL) + expire(string $key, int $timeout, string|null $mode = null)

Sets an expiration in seconds on the key in question. If connected to redis-server >= 7.0.0 you may send an additional "mode" argument which @@ -621,7 +1555,7 @@

Methods

Redis|bool
- expireAt(string $key, int $timestamp, string|null $mode = NULL) + expireAt(string $key, int $timestamp, string|null $mode = null)

Set a key to expire at an exact unix timestamp.

@@ -654,7 +1588,27 @@

Methods

pexpiretime(string $key) -

Get the expriation timestamp of a given Redis key but in milliseconds.

+

Get the expiration timestamp of a given Redis key but in milliseconds.

+
+ +
+
+ mixed +
+
+ fcall(string $fn, array $keys = [], array $args = []) + +

Invoke a function.

+
+
+
+
+ mixed +
+
+ fcall_ro(string $fn, array $keys = [], array $args = []) + +

This is a read-only variant of the FCALL command that cannot execute commands that modify data.

@@ -676,6 +1630,16 @@

Methods

Deletes all the keys of the currently selected database.

+ +
+
+ Redis|bool|string|array +
+
+ function(string $operation, mixed ...$args) + +

Functions is an API for managing code to be executed on the server.

+
@@ -714,7 +1678,7 @@

Methods

geopos(string $key, string $member, string ...$other_members) -

Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

+

Return the longitude and latitude for one or more members of a geospacially encoded sorted set.

@@ -787,6 +1751,16 @@

Methods

Retrieve a string keys value.

+ +
+
+ Redis|array|false +
+
+ getWithMeta(string $key) + +

Retrieve a value and metadata of key.

+
@@ -897,6 +1871,26 @@

Methods

Get the port we are connected to. This number will be zero if we are connected to a unix socket.

+
+
+
+ string|false +
+
+ serverName() + +

Get the server name as reported by the HELLO response.

+
+
+
+
+ string|false +
+
+ serverVersion() + +

Get the server version as reported by the HELLO response.

+
@@ -913,7 +1907,7 @@

Methods

Redis|string|array|int|false
- lcs(string $key1, string $key2, array|null $options = NULL) + lcs(string $key1, string $key2, array|null $options = null)

Get the longest common subsequence between two string keys.

@@ -950,13 +1944,22 @@

Methods

- int|false + array
getTransferredBytes() -

No description

-
+

Get the number of bytes sent and received on the socket.

+
+ +
+
+ void +
+
+ clearTransferredBytes() + +

Reset the number of bytes sent and received on the socket.

@@ -999,6 +2002,16 @@

Methods

Read every field and value from a hash.

+ +
+
+ mixed +
+
+ hGetWithMeta(string $key, string $member) + +

Retrieve a value and metadata of hash field.

+
@@ -1049,6 +2062,37 @@

Methods

Get one or more fields from a hash.

+
+
+
+ Redis|array|false +
+
+ hgetex(string $key, array $fields, string|array|null $expiry = null) + +

Get one or more fields of a hash while optionally setting expiration +information

+
+
+
+
+ Redis|int|false +
+
+ hsetex(string $key, array $fields, array|null $expiry = null) + +

Set one or more fields in a hash with optional expiration information.

+
+
+
+
+ Redis|array|false +
+
+ hgetdel(string $key, array $fields) + +

Get one or more fields and delete them

+
@@ -1062,10 +2106,10 @@

Methods

- Redis|string|array + Redis|string|array|false
- hRandField(string $key, array $options = null) + hRandField(string $key, array|null $options = null)

Get one or more random field from a hash.

@@ -1075,10 +2119,9 @@

Methods

Redis|int|false
- hSet(string $key, string $member, mixed $value) + hSet(string $key, mixed ...$fields_and_vals) -

No description

-
+

Add or update one or more hash fields and values.

@@ -1086,7 +2129,7 @@

Methods

Redis|bool
- hSetNx(string $key, string $field, string $value) + hSetNx(string $key, string $field, mixed $value)

Set a hash field and value, but only if that field does not exist

@@ -1110,16 +2153,126 @@

Methods

Get all of the values from a hash.

+ +
+
+ Redis|array|false +
+
+ hexpire(string $key, int $ttl, array $fields, string|null $mode = NULL) + +

Set the expiration on one or more fields in a hash.

+
+
+
+
+ Redis|array|false +
+
+ hpexpire(string $key, int $ttl, array $fields, string|null $mode = NULL) + +

Set the expiration on one or more fields in a hash in milliseconds.

+
+
+
+
+ Redis|array|false +
+
+ hexpireat(string $key, int $time, array $fields, string|null $mode = NULL) + +

Set the expiration time on one or more fields of a hash.

+
+
+
+
+ Redis|array|false +
+
+ hpexpireat(string $key, int $mstime, array $fields, string|null $mode = NULL) + +

Set the expiration time on one or more fields of a hash in milliseconds.

+
+
+
+
+ Redis|array|false +
+
+ httl(string $key, array $fields) + +

Get the TTL of one or more fields in a hash

+
+
+
+
+ Redis|array|false +
+
+ hpttl(string $key, array $fields) + +

Get the millisecond TTL of one or more fields in a hash

+
+
+
+
+ Redis|array|false +
+
+ hexpiretime(string $key, array $fields) + +

Get the expiration time of one or more fields in a hash

+
+
+
+
+ Redis|array|false +
+
+ hpexpiretime(string $key, array $fields) + +

Get the expiration time in milliseconds of one or more fields in a hash

+
+
+
+
+ Redis|array|false +
+
+ hpersist(string $key, array $fields) + +

Persist one or more hash fields

+
Redis|array|bool
- hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + hscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

Iterate over the fields and values of a hash in an incremental fashion.

+
+
+
+ Redis|int|false +
+
+ expiremember(string $key, string $field, int $ttl, string|null $unit = null) + +

Set an expiration on a key member (KeyDB only).

+
+
+
+
+ Redis|int|false +
+
+ expirememberat(string $key, string $field, int $timestamp) + +

Set an expiration on a key membert to a specific unix timestamp (KeyDB only).

+
@@ -1128,7 +2281,7 @@

Methods

incr(string $key, int $by = 1) -

Increment a key's value, optionally by a specifc amount.

+

Increment a key's value, optionally by a specific amount.

@@ -1176,7 +2329,7 @@

Methods

- Redis|array|false + Redis|list<string>|false
keys(string $pattern) @@ -1203,7 +2356,7 @@

Methods

lLen(string $key) -

Retrieve the lenght of a list.

+

Retrieve the length of a list.

@@ -1215,6 +2368,17 @@

Methods

Move an element from one list into another.

+ +
+
+ Redis|string|false +
+
+ blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout) + +

No description

+
+
@@ -1231,7 +2395,7 @@

Methods

Redis|null|bool|int|array
- lPos(string $key, mixed $value, array $options = null) + lPos(string $key, mixed $value, array|null $options = null)

Retrieve the index of an element in a list.

@@ -1338,12 +2502,12 @@

Methods

- Redis|array + Redis|array|false
mget(array $keys) -

Get one ore more string keys.

+

Get one or more string keys.

@@ -1351,10 +2515,9 @@

Methods

Redis|bool
- migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = NULL) + migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = null) -

No description

-
+

Proxy for the Redis MIGRATE command.

@@ -1374,7 +2537,17 @@

Methods

mset(array $key_values) -

Set one ore more string keys.

+

Set one or more string keys.

+
+ +
+
+ Redis|int|false +
+
+ msetex(array $key_vals, int|float|array|null $expiry = null) + +

Set one or more keys and values with optional expiry information.

@@ -1384,7 +2557,7 @@

Methods

msetnx(array $key_values) -

Set one ore more string keys but only if none of the key exist.

+

Set one or more string keys but only if none of the key exist.

@@ -1404,8 +2577,7 @@

Methods

object(string $subcommand, string $key) -

No description

-
+

Get encoding and other information about a key.

@@ -1413,7 +2585,7 @@

Methods

bool
- open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) + open(string $host, int $port = 6379, float $timeout = 0, string|null $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array|null $context = null) deprecated

No description

@@ -1424,10 +2596,9 @@

Methods

bool
- pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) + pconnect(string $host, int $port = 6379, float $timeout = 0, string|null $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array|null $context = null) -

No description

-
+

Connects to a Redis server creating or reusing a persistent connection.

@@ -1445,7 +2616,7 @@

Methods

bool
- pexpire(string $key, int $timeout, string|null $mode = NULL) + pexpire(string $key, int $timeout, string|null $mode = null)

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 you can pass an optional mode argument that modifies how the command will execute.

@@ -1456,7 +2627,7 @@

Methods

Redis|bool
- pexpireAt(string $key, int $timestamp, string|null $mode = NULL) + pexpireAt(string $key, int $timestamp, string|null $mode = null)

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to Redis >= 7.0.0 you can pass an optional 'mode' argument.

@@ -1474,10 +2645,10 @@

Methods

- Redis|int + Redis|int|false
- pfcount(string $key) + pfcount(array|string $key_or_keys)

Retrieve the cardinality of a Redis HyperLogLog key.

@@ -1497,7 +2668,7 @@

Methods

Redis|string|bool
- ping(string $message = NULL) + ping(string|null $message = null)

PING the redis server with an optional string argument.

@@ -1517,7 +2688,7 @@

Methods

bool
- popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) + popen(string $host, int $port = 6379, float $timeout = 0, string|null $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array|null $context = null) deprecated

No description

@@ -1570,8 +2741,7 @@

Methods

pubsub(string $command, mixed $arg = null) -

No description

-
+

Interact with the Redis PubSub subsystem.

@@ -1649,7 +2819,7 @@

Methods

Redis|bool
- restore(string $key, int $ttl, string $value, array|null $options = NULL) + restore(string $key, int $ttl, string $value, array|null $options = null)

Restore a key by the binary payload generated by the DUMP command.

@@ -1692,7 +2862,7 @@

Methods

sAddArray(string $key, array $values) -

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but +

Add one or more values to a Redis SET key. This is an alternative to Redis::sadd() but instead of being variadic, takes a single array of values.

@@ -1793,7 +2963,7 @@

Methods

- Redis|string|array|false + mixed
sRandMember(string $key, int $count = 0) @@ -1837,7 +3007,7 @@

Methods

array|false
- scan(int|null $iterator, string|null $pattern = null, int $count = 0, string $type = NULL) + scan(null|int|string $iterator, string|null $pattern = null, int $count = 0, string|null $type = null)

Incrementally scan the Redis keyspace, with optional pattern and type matching.

@@ -1877,7 +3047,7 @@

Methods

Redis|string|bool
- set(string $key, mixed $value, mixed $options = NULL) + set(string $key, mixed $value, mixed $options = null)

Create or set a Redis STRING key to a value.

@@ -1947,7 +3117,7 @@

Methods

Redis|bool
- slaveof(string $host = NULL, int $port = 6379) + slaveof(string|null $host = null, int $port = 6379) deprecated

Turn a redis instance into a replica of another or promote a replica to a primary.

@@ -1958,7 +3128,7 @@

Methods

Redis|bool
- replicaof(string $host = NULL, int $port = 6379) + replicaof(string|null $host = null, int $port = 6379)

Used to turn a Redis instance into a replica of another, or to remove replica status promoting the instance to a primary.

@@ -2064,10 +3234,20 @@

Methods

array|false
- sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + sscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

Scan the members of a redis SET key.

+ +
+
+ bool +
+
+ ssubscribe(array $channels, callable $cb) + +

Subscribes the client to the specified shard channels.

+
@@ -2088,6 +3268,17 @@

Methods

Subscribe to one or more Redis pubsub channels.

+
+
+
+ Redis|array|bool +
+
+ sunsubscribe(array $channels) + +

Unsubscribes the client from the given shard channels, +or from all of them if none is given.

+
@@ -2180,7 +3371,7 @@

Methods

wait(int $numreplicas, int $timeout)

Block the client up to the provided timeout until a certain number of replicas have confirmed -recieving them.

+receiving them.

@@ -2190,7 +3381,7 @@

Methods

xack(string $key, string $group, array $ids) -

Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but +

Acknowledge one or more messages that are pending (have been consumed using XREADGROUP but not yet acknowledged by XACK.)

@@ -2237,12 +3428,12 @@

Methods

- mixed + Redis|array|false
- xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) + xdelex(string $key, array $ids, string|null $mode = null) -

XGROUP

+

Remove one or more IDs from a stream with extended options.

@@ -2250,7 +3441,17 @@

Methods

mixed
- xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) + xgroup(string $operation, string|null $key = null, string|null $group = null, string|null $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) + +

XGROUP

+
+ +
+
+ mixed +
+
+ xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1)

Retrieve information about a stream key.

@@ -2313,7 +3514,147 @@

Methods

xrevrange(string $key, string $end, string $start, int $count = -1) -

Get a range of entries from a STREAM ke in reverse cronological order.

+

Get a range of entries from a STREAM key in reverse chronological order.

+
+ +
+
+ Redis|int|false +
+
+ vadd(string $key, array $values, mixed $element, array|null $options = null) + +

Add to a vector set

+
+
+
+
+ Redis|array|false +
+
+ vsim(string $key, mixed $member, array|null $options = null) + +

Query similarity of a vector by element or scores

+
+
+
+
+ Redis|int|false +
+
+ vcard(string $key) + +

Get the length of a vector set

+
+
+
+
+ Redis|int|false +
+
+ vdim(string $key) + +

Get the dimensions of a vector set

+
+
+
+
+ Redis|array|false +
+
+ vinfo(string $key) + +

Get various bits of information about a vector set

+
+
+
+
+ Redis|bool +
+
+ vismember(string $key, mixed $member) + +

Check if an element is a member of a vectorset

+
+
+
+
+ Redis|array|false +
+
+ vemb(string $key, mixed $member, bool $raw = false) + +

Get the embeddings for a specific member

+
+
+
+
+ Redis|array|string|false +
+
+ vrandmember(string $key, int $count = 0) + +

Get one or more random members from a vector set

+
+
+
+
+ Redis|array|false +
+
+ vrange(string $key, string $min, string $max, int $count = -1) + +

Retreive a lexographical range of elements from a vector set

+
+
+
+
+ Redis|int|false +
+
+ vrem(string $key, mixed $member) + +

Remove an element from a vector set

+
+
+
+
+ Redis|int|false +
+
+ vsetattr(string $key, mixed $member, array|string $attributes) + +

Set the attributes of a vector set element

+
+
+
+
+ Redis|array|string|false +
+
+ vgetattr(string $key, mixed $member, bool $decode = true) + +

Get the attributes of a vector set element

+
+
+
+
+ Redis|array|false +
+
+ vlinks(string $key, mixed $member, bool $withscores = false) + +

Get any adajcent values for a member of a vector set.

+
+
+
+
+ Redis|array|false +
+
+ gcra(string $key, int $maxBurst, int $requestsPerPeriod, int $period, int $tokens = 0) + +

Get rate limiting information

@@ -2328,7 +3669,7 @@

Methods

- Redis|int|false + Redis|int|float|false
zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) @@ -2351,7 +3692,7 @@

Methods

Redis|int|false
- zCount(string $key, string $start, string $end) + zCount(string $key, int|string $start, int|string $end)

Count the number of members in a sorted set with scores inside a provided range.

@@ -2373,7 +3714,7 @@

Methods

zLexCount(string $key, string $min, string $max) -

Count the number of elements in a sorted set whos members fall within the provided +

Count the number of elements in a sorted set whose members fall within the provided lexographical range.

@@ -2392,7 +3733,7 @@

Methods

Redis|array|false
- zPopMax(string $key, int $count = null) + zPopMax(string $key, int|null $count = null)

Pop one or more of the highest scoring elements from a sorted set.

@@ -2402,7 +3743,7 @@

Methods

Redis|array|false
- zPopMin(string $key, int $count = null) + zPopMin(string $key, int|null $count = null)

Pop one or more of the lowest scoring elements from a sorted set.

@@ -2412,7 +3753,7 @@

Methods

Redis|array|false
- zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null) + zRange(string $key, string|int $start, string|int $end, array|bool|null $options = null)

Retrieve a range of elements of a sorted set between a start and end point.

@@ -2442,7 +3783,7 @@

Methods

Redis|int|false
- zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL) + zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = null)

This command is similar to ZRANGE except that instead of returning the values directly it will store them in a destination key provided by the user

@@ -2453,7 +3794,7 @@

Methods

Redis|string|array
- zRandMember(string $key, array $options = null) + zRandMember(string $key, array|null $options = null)

Retrieve one or more random members from a Redis sorted set.

@@ -2563,7 +3904,7 @@

Methods

Redis|array|false
- zdiff(array $keys, array $options = null) + zdiff(array $keys, array|null $options = null)

Given one or more sorted set key names, return every element that is in the first set but not any of the others.

@@ -2607,7 +3948,7 @@

Methods

zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) -

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

+

Compute the intersection of one or more sorted sets storing the result in a new sorted set.

@@ -2615,7 +3956,7 @@

Methods

Redis|array|false
- zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + zscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

Scan the members of a sorted set incrementally, using a cursor

@@ -2635,10 +3976,20 @@

Methods

Redis|int|false
- zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) + zunionstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null)

Perform a union on one or more Redis sets and store the result in a destination sorted set.

+ +
+
+ Redis|string|false +
+
+ digest(string $key) + +

Ask the server for the XXH3 digest of a given key's value

+
@@ -2647,11 +3998,10 @@

Details

-

- - Redis - __construct(array $options = null) - + +

+ + Redis __construct(array|null $options = null)

@@ -2661,41 +4011,41 @@

Create a new Redis instance. If passed sufficient information in the options array it is also possible to connect to an instance at the same time.

NOTE: Below is an example options array with various setting

-
$options = [
-    'host'           => 'localhost',
-    'port'           => 6379,
-    'readTimeout'    => 2.5,
-    'connectTimeout' => 2.5,
-    'persistent'     => true,
-
-    // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass']
-    'auth' => ['phpredis', 'phpredis'],
-
-    // See PHP stream options for valid SSL configuration settings.
-    'ssl' => ['verify_peer' => false],
-
-    // How quickly to retry a connection after we time out or it  closes.
-    // Note that this setting is overridden by 'backoff' strategies.
-    'retryInterval'  => 100,
-
-     // Which backoff algorithm to use.  'decorrelated jitter' is
-     // likely the best one for most solution, but there are many
-     // to choose from:
-     //     REDIS_BACKOFF_ALGORITHM_DEFAULT
-     //     REDIS_BACKOFF_ALGORITHM_CONSTANT
-     //     REDIS_BACKOFF_ALGORITHM_UNIFORM
-     //     REDIS_BACKOFF_ALGORITHM_EXPONENTIAL
-     //     REDIS_BACKOFF_ALGORITHM_FULL_JITTER
-     //     REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER
-     //     REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER
-     // 'base', and 'cap' are in milliseconds and represent the first
-     // delay redis will use when reconnecting, and the maximum delay
-     // we will reach while retrying.
-    'backoff' => [
-        'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER,
-        'base'      => 500,
-        'cap'       => 750,
-    ]
+
$options = [
+   'host'           => 'localhost',
+   'port'           => 6379,
+   'readTimeout'    => 2.5,
+   'connectTimeout' => 2.5,
+   'persistent'     => true,
+
+   // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass']
+   'auth' => ['phpredis', 'phpredis'],
+
+   // See PHP stream options for valid SSL configuration settings.
+   'ssl' => ['verify_peer' => false],
+
+   // How quickly to retry a connection after we time out or it  closes.
+   // Note that this setting is overridden by 'backoff' strategies.
+   'retryInterval'  => 100,
+
+    // Which backoff algorithm to use.  'decorrelated jitter' is
+    // likely the best one for most solution, but there are many
+    // to choose from:
+    //     REDIS_BACKOFF_ALGORITHM_DEFAULT
+    //     REDIS_BACKOFF_ALGORITHM_CONSTANT
+    //     REDIS_BACKOFF_ALGORITHM_UNIFORM
+    //     REDIS_BACKOFF_ALGORITHM_EXPONENTIAL
+    //     REDIS_BACKOFF_ALGORITHM_FULL_JITTER
+    //     REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER
+    //     REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER
+    // 'base', and 'cap' are in milliseconds and represent the first
+    // delay redis will use when reconnecting, and the maximum delay
+    // we will reach while retrying.
+   'backoff' => [
+       'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER,
+       'base'      => 500,
+       'cap'       => 750,
+   ]
 ];

Note: If you do wish to connect via the constructor, only 'host' is strictly required, which will cause PhpRedis to connect to that @@ -2706,22 +4056,24 @@

Parameters

- +
arrayarray|null $options
-

Return Value

+

Return Value

- +
+
Redis
+

See also

@@ -2743,24 +4095,31 @@

See also

+

Examples

+ + + + + +
$redis = new Redis(['host' => '127.0.0.1', 'port' => 6380]);
+
-

- - - __destruct() - + +

+ + __destruct()

-

No description

- +

Destructor to clean up the Redis object.

This method will disconnect from Redis. If the connection is persistento +it will be stashed for future reuse.

@@ -2772,19 +4131,18 @@

-

- - string - _compress(string $value) - + +

+ + string _compress(string $value)

-

Compress a value with the currently configured compressor as set with -Redis::setOption().

+

Compress a value with the currently configured compressor (Redis::OPT_COMPRESSION) +exactly the same way PhpRedis does before sending data to Redis.

Parameters

@@ -2798,15 +4156,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
- +
string

The compressed result

The compressed result (or the original value if compression is disabled)

+

See also

@@ -2822,24 +4182,31 @@

See also

+

Examples

+ + + + + +
$redis->_compress('payload');
+
-

- - string - _uncompress(string $value) - + +

+ + string _uncompress(string $value)

-

Uncompress the provided argument that has been compressed with the -currently configured compressor as set with Redis::setOption().

+

Uncompress the provided argument using the compressor configured via +Redis::setOption() (Redis::OPT_COMPRESSION).

Parameters

@@ -2853,15 +4220,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
string

The uncompressed result.

+

See also

@@ -2877,16 +4246,23 @@

See also

+

Examples

+ + + + + +
$redis->_uncompress($redis->_compress('payload'));
+
-

- - string - _prefix(string $key) - + +

+ + string _prefix(string $key)

@@ -2908,28 +4284,37 @@

Parameters

-

Return Value

+

Return Value

- +
+
string

The prefixed string

+
+

Examples

+ + + + + +
$redis->_prefix('user:42');
+
-

- - string - _serialize(mixed $value) - + +

+ + string _serialize(mixed $value)

@@ -2951,15 +4336,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
string

The serialized result

+

See also

@@ -2975,16 +4362,23 @@

See also

+

Examples

+ + + + + +
$redis->_serialize(['answer' => 42]);
+
-

- - mixed - _unserialize(string $value) - + +

+ + mixed _unserialize(string $value)

@@ -3006,15 +4400,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
mixed

The unserialized result

+

See also

@@ -3030,24 +4426,32 @@

See also

+

Examples

+ + + + + +
$redis->_unserialize($redis->_serialize(['answer' => 42]));
+
-

- - string - _pack(mixed $value) - + +

+ + string _pack(mixed $value)

-

Pack the provided value with the configured serializer and compressor -as set with Redis::setOption().

+

Pack the provided value by first serializing it (if Redis::OPT_SERIALIZER is set) +and then compressing the serialized payload (if Redis::OPT_COMPRESSION is set), +mirroring exactly what PhpRedis transmits to Redis.

Parameters

@@ -3061,9 +4465,10 @@

Parameters

-

Return Value

+

Return Value

- +
+
string

The packed result having been serialized and @@ -3071,70 +4476,97 @@

Return Value

+
+

Examples

+ + + + + +
$redis->_pack(['count' => 5]);
+
-

- - mixed - _unpack(string $value) - + +

+ + string _digest(mixed $value)

-

Unpack the provided value with the configured compressor and serializer -as set with Redis::setOption().

+

Compute the XXH3 digest of a PHP value after it has been _packed, producing +the same digest Redis' DIGEST command would return for the stored value.

Parameters

- + - +
stringmixed $value

The value which has been serialized and compressed.

The value to compute the digest for.

-

Return Value

+

Return Value

- +
+
- - + +
mixed

The uncompressed and eserialized value.

string

The computed digest.

+
+

Exceptions

+ + + + + + +
RedisException
+ +

Examples

+ + + + + +
$redis->_digest(['token' => 'abc']);
+
-

- - mixed - acl(string $subcmd, string ...$args) - + +

+ + mixed _unpack(string $value)

-

No description

- +

Unpack the provided value by first uncompressing it (if Redis::OPT_COMPRESSION +is set) and then unserializing it (if Redis::OPT_SERIALIZER is set) to recover +the original PHP value.

Parameters

@@ -3142,46 +4574,50 @@

Parameters

- - - - - - - + +
string$subcmd
string...$args$value

The value which has been serialized and compressed.

-

Return Value

+

Return Value

- +
+
- +
mixed

The uncompressed and deserialized value.

+
+

Examples

+ + + + + +
$redis->_unpack($redis->_pack(['count' => 5]));
+
-

- - Redis|int|false - append(string $key, mixed $value) - + +

+ + mixed acl(string $subcmd, string ...$args)

-

Append data to a Redis STRING key.

+

Execute Redis ACL subcommands.

Parameters

@@ -3189,26 +4625,28 @@

Parameters

- - + + - - - + + +
string$key

The key in question

$subcmd
mixed$value

The data to append to the key.

string...$args
-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The new string length of the key or false on failure.

mixed
+

See also

@@ -3216,7 +4654,7 @@

See also

@@ -3227,8 +4665,7 @@

Examples

- https://redis.io/commands/append + https://redis.io/docs/latest/commands/acl/
- +
$redis->set('foo', 'hello);
-$redis->append('foo', 'world');
$redis->acl('list');
@@ -3237,11 +4674,78 @@

Examples

-

- - Redis|bool - auth(mixed $credentials) - + +

+ + Redis|int|false append(string $key, mixed $value) +

+
+ + + +
+

Append data to a Redis STRING key.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key in question

mixed$value

The data to append to the key.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

The new string length of the key or false on failure.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/append/ +
+ + +

Examples

+ + + + + +
$redis->set('foo', 'hello);
+$redis->append('foo', 'world');
+ +
+
+ +
+
+ +

+ + Redis|bool auth(mixed $credentials)

@@ -3264,15 +4768,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|bool

Whether the AUTH was successful.

+

See also

@@ -3280,23 +4786,30 @@

See also

- https://redis.io/commands/auth + https://redis.io/docs/latest/commands/auth/
+

Examples

+ + + + + +
$redis->auth('secret');
+
-

- - Redis|bool - bgSave() - + +

+ + Redis|bool bgSave()

@@ -3307,15 +4820,17 @@

-

Return Value

+

Return Value

- +
+
Redis|bool

Whether the command was successful.

+

See also

@@ -3323,23 +4838,30 @@

See also

- https://redis.io/commands/bgsave + https://redis.io/docs/latest/commands/bgsave/
+

Examples

+ + + + + +
$redis->bgSave();
+
-

- - Redis|bool - bgrewriteaof() - + +

+ + Redis|bool bgrewriteaof()

@@ -3350,15 +4872,90 @@

-

Return Value

+

Return Value

- +
+
Redis|bool

Whether the command was successful.

+
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/bgrewriteaof/ +
+ + +

Examples

+ + + + + +
$redis->bgrewriteaof();
+ +
+ + + +
+ +

+ + Redis|array|false waitaof(int $numlocal, int $numreplicas, int $timeout) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
int$numlocal
int$numreplicas
int$timeout
+ + +

Return Value

+ +
+ + + + + +
Redis|array|false
+ +

See also

@@ -3366,23 +4963,30 @@

See also

- https://redis.io/commands/bgrewriteaof + https://redis.io/docs/latest/commands/waitaof/
+

Examples

+ + + + + +
$redis->waitaof(1, 1, 5000);
+
-

- - Redis|int|false - bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) - + +

+ + Redis|int|false bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false)

@@ -3421,15 +5025,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of bits set in the requested range.

+

See also

@@ -3437,23 +5043,30 @@

See also

- https://redis.io/commands/bitcount/ + https://redis.io/docs/latest/commands/bitcount/
+

Examples

+ + + + + +
$redis->bitcount('bitmap', 0, -1);
+
-

- - Redis|int|false - bitop(string $operation, string $deskey, string $srckey, string ...$other_keys) - + +

+ + Redis|int|false bitop(string $operation, string $deskey, string $srckey, string ...$other_keys)

@@ -3490,15 +5103,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false
+
@@ -3507,11 +5122,10 @@

Return Value

-

- - Redis|int|false - bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) - + +

+ + Redis|int|false bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false)

@@ -3553,15 +5167,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The position of the first set or unset bit.

+

See also

@@ -3569,23 +5185,30 @@

See also

- https://redis.io/commands/bitpos/ + https://redis.io/docs/latest/commands/bitpos/
+

Examples

+ + + + + +
$redis->bitpos('bitmap', true, 0, -1);
+
-

- - Redis|array|null|false - blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) - + +

+ + Redis|array|null|false blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args)

@@ -3620,15 +5243,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|null|false

Can return various things depending on command and data in Redis.

+

See also

@@ -3636,7 +5261,7 @@

See also

@@ -3647,7 +5272,7 @@

Examples

- https://redis.io/commands/blpop/ + https://redis.io/docs/latest/commands/blpop/
-
$redis->blPop('list1', 'list2', 'list3', 1.5);
+
$redis->blPop('list1', 'list2', 'list3', 1.5);
 $relay->blPop(['list1', 'list2', 'list3'], 1.5);
@@ -3657,11 +5282,10 @@

Examples

-

- - Redis|array|null|false - brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) - + +

+ + Redis|array|null|false brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args)

@@ -3692,15 +5316,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|null|false
+

See also

@@ -3708,7 +5334,7 @@

See also

@@ -3722,16 +5348,23 @@

See also

- https://redis.io/commands/brpop/ + https://redis.io/docs/latest/commands/brpop/
+

Examples

+ + + + + +
$redis->brPop(['queue:critical', 'queue:default'], 5);
+
-

- - Redis|string|false - brpoplpush(string $src, string $dst, int|float $timeout) - + +

+ + Redis|string|false brpoplpush(string $src, string $dst, int|float $timeout)

@@ -3764,15 +5397,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|string|false
+

See also

@@ -3780,23 +5415,30 @@

See also

- https://redis.io/commands/brpoplpush/ + https://redis.io/docs/latest/commands/brpoplpush/
+

Examples

+ + + + + +
$redis->brpoplpush('queue:pending', 'queue:processing', 5);
+
-

- - Redis|array|false - bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) - + +

+ + Redis|array|false bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args)

@@ -3805,7 +5447,7 @@

POP the maximum scoring element off of one or more sorted sets, blocking up to a specified timeout if no elements are available.

Following are examples of the two main ways to call this method.

-

NOTE: We reccomend calling this function with an array and a timeout as the other strategy +

NOTE: We recommend calling this function with an array and a timeout as the other strategy may be deprecated in future versions of PhpRedis

@@ -3815,7 +5457,7 @@

Parameters

string|array $key - +

Either a string key or an array of one or more keys.

string|int @@ -3833,15 +5475,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

The popped elements.

+

See also

@@ -3849,7 +5493,7 @@

See also

@@ -3860,7 +5504,7 @@

Examples

- https://redis.io/commands/bzpopmax + https://redis.io/docs/latest/commands/bzpopmax/
-
$redis->bzPopMax('key1', 'key2', 'key3', 1.5);
+
$redis->bzPopMax('key1', 'key2', 'key3', 1.5);
 $redis->bzPopMax(['key1', 'key2', 'key3'], 1.5);
@@ -3870,11 +5514,10 @@

Examples

-

- - Redis|array|false - bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) - + +

+ + Redis|array|false bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args)

@@ -3906,15 +5549,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false
+

See also

@@ -3922,7 +5567,7 @@

See also

@@ -3936,16 +5581,23 @@

See also

- https://redis.io/commands/bzpopmin + https://redis.io/docs/latest/commands/bzpopmin/
+

Examples

+ + + + + +
$redis->bzPopMin(['scores:high', 'scores:low'], 1.5);
+
-

- - Redis|array|null|false - bzmpop(float $timeout, array $keys, string $from, int $count = 1) - + +

+ + Redis|array|null|false bzmpop(float $timeout, array $keys, string $from, int $count = 1)

@@ -3983,9 +5635,10 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|null|false

This function will return an array of popped elements, or false @@ -3996,19 +5649,38 @@

Return Value

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/bzmpop/ +
+ +

Examples

+ + + + + +
$redis->bzmpop(1.5, ['scores:high', 'scores:low'], 'MIN', 2);
+
-

- - Redis|array|null|false - zmpop(array $keys, string $from, int $count = 1) - + +

+ + Redis|array|null|false zmpop(array $keys, string $from, int $count = 1)

@@ -4040,15 +5712,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|null|false

An array of popped elements or false if none could be popped.

+

See also

@@ -4056,23 +5730,30 @@

See also

- https://redis.io/commands/zmpop + https://redis.io/docs/latest/commands/zmpop/
+

Examples

+ + + + + +
$redis->zmpop(['scores:high', 'scores:low'], 'MAX', 2);
+
-

- - Redis|array|null|false - blmpop(float $timeout, array $keys, string $from, int $count = 1) - + +

+ + Redis|array|null|false blmpop(float $timeout, array $keys, string $from, int $count = 1)

@@ -4110,9 +5791,10 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|null|false

One or more elements popped from the list(s) or false if all LISTs @@ -4120,6 +5802,7 @@

Return Value

+

See also

@@ -4127,23 +5810,30 @@

See also

- https://redis.io/commands/blmpop + https://redis.io/docs/latest/commands/blmpop/
+

Examples

+ + + + + +
$redis->blmpop(1.5, ['queue:critical', 'queue:default'], 'LEFT', 2);
+
-

- - Redis|array|null|false - lmpop(array $keys, string $from, int $count = 1) - + +

+ + Redis|array|null|false lmpop(array $keys, string $from, int $count = 1)

@@ -4175,9 +5865,10 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|null|false

One or more elements popped from the LIST(s) or false if all the LISTs @@ -4185,6 +5876,7 @@

Return Value

+

See also

@@ -4192,23 +5884,30 @@

See also

- https://redis.io/commands/lmpop + https://redis.io/docs/latest/commands/lmpop/
+

Examples

+ + + + + +
$redis->lmpop(['queue:critical', 'queue:default'], 'RIGHT', 2);
+
-

- - bool - clearLastError() - + +

+ + bool clearLastError()

@@ -4219,15 +5918,17 @@

-

Return Value

+

Return Value

- +
+
bool

This should always return true or throw an exception if we're not connected.

+

See also

@@ -4247,11 +5948,11 @@

Examples

-
$redis = new Redis(['host' => 'localhost']);
-$redis->set('string', 'this_is_a_string');
-$redis->smembers('string');
-var_dump($redis->getLastError());
-$redis->clearLastError();
+
$redis = new Redis(['host' => 'localhost']);
+$redis->set('string', 'this_is_a_string');
+$redis->smembers('string');
+var_dump($redis->getLastError());
+$redis->clearLastError();
 var_dump($redis->getLastError());
@@ -4261,19 +5962,17 @@

Examples

-

- - mixed - client(string $opt, mixed ...$args) - + +

+ + mixed client(string $opt, mixed ...$args)

-

No description

- +

Execute Redis CLIENT subcommands.

Parameters

@@ -4282,119 +5981,176 @@

Parameters

string $opt - +

The CLIENT subcommand to execute.

mixed ...$args - +

Additional arguments depending on the subcommand.

-

Return Value

+

Return Value

- +
+
mixed
+
- +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/client/ +
+ + +

Examples

+ + + + + +
$redis->client('list');
+
-

- - bool - close() - + +

+ + bool close()

-

No description

- +

Closes the connection to Redis

This function will close the connection whether it is persistent or not.

-

Return Value

+

Return Value

- +
+
- +
bool

Whether the connection was successfully closed.

+
+

Examples

+ + + + + +
$redis = new Redis;
+$redis->pconnect('localhost', 6379);
+$id1 = $redis->client('id');
+$redis->close();
+
+$redis = new Redis;
+$redis->pconnect('localhost', 6379);
+$id2 = $redis->client('id');
+
+// Will print "id is different"
+printf("ID is %s\n", $id1 == $id2 ? 'the same' :  'different');
+
-

- - mixed - command(string $opt = null, string|array $arg) - + +

+ + mixed command(string|null $opt = null, mixed ...$args)

-

No description

- +

Execute Redis COMMAND subcommands.

Parameters

- + - - + +
stringstring|null $opt
string|array$argmixed...$args
-

Return Value

+

Return Value

- +
+
mixed
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/command/ +
+ +

Examples

+ + + + + +
$redis->command('command');
+
-

- - mixed - config(string $operation, array|string|null $key_or_settings = NULL, string|null $value = NULL) - + +

+ + mixed config(string $operation, array|string|null $key_or_settings = null, string|null $value = null)

@@ -4426,15 +6182,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
mixed
+

See also

@@ -4442,7 +6200,7 @@

See also

@@ -4453,9 +6211,9 @@

Examples

- https://redis.io/commands/config + https://redis.io/docs/latest/commands/config/
-
$redis->config('GET', 'timeout');
-$redis->config('GET', ['timeout', 'databases']);
-$redis->config('SET', 'timeout', 30);
+
$redis->config('GET', 'timeout');
+$redis->config('GET', ['timeout', 'databases']);
+$redis->config('SET', 'timeout', 30);
 $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']);
@@ -4465,19 +6223,17 @@

Examples

-

- - bool - connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array $context = null) - + +

+ + bool connect(string $host, int $port = 6379, float $timeout = 0, string|null $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array|null $context = null)

-

No description

- +

Connect to a Redis server

Parameters

@@ -4486,63 +6242,97 @@

Parameters

string $host - +

The Redis server hostname or IP +address.

int $port - +

The Redis server port. Defaults to +6379.

float $timeout - +

The connection timeout in seconds. +Defaults to 0 (no timeout).

- string + string|null $persistent_id - +

If set, a persistent connection will +be made with this ID.

int $retry_interval - +

The number of milliseconds to wait +between connection attempts.

float $read_timeout - +

The read timeout in seconds. +Defaults to 0 (no timeout).

- array + array|null $context - +

An optional stream context to use +when connecting. +See \Redis::__construct() for more +details.

-

Return Value

+

Return Value

- +
+
- +
bool

Whether the connection was successful.

+
+

Exceptions

+ + + + + + +
RedisException
+ +

Examples

+ + + + + +
$redis = new \Redis;
+try {
+    $redis->connect('localhost', 6379, 2.5, null, 100, 2.5);
+    $foo = $redis->get('foo');
+    printf("foo: %s\n", $foo);
+} catch (Exception $ex) {
+    fprintf(STDERR, "Error: {$ex->getMessage()}\n");
+}
+
-

- - Redis|bool - copy(string $src, string $dst, array $options = null) - + +

+ + Redis|bool copy(string $src, string $dst, array|null $options = null)

@@ -4566,10 +6356,10 @@

Parameters

The name of the new key created from the source key.

- array + array|null $options

An array with modifiers on how COPY should operate.

-
$options = [
+
$options = [
     'REPLACE' => true|false # Whether to replace an existing key.
     'DB' => int             # Copy key to specific db.
 ];
@@ -4577,15 +6367,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|bool

True if the copy was completed and false if not.

+

See also

@@ -4593,7 +6385,7 @@

See also

@@ -4604,17 +6396,17 @@

Examples

- https://redis.io/commands/copy + https://redis.io/docs/latest/commands/copy/
-
$redis->pipeline()
- ->select(1)
- ->del('newkey')
- ->select(0)
- ->del('newkey')
- ->mset(['source1' => 'value1', 'exists' => 'old_value'])
- ->exec();
-
-var_dump($redis->copy('source1', 'newkey'));
-var_dump($redis->copy('source1', 'newkey', ['db' => 1]));
-var_dump($redis->copy('source1', 'exists'));
+
$redis->pipeline()
+      ->select(1)
+      ->del('newkey')
+      ->select(0)
+      ->del('newkey')
+      ->mset(['source1' => 'value1', 'exists' => 'old_value'])
+      ->exec();
+
+var_dump($redis->copy('source1', 'newkey'));
+var_dump($redis->copy('source1', 'newkey', ['db' => 1]));
+var_dump($redis->copy('source1', 'exists'));
 var_dump($redis->copy('source1', 'exists', ['REPLACE' => true]));
@@ -4624,11 +6416,10 @@

Examples

-

- - Redis|int|false - dbSize() - + +

+ + Redis|int|false dbSize()

@@ -4639,15 +6430,17 @@

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of keys or false on failure.

+

See also

@@ -4655,7 +6448,7 @@

See also

@@ -4666,11 +6459,11 @@

Examples

- https://redis.io/commands/dbsize + https://redis.io/docs/latest/commands/dbsize/
-
$redis = new Redis(['host' => 'localhost']);
-$redis->flushdb();
-$redis->set('foo', 'bar');
-var_dump($redis->dbsize());
-$redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']);
+
$redis = new Redis(['host' => 'localhost']);
+$redis->flushdb();
+$redis->set('foo', 'bar');
+var_dump($redis->dbsize());
+$redis->mset(['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd']);
 var_dump($redis->dbsize());
@@ -4680,19 +6473,19 @@

Examples

-

- - Redis|string - debug(string $key) - + +

+ + Redis|string debug(string $key)

-

No description

- +

Execute the Redis DEBUG command. Note that this is disabled by default +and can be very dangerous, even allowing you to crash the server. Use +with caution

Parameters

@@ -4701,20 +6494,22 @@

Parameters

string $key - +

The DEBUG subcommand to execute.

-

Return Value

+

Return Value

- +
+
- +
Redis|string

The result of the DEBUG command.

+
@@ -4723,11 +6518,10 @@

Return Value

-

- - Redis|int|false - decr(string $key, int $by = 1) - + +

+ + Redis|int|false decr(string $key, int $by = 1)

@@ -4756,15 +6550,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The new value of the key or false on failure.

+

See also

@@ -4772,13 +6568,13 @@

See also

@@ -4801,11 +6597,10 @@

Examples

-

- - Redis|int|false - decrBy(string $key, int $value) - + +

+ + Redis|int|false decrBy(string $key, int $value)

@@ -4831,15 +6626,17 @@

Parameters

- https://redis.io/commands/decr + https://redis.io/docs/latest/commands/decr/
- https://redis.io/commands/decrby + https://redis.io/docs/latest/commands/decrby/
-

Return Value

+

Return Value

- +
+
Redis|int|false

The new value of the key or false on failure.

+

See also

@@ -4847,7 +6644,7 @@

See also

@@ -4870,11 +6667,10 @@

Examples

-

- - Redis|int|false - del(array|string $key, string ...$other_keys) - + +

+ + Redis|int|false del(array|string $key, string ...$other_keys)

@@ -4892,7 +6688,8 @@

Parameters

- + @@ -4902,15 +6699,17 @@

Parameters

- https://redis.io/commands/decrby + https://redis.io/docs/latest/commands/decrby/
array|string $key

Either an array with one or more key names or a string with +the name of a key.

string
-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of keys that were deleted

+

See also

@@ -4918,7 +6717,7 @@

See also

@@ -4941,96 +6740,133 @@

Examples

-

- - Redis|int|false - delete(array|string $key, string ...$other_keys) - deprecated + +

+ + Redis|int|false delex(string $key, array|null $options = null)

-

- deprecated -

- - - -

-

No description

- +

Delete a key conditionally based on its value or hash digest

Parameters

- https://redis.io/commands/del + https://redis.io/docs/latest/commands/del/
- + - + - - - + + +
array|stringstring $key

The key to delete

string...$other_keysarray|null$options

An array with options to modify how DELX works.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

Returns 1 if the key was deleted, 0 if it was not.

+ +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/delex/ +
+ +

Examples

+ + + + + +
$redis->delex('session:42');
+
-

- - Redis|bool - discard() - + +

+ + Redis|int|false delifeq(string $key, mixed $value)

-

Discard a transaction currently in progress.

+

Delete a key if it's equal to the specified value. This command is +specific to Valkey >= 9.0

- -

Return Value

+

Parameters

+ + + + + + + + + + +
string$key

The key to delete

mixed$value

The value to compare against the key's value.

+ + +

Return Value

+ +
+ - - + +
Redis|bool

True if we could discard the transaction.

Redis|int|false

Returns 1 if the key was deleted, 0 if it was not.

+
+

See also

+ + + + + + +
+ https://valkey.io/commands/delifeq/ +
+

Examples

- +
$redis->getMode();
-$redis->set('foo', 'bar');
-$redis->discard();
-$redis->getMode();
$redis->delifeq('session:42', 'token');
@@ -5039,41 +6875,54 @@

Examples

-

- - Redis|string - dump(string $key) - + +

+ + Redis|int|false delete(array|string $key, string ...$other_keys) deprecated

+

+ deprecated + + + + +

-

Dump Redis' internal binary representation of a key.

$redis->zRange('new-zset', 0, -1, true);

-

+

No description

+

Parameters

- + - + + + + + +
stringarray|string $key

The key to dump.

string...$other_keys
-

Return Value

+

Return Value

- +
+
- - + +
Redis|string

A binary string representing the key's value.

Redis|int|false
+

See also

@@ -5081,7 +6930,7 @@

See also

@@ -5092,9 +6941,7 @@

Examples

- https://redis.io/commands/dump + https://redis.io/docs/latest/commands/del/
- +
$redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two');
-$binary = $redis->dump('zset');
-$redis->restore('new-zset', 0, $binary);
$redis->delete('legacy:key');
@@ -5103,40 +6950,31 @@

Examples

-

- - Redis|string|false - echo(string $str) - + +

+ + Redis|bool discard()

-

Have Redis repeat back an arbitrary string to the client.

+

Discard a transaction currently in progress.

-

Parameters

- - - - - - - -
string$str

The string to echo

- -

Return Value

+

Return Value

- +
+
- - + +
Redis|string|false

The string sent to Redis or false on failure.

Redis|bool

True if we could discard the transaction.

+

See also

@@ -5144,7 +6982,7 @@

See also

@@ -5155,7 +6993,10 @@

Examples

- https://redis.io/commands/echo + https://redis.io/docs/latest/commands/discard/
- +
$redis->echo('Hello, World');
$redis->getMode();
+$redis->set('foo', 'bar');
+$redis->discard();
+$redis->getMode();
@@ -5164,18 +7005,17 @@

Examples

-

- - mixed - eval(string $script, array $args = [], int $num_keys = 0) - + +

+ + Redis|string|false dump(string $key)

-

Execute a LUA script on the redis server.

+

Dump Redis' internal binary representation of a key.

Parameters

@@ -5183,34 +7023,23 @@

Parameters

- - - - - - - - - - - - + +
string$script

A string containing the LUA script

array$args

An array of arguments to pass to this script

int$num_keys

How many of the arguments are keys. This is needed -as redis distinguishes between key name arguments -and other data.

$key

The key to dump.

-

Return Value

+

Return Value

- +
+
- - + +
mixed

LUA scripts may return arbitrary data so this method can return -strings, arrays, nested arrays, etc.

Redis|string|false

A binary string representing the key's value.

+

See also

@@ -5218,23 +7047,169 @@

See also

- https://redis.io/commands/eval/ + https://redis.io/docs/latest/commands/dump/
+

Examples

+ + + + + +
$redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two');
+$binary = $redis->dump('zset');
+$redis->restore('new-zset', 0, $binary);
+
-

- - mixed - eval_ro(string $script_sha, array $args = [], int $num_keys = 0) - + +

+ + Redis|string|false echo(string $str) +

+
+ + + +
+

Have Redis repeat back an arbitrary string to the client.

+
+
+

Parameters

+ + + + + + + +
string$str

The string to echo

+ + +

Return Value

+ +
+ + + + + +
Redis|string|false

The string sent to Redis or false on failure.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/echo/ +
+ + +

Examples

+ + + + + +
$redis->echo('Hello, World');
+ +
+
+ +
+
+ +

+ + mixed eval(string $script, array $args = [], int $num_keys = 0) +

+
+ + + +
+

Execute a LUA script on the redis server.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script

A string containing the LUA script

array$args

An array of arguments to pass to this script

int$num_keys

How many of the arguments are keys. This is needed +as redis distinguishes between key name arguments +and other data.

+ + +

Return Value

+ +
+ + + + + +
mixed

LUA scripts may return arbitrary data so this method can return +strings, arrays, nested arrays, etc.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/eval/ +
+ + +

Examples

+ + + + + +
$redis->eval('return redis.call("set", KEYS[1], ARGV[1])', ['counter', 1], 1);
+ +
+
+ +
+
+ +

+ + mixed eval_ro(string $script_sha, array $args = [], int $num_keys = 0)

@@ -5266,15 +7241,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
mixed
+

See also

@@ -5286,20 +7263,33 @@

See also

Redis::eval_ro + + + + https://redis.io/docs/latest/commands/eval_ro/ + + +

Examples

+ + + + + +
$redis->eval_ro('return redis.call("get", KEYS[1])', ['counter'], 1);
+
-

- - mixed - evalsha(string $sha1, array $args = [], int $num_keys = 0) - + +

+ + mixed evalsha(string $sha1, array $args = [], int $num_keys = 0)

@@ -5316,7 +7306,10 @@

Parameters

string $sha1 - +

The SHA1 hash of the lua code. Note that the script +must already exist on the server, either having been +loaded with SCRIPT LOAD or having been executed directly +with EVAL first.

array @@ -5331,15 +7324,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
mixed

Returns whatever the specific script does.

+

See also

@@ -5347,7 +7342,7 @@

See also

@@ -5361,16 +7356,24 @@

See also

- https://redis.io/commands/evalsha/ + https://redis.io/docs/latest/commands/evalsha/
+

Examples

+ + + + + +
$sha = $redis->script('load', 'return redis.call("incr", KEYS[1])');
+$redis->evalsha($sha, ['counter'], 1);
+
-

- - mixed - evalsha_ro(string $sha1, array $args = [], int $num_keys = 0) - + +

+ + mixed evalsha_ro(string $sha1, array $args = [], int $num_keys = 0)

@@ -5402,15 +7405,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
mixed
+

See also

@@ -5422,20 +7427,34 @@

See also

Redis::evalsha + + + + https://redis.io/docs/latest/commands/evalsha_ro/ + + +

Examples

+ + + + + +
$sha = $redis->script('load', 'return redis.call("get", KEYS[1])');
+$redis->evalsha_ro($sha, ['counter'], 1);
+
-

- - Redis|array|false - exec() - + +

+ + Redis|array|false exec()

@@ -5446,15 +7465,17 @@

-

Return Value

+

Return Value

- +
+
Redis|array|false

The array of pipeline'd or multi replies or false on failure.

+

See also

@@ -5462,13 +7483,13 @@

See also

@@ -5493,11 +7514,11 @@

Examples

- https://redis.io/commands/exec + https://redis.io/docs/latest/commands/exec/
- https://redis.io/commands/multi + https://redis.io/docs/latest/commands/multi/
-
$res = $redis->multi()
-->set('foo', 'bar')
-->get('foo')
-->del('list')
-->rpush('list', 'one', 'two', 'three')
+
$res = $redis->multi()
+->set('foo', 'bar')
+->get('foo')
+->del('list')
+->rpush('list', 'one', 'two', 'three')
 ->exec();
@@ -5507,11 +7528,10 @@

Examples

-

- - Redis|int|bool - exists(mixed $key, mixed ...$other_keys) - + +

+ + Redis|int|bool exists(mixed $key, mixed ...$other_keys)

@@ -5538,15 +7558,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|bool

The number of keys that do exist and false on failure

+

See also

@@ -5554,7 +7576,7 @@

See also

@@ -5577,11 +7599,10 @@

Examples

-

- - Redis|bool - expire(string $key, int $timeout, string|null $mode = NULL) - + +

+ + Redis|bool expire(string $key, int $timeout, string|null $mode = null)

@@ -5604,7 +7625,7 @@

Parameters

- + @@ -5619,15 +7640,17 @@

Parameters

- https://redis.io/commands/exists + https://redis.io/docs/latest/commands/exists/
int $timeout

The number of seconds after which key will be automatically deleted.

string|null
-

Return Value

+

Return Value

- +
+
Redis|bool

True if an expiration was set and false otherwise.

+

See also

@@ -5635,23 +7658,30 @@

See also

- https://redis.io/commands/expire + https://redis.io/docs/latest/commands/expire/
+

Examples

+ + + + + +
$redis->expire('session:42', 60);
+
-

- - Redis|bool - expireAt(string $key, int $timestamp, string|null $mode = NULL) - + +

+ + Redis|bool expireAt(string $key, int $timestamp, string|null $mode = null)

@@ -5682,15 +7712,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|bool

True if an expiration was set, false if not.

+

See also

@@ -5698,13 +7730,13 @@

See also

@@ -5718,16 +7750,23 @@

See also

- https://redis.io/commands/expireat + https://redis.io/docs/latest/commands/expireat/
- https://redis.io/commands/expire + https://redis.io/docs/latest/commands/expire/
+

Examples

+ + + + + +
$redis->expireAt('session:42', time() + 300);
+
-

- - Redis|bool - failover(array|null $to = null, bool $abort = false, int $timeout = 0) - + +

+ + Redis|bool failover(array|null $to = null, bool $abort = false, int $timeout = 0)

@@ -5759,15 +7798,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|bool
+
@@ -5776,11 +7817,10 @@

Return Value

-

- - Redis|int|false - expiretime(string $key) - + +

+ + Redis|int|false expiretime(string $key)

@@ -5801,9 +7841,10 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The timestamp when the key expires, or -1 if the key has no expiry @@ -5811,6 +7852,7 @@

Return Value

+

See also

@@ -5818,7 +7860,7 @@

See also

@@ -5829,7 +7871,7 @@

Examples

- https://redis.io/commands/expiretime + https://redis.io/docs/latest/commands/expiretime/
-
$redis->setEx('mykey', 60, 'myval');
+
$redis->setEx('mykey', 60, 'myval');
 $redis->expiretime('mykey');
@@ -5839,18 +7881,17 @@

Examples

-

- - Redis|int|false - pexpiretime(string $key) - + +

+ + Redis|int|false pexpiretime(string $key)

-

Get the expriation timestamp of a given Redis key but in milliseconds.

+

Get the expiration timestamp of a given Redis key but in milliseconds.

Parameters

@@ -5864,9 +7905,10 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The expiration timestamp of this key (in milliseconds) or -1 if the @@ -5874,6 +7916,7 @@

Return Value

+

See also

@@ -5881,7 +7924,7 @@

See also

@@ -5895,45 +7938,65 @@

See also

- https://redis.io/commands/pexpiretime + https://redis.io/docs/latest/commands/pexpiretime/
+

Examples

+ + + + + +
$redis->pexpiretime('session:42');
+
-

- - Redis|bool - flushAll(bool|null $sync = null) - + +

+ + mixed fcall(string $fn, array $keys = [], array $args = [])

-

Deletes every key in all Redis databases

+

Invoke a function.

Parameters

- - - + + + + + + + + + + + + +
bool|null$sync

Whether to perform the task in a blocking or non-blocking way.

string$fn

The name of the function

array$keys

Optional list of keys

array$args

Optional list of args

-

Return Value

+

Return Value

- +
+
- - + +
Redis|boolmixed

Function may return arbitrary data so this method can return +strings, arrays, nested arrays, etc.

+

See also

@@ -5941,52 +8004,72 @@

See also

- https://redis.io/commands/flushall + https://redis.io/docs/latest/commands/fcall/
+

Examples

+ + + + + +
$redis->fcall('mylib.increment', ['counter'], [1]);
+
-

- - Redis|bool - flushDB(bool|null $sync = null) - + +

+ + mixed fcall_ro(string $fn, array $keys = [], array $args = [])

-

Deletes all the keys of the currently selected database.

+

This is a read-only variant of the FCALL command that cannot execute commands that modify data.

Parameters

- - - + + + + + + + + + + + + +
bool|null$sync

Whether to perform the task in a blocking or non-blocking way.

string$fn

The name of the function

array$keys

Optional list of keys

array$args

Optional list of args

-

Return Value

+

Return Value

- +
+
- - + +
Redis|boolmixed

Function may return arbitrary data so this method can return +strings, arrays, nested arrays, etc.

+

See also

@@ -5994,49 +8077,255 @@

See also

- https://redis.io/commands/flushdb + https://redis.io/docs/latest/commands/fcall_ro/
+

Examples

+ + + + + +
$redis->fcall_ro('mylib.peek', ['counter']);
+
-

- - Redis|int|false - geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) - + +

+ + Redis|bool flushAll(bool|null $sync = null)

-

Add one or more members to a geospacial sorted set

+

Deletes every key in all Redis databases

Parameters

- - - + + + - - +
string$key

The sorted set to add data to.

bool|null$sync

Whether to perform the task in a blocking or non-blocking way.

float
+ + +

Return Value

+ +
+ + + + + +
Redis|bool
+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/flushall/ +
+ + +

Examples

+ + + + + +
$redis->flushAll(true);
+ +
+
+ +
+
+ +

+ + Redis|bool flushDB(bool|null $sync = null) +

+
+ + + +
+

Deletes all the keys of the currently selected database.

+
+
+

Parameters

+ + + + + + + +
bool|null$sync

Whether to perform the task in a blocking or non-blocking way.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool
+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/flushdb/ +
+ + +

Examples

+ + + + + +
$redis->flushDB(true);
+ +
+
+ +
+
+ +

+ + Redis|bool|string|array function(string $operation, mixed ...$args) +

+
+ + + +
+

Functions is an API for managing code to be executed on the server.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$operation

The subcommand you intend to execute. Valid options are as follows +'LOAD' - Create a new library with the given library name and code. +'DELETE' - Delete the given library. +'LIST' - Return general information on all the libraries +'STATS' - Return information about the current function running +'KILL' - Kill the current running function +'FLUSH' - Delete all the libraries +'DUMP' - Return a serialized payload representing the current libraries +'RESTORE' - Restore the libraries represented by the given payload

mixed...$args

Additional arguments

+ + +

Return Value

+ +
+ + + + + +
Redis|bool|string|array

Depends on subcommand.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/function/ +
+ + +

Examples

+ + + + + +
$redis->function('LIST');
+ +
+
+ +
+
+ +

+ + Redis|int|false geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) +

+
+ + + +
+

Add one or more members to a geospacial sorted set

+
+
+

Parameters

+ + + + + + + + + - + @@ -6046,16 +8335,17 @@

Parameters

-
string$key

The sorted set to add data to.

float $lng

The longitude of the first member

float $lat

The lattitude of the first member.

The latitude of the first member.

string
mixed ...$other_triples_and_options

You can continue to pass longitude, lattitude, and member +

You can continue to pass longitude, latitude, and member arguments to add as many members as you wish. Optionally, the final argument may be a string with options for the command Redis documentation for the options.

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of added elements is returned. If the 'CH' option is specified, @@ -6063,6 +8353,7 @@

Return Value

+

See also

@@ -6070,7 +8361,7 @@

See also

@@ -6093,11 +8384,10 @@

Examples

-

- - Redis|float|false - geodist(string $key, string $src, string $dst, string|null $unit = null) - + +

+ + Redis|float|false geodist(string $key, string $src, string $dst, string|null $unit = null)

@@ -6137,9 +8427,10 @@

Parameters

- https://redis.io/commands/geoadd + https://redis.io/docs/latest/commands/geoadd/
-

Return Value

+

Return Value

- +
+
Redis|float|false

The calculated distance in whichever units were specified or false @@ -6147,6 +8438,7 @@

Return Value

+

See also

@@ -6154,7 +8446,7 @@

See also

@@ -6174,11 +8466,10 @@

Examples

-

- - Redis|array|false - geohash(string $key, string $member, string ...$other_members) - + +

+ + Redis|array|false geohash(string $key, string $member, string ...$other_members)

@@ -6209,15 +8500,17 @@

Parameters

- https://redis.io/commands/geodist + https://redis.io/docs/latest/commands/geodist/
-

Return Value

+

Return Value

- +
+
Redis|array|false

An array of GeoHash encoded values.

+

See also

@@ -6225,7 +8518,7 @@

See also

@@ -6251,18 +8544,17 @@

Examples

-

- - Redis|array|false - geopos(string $key, string $member, string ...$other_members) - + +

+ + Redis|array|false geopos(string $key, string $member, string ...$other_members)

-

Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

+

Return the longitude and latitude for one or more members of a geospacially encoded sorted set.

Parameters

@@ -6286,15 +8578,17 @@

Parameters

- https://redis.io/commands/geohash + https://redis.io/docs/latest/commands/geohash/
-

Return Value

+

Return Value

- +
+
- +
Redis|array|false

array of longitude and lattitude pairs.

An array of longitude and latitude pairs.

+

See also

@@ -6302,7 +8596,7 @@

See also

@@ -6322,11 +8616,10 @@

Examples

-

- - mixed - georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) - + +

+ + mixed georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = [])

@@ -6369,7 +8662,7 @@

Parameters

- https://redis.io/commands/geopos + https://redis.io/docs/latest/commands/geopos/
array $options

An array of options that modifies how the command behaves.

-
$options = [
+
$options = [
     'WITHCOORD',     # Return members and their coordinates.
     'WITHDIST',      # Return members and their distances from the center.
     'WITHHASH',      # Return members GeoHash string.
@@ -6389,15 +8682,17 @@ 

Parameters

-

Return Value

+

Return Value

- +
+
mixed

This command can return various things, depending on the options passed.

+

See also

@@ -6405,7 +8700,7 @@

See also

@@ -6425,11 +8720,10 @@

Examples

-

- - mixed - georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) - + +

+ + mixed georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = [])

@@ -6475,15 +8769,17 @@

Parameters

- https://redis.io/commands/georadius + https://redis.io/docs/latest/commands/georadius/
-

Return Value

+

Return Value

- +
+
mixed
+

See also

@@ -6494,20 +8790,33 @@

See also

Redis::georadius + + + + https://redis.io/docs/latest/commands/georadius_ro/ + + +

Examples

+ + + + + +
$redis->georadius_ro('cities', -122.335167, 47.608013, 100, 'km');
+
-

- - mixed - georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) - + +

+ + mixed georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = [])

@@ -6550,17 +8859,30 @@

Parameters

-

Return Value

+

Return Value

- +
+
mixed

This command can return various things depending on options.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/georadiusbymember/ +
+

Examples

@@ -6575,11 +8897,10 @@

Examples

-

- - mixed - georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) - + +

+ + mixed georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = [])

@@ -6620,28 +8941,48 @@

Parameters

-

Return Value

+

Return Value

- +
+
mixed
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/georadiusbymember_ro/ +
+ +

Examples

+ + + + + +
$redis->georadiusbymember_ro('cities', 'Seattle', 200, 'mi');
+
-

- - array - geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) - + +

+ + array geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = [])

@@ -6662,7 +9003,7 @@

Parameters

array|string $position -

Either a two element array with longitude and lattitude, or +

Either a two element array with longitude and latitude, or a string representing a member of the set.

@@ -6686,28 +9027,48 @@

Parameters

-

Return Value

+

Return Value

- +
+
array
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/geosearch/ +
+ +

Examples

+ + + + + +
$redis->geosearch('cities', 'Seattle', 50, 'km', ['WITHDIST']);
+
-

- - Redis|array|int|false - geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) - + +

+ + Redis|array|int|false geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = [])

@@ -6734,7 +9095,7 @@

Parameters

array|string $position -

Either a two element array with longitude and lattitude, or +

Either a two element array with longitude and latitude, or a string representing a member of the set.

@@ -6752,7 +9113,7 @@

Parameters

array $options -
$options = [
+                
$options = [
     'ASC' | 'DESC',  # The sort order of returned members
     'WITHDIST'       # Also store distances.
 
@@ -6764,28 +9125,48 @@ 

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|int|false
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/geosearchstore/ +
+ +

Examples

+ + + + + +
$redis->geosearchstore('west:cities', 'cities', 'Seattle', 50, 'km', ['DESC']);
+
-

- - mixed - get(string $key) - + +

+ + mixed get(string $key)

@@ -6806,15 +9187,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
mixed

The keys value or false if it did not exist.

+

See also

@@ -6822,7 +9205,7 @@

See also

@@ -6842,30 +9225,82 @@

Examples

-

- - mixed - getAuth() - + +

+ + Redis|array|false getWithMeta(string $key)

-

Get the authentication information on the connection, if any.

+

Retrieve a value and metadata of key.

+

Parameters

+ +
- https://redis.io/commands/get + https://redis.io/docs/latest/commands/get/
+ + + + + +
string$key

The key to query

+ + +

Return Value

+ +
+ + + + + +
Redis|array|false
+ +
+ + -

Return Value

+

Examples

+ + + +
$redis->getWithMeta('foo');
+ +
+ + + +
+ +

+ + mixed getAuth() +

+
+ + + +
+

Get the authentication information on the connection, if any.

+
+
+ +

Return Value

+ +
+
mixed

The authentication information used to authenticate the connection.

+

See also

@@ -6881,16 +9316,23 @@

See also

+

Examples

+ + + + + +
$redis->getAuth();
+
-

- - Redis|int|false - getBit(string $key, int $idx) - + +

+ + Redis|int|false getBit(string $key, int $idx)

@@ -6916,15 +9358,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false
+

See also

@@ -6932,7 +9376,7 @@

See also

@@ -6952,11 +9396,10 @@

Examples

-

- - Redis|string|bool - getEx(string $key, array $options = []) - + +

+ + Redis|string|bool getEx(string $key, array $options = [])

@@ -6978,7 +9421,7 @@

Parameters

- https://redis.io/commands/getbit + https://redis.io/docs/latest/commands/getbit/
array $options

Options to modify how the command works.

-
$options = [
+
$options = [
     'EX'     => <seconds>      # Expire in N seconds
     'PX'     => <milliseconds> # Expire in N milliseconds
     'EXAT'   => <timestamp>    # Expire at a unix timestamp (in seconds)
@@ -6989,15 +9432,17 @@ 

Parameters

-

Return Value

+

Return Value

- +
+
Redis|string|bool

The key's value or false if it didn't exist.

+

See also

@@ -7005,7 +9450,7 @@

See also

@@ -7025,11 +9470,10 @@

Examples

-

- - int - getDBNum() - + +

+ + int getDBNum()

@@ -7040,15 +9484,17 @@

-

Return Value

+

Return Value

-
- https://redis.io/comands/getex + https://redis.io/docs/latest/commands/getex/
+
+
- +
int

database we're connected to.

The database we're connected to.

+

See also

@@ -7063,23 +9509,30 @@

See also

- https://redis.io/commands/select + https://redis.io/docs/latest/commands/select/ +

Examples

+ + + + + +
$redis->getDBNum();
+
-

- - Redis|string|bool - getDel(string $key) - + +

+ + Redis|string|bool getDel(string $key)

@@ -7100,15 +9553,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|string|bool

The value of the key or false if it didn't exist.

+

See also

@@ -7116,7 +9571,7 @@

See also

@@ -7136,11 +9591,10 @@

Examples

-

- - string - getHost() - + +

+ + string getHost()

@@ -7151,28 +9605,37 @@

-

Return Value

+

Return Value

-
- https://redis.io/commands/getdel + https://redis.io/docs/latest/commands/getdel/
+
+
string

The host or Unix socket.

+
+

Examples

+ + + + + +
$redis->getHost();
+
-

- - string|null - getLastError() - + +

+ + string|null getLastError()

@@ -7183,28 +9646,37 @@

-

Return Value

+

Return Value

- +
+
string|null

The error string or NULL if there is none.

+
+

Examples

+ + + + + +
$redis->getLastError();
+
-

- - int - getMode() - + +

+ + int getMode()

@@ -7215,28 +9687,37 @@

-

Return Value

+

Return Value

- +
+
int

The mode we're in.

+
+

Examples

+ + + + + +
$redis->getMode();
+
-

- - mixed - getOption(int $option) - + +

+ + mixed getOption(int $option)

@@ -7257,15 +9738,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
mixed

The setting itself or false on failure

+

See also

@@ -7281,16 +9764,23 @@

See also

+

Examples

+ + + + + +
$redis->getOption(Redis::OPT_PREFIX);
+
-

- - string|null - getPersistentID() - + +

+ + string|null getPersistentID()

@@ -7301,28 +9791,37 @@

-

Return Value

+

Return Value

- +
+
string|null

The ID or NULL if we don't have one.

+
+

Examples

+ + + + + +
$redis->getPersistentID();
+
-

- - int - getPort() - + +

+ + int getPort()

@@ -7333,28 +9832,119 @@

-

Return Value

+

Return Value

- +
+
int

The port.

+
+ + + +

Examples

+ + + + + +
$redis->getPort();
+ +
+ + + +
+ +

+ + string|false serverName() +

+
+ + + +
+

Get the server name as reported by the HELLO response.

+
+
+ +

Return Value

+ +
+ + + + + +
string|false
+ +
+ + + +

Examples

+ + + + + +
$redis->serverName();
+ +
+
+ +
+
+ +

+ + string|false serverVersion() +

+
+ + + +
+

Get the server version as reported by the HELLO response.

+
+
+ +

Return Value

+ +
+ + + + + +
string|false
+ +
+

Examples

+ + + + + +
$redis->serverVersion();
+
-

- - Redis|string|false - getRange(string $key, int $start, int $end) - + +

+ + Redis|string|false getRange(string $key, int $start, int $end)

@@ -7385,15 +9975,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|string|false

The substring or false on failure.

+

See also

@@ -7401,7 +9993,7 @@

See also

@@ -7412,7 +10004,7 @@

Examples

- https://redis.io/commands/getrange + https://redis.io/docs/latest/commands/getrange/
-
$redis->set('silly-word', 'Supercalifragilisticexpialidocious');
+
$redis->set('silly-word', 'Supercalifragilisticexpialidocious');
 echo $redis->getRange('silly-word', 0, 4) . "\n";
@@ -7422,11 +10014,10 @@

Examples

-

- - Redis|string|array|int|false - lcs(string $key1, string $key2, array|null $options = NULL) - + +

+ + Redis|string|array|int|false lcs(string $key1, string $key2, array|null $options = null)

@@ -7452,8 +10043,8 @@

Parameters

array|null $options -

An optional array of modifiers for the comand.

-
$options = [
+                

An optional array of modifiers for the command.

+
$options = [
     'MINMATCHLEN'  => int  # Exclude matching substrings that are less than this value
 
     'WITHMATCHLEN' => bool # Whether each match should also include its length.
@@ -7468,15 +10059,17 @@ 

Parameters

-

Return Value

+

Return Value

- +
+
Redis|string|array|int|false

Various reply types depending on options.

+

See also

@@ -7484,7 +10077,7 @@

See also

@@ -7495,8 +10088,8 @@

Examples

- https://redis.io/commands/lcs + https://redis.io/docs/latest/commands/lcs/
-
$redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc');
-$redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc');
+
$redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc');
+$redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc');
 echo $redis->lcs('seq1', 'seq2') . "\n";
@@ -7506,11 +10099,10 @@

Examples

-

- - float - getReadTimeout() - + +

+ + float getReadTimeout()

@@ -7521,28 +10113,37 @@

-

Return Value

+

Return Value

- +
+
float

The timeout.

+
+

Examples

+ + + + + +
$redis->getReadTimeout();
+
-

- - Redis|string|false - getset(string $key, mixed $value) - + +

+ + Redis|string|false getset(string $key, mixed $value)

@@ -7568,15 +10169,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|string|false

The old value of the key or false if it didn't exist.

+

See also

@@ -7584,7 +10187,7 @@

See also

@@ -7595,7 +10198,7 @@

Examples

- https://redis.io/commands/getset + https://redis.io/docs/latest/commands/getset/
-
$redis->getset('captain', 'Pike');
+
$redis->getset('captain', 'Pike');
 $redis->getset('captain', 'Kirk');
@@ -7605,11 +10208,10 @@

Examples

-

- - float|false - getTimeout() - + +

+ + float|false getTimeout()

@@ -7620,61 +10222,119 @@

-

Return Value

+

Return Value

- +
+
float|false

The currently set timeout or false on failure (e.g. we aren't connected).

+
+

Examples

+ + + + + +
$redis->getTimeout();
+
-

- - int|false - getTransferredBytes() - + +

+ + array getTransferredBytes()

-

No description

- +

Get the number of bytes sent and received on the socket.

-

Return Value

+

Return Value

+ +
+ + + + + +
array

An array in the form [$sent_bytes, $received_bytes]

+ +
+ + + +

Examples

+ + + +
$redis->getTransferredBytes();
+ +
+
+ +
+
+ +

+ + void clearTransferredBytes() +

+
+ + + +
+

Reset the number of bytes sent and received on the socket.

+
+
+ +

Return Value

+ +
+ - +
int|falsevoid
+
+

Examples

+ + + + + +
$redis->clearTransferredBytes();
+
-

- - Redis|int|false - hDel(string $key, string $field, string ...$other_fields) - + +

+ + Redis|int|false hDel(string $key, string $field, string ...$other_fields)

@@ -7705,15 +10365,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of fields actually removed.

+

See also

@@ -7721,7 +10383,7 @@

See also

@@ -7741,11 +10403,10 @@

Examples

-

- - Redis|bool - hExists(string $key, string $field) - + +

+ + Redis|bool hExists(string $key, string $field)

@@ -7771,15 +10432,17 @@

Parameters

- https://redis.io/commands/hdel + https://redis.io/docs/latest/commands/hdel/
-

Return Value

+

Return Value

- +
+
Redis|bool

True if it exists, false if not.

+

See also

@@ -7787,7 +10450,7 @@

See also

@@ -7807,11 +10470,10 @@

Examples

-

- - mixed - hGet(string $key, string $member) - + +

+ + mixed hGet(string $key, string $member)

@@ -7838,15 +10500,17 @@

Parameters

- https://redis.io/commands/hexists + https://redis.io/docs/latest/commands/hexists/
-

Return Value

+

Return Value

- +
+
mixed
+ @@ -7855,11 +10519,10 @@

Return Value

-

- - Redis|array|false - hGetAll(string $key) - + +

+ + Redis|array|false hGetAll(string $key)

@@ -7880,15 +10543,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

All fields and values or false if the key didn't exist.

+

See also

@@ -7896,7 +10561,7 @@

See also

@@ -7916,11 +10581,66 @@

Examples

-

- - Redis|int|false - hIncrBy(string $key, string $field, int $value) - + +

+ + mixed hGetWithMeta(string $key, string $member) +

+
+ + + +
+

Retrieve a value and metadata of hash field.

+
+
+

Parameters

+ +
- https://redis.io/commands/hgetall + https://redis.io/docs/latest/commands/hgetall/
+ + + + + + + + + + +
string$key

The key to query

string$member

The key to query

+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +

Examples

+ + + + + +
$redis->hgetWithMeta('foo', 'field');
+ +
+ + + +
+ +

+ + Redis|int|false hIncrBy(string $key, string $field, int $value)

@@ -7951,15 +10671,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The new value of the field.

+

See also

@@ -7967,7 +10689,7 @@

See also

@@ -7978,7 +10700,7 @@

Examples

- https://redis.io/commands/hincrby + https://redis.io/docs/latest/commands/hincrby/
-
$redis->hMSet('player:1', ['name' => 'Alice', 'score' => 0]);
+
$redis->hMSet('player:1', ['name' => 'Alice', 'score' => 0]);
 $redis->hincrby('player:1', 'score', 10);
@@ -7988,11 +10710,10 @@

Examples

-

- - Redis|float|false - hIncrByFloat(string $key, string $field, float $value) - + +

+ + Redis|float|false hIncrByFloat(string $key, string $field, float $value)

@@ -8023,15 +10744,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|float|false

The field value after incremented.

+

See also

@@ -8039,7 +10762,7 @@

See also

@@ -8059,11 +10782,10 @@

Examples

-

- - Redis|array|false - hKeys(string $key) - + +

+ + Redis|array|false hKeys(string $key)

@@ -8084,15 +10806,17 @@

Parameters

- https://redis.io/commands/hincrbyfloat + https://redis.io/docs/latest/commands/hincrbyfloat/
-

Return Value

+

Return Value

- +
+
Redis|array|false

The fields in the hash or false if the hash doesn't exist.

+

See also

@@ -8100,7 +10824,7 @@

See also

@@ -8120,11 +10844,10 @@

Examples

-

- - Redis|int|false - hLen(string $key) - + +

+ + Redis|int|false hLen(string $key)

@@ -8145,15 +10868,17 @@

Parameters

- https://redis.io/commands/hkeys + https://redis.io/docs/latest/commands/hkeys/
-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of fields or false if the key didn't exist.

+

See also

@@ -8161,7 +10886,7 @@

See also

@@ -8181,11 +10906,10 @@

Examples

-

- - Redis|array|false - hMget(string $key, array $fields) - + +

+ + Redis|array|false hMget(string $key, array $fields)

@@ -8211,15 +10935,17 @@

Parameters

- https://redis.io/commands/hlen + https://redis.io/docs/latest/commands/hlen/
-

Return Value

+

Return Value

- +
+
Redis|array|false

The fields and values or false if the key didn't exist.

+

See also

@@ -8227,7 +10953,7 @@

See also

@@ -8247,18 +10973,18 @@

Examples

-

- - Redis|bool - hMset(string $key, array $fieldvals) - + +

+ + Redis|array|false hgetex(string $key, array $fields, string|array|null $expiry = null)

-

Add or update one or more hash fields and values

+

Get one or more fields of a hash while optionally setting expiration +information

Parameters

@@ -8267,25 +10993,32 @@

Parameters

- + - - + + + + + + +
- https://redis.io/commands/hmget + https://redis.io/docs/latest/commands/hmget/
string $key

The hash to create/update

The hash to query.

array$fieldvals

An associative array with fields and their values.

$fields

One or more fields to query in the hash.

string|array|null$expiry

Info about the expiration

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True if the operation was successful

Redis|array|false

The fields and values or false if the key didn't exist.

+

See also

@@ -8293,7 +11026,7 @@

See also

@@ -8304,7 +11037,7 @@

Examples

- https://redis.io/commands/hmset + https://redis.io/docs/latest/commands/hgetex/
- +
$redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]);
$redis->hgetex('profiles', ['name', 'email'], ['EX' => 60]);
@@ -8313,18 +11046,17 @@

Examples

-

- - Redis|string|array - hRandField(string $key, array $options = null) - + +

+ + Redis|int|false hsetex(string $key, array $fields, array|null $expiry = null)

-

Get one or more random field from a hash.

+

Set one or more fields in a hash with optional expiration information.

Parameters

@@ -8333,29 +11065,32 @@

Parameters

string $key -

The hash to query.

+

The hash to create/update.

array - $options -

An array of options to modify how the command behaves.

-
$options = [
-    'COUNT'      => int  # An optional number of fields to return.
-    'WITHVALUES' => bool # Also return the field values.
-];
+ $fields +

An array with fields values.

+ + + array|null + $expiry +

Info about the expiration

-

Return Value

+

Return Value

- +
+
- - + +
Redis|string|array

One or more random fields (and possibly values).

Redis|int|false

One if fields were set zero if not.

+

See also

@@ -8363,7 +11098,7 @@

See also

@@ -8374,10 +11109,7 @@

Examples

- https://redis.io/commands/hrandfield + https://redis.io/docs/latest/commands/hsetex/
- - - - +
$redis->hrandfield('settings');
$redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]);
$redis->hsetex('profiles', ['token' => 'abc123'], ['EX' => 60]);
@@ -8386,19 +11118,17 @@

Examples

-

- - Redis|int|false - hSet(string $key, string $member, mixed $value) - + +

+ + Redis|array|false hgetdel(string $key, array $fields)

-

No description

- +

Get one or more fields and delete them

Parameters

@@ -8407,50 +11137,65 @@

Parameters

string $key - - - - string - $member - +

The hash in question

- mixed - $value - + array + $fields +

One or more fields

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|falseRedis|array|false

The field and values or false on failure

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/hgetdel/ +
+ +

Examples

+ + + + + +
$redis->hgetdel('profiles', ['token']);
+
-

- - Redis|bool - hSetNx(string $key, string $field, string $value) - + +

+ + Redis|bool hMset(string $key, array $fieldvals)

-

Set a hash field and value, but only if that field does not exist

+

Add or update one or more hash fields and values

Parameters

@@ -8459,30 +11204,27 @@

Parameters

string $key -

The hash to update.

- - - string - $field -

The value to set.

+

The hash to create/update

- string - $value - + array + $fieldvals +

An associative array with fields and their values.

-

Return Value

+

Return Value

- +
+
- +
Redis|bool

True if the field was set and false if not.

True if the operation was successful

+

See also

@@ -8490,7 +11232,7 @@

See also

@@ -8501,8 +11243,7 @@

Examples

- https://redis.io/commands/hsetnx + https://redis.io/docs/latest/commands/hmset/
- +
$redis->hsetnx('player:1', 'lock', 'enabled');
-$redis->hsetnx('player:1', 'lock', 'enabled');
$redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]);
@@ -8511,18 +11252,17 @@

Examples

-

- - Redis|int|false - hStrLen(string $key, string $field) - + +

+ + Redis|string|array|false hRandField(string $key, array|null $options = null)

-

Get the string length of a hash field

+

Get one or more random field from a hash.

Parameters

@@ -8534,22 +11274,28 @@

Parameters

The hash to query.

- string - $field -

The field to query.

+ array|null + $options +

An array of options to modify how the command behaves.

+
$options = [
+    'COUNT'      => int  # An optional number of fields to return.
+    'WITHVALUES' => bool # Also return the field values.
+];
-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The string length of the field or false.

Redis|string|array|false

One or more random fields (and possibly values).

+

See also

@@ -8557,7 +11303,7 @@

See also

@@ -8568,10 +11314,10 @@

Examples

- https://redis.io/commands/hstrlen + https://redis.io/docs/latest/commands/hrandfield/
- + + + +
$redis = new Redis(['host' => 'localhost']);
-$redis->del('hash');
-$redis->hmset('hash', ['50bytes' => str_repeat('a', 50)]);
-$redis->hstrlen('hash', '50bytes');
$redis->hrandfield('settings');
$redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]);
@@ -8580,18 +11326,17 @@

Examples

-

- - Redis|array|false - hVals(string $key) - + +

+ + Redis|int|false hSet(string $key, mixed ...$fields_and_vals)

-

Get all of the values from a hash.

+

Add or update one or more hash fields and values.

Parameters

@@ -8600,20 +11345,28 @@

Parameters

string $key -

The hash to query.

+

The hash to create/update.

+ + + mixed + ...$fields_and_vals +

Argument pairs of fields and values. Alternatively, an associative array with the +fields and their values.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|false

The values from the hash.

Redis|int|false

The number of fields that were added, or false on failure.

+

See also

@@ -8621,7 +11374,7 @@

See also

@@ -8632,7 +11385,10 @@

Examples

- https://redis.io/commands/hvals + https://redis.io/docs/latest/commands/hset/
- + + + +
$redis->hvals('player:1');
$redis->hSet('player:1', 'name', 'Kim', 'score', 78);
$redis->hSet('player:1', ['name' => 'Kim', 'score' => 78]);
@@ -8641,18 +11397,17 @@

Examples

-

- - Redis|array|bool - hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) - + +

+ + Redis|bool hSetNx(string $key, string $field, mixed $value)

-

Iterate over the fields and values of a hash in an incremental fashion.

+

Set a hash field and value, but only if that field does not exist

Parameters

@@ -8661,37 +11416,32 @@

Parameters

string $key -

The hash to query.

- - - int|null - $iterator -

The scan iterator, which should be initialized to NULL before the first call. -This value will be updated after every call to hscan, until it reaches zero -meaning the scan is complete.

+

The hash to update.

- string|null - $pattern -

An optional glob-style pattern to filter fields with.

+ string + $field +

The value to set.

- int - $count -

An optional hint to Redis about how many fields and values to return per HSCAN.

+ mixed + $value + -

Return Value

+

Return Value

- +
+
- - + +
Redis|array|bool

An array with a subset of fields and values.

Redis|bool

True if the field was set and false if not.

+

See also

@@ -8699,13 +11449,7 @@

See also

- - - - @@ -8716,26 +11460,8 @@

Examples

- https://redis.io/commands/hscan -
- https://redis.io/commands/scan + https://redis.io/docs/latest/commands/hsetnx/
- +
$redis = new Redis(['host' => 'localhost']);
-
-$redis->del('big-hash');
-
-for ($i = 0; $i < 1000; $i++) {
- $fields["field:$i"] = "value:$i";
-}
-
-$redis->hmset('big-hash', $fields);
-
-$it = NULL;
-
-do {
- // Scan the hash but limit it to fields that match '*:1?3'
- $fields = $redis->hscan('big-hash', $it, '*:1?3');
-
- foreach ($fields as $field => $value) {
- echo "[$field] => $value\n";
- }
-} while ($it != 0);
$redis->hsetnx('player:1', 'lock', 'enabled');
+$redis->hsetnx('player:1', 'lock', 'enabled');
@@ -8744,18 +11470,17 @@

Examples

-

- - Redis|int|false - incr(string $key, int $by = 1) - + +

+ + Redis|int|false hStrLen(string $key, string $field)

-

Increment a key's value, optionally by a specifc amount.

+

Get the string length of a hash field

Parameters

@@ -8764,25 +11489,27 @@

Parameters

string $key -

The key to increment

+

The hash to query.

- int - $by -

An optional amount to increment by.

+ string + $field +

The field to query.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

The new value of the key after incremented.

The string length of the field or false.

+

See also

@@ -8790,13 +11517,7 @@

See also

- - - - @@ -8807,10 +11528,10 @@

Examples

- https://redis.io/commands/incr -
- https://redis.io/commands/incrby + https://redis.io/docs/latest/commands/hstrlen/
- - - - +
$redis->incr('mycounter');
$redis->incr('mycounter', 10);
$redis = new Redis(['host' => 'localhost']);
+$redis->del('hash');
+$redis->hmset('hash', ['50bytes' => str_repeat('a', 50)]);
+$redis->hstrlen('hash', '50bytes');
@@ -8819,18 +11540,17 @@

Examples

-

- - Redis|int|false - incrBy(string $key, int $value) - + +

+ + Redis|array|false hVals(string $key)

-

Increment a key by a specific integer value

+

Get all of the values from a hash.

Parameters

@@ -8839,25 +11559,22 @@

Parameters

string $key -

The key to increment.

- - - int - $value -

The amount to increment.

+

The hash to query.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|falseRedis|array|false

The values from the hash.

+

See also

@@ -8865,7 +11582,7 @@

See also

@@ -8876,11 +11593,7 @@

Examples

- https://redis.io/commands/incrby + https://redis.io/docs/latest/commands/hvals/
- +
$redis->set('primes', 2);
-$redis->incrby('primes', 1);
-$redis->incrby('primes', 2);
-$redis->incrby('primes', 2);
-$redis->incrby('primes', 4);
$redis->hvals('player:1');
@@ -8889,18 +11602,17 @@

Examples

-

- - Redis|float|false - incrByFloat(string $key, float $value) - + +

+ + Redis|array|false hexpire(string $key, int $ttl, array $fields, string|null $mode = NULL)

-

Increment a numeric key by a floating point value.

+

Set the expiration on one or more fields in a hash.

Parameters

@@ -8909,34 +11621,56 @@

Parameters

string $key -

The key to increment

+

The hash to update.

- float - $value -

How much to increment (or decrement) the value.

+ int + $ttl +

The time to live in seconds.

+ + + array + $fields +

The fields to set the expiration on.

+ + + string|null + $mode +

An optional mode (NX, XX, ETC)

-

Return Value

+

Return Value

- +
+
- - + +
Redis|float|false

The new value of the key or false if the key didn't contain a string.

Redis|array|false
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/hexpire/ +
+

Examples

- +
$redis->incrbyfloat('tau', 3.1415926);
-$redis->incrbyfloat('tau', 3.1415926);
$redis->hexpire('profiles', 300, ['token'], 'NX');
@@ -8945,21 +11679,17 @@

Examples

-

- - Redis|array|false - info(string ...$sections) - + +

+ + Redis|array|false hpexpire(string $key, int $ttl, array $fields, string|null $mode = NULL)

-

Retrieve information about the connected redis-server. If no arguments are passed to -this function, redis will return every info field. Alternatively you may pass a specific -section you want returned (e.g. 'server', or 'memory') to receive only information pertaining -to that section.

If connected to Redis server >= 7.0.0 you may pass multiple optional sections.

+

Set the expiration on one or more fields in a hash in milliseconds.

Parameters

@@ -8967,21 +11697,38 @@

Parameters

- - + + + + + + + + + + + + + + + + +
string...$sections

Optional section(s) you wish Redis server to return.

$key

The hash to update.

int$ttl

The time to live in milliseconds.

array$fields

The fields to set the expiration on.

string|null$mode

An optional mode (NX, XX, ETC)

-

Return Value

+

Return Value

- +
+
Redis|array|false
+

See also

@@ -8989,63 +11736,37 @@

See also

- https://redis.io/commands/info/ + https://redis.io/docs/latest/commands/hexpire/
-
-
- -
-
-

- - bool - isConnected() - -

-
- - - -
-

Check if we are currently connected to a Redis instance.

-
-
- -

Return Value

+

Examples

- - - - -
bool

True if we are, false if not

+ +
$redis->hpexpire('profiles', 1500, ['token']);
+ + - - -
-

- - Redis|array|false - keys(string $pattern) - + +

+ + Redis|array|false hexpireat(string $key, int $time, array $fields, string|null $mode = NULL)

-

No description

- +

Set the expiration time on one or more fields of a hash.

Parameters

@@ -9053,42 +11774,76 @@

Parameters

- - + + + + + + + + + + + + + + + + +
string$pattern$key

The hash to update.

int$time

The time to live in seconds.

array$fields

The fields to set the expiration on.

string|null$mode

An optional mode (NX, XX, ETC)

-

Return Value

+

Return Value

- +
+
- +
Redis|array|falseRedis|array|false
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/hexpire/ +
+ +

Examples

+ + + + + +
$redis->hexpireat('profiles', time() + 600, ['token']);
+
-

- - Redis|int|false - lInsert(string $key, string $pos, mixed $pivot, mixed $value) - + +

+ + Redis|array|false hpexpireat(string $key, int $mstime, array $fields, string|null $mode = NULL)

-

No description

- +

Set the expiration time on one or more fields of a hash in milliseconds.

Parameters

@@ -9097,55 +11852,75 @@

Parameters

string $key - +

The hash to update.

- string - $pos - + int + $mstime +

The time to live in milliseconds.

- mixed - $pivot - + array + $fields +

The fields to set the expiration on.

- mixed - $value - + string|null + $mode +

An optional mode (NX, XX, ETC)

-

Return Value

+

Return Value

- +
+
- +
Redis|int|falseRedis|array|false
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/hexpire/ +
+ +

Examples

+ + + + + +
$redis->hpexpireat('profiles', (int) (microtime(true) * 1000) + 60000, ['token']);
+
-

- - Redis|int|false - lLen(string $key) - + +

+ + Redis|array|false httl(string $key, array $fields)

-

Retrieve the lenght of a list.

+

Get the TTL of one or more fields in a hash

Parameters

@@ -9154,40 +11929,65 @@

Parameters

string $key -

The list

+

The hash to query.

+ + + array + $fields +

The fields to query.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The number of elements in the list or false on failure.

Redis|array|false
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/httl/ +
+ +

Examples

+ + + + + +
$redis->httl('profiles', ['token']);
+
-

- - Redis|string|false - lMove(string $src, string $dst, string $wherefrom, string $whereto) - + +

+ + Redis|array|false hpttl(string $key, array $fields)

-

Move an element from one list into another.

+

Get the millisecond TTL of one or more fields in a hash

Parameters

@@ -9195,47 +11995,47 @@

Parameters

- - - - - - - - - - - - + + - - - + + +
string$src

The source list.

string$dst

The destination list

string$wherefrom

Where in the source list to retrieve the element. This can be either -Redis::LEFT, or Redis::RIGHT.

$key

The hash to query.

string$whereto

Where in the destination list to put the element. This can be either -Redis::LEFT, or Redis::RIGHT.

array$fields

The fields to query.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|string|false

The element removed from the source list.

Redis|array|false
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/hpttl/ +
+

Examples

- +
$redis->rPush('numbers', 'one', 'two', 'three');
-$redis->lMove('numbers', 'odds', Redis::LEFT, Redis::LEFT);
$redis->hpttl('profiles', ['token']);
@@ -9244,18 +12044,17 @@

Examples

-

- - Redis|bool|string|array - lPop(string $key, int $count = 0) - + +

+ + Redis|array|false hexpiretime(string $key, array $fields)

-

Pop one or more elements off a list.

+

Get the expiration time of one or more fields in a hash

Parameters

@@ -9264,26 +12063,27 @@

Parameters

string $key -

The list to pop from.

+

The hash to query.

- int - $count -

Optional number of elements to remove. By default one element is popped.

+ array + $fields +

The fields to query.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool|string|array

Will return the element(s) popped from the list or false/NULL -if none was removed.

Redis|array|false
+

See also

@@ -9291,7 +12091,7 @@

See also

@@ -9302,10 +12102,7 @@

Examples

- https://redis.io/commands/lpop + https://redis.io/docs/latest/commands/hexpiretime/
- - - - +
$redis->lpop('mylist');
$redis->lpop('mylist', 4);
$redis->hexpiretime('profiles', ['token']);
@@ -9314,18 +12111,17 @@

Examples

-

- - Redis|null|bool|int|array - lPos(string $key, mixed $value, array $options = null) - + +

+ + Redis|array|false hpexpiretime(string $key, array $fields)

-

Retrieve the index of an element in a list.

+

Get the expiration time in milliseconds of one or more fields in a hash

Parameters

@@ -9334,65 +12130,65 @@

Parameters

string $key -

The list to query.

- - - mixed - $value -

The value to search for.

+

The hash to query.

array - $options -

Options to configure how the command operates

-
$options = [
-    # How many matches to return.  By default a single match is returned.
-    # If count is set to zero, it means unlimited.
-    'COUNT' => <num-matches>
-
-    # Specify which match you want returned.  `RANK` 1 means "the first match"
-    # 2 means the second, and so on.  If passed as a negative number the
-    # RANK is computed right to left, so a `RANK` of -1 means "the last match".
-    'RANK'  => <rank>
-
-    # This argument allows you to limit how many elements Redis will search before
-    # returning.  This is useful to prevent Redis searching very long lists while
-    # blocking the client.
-    'MAXLEN => <max-len>
-];
+ $fields +

The fields to query.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|null|bool|int|array

Returns one or more of the matching indexes, or null/false if none were found.

Redis|array|false
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/hpexpiretime/ +
+ +

Examples

+ + + + + +
$redis->hpexpiretime('profiles', ['token']);
+
-

- - Redis|int|false - lPush(string $key, mixed ...$elements) - + +

+ + Redis|array|false hpersist(string $key, array $fields)

-

Prepend one or more elements to a list.

+

Persist one or more hash fields

Parameters

@@ -9401,25 +12197,27 @@

Parameters

string $key -

The list to prepend.

+

The hash to query.

- mixed - ...$elements -

One or more elements to prepend.

+ array + $fields +

The fields to query.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The new length of the list after prepending.

Redis|array|false
+

See also

@@ -9427,7 +12225,7 @@

See also

@@ -9438,7 +12236,7 @@

Examples

- https://redis.io/commands/lpush + https://redis.io/docs/latest/commands/hpersist/
- +
$redis->lPush('mylist', 'cat', 'bear', 'aligator');
$redis->hpersist('profiles', ['token']);
@@ -9447,18 +12245,17 @@

Examples

-

- - Redis|int|false - rPush(string $key, mixed ...$elements) - + +

+ + Redis|array|bool hscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

-

Append one or more elements to a list.

+

Iterate over the fields and values of a hash in an incremental fashion.

Parameters

@@ -9467,25 +12264,39 @@

Parameters

string $key -

The list to append to.

+

The hash to query.

- mixed - ...$elements -

one or more elements to append.

+ null|int|string + $iterator +

The scan iterator, which should be initialized to NULL before the first call. +This value will be updated after every call to hscan, until it reaches zero +meaning the scan is complete.

+ + + string|null + $pattern +

An optional glob-style pattern to filter fields with.

+ + + int + $count +

An optional hint to Redis about how many fields and values to return per HSCAN.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The new length of the list

Redis|array|bool

An array with a subset of fields and values.

+

See also

@@ -9493,7 +12304,13 @@

See also

+ + + + @@ -9504,7 +12321,26 @@

Examples

- https://redis.io/commands/rpush + https://redis.io/docs/latest/commands/hscan/ +
+ https://redis.io/docs/latest/commands/scan/
- +
$redis->rPush('mylist', 'xray', 'yankee', 'zebra');
$redis = new Redis(['host' => 'localhost']);
+
+$redis->del('big-hash');
+
+for ($i = 0; $i < 1000; $i++) {
+    $fields["field:$i"] = "value:$i";
+}
+
+$redis->hmset('big-hash', $fields);
+
+$it = null;
+
+do {
+    // Scan the hash but limit it to fields that match '*:1?3'
+    $fields = $redis->hscan('big-hash', $it, '*:1?3');
+
+    foreach ($fields as $field => $value) {
+        echo "[$field] => $value\n";
+    }
+} while ($it != 0);
@@ -9513,18 +12349,17 @@

Examples

-

- - Redis|int|false - lPushx(string $key, mixed $value) - + +

+ + Redis|int|false expiremember(string $key, string $field, int $ttl, string|null $unit = null)

-

Prepend an element to a list but only if the list exists

+

Set an expiration on a key member (KeyDB only).

Parameters

@@ -9533,45 +12368,81 @@

Parameters

string $key -

The key to prepend to.

+

The key to expire

- mixed - $value -

The value to prepend.

+ string + $field +

The field to expire

+ + + int + $ttl + + + + string|null + $unit +

The unit of the ttl (s, or ms).

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

The new length of the list.

+
+

See also

+ + + + + + + + + + +
+ https://docs.keydb.dev/docs/commands/#expiremember +
+ https://redis.io/docs/latest/commands/expiremember/ +
+ +

Examples

+ + + + + +
$redis->expiremember('profiles', 'token', 60);
+
-

- - Redis|int|false - rPushx(string $key, mixed $value) - + +

+ + Redis|int|false expirememberat(string $key, string $field, int $timestamp)

-

Append an element to a list but only if the list exists

+

Set an expiration on a key membert to a specific unix timestamp (KeyDB only).

Parameters

@@ -9580,45 +12451,76 @@

Parameters

string $key -

The key to prepend to.

+

The key to expire

- mixed - $value -

The value to prepend.

+ string + $field +

The field to expire

+ + + int + $timestamp +

The unix timestamp to expire at.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

The new length of the list.

+
+

See also

+ + + + + + + + + + +
+ https://docs.keydb.dev/docs/commands/#expirememberat +
+ https://redis.io/docs/latest/commands/expirememberat/ +
+ +

Examples

+ + + + + +
$redis->expirememberat('profiles', 'token', time() + 300);
+
-

- - Redis|bool - lSet(string $key, int $index, mixed $value) - + +

+ + Redis|int|false incr(string $key, int $by = 1)

-

Set a list element at an index to a specific value.

+

Increment a key's value, optionally by a specific amount.

Parameters

@@ -9627,30 +12529,27 @@

Parameters

string $key -

The list to modify.

+

The key to increment

int - $index -

The position of the element to change.

- - - mixed - $value -

The new value.

+ $by +

An optional amount to increment by.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True if the list was modified.

Redis|int|false

The new value of the key after incremented.

+

See also

@@ -9658,42 +12557,75 @@

See also

+ + + +
- https://redis.io/commands/lset + https://redis.io/docs/latest/commands/incr/ +
+ https://redis.io/docs/latest/commands/incrby/
+

Examples

+ + + + + + + + +
$redis->incr('mycounter');
$redis->incr('mycounter', 10);
+
-

- - int - lastSave() - + +

+ + Redis|int|false incrBy(string $key, int $value)

-

Retrieve the last time Redis' database was persisted to disk.

+

Increment a key by a specific integer value

- -

Return Value

+

Parameters

+ + + + + + + + + + +
string$key

The key to increment.

int$value

The amount to increment.

+ + +

Return Value

+ +
+ - - + +
int

The unix timestamp of the last save time

Redis|int|false
+

See also

@@ -9701,30 +12633,41 @@

See also

- https://redis.io/commands/lastsave + https://redis.io/docs/latest/commands/incrby/
+

Examples

+ + + + + +
$redis->set('primes', 2);
+$redis->incrby('primes', 1);
+$redis->incrby('primes', 2);
+$redis->incrby('primes', 2);
+$redis->incrby('primes', 4);
+
-

- - mixed - lindex(string $key, int $index) - + +

+ + Redis|float|false incrByFloat(string $key, float $value)

-

Get the element of a list by its index.

+

Increment a numeric key by a floating point value.

Parameters

@@ -9733,90 +12676,47 @@

Parameters

string $key -

The key to query

+

The key to increment

- int - $index -

The index to check.

+ float + $value +

How much to increment (or decrement) the value.

-

Return Value

+

Return Value

- +
+
- - + +
mixed

The index or NULL/false if the element was not found.

Redis|float|false

The new value of the key or false if the key didn't contain a string.

+
- -
-
- -
-
-

- - Redis|array|false - lrange(string $key, int $start, int $end) - -

-
- - - -
-

Retrieve elements from a list.

-
-
-

Parameters

+

See also

- - - - - - - - - - - - - + +
string$key

The list to query.

int$start

The beginning index to retrieve. This number can be negative -meaning start from the end of the list.

int$end

The end index to retrieve. This can also be negative to start -from the end of the list.

+ https://redis.io/docs/latest/commands/incrbyfloat/ +
-

Return Value

- - - - - - -
Redis|array|false

The range of elements between the indexes.

- - - -

Examples

- - - - +
$redis->lrange('mylist', 0, -1);  // the whole list
$redis->lrange('mylist', -2, -1); // the last two elements in the list.
$redis->incrbyfloat('tau', 3.1415926);
+$redis->incrbyfloat('tau', 3.1415926);
@@ -9825,18 +12725,20 @@

Examples

-

- - Redis|int|false - lrem(string $key, mixed $value, int $count = 0) - + +

+ + Redis|array|false info(string ...$sections)

-

Remove one or more matching elements from a list.

+

Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

If connected to Redis server >= 7.0.0 you may pass multiple optional sections.

Parameters

@@ -9844,31 +12746,23 @@

Parameters

- - - - - - - - - - - - + +
string$key

The list to truncate.

mixed$value

The value to remove.

int$count

How many elements matching the value to remove.

...$sections

Optional section(s) you wish Redis server to return.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The number of elements removed.

Redis|array|false
+

See also

@@ -9876,62 +12770,51 @@

See also

- https://redis.io/commands/lrem + https://redis.io/docs/latest/commands/info/
+

Examples

+ + + + + +
$redis->info('server', 'stats');
+
-

- - Redis|bool - ltrim(string $key, int $start, int $end) - + +

+ + bool isConnected()

-

Trim a list to a subrange of elements.

+

Check if we are currently connected to a Redis instance.

-

Parameters

- - - - - - - - - - - - - - - - - -
string$key

The list to trim

int$start

The starting index to keep

int$end

The ending index to keep.

- -

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

true if the list was trimmed.

bool

True if we are, false if not

+
@@ -9939,7 +12822,7 @@

Examples

- +
$redis->ltrim('mylist', 0, 3);  // Keep the first four elements
$redis->isConnected();
@@ -9948,48 +12831,61 @@

Examples

-

- - Redis|array - mget(array $keys) - + +

+ + Redis|list&lt;string&gt;|false keys(string $pattern)

-

Get one ore more string keys.

+

No description

+

Parameters

- - - + + +
array$keys

The keys to retrieve

string$pattern
-

Return Value

+

Return Value

- +
+
- - + +
Redis|array

an array of keys with their values.

Redis|list<string>|false
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/keys/ +
+

Examples

- +
$redis->mget(['key1', 'key2']);
$redis->keys('session:*');
@@ -9998,11 +12894,10 @@

Examples

-

- - Redis|bool - migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = NULL) - + +

+ + Redis|int|false lInsert(string $key, string $pos, mixed $pivot, mixed $value)

@@ -10018,145 +12913,100 @@

Parameters

- - - - - - - - - - - - - - - - - - - - - - + + - - + + - +
string$host
int$port
string|array $key
int$dstdb
int$timeout
bool$copystring$pos
bool$replacemixed$pivot
mixed$credentials$value
-

Return Value

+

Return Value

- +
+
- +
Redis|boolRedis|int|false
+
- -
-
- -
-
-

- - Redis|bool - move(string $key, int $index) - -

-
- - - -
-

Move a key to a different database on the same redis instance.

-
-
-

Parameters

+

See also

- - - - - - - +
string$key

The key to move

int$index + https://redis.io/docs/latest/commands/linsert/ +
-

Return Value

+

Examples

- - - - -
Redis|bool

True if the key was moved

+ +
$redis->lInsert('letters', Redis::AFTER, 'b', 'beta');
+ + - - -
-

- - Redis|bool - mset(array $key_values) - + +

+ + Redis|int|false lLen(string $key)

-

Set one ore more string keys.

+

Retrieve the length of a list.

Parameters

- - - + + +
array$key_values

An array with keys and their values.

string$key

The list

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True if the keys could be set.

Redis|int|false

The number of elements in the list or false on failure.

+

See also

@@ -10164,7 +13014,7 @@

See also

@@ -10175,7 +13025,7 @@

Examples

- https://redis.io/commands/mset + https://redis.io/docs/latest/commands/llen/
- +
$redis->mSet(['foo' => 'bar', 'baz' => 'bop']);
$redis->lLen('queue');
@@ -10184,40 +13034,62 @@

Examples

-

- - Redis|bool - msetnx(array $key_values) - + +

+ + Redis|string|false lMove(string $src, string $dst, string $wherefrom, string $whereto)

-

Set one ore more string keys but only if none of the key exist.

+

Move an element from one list into another.

Parameters

- - - + + + + + + + + + + + + + + + + + +
array$key_values

An array of keys with their values.

string$src

The source list.

string$dst

The destination list

string$wherefrom

Where in the source list to retrieve the element. This can be either

+
    +
  • Redis::LEFT, or Redis::RIGHT.
  • +
string$whereto

Where in the destination list to put the element. This can be either

+
    +
  • Redis::LEFT, or Redis::RIGHT.
  • +
-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True if the keys were set and false if not.

Redis|string|false

The element removed from the source list.

+

See also

@@ -10225,7 +13097,7 @@

See also

@@ -10236,7 +13108,8 @@

Examples

- https://redis.io/commands/msetnx + https://redis.io/docs/latest/commands/lmove/
- +
$redis->msetnx(['foo' => 'bar', 'baz' => 'bop']);
$redis->rPush('numbers', 'one', 'two', 'three');
+$redis->lMove('numbers', 'odds', Redis::LEFT, Redis::LEFT);
@@ -10245,41 +13118,2644 @@

Examples

-

- - bool|Redis - multi(int $value = Redis::MULTI) - + +

+ + Redis|string|false blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout)

-

Begin a transaction.

+

No description

+

Parameters

- - - - -
int$value

The type of transaction to start. This can either be Redis::MULTI or -`Redis::PIPELINE'.

+ string + $src + + + + string + $dst + + + + string + $wherefrom + + + + string + $whereto + + + + float + $timeout + + + + + +

Return Value

+ +
+ + + + + +
Redis|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + Redis|bool|string|array lPop(string $key, int $count = 0) +

+
+ + + +
+

Pop one or more elements off a list.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The list to pop from.

int$count

Optional number of elements to remove. By default one element is popped.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool|string|array

Will return the element(s) popped from the list or false/NULL +if none was removed.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/lpop/ +
+ + +

Examples

+ + + + + + + + +
$redis->lpop('mylist');
$redis->lpop('mylist', 4);
+ +
+
+ +
+
+ +

+ + Redis|null|bool|int|array lPos(string $key, mixed $value, array|null $options = null) +

+
+ + + +
+

Retrieve the index of an element in a list.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The list to query.

mixed$value

The value to search for.

array|null$options

Options to configure how the command operates

+
$options = [
+    # How many matches to return.  By default a single match is returned.
+    # If count is set to zero, it means unlimited.
+    'COUNT' => <num-matches>
+
+    # Specify which match you want returned.  `RANK` 1 means "the first match"
+    # 2 means the second, and so on.  If passed as a negative number the
+    # RANK is computed right to left, so a `RANK` of -1 means "the last match".
+    'RANK'  => <rank>
+
+    # This argument allows you to limit how many elements Redis will search before
+    # returning.  This is useful to prevent Redis searching very long lists while
+    # blocking the client.
+    'MAXLEN => <max-len>
+];
+ + +

Return Value

+ +
+ + + + + +
Redis|null|bool|int|array

Returns one or more of the matching indexes, or null/false if none were found.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/lpos/ +
+ + +

Examples

+ + + + + +
$redis->lPos('queue', 'job-42');
+ +
+
+ +
+
+ +

+ + Redis|int|false lPush(string $key, mixed ...$elements) +

+
+ + + +
+

Prepend one or more elements to a list.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The list to prepend.

mixed...$elements

One or more elements to prepend.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

The new length of the list after prepending.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/lpush/ +
+ + +

Examples

+ + + + + +
$redis->lPush('mylist', 'cat', 'bear', 'aligator');
+ +
+
+ +
+
+ +

+ + Redis|int|false rPush(string $key, mixed ...$elements) +

+
+ + + +
+

Append one or more elements to a list.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The list to append to.

mixed...$elements

one or more elements to append.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

The new length of the list

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/rpush/ +
+ + +

Examples

+ + + + + +
$redis->rPush('mylist', 'xray', 'yankee', 'zebra');
+ +
+
+ +
+
+ +

+ + Redis|int|false lPushx(string $key, mixed $value) +

+
+ + + +
+

Prepend an element to a list but only if the list exists

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to prepend to.

mixed$value

The value to prepend.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

The new length of the list.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/lpushx/ +
+ + +

Examples

+ + + + + +
$redis->lPushx('queue', 'job-42');
+ +
+
+ +
+
+ +

+ + Redis|int|false rPushx(string $key, mixed $value) +

+
+ + + +
+

Append an element to a list but only if the list exists

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to prepend to.

mixed$value

The value to prepend.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

The new length of the list.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/rpushx/ +
+ + +

Examples

+ + + + + +
$redis->rPushx('queue', 'job-99');
+ +
+
+ +
+
+ +

+ + Redis|bool lSet(string $key, int $index, mixed $value) +

+
+ + + +
+

Set a list element at an index to a specific value.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The list to modify.

int$index

The position of the element to change.

mixed$value

The new value.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool

True if the list was modified.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/lset/ +
+ + +

Examples

+ + + + + +
$redis->lSet('queue', 0, 'job-42');
+ +
+
+ +
+
+ +

+ + int lastSave() +

+
+ + + +
+

Retrieve the last time Redis' database was persisted to disk.

+
+
+ +

Return Value

+ +
+ + + + + +
int

The unix timestamp of the last save time

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/lastsave/ +
+ + +

Examples

+ + + + + +
$redis->lastSave();
+ +
+
+ +
+
+ +

+ + mixed lindex(string $key, int $index) +

+
+ + + +
+

Get the element of a list by its index.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to query

int$index

The index to check.

+ + +

Return Value

+ +
+ + + + + +
mixed

The index or NULL/false if the element was not found.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/lindex/ +
+ + +

Examples

+ + + + + +
$redis->lindex('queue', 0);
+ +
+
+ +
+
+ +

+ + Redis|array|false lrange(string $key, int $start, int $end) +

+
+ + + +
+

Retrieve elements from a list.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The list to query.

int$start

The beginning index to retrieve. This number can be negative +meaning start from the end of the list.

int$end

The end index to retrieve. This can also be negative to start +from the end of the list.

+ + +

Return Value

+ +
+ + + + + +
Redis|array|false

The range of elements between the indexes.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/lrange/ +
+ + +

Examples

+ + + + + + + + +
$redis->lrange('mylist', 0, -1);  // the whole list
$redis->lrange('mylist', -2, -1); // the last two elements in the list.
+ +
+
+ +
+
+ +

+ + Redis|int|false lrem(string $key, mixed $value, int $count = 0) +

+
+ + + +
+

Remove one or more matching elements from a list.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The list to truncate.

mixed$value

The value to remove.

int$count

How many elements matching the value to remove.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

The number of elements removed.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/lrem/ +
+ + +

Examples

+ + + + + +
$redis->lrem('queue', 0, 'expired-job');
+ +
+
+ +
+
+ +

+ + Redis|bool ltrim(string $key, int $start, int $end) +

+
+ + + +
+

Trim a list to a subrange of elements.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The list to trim

int$start

The starting index to keep

int$end

The ending index to keep.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool

true if the list was trimmed.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/ltrim/ +
+ + +

Examples

+ + + + + +
$redis->ltrim('mylist', 0, 3);  // Keep the first four elements
+ +
+
+ +
+
+ +

+ + Redis|array|false mget(array $keys) +

+
+ + + +
+

Get one or more string keys.

+
+
+

Parameters

+ + + + + + + +
array$keys

The keys to retrieve

+ + +

Return Value

+ +
+ + + + + +
Redis|array|false

an array of keys with their values.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/mget/ +
+ + +

Examples

+ + + + + +
$redis->mget(['key1', 'key2']);
+ +
+
+ +
+
+ +

+ + Redis|bool migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, mixed $credentials = null) +

+
+ + + +
+

Proxy for the Redis MIGRATE command.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host

The destination redis host.

int$port

The destination redis port.

string|array$key

The key or array of keys to migrate.

int$dstdb

The destination database index.

int$timeout

The timeout for the operation in +milliseconds.

bool$copy

Whether to copy the key(s) or move +them.

bool$replace

Whether to replace existing keys on +the destination.

mixed$credentials

Optional credentials for +authenticating to the destination +server.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool
+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/migrate/ +
+ + +

Examples

+ + + + + +
$redis->connect('localhost', 6379);
+$redis->set('foo', '6379_key');
+
+// Move the key to localhost:9999 with a 5 second timeout
+var_dump($redis->migrate('localhost', 9999, 'foo', 0, 5000));
+ +
+
+ +
+
+ +

+ + Redis|bool move(string $key, int $index) +

+
+ + + +
+

Move a key to a different database on the same redis instance.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to move

int$index
+ + +

Return Value

+ +
+ + + + + +
Redis|bool

True if the key was moved

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/move/ +
+ + +

Examples

+ + + + + +
$redis->move('cart:42', 1);
+ +
+
+ +
+
+ +

+ + Redis|bool mset(array $key_values) +

+
+ + + +
+

Set one or more string keys.

+
+
+

Parameters

+ + + + + + + +
array$key_values

An array with keys and their values.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool

True if the keys could be set.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/mset/ +
+ + +

Examples

+ + + + + +
$redis->mSet(['foo' => 'bar', 'baz' => 'bop']);
+ +
+
+ +
+
+ +

+ + Redis|int|false msetex(array $key_vals, int|float|array|null $expiry = null) +

+
+ + + +
+

Set one or more keys and values with optional expiry information.

+
+
+

Parameters

+ + + + + + + + + + + + +
array$key_vals

An array of keys with their values.

int|float|array|null$expiry

An optional array with expiry information.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

1 if all keys were set, 0 if none were.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/commands/msetex +
+ + +

Examples

+ + + + + +
$redis->msetex(['foo' => 'bar', 'baz' => 'bop'], ['EX' => 60]);
+ +
+
+ +
+
+ +

+ + Redis|bool msetnx(array $key_values) +

+
+ + + +
+

Set one or more string keys but only if none of the key exist.

+
+
+

Parameters

+ + + + + + + +
array$key_values

An array of keys with their values.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool

True if the keys were set and false if not.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/msetnx/ +
+ + +

Examples

+ + + + + +
$redis->msetnx(['foo' => 'bar', 'baz' => 'bop']);
+ +
+
+ +
+
+ +

+ + bool|Redis multi(int $value = Redis::MULTI) +

+
+ + + +
+

Begin a transaction.

+
+
+

Parameters

+ + + + + + + +
int$value

The type of transaction to start. This can either be Redis::MULTI or +`Redis::PIPELINE'.

+ + +

Return Value

+ +
+ + + + + +
bool|Redis

True if the transaction could be started.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/multi/ +
+ + +

Examples

+ + + + + +
$redis->multi();
+$redis->set('foo', 'bar');
+$redis->get('foo');
+$redis->exec();
+ +
+
+ +
+
+ +

+ + Redis|int|string|false object(string $subcommand, string $key) +

+
+ + + +
+

Get encoding and other information about a key.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$subcommand

The subcommand to execute. This can be either 'encoding', 'freq', or 'idle'.

string$key

The key to query.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|string|false

The requested information about the key.

+ +
+ + + +

Examples

+ + + + + +
$redis->del('list1');
+$redis->rPush('list1', 'a', 'b', 'c');
+echo $redis->object('encoding', 'list1');
+ +
+
+ +
+
+ +

+ + bool open(string $host, int $port = 6379, float $timeout = 0, string|null $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array|null $context = null) deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
string|null$persistent_id
int$retry_interval
float$read_timeout
array|null$context
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +

Examples

+ + + + + +
$redis->open('127.0.0.1', 6379);
+ +
+
+ +
+
+ +

+ + bool pconnect(string $host, int $port = 6379, float $timeout = 0, string|null $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array|null $context = null) +

+
+ + + +
+

Connects to a Redis server creating or reusing a persistent connection.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host

The Redis server hostname.

int$port

The Redis server port.

float$timeout

Connection timeout in seconds.

string|null$persistent_id

An optional persistent ID to use for the connection.

int$retry_interval

The number of microseconds to wait before retrying a connection.

float$read_timeout

Read timeout in seconds.

array|null$context

An optional stream context array.

+ + +

Return Value

+ +
+ + + + + +
bool

True if the connection was successful.

+ +
+ +

Exceptions

+ + + + + + +
RedisException
+ + + +

Examples

+ + + + + +
try {
+    $redis = new Redis();
+    $redis->pconnect('localhost', 6379);
+} catch (Exception $ex) {
+   echo "Could not connect to Redis: ", $ex->getMessage(), "\n";
+}
+ +
+
+ +
+
+ +

+ + Redis|bool persist(string $key) +

+
+ + + +
+

Remove the expiration from a key.

+
+
+

Parameters

+ + + + + + + +
string$key

The key to operate against.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool

True if a timeout was removed and false if it was not or the key didn't exist.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/persist/ +
+ + +

Examples

+ + + + + +
$redis->persist('session:42');
+ +
+
+ +
+
+ +

+ + bool pexpire(string $key, int $timeout, string|null $mode = null) +

+
+ + + +
+

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 +you can pass an optional mode argument that modifies how the command will execute.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The key to set an expiration on.

int$timeout

The number of milliseconds after which key will be automatically deleted.

string|null$mode

A two character modifier that changes how the +command works.

+ + +

Return Value

+ +
+ + + + + +
bool

True if an expiry was set on the key, and false otherwise.

+ +
+ + +

See also

+ + + + + + + + + + +
+ +Redis::expire + for a description of the mode argument.
+ https://redis.io/docs/latest/commands/pexpire/ +
+ + +

Examples

+ + + + + +
$redis->pexpire('session:42', 5000);
+ +
+
+ +
+
+ +

+ + Redis|bool pexpireAt(string $key, int $timestamp, string|null $mode = null) +

+
+ + + +
+

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to +Redis >= 7.0.0 you can pass an optional 'mode' argument.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timestamp
string|null$mode
+ + +

Return Value

+ +
+ + + + + +
Redis|bool
+ +
+ + +

See also

+ + + + + + + + + + +
+ +Redis::expire + For a description of the mode argument.
+ https://redis.io/docs/latest/commands/pexpireat/ + @param string $key The key to set an expiration on. +@param int $timestamp The unix timestamp to expire at. +@param string|null $mode A two character modifier that changes how the + command works. + +@return Redis|bool True if an expiration was set on the key, false otherwise.
+ + +

Examples

+ + + + + +
$redis->pexpireAt('session:42', (int) (microtime(true) * 1000) + 60000);
+ +
+
+ +
+
+ +

+ + Redis|int pfadd(string $key, array $elements) +

+
+ + + +
+

Add one or more elements to a Redis HyperLogLog key

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key in question.

array$elements

One or more elements to add.

+ + +

Return Value

+ +
+ + + + + +
Redis|int

Returns 1 if the set was altered, and zero if not.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/pfadd/ +
+ + +

Examples

+ + + + + +
$redis->pfadd('visitors', ['alice', 'bob']);
+ +
+
+ +
+
+ +

+ + Redis|int|false pfcount(array|string $key_or_keys) +

+
+ + + +
+

Retrieve the cardinality of a Redis HyperLogLog key.

+
+
+

Parameters

+ + + + + + + +
array|string$key_or_keys

Either one key or an array of keys

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

The estimated cardinality of the set.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/pfcount/ +
+ + +

Examples

+ + + + + +
$redis->pfcount(['visitors:today', 'visitors:yesterday']);
+ +
+
+ +
+
+ +

+ + Redis|bool pfmerge(string $dst, array $srckeys) +

+
+ + + +
+

Merge one or more source HyperLogLog sets into a destination set.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$dst

The destination key.

array$srckeys

One or more source keys.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool

Always returns true.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/pfmerge/ +
+ + +

Examples

+ + + + + +
$redis->pfmerge('visitors:all', ['visitors:today', 'visitors:yesterday']);
+ +
+
+ +
+
+ +

+ + Redis|string|bool ping(string|null $message = null) +

+
+ + + +
+

PING the redis server with an optional string argument.

+
+
+

Parameters

+ + + + + + + +
string|null$message

An optional string message that Redis will reply with, if passed.

+ + +

Return Value

+ +
+ + + + + +
Redis|string|bool

If passed no message, this command will simply return true. +If a message is passed, it will return the message.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/ping/ +
+ + +

Examples

+ + + + + + + + +
$redis->ping();
$redis->ping('beep boop');
+ +
+
+ +
+
+ +

+ + bool|Redis pipeline() +

+
+ + + +
+

Enter into pipeline mode.

Pipeline mode is the highest performance way to send many commands to Redis +as they are aggregated into one stream of commands and then all sent at once +when the user calls Redis::exec().

+

NOTE: That this is shorthand for Redis::multi(Redis::PIPELINE)

+
+
+ +

Return Value

+ +
+ + + + + +
bool|Redis

The redis object is returned, to facilitate method chaining.

+ +
+ + + +

Examples

+ + + + + +
$redis->pipeline()
+->set('foo', 'bar')
+->del('mylist')
+->rpush('mylist', 'a', 'b', 'c')
+->exec();
+ +
+
+ +
+
+ +

+ + bool popen(string $host, int $port = 6379, float $timeout = 0, string|null $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, array|null $context = null) deprecated +

+
+

+ deprecated + + + + +

+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
string|null$persistent_id
int$retry_interval
float$read_timeout
array|null$context
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +

Examples

+ + + + + +
$redis->popen('127.0.0.1', 6379, 0.0, 'cache');
+ +
+
+ +
+
+ +

+ + Redis|bool psetex(string $key, int $expire, mixed $value) +

+
+ + + +
+

Set a key with an expiration time in milliseconds

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key

The key to set

int$expire

The TTL to set, in milliseconds.

mixed$value

The value to set the key to.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool

True if the key could be set.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/psetex/ +
+ + +

Examples

+ + + + + +
$redis->psetex('mykey', 1000, 'myval');
+ +
+
+ +
+
+ +

+ + bool psubscribe(array $patterns, callable $cb) +

+
+ + + +
+

Subscribe to one or more glob-style patterns

+
+
+

Parameters

+ + + + + + + + + + + + +
array$patterns

One or more patterns to subscribe to.

callable$cb

A callback with the following prototype:

+
function ($redis, $channel, $message) { }
+ + +

Return Value

+ +
+ + + + + +
bool

True if we were subscribed.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/psubscribe/ +
+ + +

Examples

+ + + + + +
$redis->psubscribe(['user:*'], function (Redis $client, string $pattern, string $channel, string $message): void {
+    printf('[%s] %s' . PHP_EOL, $channel, $message);
+});
+ +
+
+ +
+
+ +

+ + Redis|int|false pttl(string $key) +

+
+ + + +
+

Get a keys time to live in milliseconds.

+
+
+

Parameters

+ + + + + + + +
string$key

The key to check.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

The key's TTL or one of two special values if it has none.

+
-1 - The key has no TTL.
+-2 - The key did not exist.
+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/pttl/ +
+ + +

Examples

+ + + + + +
$redis->pttl('ttl-key');
+ +
+
+ +
+
+ +

+ + Redis|int|false publish(string $channel, string $message) +

+
+ + + +
+

Publish a message to a pubsub channel

+
+
+

Parameters

+ + + + + + + + + + + + +
string$channel

The channel to publish to.

string$message

The message itself.

+ + +

Return Value

+ +
+ + + + + +
Redis|int|false

The number of subscribed clients to the given channel.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/publish/ +
+ + +

Examples

+ + + + + +
$redis->publish('updates', 'build complete');
+ +
+
+ +
+
+ +

+ + mixed pubsub(string $command, mixed $arg = null) +

+
+ + + +
+

Interact with the Redis PubSub subsystem.

+
+
+

Parameters

+ + + + + + + + + + + + +
string$command

The PubSub command to execute. This can be one of:

mixed$arg

An optional argument to the command.

+ + +

Return Value

+ +
+ + + + + +
mixed

Can return any number of things depending on the command executed.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/pubsub/ +
+ + +

Examples

+ + + + + +
$redis->pubsub('channels');
+ +
+
+ +
+
+ +

+ + Redis|array|bool punsubscribe(array $patterns) +

+
+ + + +
+

Unsubscribe from one or more channels by pattern

+
+
+

Parameters

+ + + + + + + +
array$patterns

One or more glob-style patterns of channel names.

-

Return Value

+

Return Value

- +
+
- - + +
bool|Redis

True if the transaction could be started.

Redis|array|bool

The array of subscribed patterns or false on failure.

+

See also

@@ -10287,7 +15763,20 @@

See also

+ + + + + + + + @@ -10298,10 +15787,7 @@

Examples

- https://redis.io/commands/multi + https://redis.io/docs/latest/commands/punsubscribe/ +
+ https://redis.io/docs/latest/commands/subscribe/ +
+ +Redis::subscribe
- +
$redis->multi();
-$redis->set('foo', 'bar');
-$redis->get('foo');
-$redis->exec();
$redis->punsubscribe(['user:*', 'room:*']);
@@ -10310,19 +15796,17 @@

Examples

-

- - Redis|int|string|false - object(string $subcommand, string $key) - + +

+ + Redis|array|string|bool rPop(string $key, int $count = 0)

-

No description

- +

Pop one or more elements from the end of a list.

Parameters

@@ -10330,127 +15814,122 @@

Parameters

- - + + - - - + + +
string$subcommand$key

A redis LIST key name.

string$keyint$count

The maximum number of elements to pop at once. +NOTE: The count argument requires Redis >= 6.2.0

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|string|falseRedis|array|string|bool

One or more popped elements or false if all were empty.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/rpop/ +
+ +

Examples

+ + + + + + + + +
$redis->rPop('mylist');
$redis->rPop('mylist', 4);
+
-

- - bool - open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) - deprecated + +

+ + Redis|string|false randomKey()

-

- deprecated - - - - -

-

No description

- +

Return a random key from the current database

-

Parameters

+ +

Return Value

+ +
+ + + + + +
Redis|string|false

A random key name or false if no keys exist

+ +
+ + +

See also

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
string$host
int$port
float$timeout
string$persistent_id
int$retry_interval
float$read_timeout
array$context + https://redis.io/docs/latest/commands/randomkey/ +
-

Return Value

+

Examples

- - - - -
bool
+ +
$redis->randomKey();
+ + - - -
-

- - bool - pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) - + +

+ + mixed rawcommand(string $command, mixed ...$args)

-

No description

- +

Execute any arbitrary Redis command by name.

Parameters

@@ -10458,71 +15937,61 @@

Parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - + + +
string$host
int$port
float$timeout
string$persistent_id
int$retry_interval
float$read_timeout$command

The command to execute

array$contextmixed...$args

One or more arguments to pass to the command.

-

Return Value

+

Return Value

- +
+
- - + +
boolmixed

Can return any number of things depending on command executed.

+
+

Examples

+ + + + + + + + + + + +
$redis->rawCommand('del', 'mystring', 'mylist');
$redis->rawCommand('set', 'mystring', 'myvalue');
$redis->rawCommand('rpush', 'mylist', 'one', 'two', 'three');
+
-

- - Redis|bool - persist(string $key) - + +

+ + Redis|bool rename(string $old_name, string $new_name)

-

Remove the expiration from a key.

+

Unconditionally rename a key from $old_name to $new_name

Parameters

@@ -10530,42 +15999,66 @@

Parameters

- - + + + + + + +
string$key

The key to operate against.

$old_name

The original name of the key

string$new_name

The new name for the key

-

Return Value

+

Return Value

- +
+
- +
Redis|bool

True if a timeout was removed and false if it was not or the key didn't exist.

True if the key was renamed or false if not.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/rename/ +
+ +

Examples

+ + + + + +
$redis->rename('config:pending', 'config:active');
+
-

- - bool - pexpire(string $key, int $timeout, string|null $mode = NULL) - + +

+ + Redis|bool renameNx(string $key_src, string $key_dst)

-

Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 -you can pass an optional mode argument that modifies how the command will execute.

Redis::expire() for a description of the mode argument.

+

Renames $key_src to $key_dst but only if newkey does not exist.

Parameters

@@ -10573,87 +16066,84 @@

Parameters

- - + + - - - + + + +
string$key

The key to set an expiration on. -@param string $mode A two character modifier that changes how the -command works.

-

@return Redis|bool True if an expiry was set on the key, and false otherwise.

$key_src

The source key name

int$timeoutstring$key_dst

The destination key name.

+ + +

Return Value

+ +
+ + + + + +
Redis|bool

True if the key was renamed, false if not.

+ +
+ + +

See also

+ + - - +
string|null$mode + https://redis.io/docs/latest/commands/renamenx/ +
-

Return Value

+

Examples

- - - - -
bool
+ +
$redis->set('src', 'src_key');
+$redis->set('existing-dst', 'i_exist');
+
+$redis->renamenx('src', 'dst');
+$redis->renamenx('dst', 'existing-dst');
+ + - - -
-

- - Redis|bool - pexpireAt(string $key, int $timestamp, string|null $mode = NULL) - + +

+ + Redis|bool reset()

-

Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to -Redis >= 7.0.0 you can pass an optional 'mode' argument.

+

Reset the state of the connection.

-

Parameters

- - - - - - - - - - - - - - - - - -
string$key
int$timestamp
string|null$mode
- -

Return Value

+

Return Value

- +
+
- +
Redis|bool

Should always return true unless there is an error.

+

See also

@@ -10661,37 +16151,37 @@

See also

- + +
- -Redis::expire + https://redis.io/docs/latest/commands/reset/ For a description of the mode argument. +
-@param string $key The key to set an expiration on. -@param string $mode A two character modifier that changes how the - command works. + +

Examples

-@return Redis|bool True if an expiration was set on the key, false otherwise. + + +
$redis->reset();
-
-

- - Redis|int - pfadd(string $key, array $elements) - + +

+ + Redis|bool restore(string $key, int $ttl, string $value, array|null $options = null)

-

Add one or more elements to a Redis HyperLogLog key

+

Restore a key by the binary payload generated by the DUMP command.

Parameters

@@ -10700,25 +16190,50 @@

Parameters

string $key -

The key in question.

+

The name of the key you wish to create.

- array - $elements -

One or more elements to add.

+ int + $ttl +

What Redis should set the key's TTL (in milliseconds) to once it is created. +Zero means no TTL at all.

+ + + string + $value +

The serialized binary value of the string (generated by DUMP).

+ + + array|null + $options +

An array of additional options that modifies how the command operates.

+
$options = [
+    'ABSTTL'          # If this is present, the `$ttl` provided by the user should
+                      # be an absolute timestamp, in milliseconds()
+
+    'REPLACE'         # This flag instructs Redis to store the key even if a key with
+                      # that name already exists.
+
+    'IDLETIME' => int # Tells Redis to set the keys internal 'idletime' value to a
+                      # specific number (see the Redis command OBJECT for more info).
+    'FREQ'     => int # Tells Redis to set the keys internal 'FREQ' value to a specific
+                      # number (this relates to Redis' LFU eviction algorithm).
+];
-

Return Value

+

Return Value

- +
+
- - + +
Redis|int

Returns 1 if the set was altered, and zero if not.

Redis|bool

True if the key was stored, false if not.

+

See also

@@ -10726,52 +16241,68 @@

See also

+ + + + + + + +
- https://redis.io/commands/pfadd + https://redis.io/docs/latest/commands/restore/ +
+ https://redis.io/docs/latest/commands/dump/ +
+ +Redis::dump
+

Examples

+ + + + + +
$redis->sAdd('captains', 'Janeway', 'Picard', 'Sisko', 'Kirk', 'Archer');
+$serialized = $redis->dump('captains');
+
+$redis->restore('captains-backup', 0, $serialized);
+
-

- - Redis|int - pfcount(string $key) - + +

+ + mixed role()

-

Retrieve the cardinality of a Redis HyperLogLog key.

+

Query whether the connected instance is a primary or replica

-

Parameters

- - - - - - - -
string$key

The key name we wish to query.

- -

Return Value

+

Return Value

- +
+
- - + +
Redis|int

The estimated cardinality of the set.

mixed

Will return an array with the role of the connected instance unless there is +an error.

+

See also

@@ -10779,30 +16310,38 @@

See also

- https://redis.io/commands/pfcount + https://redis.io/docs/latest/commands/role/
+

Examples

+ + + + + +
$redis->role();
+
-

- - Redis|bool - pfmerge(string $dst, array $srckeys) - + +

+ + Redis|string|false rpoplpush(string $srckey, string $dstkey)

-

Merge one or more source HyperLogLog sets into a destination set.

+

Atomically pop an element off the end of a Redis LIST and push it to the beginning of +another.

Parameters

@@ -10810,26 +16349,28 @@

Parameters

- - + + - - - + + +
string$dst

The destination key.

$srckey

The source key to pop from.

array$srckeys

One or more source keys.

string$dstkey

The destination key to push to.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

Always returns true.

Redis|string|false

The popped element or false if the source key was empty.

+

See also

@@ -10837,30 +16378,43 @@

See also

- https://redis.io/commands/pfmerge + https://redis.io/docs/latest/commands/rpoplpush/
+

Examples

+ + + + + +
$redis->pipeline()
+      ->del('list1', 'list2')
+      ->rpush('list1', 'list1-1', 'list1-2')
+      ->rpush('list2', 'list2-1', 'list2-2')
+      ->exec();
+
+$redis->rpoplpush('list2', 'list1');
+
-

- - Redis|string|bool - ping(string $message = NULL) - + +

+ + Redis|int|false sAdd(string $key, mixed $value, mixed ...$other_values)

-

PING the redis server with an optional string argument.

+

Add one or more values to a Redis SET key.

Parameters

@@ -10868,22 +16422,33 @@

Parameters

- - + + + + + + + + + + + +
string$message

An optional string message that Redis will reply with, if passed.

$key

The key name

mixed$value

A value to add to the set.

mixed...$other_values

One or more additional values to add

-

Return Value

+

Return Value

- +
+
- - + +
Redis|string|bool

If passed no message, this command will simply return true. -If a message is passed, it will return the message.

Redis|int|false

The number of values added to the set.

+

See also

@@ -10891,7 +16456,7 @@

See also

@@ -10902,10 +16467,10 @@

Examples

- https://redis.io/commands/ping + https://redis.io/docs/latest/commands/sadd/
- - - - +
$redis->ping();
$redis->ping('beep boop');
$redis->del('myset');
+
+$redis->sadd('myset', 'foo', 'bar', 'baz');
+$redis->sadd('myset', 'foo', 'new');
@@ -10914,73 +16479,95 @@

Examples

-

- - bool|Redis - pipeline() - + +

+ + int sAddArray(string $key, array $values)

-

Enter into pipeline mode.

Pipeline mode is the highest performance way to send many commands to Redis -as they are aggregated into one stream of commands and then all sent at once -when the user calls Redis::exec().

-

NOTE: That this is shorthand for Redis::multi(Redis::PIPELINE)

+

Add one or more values to a Redis SET key. This is an alternative to Redis::sadd() but +instead of being variadic, takes a single array of values.

- -

Return Value

+

Parameters

+ + + + + + + + + + +
string$key

The set to add values to.

array$values

One or more members to add to the set.

+ + +

Return Value

+ +
+ - - + +
bool|Redis

The redis object is returned, to facilitate method chaining.

int

The number of members added to the set.

+
+

See also

+ + + + + + + + + + +
+ https://redis.io/docs/latest/commands/sadd/ +
+ \Redis::sadd() +
+

Examples

- +
$redis->pipeline()
-->set('foo', 'bar')
-->del('mylist')
-->rpush('mylist', 'a', 'b', 'c')
-->exec();
$redis->del('myset');
+
+$redis->sAddArray('myset', ['foo', 'bar', 'baz']);
+$redis->sAddArray('myset', ['foo', 'new']);
-
- -
-
-

- - bool - popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL) - deprecated -

-
-

- deprecated - - - - -

+
+ +
+
+ +

+ + Redis|array|false sDiff(string $key, string ...$other_keys) +

+
-

No description

- +

Given one or more Redis SETS, this command returns all of the members from the first +set that are not in any subsequent set.

Parameters

@@ -10988,71 +16575,75 @@

Parameters

- - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - + +
string$host
int$port
float$timeout$key

The first set

string$persistent_id
int$retry_interval
float$read_timeout
array$context...$other_keys

One or more additional sets

-

Return Value

+

Return Value

- +
+
- - + +
boolRedis|array|false

Returns the elements from keys 2..N that don't exist in the +first sorted set, or false on failure.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/sdiff/ +
+ +

Examples

+ + + + + +
$redis->pipeline()
+      ->del('set1', 'set2', 'set3')
+      ->sadd('set1', 'apple', 'banana', 'carrot', 'date')
+      ->sadd('set2', 'carrot')
+      ->sadd('set3', 'apple', 'carrot', 'eggplant')
+      ->exec();
+
+$redis->sdiff('set1', 'set2', 'set3');
+
-

- - Redis|bool - psetex(string $key, int $expire, mixed $value) - + +

+ + Redis|int|false sDiffStore(string $dst, string $key, string ...$other_keys)

-

Set a key with an expiration time in milliseconds

+

This method performs the same operation as SDIFF except it stores the resulting diff +values in a specified destination key.

Parameters

@@ -11060,39 +16651,58 @@

Parameters

- - + + - - - + + + - - - + + +
string$key

The key to set

$dst

The key where to store the result

int$expire

The TTL to set, in milliseconds.

string$key

The first key to perform the DIFF on

mixed$value

The value to set the key to.

string...$other_keys

One or more additional keys.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True if the key could be set.

Redis|int|false

The number of values stored in the destination set or false on failure.

+
+

See also

+ + + + + + + + + + +
+ https://redis.io/docs/latest/commands/sdiffstore/ +
+ \Redis::sdiff() +
+

Examples

- +
$redis->psetex('mykey', 1000, 'myval');
$redis->sDiffStore('diff:set', 'set:all', 'set:archived');
@@ -11101,46 +16711,47 @@

Examples

-

- - bool - psubscribe(array $patterns, callable $cb) - + +

+ + Redis|array|false sInter(array|string $key, string ...$other_keys)

-

Subscribe to one or more glob-style patterns

+

Given one or more Redis SET keys, this command will return all of the elements that are +in every one.

Parameters

- - - + + + - - - + + +
array$patterns

One or more patterns to subscribe to.

array|string$key

The first SET key to intersect.

callable$cb

A callback with the following prototype:

-
function ($redis, $channel, $message) { }
string...$other_keys

One or more Redis SET keys.

-

Return Value

+

Return Value

- +
+
- - + +
bool

True if we were subscribed.

Redis|array|false
+

See also

@@ -11148,54 +16759,74 @@

See also

- https://redis.io/commands/psubscribe + https://redis.io/docs/latest/commands/sinter/
+

Examples

+ + + + + +
$redis->pipeline()
+      ->del('alice_likes', 'bob_likes', 'bill_likes')
+      ->sadd('alice_likes', 'asparagus', 'broccoli', 'carrot', 'potato')
+      ->sadd('bob_likes', 'asparagus', 'carrot', 'potato')
+      ->sadd('bill_likes', 'broccoli', 'potato')
+      ->exec();
+
+var_dump($redis->sinter('alice_likes', 'bob_likes', 'bill_likes'));
+
-

- - Redis|int|false - pttl(string $key) - + +

+ + Redis|int|false sintercard(array $keys, int $limit = -1)

-

Get a keys time to live in milliseconds.

+

Compute the intersection of one or more sets and return the cardinality of the result.

Parameters

- - - + + + + + + + +
string$key

The key to check.

array$keys

One or more set key names.

int$limit

A maximum cardinality to return. This is useful to put an upper bound +on the amount of work Redis will do.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

The key's TTL or one of two special values if it has none.

-
-1 - The key has no TTL.
--2 - The key did not exist.
The
+

See also

@@ -11203,7 +16834,7 @@

See also

@@ -11214,7 +16845,11 @@

Examples

- https://redis.io/commands/pttl + https://redis.io/docs/latest/commands/sintercard/
- +
$redis->pttl('ttl-key');
$redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot');
+$redis->sAdd('set2', 'apple',         'banana');
+$redis->sAdd('set3',          'pear', 'banana');
+
+$redis->sInterCard(['set1', 'set2', 'set3']);
@@ -11223,45 +16858,50 @@

Examples

-

- - Redis|int|false - publish(string $channel, string $message) - + +

+ + Redis|int|false sInterStore(array|string $key, string ...$other_keys)

-

Publish a message to a pubsub channel

+

Perform the intersection of one or more Redis SETs, storing the result in a destination +key, rather than returning them.

Parameters

- - - + + + - - + +
string$channel

The channel to publish to.

array|string$key

Either a string key, or an array of keys (with at least two +elements, consisting of the destination key name and one +or more source keys names.

string$message

The message itself.

...$other_keys

If the first argument was a string, subsequent arguments should +be source key names.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

The number of subscribed clients to the given channel.

The number of values stored in the destination key or false on failure.

+

See also

@@ -11269,31 +16909,44 @@

See also

+ + + +
- https://redis.io/commands/publish + https://redis.io/docs/latest/commands/sinterstore/ +
+ \Redis::sinter()
+

Examples

+ + + + + +
$redis->sInterStore(['dst', 'src1', 'src2', 'src3']);
+$redis->sInterStore('dst', 'src1', 'src'2', 'src3');
+
-

- - mixed - pubsub(string $command, mixed $arg = null) - + +

+ + Redis|array|false sMembers(string $key)

-

No description

- +

Retrieve every member from a set key.

Parameters

@@ -11301,68 +16954,97 @@

Parameters

- - - - - - - + +
string$command
mixed$arg$key

The set name.

-

Return Value

+

Return Value

- +
+
- - + +
mixedRedis|array|false

Every element in the set or false on failure.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/smembers/ +
+ +

Examples

+ + + + + +
$redis->sAdd('tng-crew', ...['Picard', 'Riker', 'Data', 'Worf', 'La Forge', 'Troi', 'Crusher', 'Broccoli']);
+$redis->sMembers('tng-crew');
+
-

- - Redis|array|bool - punsubscribe(array $patterns) - + +

+ + Redis|array|false sMisMember(string $key, string $member, string ...$other_members)

-

Unsubscribe from one or more channels by pattern

+

Check if one or more values are members of a set.

Parameters

- - - + + + + + + + + + + + + +
array$patterns

One or more glob-style patterns of channel names.

string$key

The set to query.

string$member

The first value to test if exists in the set.

string...$other_members

Any number of additional values to check.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|bool

The array of subscribed patterns or false on failure.

Redis|array|false

An array of integers representing whether each passed value +was a member of the set.

+

See also

@@ -11370,43 +17052,51 @@

See also

-
- https://redis.io/commands/punsubscribe + https://redis.io/docs/latest/commands/smismember/
- https://redis.io/commands/subscribe + https://redis.io/docs/latest/commands/smember/
- -Redis::subscribe + + \Redis::smember()
+

Examples

+ + + + + +
$redis->sAdd('ds9-crew', ...["Sisko", "Kira", "Dax", "Worf", "Bashir", "O'Brien"]);
+$members = $redis->sMIsMember('ds9-crew', ...['Sisko', 'Picard', 'Data', 'Worf']);
+
-

- - Redis|array|string|bool - rPop(string $key, int $count = 0) - + +

+ + Redis|bool sMove(string $src, string $dst, mixed $value)

-

Pop one or more elements from the end of a list.

+

Pop a member from one set and push it onto another. This command will create the +destination set if it does not currently exist.

Parameters

@@ -11414,27 +17104,33 @@

Parameters

- - + + - - - + + + + + + + +
string$key

A redis LIST key name.

$src

The source set.

int$count

The maximum number of elements to pop at once. -NOTE: The count argument requires Redis >= 6.2.0

string$dst

The destination set.

mixed$value

The member you wish to move.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|string|bool

One ore more popped elements or false if all were empty.

Redis|bool

True if the member was moved, and false if it wasn't in the set.

+

See also

@@ -11442,7 +17138,7 @@

See also

@@ -11453,10 +17149,10 @@

Examples

- https://redis.io/commands/rpop + https://redis.io/docs/latest/commands/smove/
- - - - +
$redis->rPop('mylist');
$redis->rPop('mylist', 4);
$redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
+$redis->sMove('numbers', 'evens', 'zero');
+$redis->sMove('numbers', 'evens', 'two');
+$redis->sMove('numbers', 'evens', 'four');
@@ -11465,30 +17161,47 @@

Examples

-

- - Redis|string|false - randomKey() - + +

+ + Redis|string|array|false sPop(string $key, int $count = 0)

-

Return a random key from the current database

+

Remove one or more elements from a set.

- -

Return Value

+

Parameters

+ + + + + + + + + + +
string$key

The set in question.

int$count

An optional number of members to pop. This defaults to +removing one element.

+ + +

Return Value

+ +
+ - - + +
Redis|string|false

A random key name or false if no keys exist

Redis|string|array|false
+

See also

@@ -11496,30 +17209,39 @@

See also

- https://redis.io/commands/randomkey + https://redis.io/docs/latest/commands/spop/
+

Examples

+ + + + + +
$redis->del('numbers', 'evens');
+$redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
+$redis->sPop('numbers');
+
-

- - mixed - rawcommand(string $command, mixed ...$args) - + +

+ + mixed sRandMember(string $key, int $count = 0)

-

Execute any arbitrary Redis command by name.

+

Retrieve one or more random members of a set.

Parameters

@@ -11527,40 +17249,58 @@

Parameters

- - + + - - - + + +
string$command

The command to execute

$key

The set to query.

mixed...$args

One or more arguments to pass to the command.

int$count

An optional count of members to return.

+

If this value is positive, Redis will return up to the requested +number but with unique elements that will never repeat. This means +you may receive fewer then $count replies.

+

If the number is negative, Redis will return the exact number requested +but the result may contain duplicate elements.

-

Return Value

+

Return Value

- +
+
- +
mixed

Can return any number of things depending on command executed.

One or more random members or false on failure.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/srandmember/ +
+

Examples

- + - + - +
$redis->rawCommand('del', 'mystring', 'mylist');
$redis->sRandMember('myset');
$redis->rawCommand('set', 'mystring', 'myvalue');
$redis->sRandMember('myset', 10);
$redis->rawCommand('rpush', 'mylist', 'one', 'two', 'three');
$redis->sRandMember('myset', -10);
@@ -11569,18 +17309,17 @@

Examples

-

- - Redis|bool - rename(string $old_name, string $new_name) - + +

+ + Redis|array|false sUnion(string $key, string ...$other_keys)

-

Unconditionally rename a key from $old_name to $new_name

+

Returns the union of one or more Redis SET keys.

Parameters

@@ -11588,26 +17327,28 @@

Parameters

- - + + - - + +
string$old_name

The original name of the key

$key

The first SET to do a union with

string$new_name

The new name for the key

...$other_keys

One or more subsequent keys

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True if the key was renamed or false if not.

Redis|array|false

The union of the one or more input sets or false on failure.

+

See also

@@ -11615,30 +17356,37 @@

See also

- https://redis.io/commands/rename + https://redis.io/docs/latest/commands/sunion/
+

Examples

+ + + + + +
$redis->sunion('set1', 'set2');
+
-

- - Redis|bool - renameNx(string $key_src, string $key_dst) - + +

+ + Redis|int|false sUnionStore(string $dst, string $key, string ...$other_keys)

-

Renames $key_src to $key_dst but only if newkey does not exist.

+

Perform a union of one or more Redis SET keys and store the result in a new set

Parameters

@@ -11646,26 +17394,34 @@

Parameters

- - + + - - + + + + + + +
string$key_src

The source key name

$dst

The destination key

string$key_dst

The destination key name.

$key

The first source key

string...$other_keys

One or more additional source keys

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True if the key was renamed, false if not.

Redis|int|false

The number of elements stored in the destination SET or +false on failure.

+

See also

@@ -11673,7 +17429,13 @@

See also

+ + + + @@ -11684,11 +17446,7 @@

Examples

- https://redis.io/commands/renamenx + https://redis.io/docs/latest/commands/sunionstore/ +
+ \Redis::sunion()
- +
$redis->set('src', 'src_key');
-$redis->set('existing-dst', 'i_exist');
-
-$redis->renamenx('src', 'dst');
-$redis->renamenx('dst', 'existing-dst');
$redis->sUnionStore('union:set', 'set:a', 'set:b');
@@ -11697,121 +17455,146 @@

Examples

-

- - Redis|bool - reset() - + +

+ + Redis|bool save()

-

Reset the state of the connection.

+

Persist the Redis database to disk. This command will block the server until the save is +completed. For a nonblocking alternative, see Redis::bgsave().

-

Return Value

+

Return Value

- +
+
- +
Redis|bool

Should always return true unless there is an error.

Returns true unless an error occurs.

+
+

See also

+ + + + + + + + + + +
+ https://redis.io/docs/latest/commands/save/ +
+ \Redis::bgsave() +
+ +

Examples

+ + + + + +
$redis->save();
+
-

- - Redis|bool - restore(string $key, int $ttl, string $value, array|null $options = NULL) - + +

+ + array|false scan(null|int|string $iterator, string|null $pattern = null, int $count = 0, string|null $type = null)

-

Restore a key by the binary payload generated by the DUMP command.

+

Incrementally scan the Redis keyspace, with optional pattern and type matching.

A note about Redis::SCAN_NORETRY and Redis::SCAN_RETRY.

+

For convenience, PhpRedis can retry SCAN commands itself when Redis returns an empty array of +keys with a nonzero iterator. This can happen when matching against a pattern that very few +keys match inside a key space with a great many keys. The following example demonstrates how +to use Redis::scan() with the option disabled and enabled.

Parameters

- - - + + + - - - + + + - - - + + + - - - + + +
string$key

The name of the key you wish to create.

null|int|string$iterator

The cursor returned by Redis for every subsequent call to SCAN. On +the initial invocation of the call, it should be initialized by the +caller to NULL. Each time SCAN is invoked, the iterator will be +updated to a new number, until finally Redis will set the value to +zero, indicating that the scan is complete.

int$ttl

What Redis should set the key's TTL (in milliseconds) to once it is created. -Zero means no TTL at all.

string|null$pattern

An optional glob-style pattern for matching key names. If passed as +NULL, it is the equivalent of sending '*' (match every key).

string$value

The serialized binary value of the string (generated by DUMP).

int$count

A hint to redis that tells it how many keys to return in a single +call to SCAN. The larger the number, the longer Redis may block +clients while iterating the key space.

array|null$options

An array of additional options that modifies how the command operates.

-
$options = [
-    'ABSTTL'          # If this is present, the `$ttl` provided by the user should
-                      # be an absolute timestamp, in milliseconds()
-
-    'REPLACE'         # This flag instructs Redis to store the key even if a key with
-                      # that name already exists.
-
-    'IDLETIME' => int # Tells Redis to set the keys internal 'idletime' value to a
-                      # specific number (see the Redis command OBJECT for more info).
-    'FREQ'     => int # Tells Redis to set the keys internal 'FREQ' value to a specific
-                      # number (this relates to Redis' LFU eviction algorithm).
-];
string|null$type

An optional argument to specify which key types to scan (e.g. +'STRING', 'LIST', 'SET')

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True if the key was stored, false if not.

array|false

An array of keys, or false if no keys were returned for this +invocation of scan. Note that it is possible for Redis to return +zero keys before having scanned the entire key space, so the caller +should instead continue to SCAN until the iterator reference is +returned to zero.

+

See also

- - - - - - + +
- https://redis.io/commands/restore -
@@ -11822,10 +17605,30 @@

Examples

- https://redis.io/commands/dump + https://redis.io/docs/latest/commands/scan/
- -Redis::dump + +Redis::setOption
- +
$redis->sAdd('captains', 'Janeway', 'Picard', 'Sisko', 'Kirk', 'Archer');
-$serialized = $redis->dump('captains');
-
-$redis->restore('captains-backup', 0, $serialized);
$redis = new Redis(['host' => 'localhost']);
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
+
+$it = null;
+
+do {
+    $keys = $redis->scan($it, '*zorg*');
+    foreach ($keys as $key) {
+        echo "KEY: $key\n";
+    }
+} while ($it != 0);
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+
+$it = null;
+
+// When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an
+// empty array of keys when the iterator is nonzero.
+while ($keys = $redis->scan($it, '*zorg*')) {
+    foreach ($keys as $key) {
+        echo "KEY: $key\n";
+    }
+}
@@ -11834,52 +17637,79 @@

Examples

-

- - mixed - role() - + +

+ + Redis|int|false scard(string $key)

-

Query whether the connected instance is a primary or replica

+

Retrieve the number of members in a Redis set.

- -

Return Value

+

Parameters

+ + + + + +
string$key

The set to get the cardinality of.

+ + +

Return Value

+ +
+ - - + +
mixed

Will return an array with the role of the connected instance unless there is -an error.

Redis|int|false

The cardinality of the set or false on failure.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/scard/ +
+ +

Examples

+ + + + + +
$redis->scard('set');
+
-

- - Redis|string|false - rpoplpush(string $srckey, string $dstkey) - + +

+ + mixed script(string $command, mixed ...$args)

-

Atomically pop an element off the end of a Redis LIST and push it to the beginning of -another.

+

An administrative command used to interact with LUA scripts stored on the server.

Parameters

@@ -11887,26 +17717,28 @@

Parameters

- - + + - - - + + +
string$srckey

The source key to pop from.

$command

The script suboperation to execute.

string$dstkey

The destination key to push to.

mixed...$args

One or more additional argument

-

Return Value

+

Return Value

- +
+
- - + +
Redis|string|false

The popped element or false if the source key was empty.

mixed

This command returns various things depending on the specific operation executed.

+

See also

@@ -11914,7 +17746,7 @@

See also

@@ -11925,13 +17757,10 @@

Examples

- https://redis.io/commands/rpoplpush + https://redis.io/docs/latest/commands/script/
- + + + +
$redis->pipeline()
- ->del('list1', 'list2')
- ->rpush('list1', 'list1-1', 'list1-2')
- ->rpush('list2', 'list2-1', 'list2-2')
- ->exec();
-
-$redis->rpoplpush('list2', 'list1');
$redis->script('load', 'return 1');
$redis->script('exists', sha1('return 1'));
@@ -11940,50 +17769,41 @@

Examples

-

- - Redis|int|false - sAdd(string $key, mixed $value, mixed ...$other_values) - + +

+ + Redis|bool select(int $db)

-

Add one or more values to a Redis SET key.

+

Select a specific Redis database.

Parameters

- - - - - - - - - - - - - + + +
string$key

The key name

mixed$value
mixed...$other_valuesint$db

The database to select. Note that by default Redis has 16 databases (0-15).

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The number of values added to the set.

Redis|bool

true on success and false on failure

+

See also

@@ -11991,7 +17811,7 @@

See also

@@ -12002,10 +17822,7 @@

Examples

- https://redis.io/commands/sadd + https://redis.io/docs/latest/commands/select/
- +
$redis->del('myset');
-
-$redis->sadd('myset', 'foo', 'bar', 'baz');
-$redis->sadd('myset', 'foo', 'new');
$redis->select(1);
@@ -12014,19 +17831,17 @@

Examples

-

- - int - sAddArray(string $key, array $values) - + +

+ + Redis|string|bool set(string $key, mixed $value, mixed $options = null)

-

Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but -instead of being variadic, takes a single array of values.

+

Create or set a Redis STRING key to a value.

Parameters

@@ -12035,25 +17850,45 @@

Parameters

string $key -

The set to add values to.

+

The key name to set.

- array - $values -

One or more members to add to the set.

+ mixed + $value +

The value to set the key to.

+ + + mixed + $options +

Either an array with options for how to perform the set or an +integer with an expiration. If an expiration is set PhpRedis +will actually send the SETEX command.

+

OPTION DESCRIPTION

+
+

['EX' => 60] expire 60 seconds. +['PX' => 6000] expire in 6000 milliseconds. +['EXAT' => time() + 10] expire in 10 seconds. +['PXAT' => time()*1000 + 1000] expire in 1 second. +['KEEPTTL' => true] Redis will not update the key's current TTL. +['XX'] Only set the key if it already exists. +['NX'] Only set the key if it doesn't exist. +['GET'] Instead of returning +OK return the previous value of the +key or NULL if the key didn't exist.

-

Return Value

+

Return Value

- +
+
- - + +
int

The number of members added to the set.

Redis|string|bool

True if the key was set or false on failure.

+

See also

@@ -12061,13 +17896,13 @@

See also

@@ -12078,10 +17913,10 @@

Examples

- https://redis.io/commands/sadd + https://redis.io/docs/latest/commands/set/
- \Redis::sadd() + https://redis.io/docs/latest/commands/setex/
- + + + +
$redis->del('myset');
-
-$redis->sAddArray('myset', ['foo', 'bar', 'baz']);
-$redis->sAddArray('myset', ['foo', 'new']);
$redis->set('key', 'value');
$redis->set('key', 'expires_in_60_seconds', 60);
@@ -12090,19 +17925,17 @@

Examples

-

- - Redis|array|false - sDiff(string $key, string ...$other_keys) - + +

+ + Redis|int|false setBit(string $key, int $idx, bool $value)

-

Given one or more Redis SETS, this command returns all of the members from the first -set that are not in any subsequent set.

+

Set a specific bit in a Redis string to zero or one

Parameters

@@ -12111,26 +17944,32 @@

Parameters

string $key -

The first set

+

The Redis STRING key to modify

- string - ...$other_keys -

One or more additional sets

+ int + $idx + + + + bool + $value +

Whether to set the bit to zero or one.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|false

Returns the elements from keys 2..N that don't exist in the -first sorted set, or false on failure.

Redis|int|false

The original value of the bit or false on failure.

+

See also

@@ -12138,7 +17977,7 @@

See also

@@ -12149,14 +17988,8 @@

Examples

- https://redis.io/commands/sdiff + https://redis.io/docs/latest/commands/setbit/
- +
$redis->pipeline()
- ->del('set1', 'set2', 'set3')
- ->sadd('set1', 'apple', 'banana', 'carrot', 'date')
- ->sadd('set2', 'carrot')
- ->sadd('set3', 'apple', 'carrot', 'eggplant')
- ->exec();
-
-$redis->sdiff('set1', 'set2', 'set3');
$redis->set('foo', 'bar');
+$redis->setbit('foo', 7, 1);
@@ -12165,19 +17998,17 @@

Examples

-

- - Redis|int|false - sDiffStore(string $dst, string $key, string ...$other_keys) - + +

+ + Redis|int|false setRange(string $key, int $index, string $value)

-

This method performs the same operation as SDIFF except it stores the resulting diff -values in a specified destination key.

+

Update or append to a Redis string at a specific starting index

Parameters

@@ -12185,31 +18016,33 @@

Parameters

- - + + - - - + + + - - + +
string$dst

The key where to store the result

$key

The key to update

string$key

The first key to perform the DIFF on

int$index

Where to insert the provided value

string...$other_keys

One or more additional keys.

$value

The value to copy into the string.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

The number of values stored in the destination set or false on failure.

The new length of the string or false on failure

+

See also

@@ -12217,64 +18050,143 @@

See also

+
- https://redis.io/commands/sdiffstore + https://redis.io/docs/latest/commands/setrange/
+ + +

Examples

+ + - - +
- \Redis::sdiff() -
$redis->set('message', 'Hello World');
+$redis->setRange('message', 6, 'Redis');
-
-

- - Redis|array|false - sInter(array|string $key, string ...$other_keys) - + +

+ + bool setOption(int $option, mixed $value)

-

Given one or more Redis SET keys, this command will return all of the elements that are -in every one.

+

Set a configurable option on the Redis object.

Following are a list of options you can set:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OPTIONTYPEDESCRIPTION
OPT_MAX_RETRIESintThe maximum number of times Redis will attempt to reconnect if it gets disconnected, before throwing an exception.
OPT_SCANenumRedis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY. Whether PhpRedis should automatically SCAN again when zero keys but a nonzero iterator are returned.
OPT_SERIALIZERenumSet the automatic data serializer.
Redis::SERIALIZER_NONE
Redis::SERIALIZER_PHP
Redis::SERIALIZER_IGBINARY
Redis::SERIALIZER_MSGPACK, Redis::SERIALIZER_JSON
OPT_PREFIXstringA string PhpRedis will use to prefix every key we read or write.
OPT_READ_TIMEOUTfloatHow long PhpRedis will block for a response from Redis before throwing a 'read error on connection' exception.
OPT_TCP_KEEPALIVEboolSet or disable TCP_KEEPALIVE on the connection.
OPT_COMPRESSIONenumSet the compression algorithm
Redis::COMPRESSION_NONE
Redis::COMPRESSION_LZF
Redis::COMPRESSION_LZ4
Redis::COMPRESSION_ZSTD
OPT_REPLY_LITERALboolIf set to true, PhpRedis will return the literal string Redis returns for LINE replies (e.g. '+OK'), rather than true.
OPT_COMPRESSION_LEVELintSet a specific compression level if Redis is compressing data.
OPT_NULL_MULTIBULK_AS_NULLboolCauses PhpRedis to return NULL rather than false for NULL MULTIBULK replies
OPT_BACKOFF_ALGORITHMenumThe exponential backoff strategy to use.
OPT_BACKOFF_BASEintThe minimum delay between retries when backing off.
OPT_BACKOFF_CAPintThe maximum delay between replies when backing off.

Parameters

- - - + + + - - - + + +
array|string$key

The first SET key to intersect.

int$option

The option constant.

string...$other_keys

One or more Redis SET keys.

mixed$value

The option value.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|falsebool

true if the setting was updated, false if not.

+

See also

@@ -12282,9 +18194,17 @@

See also

+ + + +
- https://redis.io/commands/sinter + +Redis::getOption
+ +Redis::__construct + for details about backoff strategies.
@@ -12293,15 +18213,7 @@

Examples

- +
$redis->pipeline()
- ->del('alice_likes', 'bob_likes', 'bill_likes')
- ->sadd('alice_likes', 'asparagus', 'broccoli', 'carrot', 'potato')
- ->sadd('bob_likes', 'asparagus', 'carrot', 'potato')
- ->sadd('bill_likes', 'broccoli', 'potato')
- ->exec();
-
-var_dump($redis->sinter('alice_likes', 'bob_likes', 'bill_likes'));
-</code>
$redis->setOption(Redis::OPT_PREFIX, 'app:');
@@ -12310,46 +18222,51 @@

Examples

-

- - Redis|int|false - sintercard(array $keys, int $limit = -1) - + +

+ + Redis|bool setex(string $key, int $expire, mixed $value)

-

Compute the intersection of one or more sets and return the cardinality of the result.

+

Set a Redis STRING key with a specific expiration in seconds.

Parameters

- - - + + + - - + + + + + + +
array$keys

One or more set key names.

string$key

The name of the key to set.

int$limit

A maximum cardinality to return. This is useful to put an upper bound -on the amount of work Redis will do.

$expire

The key's expiration in seconds.

mixed$value

The value to set the key.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|falseTheRedis|bool

True on success or false on failure.

+

See also

@@ -12357,7 +18274,7 @@

See also

@@ -12368,13 +18285,7 @@

Examples

- https://redis.io/commands/sintercard + https://redis.io/docs/latest/commands/setex/
- +
$redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot');
-$redis->sAdd('set2', 'apple', 'banana');
-$redis->sAdd('set3', 'pear', 'banana');
-
-$redis->sInterCard(['set1', 'set2', 'set3']);
-?>
-</code>
$redis->setex('60s-ttl', 60, 'some-value');
@@ -12383,47 +18294,46 @@

Examples

-

- - Redis|int|false - sInterStore(array|string $key, string ...$other_keys) - + +

+ + Redis|bool setnx(string $key, mixed $value)

-

Perform the intersection of one or more Redis SETs, storing the result in a destination -key, rather than returning them.

+

Set a key to a value, but only if that key does not already exist.

Parameters

- + - + - - - + + +
array|stringstring $key

The key name to set.

string...$other_keys

If the first argument was a string, subsequent arguments should -be source key names.

mixed$value

What to set the key to.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The number of values stored in the destination key or false on failure.

Redis|bool

Returns true if the key was set and false otherwise.

+

See also

@@ -12431,13 +18341,7 @@

See also

- - - - @@ -12448,12 +18352,10 @@

Examples

- https://redis.io/commands/sinterstore -
- \Redis::sinter() + https://redis.io/docs/latest/commands/setnx/
- + - +
$redis->sInterStore(['dst', 'src1', 'src2', 'src3']);
$redis->setnx('existing-key', 'existing-value');
$redis->sInterStore('dst', 'src1', 'src'2', 'src3');
-?>
-</code>
$redis->setnx('new-key', 'new-value');
@@ -12462,18 +18364,17 @@

Examples

-

- - Redis|array|false - sMembers(string $key) - + +

+ + Redis|bool sismember(string $key, mixed $value)

-

Retrieve every member from a set key.

+

Check whether a given value is the member of a Redis SET.

Parameters

@@ -12482,20 +18383,27 @@

Parameters

string $key -

The set name.

+

The redis set to check.

+ + + mixed + $value +

The value to test.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|false

Every element in the set or false on failure.

Redis|bool

True if the member exists and false if not.

+

See also

@@ -12503,7 +18411,7 @@

See also

@@ -12514,8 +18422,7 @@

Examples

- https://redis.io/commands/smembers + https://redis.io/docs/latest/commands/sismember/
- +
$redis->sAdd('tng-crew', ...['Picard', 'Riker', 'Data', 'Worf', 'La Forge', 'Troi', 'Crusher', 'Broccoli']);
-$redis->sMembers('tng-crew');
$redis->sismember('myset', 'mem1', 'mem2');
@@ -12524,51 +18431,58 @@

Examples

-

- - Redis|array|false - sMisMember(string $key, string $member, string ...$other_members) - + +

+ + Redis|bool slaveof(string|null $host = null, int $port = 6379) deprecated

+

+ deprecated + + + + +

-

Check if one or more values are members of a set.

+

Turn a redis instance into a replica of another or promote a replica +to a primary.

This method and the corresponding command in Redis has been marked deprecated +and users should instead use Redis::replicaof() if connecting to redis-server

+
+

= 5.0.0.

+

Parameters

- - - - - - - - + + + - - - + + +
string$key

The set to query.

string$member

The first value to test if exists in the set.

string|null$host
string...$other_members

Any number of additional values to check.

int$port
-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|false

An array of integers representing whether each passed value -was a member of the set.

Redis|bool
+

See also

@@ -12576,19 +18490,20 @@

See also

@@ -12599,8 +18514,7 @@

Examples

- https://redis.io/commands/smismember + https://redis.io/docs/latest/commands/slaveof/
- https://redis.io/commands/smember + https://redis.io/docs/latest/commands/replicaof/
- \Redis::smember() + +Redis::replicaof
- +
$redis->sAdd('ds9-crew', ...["Sisko", "Kira", "Dax", "Worf", "Bashir", "O'Brien"]);
-$members = $redis->sMIsMember('ds9-crew', ...['Sisko', 'Picard', 'Data', 'Worf']);
$redis->slaveof('10.0.0.5', 6380);
@@ -12609,51 +18523,48 @@

Examples

-

- - Redis|bool - sMove(string $src, string $dst, mixed $value) - + +

+ + Redis|bool replicaof(string|null $host = null, int $port = 6379)

-

Pop a member from one set and push it onto another. This command will create the -destination set if it does not currently exist.

+

Used to turn a Redis instance into a replica of another, or to remove +replica status promoting the instance to a primary.

Parameters

- - - - - - - - + + + - - - + + +
string$src

The source set.

string$dst

The destination set.

string|null$host

The host of the primary to start replicating.

mixed$value

The member you wish to move.

int$port

The port of the primary to start replicating.

-

Return Value

+

Return Value

- +
+
- +
Redis|bool

True if the member was moved, and false if it wasn't in the set.

Success if we were successfully able to start replicating a primary or +were able to promote the replicat to a primary.

+

See also

@@ -12661,7 +18572,20 @@

See also

+ + + + + + + + @@ -12672,10 +18596,14 @@

Examples

- https://redis.io/commands/smove + https://redis.io/docs/latest/commands/replicaof/ +
+ https://redis.io/docs/latest/commands/slaveof/ +
+ +Redis::slaveof
- +
$redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
-$redis->sMove('numbers', 'evens', 'zero');
-$redis->sMove('numbers', 'evens', 'two');
-$redis->sMove('numbers', 'evens', 'four');
$redis = new Redis(['host' => 'localhost']);
+
+// Attempt to become a replica of a Redis instance at 127.0.0.1:9999
+$redis->replicaof('127.0.0.1', 9999);
+
+// When passed no arguments, PhpRedis will deliver the command `REPLICAOF NO ONE`
+// attempting to promote the instance to a primary.
+$redis->replicaof();
@@ -12684,46 +18612,48 @@

Examples

-

- - Redis|string|array|false - sPop(string $key, int $count = 0) - + +

+ + Redis|int|false touch(array|string $key_or_array, string ...$more_keys)

-

Remove one or more elements from a set.

+

Update one or more keys last modified metadata.

Parameters

- - - + + + - - - + + +
string$key

The set in question.

array|string$key_or_array

Either the first key or if passed as the only argument +an array of keys.

int$count

An optional number of members to pop. This defaults to -removing one element.

string...$more_keys

One or more keys to send to the command.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|string|array|falseRedis|int|false

This command returns the number of keys that exist and +had their last modified time reset

+

See also

@@ -12731,7 +18661,7 @@

See also

@@ -12742,9 +18672,7 @@

Examples

- https://redis.io/commands/spop + https://redis.io/docs/latest/commands/touch/
- +
$redis->del('numbers', 'evens');
-$redis->sAdd('numbers', 'zero', 'one', 'two', 'three', 'four');
-$redis->sPop('numbers');
$redis->touch('cache:1', 'cache:2');
@@ -12753,18 +18681,18 @@

Examples

-

- - Redis|string|array|false - sRandMember(string $key, int $count = 0) - + +

+ + mixed slowlog(string $operation, int $length = 0)

-

Retrieve one or more random members of a set.

+

Interact with Redis' slowlog functionality in various ways, depending +on the value of 'operation'.

Parameters

@@ -12772,31 +18700,36 @@

Parameters

- - + + - - + +
string$key

The set to query.

$operation

The operation you wish to perform.  This can +be one of the following values: +'GET' - Retrieve the Redis slowlog as an array. +'LEN' - Retrieve the length of the slowlog. +'RESET' - Remove all slowlog entries.

int$count

An optional count of members to return.

-

If this value is positive, Redis will return up to the requested -number but with unique elements that will never repeat. This means -you may recieve fewer then $count replies.

-

If the number is negative, Redis will return the exact number requested -but the result may contain duplicate elements.

$length

This optional argument can be passed when operation +is 'get' and will specify how many elements to retrieve. +If omitted Redis will send up to a default number of +entries, which is configurable.

+

Note: With Redis >= 7.0.0 you can send -1 to mean "all".

-

Return Value

+

Return Value

- +
+
- - + +
Redis|string|array|false

One or more random members or false on failure.

mixed
+

See also

@@ -12804,7 +18737,7 @@

See also

@@ -12815,13 +18748,13 @@

Examples

- https://redis.io/commands/srandmember + https://redis.io/docs/latest/commands/slowlog/
- + - + - +
$redis->sRandMember('myset');
$redis->slowlog('get', -1);   // Retrieve all slowlog entries.
$redis->sRandMember('myset', 10);
$redis->slowlog('len');       // Retrieve slowlog length.
$redis->sRandMember('myset', -10);
$redis->slowlog('reset');     // Reset the slowlog.
@@ -12830,18 +18763,17 @@

Examples

-

- - Redis|array|false - sUnion(string $key, string ...$other_keys) - + +

+ + mixed sort(string $key, array|null $options = null)

-

Returns the union of one or more Redis SET keys.

+

Sort the contents of a Redis key in various ways.

Parameters

@@ -12850,25 +18782,31 @@

Parameters

string $key -

The first SET to do a union with

+

The key you wish to sort

- string - ...$other_keys -

One or more subsequent keys

+ array|null + $options +

Various options controlling how you would like the +data sorted. See blow for a detailed description +of this options array.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|false

The union of the one or more input sets or false on failure.

mixed

This command can either return an array with the sorted data +or the number of elements placed in a destination set when +using the STORE option.

+

See also

@@ -12876,7 +18814,7 @@

See also

@@ -12887,7 +18825,17 @@

Examples

- https://redis.io/commands/sunion + https://redis.io/docs/latest/commands/sort/
- +
$redis->sunion('set1', 'set2');
$options = [
+    'SORT'  => 'ASC'|| 'DESC' // Sort in descending or descending order.
+    'ALPHA' => true || false  // Whether to sort alphanumerically.
+    'LIMIT' => [0, 10]        // Return a subset of the data at offset, count
+    'BY'    => 'weight_*'     // For each element in the key, read data from the
+                                 external key weight_* and sort based on that value.
+    'GET'   => 'weight_*'     // For each element in the source key, retrieve the
+                                 data from key weight_* and return that in the result
+                                 rather than the source keys' element.  This can
+                                 be used in combination with 'BY'
+];
@@ -12896,18 +18844,17 @@

Examples

-

- - Redis|int|false - sUnionStore(string $dst, string $key, string ...$other_keys) - + +

+ + mixed sort_ro(string $key, array|null $options = null)

-

Perform a union of one or more Redis SET keys and store the result in a new set

+

This is simply a read-only variant of the sort command

Parameters

@@ -12915,32 +18862,28 @@

Parameters

- - - - - - + - - - + + +
string$dst

The destination key

string $key

The first source key

string...$other_keys

One or more additional source keys

array|null$options
-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The number of elements stored in the destination SET or -false on failure.

mixed
+

See also

@@ -12948,49 +18891,101 @@

See also

- https://redis.io/commands/sunionstore + +Redis::sort
- \Redis::sunion() + https://redis.io/docs/latest/commands/sort_ro/
+

Examples

+ + + + + +
$redis->sort_ro('numbers', ['LIMIT' => [0, 5]]);
+
-

- - Redis|bool - save() - + +

+ + array sortAsc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated

+

+ deprecated + + + + +

-

Persist the Redis database to disk. This command will block the server until the save is -completed. For a nonblocking alternative, see Redis::bgsave().

+

No description

+
- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string|null$pattern
mixed$get
int$offset
int$count
string|null$store
+ + +

Return Value

+ +
+ - - + +
Redis|bool

Returns true unless an error occurs.

array
+

See also

@@ -12998,89 +18993,94 @@

See also

+
- https://redis.io/commands/save + https://redis.io/docs/latest/commands/sort/
+ + +

Examples

+ + - - +
- \Redis::bgsave() -
$redis->sortAsc('numbers');
-
-

- - array|false - scan(int|null $iterator, string|null $pattern = null, int $count = 0, string $type = NULL) - + +

+ + array sortAscAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated

+

+ deprecated + + + + +

-

Incrementally scan the Redis keyspace, with optional pattern and type matching.

A note about Redis::SCAN_NORETRY and Redis::SCAN_RETRY.

-

For convenience, PhpRedis can retry SCAN commands itself when Redis returns an empty array of -keys with a nonzero iterator. This can happen when matching against a pattern that very few -keys match inside a key space with a great many keys. The following example demonstrates how -to use Redis::scan() with the option disabled and enabled.

+

No description

+

Parameters

- - - + + + - + + + + + + + + + + + - + - - - + + +
int|null$iterator

The cursor returned by Redis for every subsequent call to SCAN. On -the initial invocation of the call, it should be initialized by the -caller to NULL. Each time SCAN is invoked, the iterator will be -updated to a new number, until finally Redis will set the value to -zero, indicating that the scan is complete.

string$key
string|null $pattern

An optional glob-style pattern for matching key names. If passed as -NULL, it is the equivalent of sending '*' (match every key).

mixed$get
int$offset
int $count

A hint to redis that tells it how many keys to return in a single -call to SCAN. The larger the number, the longer Redis may block -clients while iterating the key space.

string$type

An optional argument to specify which key types to scan (e.g. -'STRING', 'LIST', 'SET')

string|null$store
-

Return Value

+

Return Value

- +
+
- - + +
array|false

An array of keys, or false if no keys were returned for this -invocation of scan. Note that it is possible for Redis to return -zero keys before having scanned the entire key space, so the caller -should instead continue to SCAN until the iterator reference is -returned to zero.

array
+

See also

@@ -13088,14 +19088,7 @@

See also

- - - - @@ -13106,30 +19099,7 @@

Examples

- https://redis.io/commands/scan -
- -Redis::setOption + https://redis.io/docs/latest/commands/sort/
- +
$redis = new Redis(['host' => 'localhost']);
-
-$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
-
-$it = NULL;
-
-do {
- $keys = $redis->scan($it, '*zorg*');
- foreach ($keys as $key) {
- echo "KEY: $key\n";
- }
-} while ($it != 0);
-
-$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
-
-$it = NULL;
-
-// When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an
-// empty array of keys when the iterator is nonzero.
-while ($keys = $redis->scan($it, '*zorg*')) {
- foreach ($keys as $key) {
- echo "KEY: $key\n";
- }
-}
$redis->sortAscAlpha('tags');
@@ -13138,18 +19108,25 @@

Examples

-

- - Redis|int|false - scard(string $key) - + +

+ + array sortDesc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated

+

+ deprecated + + + + +

-

Retrieve the number of members in a Redis set.

+

No description

+

Parameters

@@ -13158,20 +19135,47 @@

Parameters

string $key -

The set to get the cardinality of.

+ + + + string|null + $pattern + + + + mixed + $get + + + + int + $offset + + + + int + $count + + + + string|null + $store + -

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The cardinality of the set or false on failure.

array
+

See also

@@ -13179,7 +19183,7 @@

See also

@@ -13190,8 +19194,7 @@

Examples

- https://redis.io/commands/scard + https://redis.io/docs/latest/commands/sort/
- +
$redis->scard('set');
-</code>
$redis->sortDesc('numbers');
@@ -13200,45 +19203,74 @@

Examples

-

- - mixed - script(string $command, mixed ...$args) - + +

+ + array sortDescAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) deprecated

+

+ deprecated + + + + +

-

An administrative command used to interact with LUA scripts stored on the server.

+

No description

+

Parameters

- - - + + + + + + + + + + + + + + + + + + + + + + + - - - + + +
string$command

The script suboperation to execute.

string$key
string|null$pattern
mixed$get
int$offset
int$count
mixed...$args

One ore more additional argument

string|null$store
-

Return Value

+

Return Value

- +
+
- - + +
mixed

This command returns various things depending on the specific operation executed.

array
+

See also

@@ -13246,7 +19278,7 @@

See also

@@ -13257,10 +19289,7 @@

Examples

- https://redis.io/commands/script + https://redis.io/docs/latest/commands/sort/
- - - - +
$redis->script('load', 'return 1');
$redis->script('exists', sha1('return 1'));
$redis->sortDescAlpha('tags');
@@ -13269,40 +19298,51 @@

Examples

-

- - Redis|bool - select(int $db) - + +

+ + Redis|int|false srem(string $key, mixed $value, mixed ...$other_values)

-

Select a specific Redis database.

+

Remove one or more values from a Redis SET key.

Parameters

- - - + + + + + + + + + + + + +
int$db

The database to select. Note that by default Redis has 16 databases (0-15).

string$key

The Redis SET key in question.

mixed$value

The first value to remove.

mixed...$other_values

One or more additional values to remove.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

true on success and false on failure

Redis|int|false

The number of values removed from the set or false on failure.

+

See also

@@ -13310,7 +19350,7 @@

See also

@@ -13321,7 +19361,7 @@

Examples

- https://redis.io/commands/select + https://redis.io/docs/latest/commands/srem/
- +
$redis->select(1);
$redis->sRem('set1', 'mem1', 'mem2', 'not-in-set');
@@ -13330,18 +19370,17 @@

Examples

-

- - Redis|string|bool - set(string $key, mixed $value, mixed $options = NULL) - + +

+ + array|false sscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

-

Create or set a Redis STRING key to a value.

+

Scan the members of a redis SET key.

Parameters

@@ -13350,43 +19389,42 @@

Parameters

string $key -

The key name to set.

+

The Redis SET key in question.

- mixed - $value -

The value to set the key to.

+ null|int|string + $iterator +

A reference to an iterator which should be initialized to NULL that +PhpRedis will update with the value returned from Redis after each +subsequent call to SSCAN. Once this cursor is zero you know all +members have been traversed.

- mixed - $options -

Either an array with options for how to perform the set or an -integer with an expiration. If an expiration is set PhpRedis -will actually send the SETEX command.

-

OPTION DESCRIPTION

-
-

['EX' => 60] expire 60 seconds. -['PX' => 6000] expire in 6000 milliseconds. -['EXAT' => time() + 10] expire in 10 seconds. -['PXAT' => time()*1000 + 1000] expire in 1 second. -['KEEPTTL' => true] Redis will not update the key's current TTL. -['XX'] Only set the key if it already exists. -['NX'] Only set the key if it doesn't exist. -['GET'] Instead of returning +OK return the previous value of the -key or NULL if the key didn't exist.

+ string|null + $pattern +

An optional glob style pattern to match against, so Redis only +returns the subset of members matching this pattern.

+ + + int + $count +

A hint to Redis as to how many members it should scan in one command +before returning members for that iteration.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|string|bool

True if the key was set or false on failure.

array|false
+

See also

@@ -13394,13 +19432,20 @@

See also

+ + + + @@ -13411,10 +19456,42 @@

Examples

- https://redis.io/commands/set + https://redis.io/docs/latest/commands/sscan/ +
+ https://redis.io/docs/latest/commands/scan/
- https://redis.io/commands/setex + +Redis::setOption
- - - - +
$redis->set('key', 'value');
$redis->set('key', 'expires_in_60_seconds', 60);
$redis->del('myset');
+for ($i = 0; $i < 10000; $i++) {
+    $redis->sAdd('myset', "member:$i");
+}
+$redis->sadd('myset', 'foofoo');
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
+
+$scanned = 0;
+$it = null;
+
+// Without Redis::SCAN_RETRY we may receive empty results and
+// a nonzero iterator.
+do {
+    // Scan members containing '5'
+    $members = $redis->sscan('myset', $it, '*5*');
+    foreach ($members as $member) {
+         echo "NORETRY: $member\n";
+         $scanned++;
+    }
+} while ($it != 0);
+echo "TOTAL: $scanned\n";
+
+$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
+
+$scanned = 0;
+$it = null;
+
+// With Redis::SCAN_RETRY PhpRedis will never return an empty array
+// when the cursor is non-zero
+while (($members = $redis->sscan('myset', $it, '*5*'))) {
+    foreach ($members as $member) {
+        echo "RETRY: $member\n";
+        $scanned++;
+    }
+}
@@ -13423,50 +19500,48 @@

Examples

-

- - Redis|int|false - setBit(string $key, int $idx, bool $value) - + +

+ + bool ssubscribe(array $channels, callable $cb)

-

Set a specific bit in a Redis string to zero or one

+

Subscribes the client to the specified shard channels.

Parameters

- - - - - - - - + + + - - - + + +
string$key

The Redis STRING key to modify

int$idxarray$channels

One or more channel names.

bool$value

Whether to set the bit to zero or one.

callable$cb

The callback PhpRedis will invoke when we receive a message +from one of the subscribed channels.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The original value of the bit or false on failure.

bool

True on success, false on faiilure. Note that this command will block the +client in a subscribe loop, waiting for messages to arrive.

+

See also

@@ -13474,7 +19549,7 @@

See also

@@ -13485,8 +19560,21 @@

Examples

- https://redis.io/commands/setbit + https://redis.io/docs/latest/commands/ssubscribe/
- +
$redis->set('foo', 'bar');
-$redis->setbit('foo', 7, 1);
$redis = new Redis(['host' => 'localhost']);
+
+$redis->ssubscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
+    echo "[$channel]: $message\n";
+
+    // Unsubscribe from the message channel when we read 'quit'
+    if ($message == 'quit') {
+        echo "Unsubscribing from '$channel'\n";
+        $redis->sunsubscribe([$channel]);
+    }
+});
+
+// Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be
+// broken and this command will execute.
+echo "Subscribe loop ended\n";
@@ -13495,18 +19583,17 @@

Examples

-

- - Redis|int|false - setRange(string $key, int $index, string $value) - + +

+ + Redis|int|false strlen(string $key)

-

Update or append to a Redis string at a specific starting index

+

Retrieve the length of a Redis STRING key.

Parameters

@@ -13515,30 +19602,23 @@

Parameters

string $key -

The key to update

- - - int - $index -

Where to insert the provided value

- - - string - $value -

The value to copy into the string.

+

The key we want the length of.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

The new length of the string or false on failure

The length of the string key if it exists, zero if it does not, and +false on failure.

+

See also

@@ -13546,7 +19626,7 @@

See also

@@ -13557,8 +19637,7 @@

Examples

- https://redis.io/commands/setrange + https://redis.io/docs/latest/commands/strlen/
- +
$redis->set('message', 'Hello World');
-$redis->setRange('message', 6, 'Redis');
$redis->strlen('mykey');
@@ -13567,121 +19646,48 @@

Examples

-

- - bool - setOption(int $option, mixed $value) - + +

+ + bool subscribe(array $channels, callable $cb)

-

Set a configurable option on the Redis object.

Following are a list of options you can set:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
OPTIONTYPEDESCRIPTION
OPT_MAX_RETRIESintThe maximum number of times Redis will attempt to reconnect if it gets disconnected, before throwing an exception.
OPT_SCANenumRedis::OPT_SCAN_RETRY, or Redis::OPT_SCAN_NORETRY. Whether PhpRedis should automatically SCAN again when zero keys but a nonzero iterator are returned.
OPT_SERIALIZERenumSet the automatic data serializer.
Redis::SERIALIZER_NONE
Redis::SERIALIZER_PHP
Redis::SERIALIZER_IGBINARY
Redis::SERIALIZER_MSGPACK, Redis::SERIALIZER_JSON
OPT_PREFIXstringA string PhpRedis will use to prefix every key we read or write.
OPT_READ_TIMEOUTfloatHow long PhpRedis will block for a response from Redis before throwing a 'read error on connection' exception.
OPT_TCP_KEEPALIVEboolSet or disable TCP_KEEPALIVE on the connection.
OPT_COMPRESSIONenumSet the compression algorithm
Redis::COMPRESSION_NONE
Redis::COMPRESSION_LZF
Redis::COMPRESSION_LZ4
Redis::COMPRESSION_ZSTD
OPT_REPLY_LITERALboolIf set to true, PhpRedis will return the literal string Redis returns for LINE replies (e.g. '+OK'), rather than true.
OPT_COMPRESSION_LEVELintSet a specific compression level if Redis is compressing data.
OPT_NULL_MULTIBULK_AS_NULLboolCauses PhpRedis to return NULL rather than false for NULL MULTIBULK replies
OPT_BACKOFF_ALGORITHMenumThe exponential backoff strategy to use.
OPT_BACKOFF_BASEintThe minimum delay between retries when backing off.
OPT_BACKOFF_CAPintThe maximum delay between replies when backing off.

+

Subscribe to one or more Redis pubsub channels.

Parameters

- - - + + + - - - + + +
int$option

The option constant.

array$channels

One or more channel names.

mixed$value

The option value.

callable$cb

The callback PhpRedis will invoke when we receive a message +from one of the subscribed channels.

-

Return Value

+

Return Value

- +
+
- +
bool

true if the setting was updated, false if not.

True on success, false on faiilure. Note that this command will block the +client in a subscribe loop, waiting for messages to arrive.

+

See also

@@ -13689,78 +19695,111 @@

See also

+
- -Redis::getOption + https://redis.io/docs/latest/commands/subscribe/
+ + +

Examples

+ + - - +
- -Redis::__construct - for details about backoff strategies.
$redis = new Redis(['host' => 'localhost']);
+
+$redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
+    echo "[$channel]: $message\n";
+
+    // Unsubscribe from the message channel when we read 'quit'
+    if ($message == 'quit') {
+        echo "Unsubscribing from '$channel'\n";
+        $redis->unsubscribe([$channel]);
+    }
+});
+
+// Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be
+// broken and this command will execute.
+echo "Subscribe loop ended\n";
-
-

- - Redis|bool - setex(string $key, int $expire, mixed $value) - + +

+ + Redis|array|bool sunsubscribe(array $channels)

-

Set a Redis STRING key with a specific expiration in seconds.

+

Unsubscribes the client from the given shard channels, +or from all of them if none is given.

Parameters

- - - - - - - - - - - - - + + +
string$key

The name of the key to set.

int$expire

The key's expiration in seconds.

mixed$value

The value to set the key.

array$channels

One or more channels to unsubscribe from.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True on success or false on failure.

Redis|array|bool

The array of unsubscribed channels.

+
+

See also

+ + + + + + + + + + +
+ https://redis.io/docs/latest/commands/sunsubscribe/ +
+ +Redis::ssubscribe +
+

Examples

- +
$redis->setex('60s-ttl', 60, 'some-value');
$redis->ssubscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
+    if ($message == 'quit') {
+        echo "$channel => 'quit' detected, unsubscribing!\n";
+        $redis->sunsubscribe([$channel]);
+    } else {
+        echo "$channel => $message\n";
+    }
+});
+
+echo "We've unsubscribed from both channels, exiting\n";
@@ -13769,45 +19808,48 @@

Examples

-

- - Redis|bool - setnx(string $key, mixed $value) - + +

+ + Redis|bool swapdb(int $src, int $dst)

-

Set a key to a value, but only if that key does not already exist.

+

Atomically swap two Redis databases so that all of the keys in the source database will +now be in the destination database and vice-versa.

Note: This command simply swaps Redis' internal pointer to the database and is therefore +very fast, regardless of the size of the underlying databases.

Parameters

- - - + + + - - - + + +
string$key

The key name to set.

int$src

The source database number

mixed$value

What to set the key to.

int$dst

The destination database number

-

Return Value

+

Return Value

- +
+
- +
Redis|bool

Returns true if the key was set and false otherwise.

Success if the databases could be swapped and false on failure.

+

See also

@@ -13815,7 +19857,14 @@

See also

+ + + + @@ -13826,10 +19875,10 @@

Examples

- https://redis.io/commands/setnx + https://redis.io/docs/latest/commands/swapdb/ +
+ +Redis::del
- - - - +
$redis->setnx('existing-key', 'existing-value');
$redis->setnx('new-key', 'new-value');
$redis->select(0);
+$redis->set('db0-key', 'db0-value');
+$redis->swapdb(0, 1);
+$redis->get('db0-key');
@@ -13838,53 +19887,51 @@

Examples

-

- - Redis|bool - sismember(string $key, mixed $value) - + +

+ + Redis|array time()

-

Check whether a given value is the member of a Redis SET.

+

Retrieve the server time from the connected Redis instance.

-

Parameters

- - - - - - - - - - - - -
string$key

The redis set to check.

mixed$value

The value to test.

- -

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

True if the member exists and false if not.

Redis|array

A two element array consisting of a Unix Timestamp and the number of microseconds +elapsed since the second.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/time/ +
+

Examples

- +
$redis->sismember('myset', 'mem1', 'mem2');
$redis->time();
@@ -13893,30 +19940,17 @@

Examples

-

- - Redis|bool - slaveof(string $host = NULL, int $port = 6379) - deprecated + +

+ + Redis|int|false ttl(string $key)

-

- deprecated - - - - -

-

Turn a redis instance into a replica of another or promote a replica -to a primary.

This method and the corresponding command in Redis has been marked deprecated -and users should instead use Redis::replicaof() if connecting to redis-server

-
-

= 5.0.0.

-

+

Get the amount of time a Redis key has before it will expire, in seconds.

Parameters

@@ -13924,26 +19958,25 @@

Parameters

- - - - - - - + +
string$host
int$port$key

The Key we want the TTL for.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|boolRedis|int|false

(a) The number of seconds until the key expires, or -1 if the key has +no expiration, and -2 if the key does not exist. In the event of an +error, this command will return false.

+

See also

@@ -13951,44 +19984,37 @@

See also

- - - - +
- https://redis.io/commands/slaveof -
- https://redis.io/commands/replicaof + https://redis.io/docs/latest/commands/ttl/
+ + +

Examples

+ + - - +
- -Redis::replicaof -
$redis->ttl('mykey');
-
-

- - Redis|bool - replicaof(string $host = NULL, int $port = 6379) - + +

+ + Redis|int|false type(string $key)

-

Used to turn a Redis instance into a replica of another, or to remove -replica status promoting the instance to a primary.

+

Get the type of a given Redis key.

Parameters

@@ -13996,27 +20022,32 @@

Parameters

- - - - - - - + +
string$host

The host of the primary to start replicating.

int$port

The port of the primary to start replicating.

$key

The key to check

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

Success if we were successfully able to start replicating a primary or -were able to promote teh replicat to a primary.

Redis|int|false

The Redis type constant or false on failure.

+

The Redis class defines several type constants that correspond with Redis key types.

+
Redis::REDIS_NOT_FOUND
+Redis::REDIS_STRING
+Redis::REDIS_SET
+Redis::REDIS_LIST
+Redis::REDIS_ZSET
+Redis::REDIS_HASH
+Redis::REDIS_STREAM
+Redis::REDIS_VECTORSET
+

See also

@@ -14024,20 +20055,7 @@

See also

- - - - - - - - @@ -14048,14 +20066,9 @@

Examples

- https://redis.io/commands/replicaof -
- https://redis.io/commands/slaveof -
- -Redis::slaveof + https://redis.io/docs/latest/commands/type/
- +
$redis = new Redis(['host' => 'localhost']);
-
-// Attempt to become a replica of a Redis instance at 127.0.0.1:9999
-$redis->replicaof('127.0.0.1', 9999);
-
-// When passed no arguments, PhpRedis will deliver the command `REPLICAOF NO ONE`
-// attempting to promote the instance to a primary.
-$redis->replicaof();
foreach ($redis->keys('*') as $key) {
+    echo "$key => " . $redis->type($key) . "\n";
+}
@@ -14064,18 +20077,19 @@

Examples

-

- - Redis|int|false - touch(array|string $key_or_array, string ...$more_keys) - + +

-

Update one or more keys last modified metadata.

+

Delete one or more keys from the Redis database. Unlike this operation, the actual +deletion is asynchronous, meaning it is safe to delete large keys without fear of +Redis blocking for a long period of time.

Parameters

@@ -14083,94 +20097,108 @@

Parameters

- - + + - - + +
array|string$key_or_array$key

Either an array with one or more keys or a string with +the first key to delete.

string...$more_keys

One or more keys to send to the command.

...$other_keys

If the first argument passed to this method was a string +you may pass any number of additional key names.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

This command returns the number of keys that exist and -had their last modified time reset

The number of keys deleted or false on failure.

+

See also

- - + + + + + + + + + + + +
- https://redis.io/commands/touch/ - + https://redis.io/docs/latest/commands/unlink/ +
+ https://redis.io/docs/latest/commands/del/ +
+ +Redis::del +
+ + +

Examples

+ + + + + + +
$redis->unlink('key1', 'key2', 'key3');
$redis->unlink(['key1', 'key2', 'key3']);
-
-

- - mixed - slowlog(string $operation, int $length = 0) - + +

+ + Redis|array|bool unsubscribe(array $channels)

-

Interact with Redis' slowlog functionality in various ways, depending -on the value of 'operation'.

+

Unsubscribe from one or more subscribed channels.

Parameters

- - - - - - - - + + +
string$operation

The operation you wish to perform.  This can -be one of the following values: -'GET' - Retrieve the Redis slowlog as an array. -'LEN' - Retrieve the length of the slowlog. -'RESET' - Remove all slowlog entries.

int$length

This optional argument can be passed when operation -is 'get' and will specify how many elements to retrieve. -If omitted Redis will send up to a default number of -entries, which is configurable.

-

Note: With Redis >= 7.0.0 you can send -1 to mean "all".

array$channels

One or more channels to unsubscribe from.

-

Return Value

+

Return Value

- +
+
- - + +
mixedRedis|array|bool

The array of unsubscribed channels.

+

See also

@@ -14178,7 +20206,14 @@

See also

+ + + + @@ -14189,13 +20224,16 @@

Examples

- https://redis.io/commands/slowlog/ + https://redis.io/docs/latest/commands/unsubscribe/ +
+ +Redis::subscribe
- - - - - - - +
$redis->slowlog('get', -1);   // Retrieve all slowlog entries.
$redis->slowlog('len');       // Retrieve slowlog length.
$redis->slowlog('reset');     // Reset the slowlog.
$redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
+    if ($message == 'quit') {
+        echo "$channel => 'quit' detected, unsubscribing!\n";
+        $redis->unsubscribe([$channel]);
+    } else {
+        echo "$channel => $message\n";
+    }
+});
+
+echo "We've unsubscribed from both channels, exiting\n";
@@ -14204,49 +20242,31 @@

Examples

-

- - mixed - sort(string $key, array|null $options = null) - + +

+ + Redis|bool unwatch()

-

Sort the contents of a Redis key in various ways.

+

Remove any previously WATCH'ed keys in a transaction.

-

Parameters

- - - - - - - - - - - - -
string$key

The key you wish to sort

array|null$options

Various options controlling how you would like the -data sorted. See blow for a detailed description -of this options array.

- -

Return Value

+

Return Value

- +
+
- - + +
mixed

This command can either return an array with the sorted data -or the number of elements placed in a destination set when -using the STORE option.

Redis|bool

True on success and false on failure.

+

See also

@@ -14254,7 +20274,20 @@

See also

+ + + + + + + + @@ -14265,17 +20298,7 @@

Examples

- https://redis.io/commands/sort/ + https://redis.io/docs/latest/commands/unwatch/ +
+ https://redis.io/docs/latest/commands/unwatch/ +
+ +Redis::watch
- +
$options = [
- 'SORT' => 'ASC'|| 'DESC' // Sort in descending or descending order.
- 'ALPHA' => true || false // Whether to sort alphanumerically.
- 'LIMIT' => [0, 10] // Return a subset of the data at offset, count
- 'BY' => 'weight_*' // For each element in the key, read data from the
- external key weight_* and sort based on that value.
- 'GET' => 'weight_*' // For each element in the source key, retrieve the
- data from key weight_* and return that in the result
- rather than the source keys' element. This can
- be used in combination with 'BY'
-];
$redis->unwatch();
@@ -14284,45 +20307,47 @@

Examples

-

- - mixed - sort_ro(string $key, array|null $options = null) - + +

+ + Redis|bool watch(array|string $key, string ...$other_keys)

-

This is simply a read-only variant of the sort command

+

Watch one or more keys for conditional execution of a transaction.

Parameters

- + - + - - - + + +
stringarray|string $key

Either an array with one or more key names, or a string key name

array|null$optionsstring...$other_keys

If the first argument was passed as a string, any number of additional +string key names may be passed variadically.

-

Return Value

+

Return Value

- +
+
- +
mixedRedis|bool
+

See also

@@ -14330,114 +20355,135 @@

See also

+ + + +
- -Redis::sort + https://redis.io/docs/latest/commands/watch/ +
+ https://redis.io/docs/latest/commands/unwatch/
+

Examples

+ + + + + +
$redis1 = new Redis(['host' => 'localhost']);
+$redis2 = new Redis(['host' => 'localhost']);
+
+// Start watching 'incr-key'
+$redis1->watch('incr-key');
+
+// Retrieve its value.
+$val = $redis1->get('incr-key');
+
+// A second client modifies 'incr-key' after we read it.
+$redis2->set('incr-key', 0);
+
+// Because another client changed the value of 'incr-key' after we read it, this
+// is no longer a proper increment operation, but because we are `WATCH`ing the
+// key, this transaction will fail and we can try again.
+//
+// If were to comment out the above `$redis2->set('incr-key', 0)` line the
+// transaction would succeed.
+$redis1->multi();
+$redis1->set('incr-key', $val + 1);
+$res = $redis1->exec();
+
+// bool(false)
+var_dump($res);
+
-

- - array - sortAsc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) - deprecated + +

+ + int|false wait(int $numreplicas, int $timeout)

-

- deprecated - - - - -

-

No description

- +

Block the client up to the provided timeout until a certain number of replicas have confirmed +receiving them.

Parameters

- - - - - - - - - - - - - - - - - + + - - - - - - - + +
string$key
string|null$pattern
mixed$get
int$offset$numreplicas

The number of replicas we want to confirm write operations

int$count
string|null$store$timeout

How long to wait (zero meaning forever).

-

Return Value

+

Return Value

- +
+
- - + +
arrayint|false

The number of replicas that have confirmed or false on failure.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/wait/ +
+ +

Examples

+ + + + + +
$redis->wait(1, 1000);
+
-

- - array - sortAscAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) - deprecated + +

+ + int|false xack(string $key, string $group, array $ids)

-

- deprecated - - - - -

-

No description

- +

Acknowledge one or more messages that are pending (have been consumed using XREADGROUP but +not yet acknowledged by XACK.)

Parameters

@@ -14446,148 +20492,105 @@

Parameters

string $key - - - - string|null - $pattern - - - - mixed - $get - - - - int - $offset - +

The stream to query.

- int - $count - + string + $group +

The consumer group to use.

- string|null - $store - + array + $ids +

An array of stream entry IDs.

-

Return Value

+

Return Value

- +
+
- - + +
arrayint|false

The number of acknowledged messages

+
- -
-
- -
-
-

- - array - sortDesc(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) - deprecated -

-
-

- deprecated - - - - -

- - - -
-

No description

- -
-
-

Parameters

+

See also

- - - - - - - - - - - - - - - - - + - - + - - +
string$key
string|null$pattern
mixed$get
int$offset + https://redis.io/docs/latest/commands/xack/ +
int$count + https://redis.io/docs/latest/commands/xreadgroup/ +
string|null$store + +Redis::xack +
-

Return Value

+

Examples

- - - - -
array
+ +
$redis->xAdd('ships', '*', ['name' => 'Enterprise']);
+$redis->xAdd('ships', '*', ['name' => 'Defiant']);
+
+$redis->xGroup('CREATE', 'ships', 'Federation', '0-0');
+
+// Consume a single message with the consumer group 'Federation'
+$ship = $redis->xReadGroup('Federation', 'Picard', ['ships' => '>'], 1);
+
+/* Retrieve the ID of the message we read.
+assert(isset($ship['ships']));
+$id = key($ship['ships']);
+
+// The message we just read is now pending.
+$res = $redis->xPending('ships', 'Federation'));
+var_dump($res);
+
+// We can tell Redis we were able to process the message by using XACK
+$res = $redis->xAck('ships', 'Federation', [$id]);
+assert($res === 1);
+
+// The message should no longer be pending.
+$res = $redis->xPending('ships', 'Federation');
+var_dump($res);
+ + - - -
-

- - array - sortDescAlpha(string $key, string|null $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, string|null $store = null) - deprecated + +

+ + Redis|string|false xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false)

-

- deprecated - - - - -

-

No description

- +

Append a message to a stream.

Parameters

@@ -14596,65 +20599,95 @@

Parameters

string $key - +

The stream name.

- string|null - $pattern - + string + $id +

The ID for the message we want to add. This can be the special value '' +which means Redis will generate the ID that appends the message to the +end of the stream. It can also be a value in the form - which will +generate an ID that appends to the end of entries with the same value +(if any exist).

- mixed - $get + array + $values int - $offset - + $maxlen +

If specified Redis will append the new message but trim any number of the +oldest messages in the stream until the length is <= $maxlen.

- int - $count - + bool + $approx +

Used in conjunction with $maxlen, this flag tells Redis to trim the stream +but in a more efficient way, meaning the trimming may not be exactly to +$maxlen values.

- string|null - $store - + bool + $nomkstream +

If passed as TRUE, the stream must exist for Redis to append the message.

-

Return Value

+

Return Value

- +
+
- +
arrayRedis|string|false
+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/xadd/ +
+ +

Examples

+ + + + + + + + +
$redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']);
$redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']);
+
-

- - Redis|int|false - srem(string $key, mixed $value, mixed ...$other_values) - + +

+ + Redis|bool|array xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false)

-

Remove one or more values from a Redis SET key.

+

This command allows a consumer to claim pending messages that have been idle for a specified period of time.

Its purpose is to provide a mechanism for picking up messages that may have had a failed consumer.

Parameters

@@ -14663,30 +20696,52 @@

Parameters

string $key -

The Redis SET key in question.

+

The stream to check.

- mixed - $value -

The first value to remove.

+ string + $group +

The consumer group to query.

- mixed - ...$other_values - + string + $consumer +

Which consumer to check.

+ + + int + $min_idle +

The minimum time in milliseconds for the message to have been pending.

+ + + string + $start +

The minimum message id to check.

+ + + int + $count +

An optional limit on how many messages are returned.

+ + + bool + $justid +

If the client only wants message IDs and not all of their data.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The number of values removed from the set or false on failure.

Redis|bool|array

An array of pending IDs or false if there are none, or on failure.

+

See also

@@ -14694,7 +20749,19 @@

See also

+ + + + + + + + @@ -14705,7 +20772,23 @@

Examples

- https://redis.io/commands/srem + https://redis.io/docs/latest/commands/xautoclaim/ +
+ https://redis.io/docs/latest/commands/xclaim/ +
+ https://redis.io/docs/data-types/streams-tutorial/
- +
$redis->sRem('set1', 'mem1', 'mem2', 'not-in-set');
$redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
+
+$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
+
+// Consume the ['name' => 'Defiant'] message
+$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
+
+// The "Jem'Hadar" consumer has the message presently
+$pending = $redis->xPending('ships', 'combatants');
+var_dump($pending);
+
+// Assume control of the pending message with a different consumer.
+$res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0');
+
+// Now the 'Sisko' consumer owns the message
+$pending = $redis->xPending('ships', 'combatants');
+var_dump($pending);
@@ -14714,18 +20797,18 @@

Examples

-

- - array|false - sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) - + +

+ + Redis|array|bool xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options)

-

Scan the members of a redis SET key.

+

This method allows a consumer to take ownership of pending stream entries, by ID. Another +command that does much the same thing but does not require passing specific IDs is Redis::xAutoClaim.

Parameters

@@ -14734,40 +20817,62 @@

Parameters

string $key -

The Redis SET key in question.

+

The stream we wish to claim messages for.

- int|null - $iterator -

A reference to an iterator which should be initialized to NULL that -PhpRedis will update with the value returned from Redis after each -subsequent call to SSCAN. Once this cursor is zero you know all -members have been traversed.

+ string + $group +

Our consumer group.

- string|null - $pattern -

An optional glob style pattern to match against, so Redis only -returns the subset of members matching this pattern.

+ string + $consumer +

Our consumer.

int - $count -

A hint to Redis as to how many members it should scan in one command -before returning members for that iteration.

+ $min_idle +

The minimum idle-time in milliseconds a message must have for ownership to be transferred.

+ + + array + $ids + + + + array + $options +

An options array that modifies how the command operates.

+
# Following is an options array describing every option you can pass.  Note that
+# 'IDLE', and 'TIME' are mutually exclusive.
+$options = [
+    'IDLE'       => 3           # Set the idle time of the message to a 3.  By default
+                                # the idle time is set to zero.
+    'TIME'       => 1000*time() # Same as IDLE except it takes a unix timestamp in
+                                # milliseconds.
+    'RETRYCOUNT' => 0           # Set the retry counter to zero.  By default XCLAIM
+                                # doesn't modify the counter.
+    'FORCE'                     # Creates the pending message entry even if IDs are
+                                # not already
+                                # in the PEL with another client.
+    'JUSTID'                    # Return only an array of IDs rather than the messages
+                                # themselves.
+];
-

Return Value

+

Return Value

- +
+
- - + +
array|falseRedis|array|bool

An array of claimed messages or false on failure.

+

See also

@@ -14775,20 +20880,13 @@

See also

- - - - @@ -14799,42 +20897,26 @@

Examples

- https://redis.io/commands/sscan -
- https://redis.io/commands/scan + https://redis.io/docs/latest/commands/xclaim/
- -Redis::setOption + https://redis.io/docs/latest/commands/xautoclaim./
- +
$redis->del('myset');
-for ($i = 0; $i < 10000; $i++) {
- $redis->sAdd('myset', "member:$i");
-}
-$redis->sadd('myset', 'foofoo');
-
-$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
-
-$scanned = 0;
-$it = NULL;
-
-// Without Redis::SCAN_RETRY we may receive empty results and
-// a nonzero iterator.
-do {
- // Scan members containing '5'
- $members = $redis->sscan('myset', $it, '*5*');
- foreach ($members as $member) {
- echo "NORETRY: $member\n";
- $scanned++;
- }
-} while ($it != 0);
-echo "TOTAL: $scanned\n";
-
-$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
-
-$scanned = 0;
-$it = NULL;
-
-// With Redis::SCAN_RETRY PhpRedis will never return an empty array
-// when the cursor is non-zero
-while (($members = $redis->sscan('myset', $it, '*5*'))) {
- foreach ($members as $member) {
- echo "RETRY: $member\n";
- $scanned++;
- }
-}
$redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
+
+$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
+
+// Consume the ['name' => 'Defiant'] message
+$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
+
+// The "Jem'Hadar" consumer has the message presently
+$pending = $redis->xPending('ships', 'combatants');
+var_dump($pending);
+
+assert($pending && isset($pending[1]));
+
+// Claim the message by ID.
+$claimed = $redis->xClaim('ships', 'combatants', 'Sisko', 0, [$pending[1]], ['JUSTID']);
+var_dump($claimed);
+
+// Now the 'Sisko' consumer owns the message
+$pending = $redis->xPending('ships', 'combatants');
+var_dump($pending);
@@ -14843,18 +20925,17 @@

Examples

-

- - Redis|int|false - strlen(string $key) - + +

+ + Redis|int|false xdel(string $key, array $ids)

-

Retrieve the length of a Redis STRING key.

+

Remove one or more specific IDs from a stream.

Parameters

@@ -14863,21 +20944,27 @@

Parameters

string $key -

The key we want the length of.

+

The stream to modify.

+ + + array + $ids +

One or more message IDs to remove.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

The length of the string key if it exists, zero if it does not, and -false on failure.

The number of messages removed or false on failure.

+

See also

@@ -14885,7 +20972,7 @@

See also

@@ -14896,7 +20983,7 @@

Examples

- https://redis.io/commands/strlen + https://redis.io/docs/latest/commands/xdel/
- +
$redis->strlen('mykey');
$redis->xDel('stream', ['1-1', '2-1', '3-1']);
@@ -14905,47 +20992,53 @@

Examples

-

- - bool - subscribe(array $channels, callable $cb) - + +

+ + Redis|array|false xdelex(string $key, array $ids, string|null $mode = null)

-

Subscribe to one or more Redis pubsub channels.

+

Remove one or more IDs from a stream with extended options.

Parameters

+ + + + + - - + + - - - + + +
string$key

The stream to modify.

array$channels

One or more channel names.

$ids

One or more message IDs to remove.

callable$cb

The callback PhpRedis will invoke when we receive a message -from one of the subscribed channels.

string|null$mode

An optional mode argument. Valid modes +are as follows: KEEPREF | DELREF | ACKED

-

Return Value

+

Return Value

- +
+
- - + +
bool

True on success, false on faiilure. Note that this command will block the -client in a subscribe loop, waiting for messages to arrive.

Redis|array|false

An array corresponding to IDs. 1 if the id was +deleted and 0 if not.

+

See also

@@ -14953,7 +21046,7 @@

See also

@@ -14964,21 +21057,8 @@

Examples

- https://redis.io/commands/subscribe + https://redis.io/docs/latest/commands/xdelex/
- +
$redis = new Redis(['host' => 'localhost']);
-
-$redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
- echo "[$channel]: $message\n";
-
- // Unsubscribe from the message channel when we read 'quit'
- if ($message == 'quit') {
- echo "Unsubscribing from '$channel'\n";
- $redis->unsubscribe([$channel]);
- }
-});
-
-// Once we read 'quit' from both channel-1 and channel-2 the subscribe loop will be
-// broken and this command will execute.
-echo "Subscribe loop ended\n";
$redis->xadd('s', '*', ['field' => 'value1']);
+$redis->xdelex('s', ['1-0'], 'KEEPREF');
@@ -14987,47 +21067,85 @@

Examples

-

- - Redis|bool - swapdb(int $src, int $dst) - + +

+ + mixed xgroup(string $operation, string|null $key = null, string|null $group = null, string|null $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2)

-

Atomically swap two Redis databases so that all of the keys in the source database will -now be in the destination database and vice-versa.

Note: This command simply swaps Redis' internal pointer to the database and is therefore -very fast, regardless of the size of the underlying databases.

+

XGROUP

Perform various operation on consumer groups for a particular Redis STREAM. What the command does +is primarily based on which operation is passed.

Parameters

- - - + + + + + + + + + + + + + + + + + + + + + + + - - + +
int$src

The source database number

string$operation

The subcommand you intend to execute. Valid options are as follows +'HELP' - Redis will return information about the command +Requires: none +'CREATE' - Create a consumer group. +Requires: Key, group, consumer. +'SETID' - Set the ID of an existing consumer group for the stream. +Requires: Key, group, id. +'CREATECONSUMER' - Create a new consumer group for the stream. You must +also pass key, group, and the consumer name you wish to +create. +Requires: Key, group, consumer. +'DELCONSUMER' - Delete a consumer from group attached to the stream. +Requires: Key, group, consumer. +'DESTROY' - Delete a consumer group from a stream. +Requires: Key, group.

string|null$key

The STREAM we're operating on.

string|null$group

The consumer group we want to create/modify/delete.

string|null$id_or_consumer

The STREAM id (e.g. '$') or consumer group. See the operation section +for information about which to send.

bool$mkstream

This flag may be sent in combination with the 'CREATE' operation, and +cause Redis to also create the STREAM if it doesn't currently exist.

int$dst

The destination database number

$entries_read

Allows you to set Redis' 'entries-read' STREAM value. This argument is +only relevant to the 'CREATE' and 'SETID' operations. +Note: Requires Redis >= 7.0.0.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool

Success if the databases could be swapped and false on failure.

mixed

This command return various results depending on the operation performed.

+

See also

@@ -15035,14 +21153,7 @@

See also

- - - - @@ -15053,10 +21164,7 @@

Examples

- https://redis.io/commands/swapdb -
- -Redis::del + https://redis.io/docs/latest/commands/xgroup/
- +
$redis->select(0);
-$redis->set('db0-key', 'db0-value');
-$redis->swapdb(0, 1);
-$redis->get('db0-key');
$redis->xgroup('CREATE', 'mystream', 'workers', '$');
@@ -15065,31 +21173,56 @@

Examples

-

- - Redis|array - time() - + +

+ + mixed xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1)

-

Retrieve the server time from the connected Redis instance.

+

Retrieve information about a stream key.

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + + + + + + +
string$operation

The specific info operation to perform.

string|null$arg1

The first argument (depends on operation)

string|null$arg2

The second argument

int$count

The COUNT argument to XINFO STREAM

+ + +

Return Value

+ +
+ - - + +
Redis|array

two element array consisting of a Unix Timestamp and the number of microseconds -elapsed since the second.

mixed

This command can return different things depending on the operation being called.

+

See also

@@ -15097,7 +21230,7 @@

See also

@@ -15108,7 +21241,13 @@

Examples

- https://redis.io/commands/time + https://redis.io/docs/latest/commands/xinfo/
- + + + + + + +
$redis->time();
$redis->xInfo('CONSUMERS', 'stream');
$redis->xInfo('GROUPS', 'stream');
$redis->xInfo('STREAM', 'stream');
@@ -15117,18 +21256,17 @@

Examples

-

- - Redis|int|false - ttl(string $key) - + +

+ + Redis|int|false xlen(string $key)

-

Get the amount of time a Redis key has before it will expire, in seconds.

+

Get the number of messages in a Redis STREAM key.

Parameters

@@ -15137,22 +21275,22 @@

Parameters

string $key -

The Key we want the TTL for.

+

The Stream to check.

-

Return Value

+

Return Value

- +
+
- +
Redis|int|false

(a) The number of seconds until the key expires, or -1 if the key has -no expiration, and -2 if the key does not exist. In the event of an -error, this command will return false.

The number of messages or false on failure.

+

See also

@@ -15160,7 +21298,7 @@

See also

@@ -15171,7 +21309,7 @@

Examples

- https://redis.io/commands/ttl + https://redis.io/docs/latest/commands/xlen/
- +
$redis->ttl('mykey');
$redis->xLen('stream');
@@ -15180,18 +21318,18 @@

Examples

-

- - Redis|int|false - type(string $key) - + +

+ + Redis|array|false xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null)

-

Get the type of a given Redis key.

+

Interact with stream messages that have been consumed by a consumer group but not yet +acknowledged with XACK.

Parameters

@@ -15200,28 +21338,47 @@

Parameters

string $key -

The key to check

+

The stream to inspect.

+ + + string + $group +

The user group we want to see pending messages from.

+ + + string|null + $start +

The minimum ID to consider.

+ + + string|null + $end +

The maximum ID to consider.

+ + + int + $count +

Optional maximum number of messages to return.

+ + + string|null + $consumer +

If provided, limit the returned messages to a specific consumer.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The Redis type constant or false on failure.

-

The Redis class defines several type constants that correspond with Redis key types.

-
Redis::REDIS_NOT_FOUND
-Redis::REDIS_STRING
-Redis::REDIS_SET
-Redis::REDIS_LIST
-Redis::REDIS_ZSET
-Redis::REDIS_HASH
-Redis::REDIS_STREAM
Redis|array|false

The pending messages belonging to the stream or false on failure.

+

See also

@@ -15229,7 +21386,13 @@

See also

+ + + + @@ -15240,9 +21403,7 @@

Examples

- https://redis.io/commands/type + https://redis.io/docs/latest/commands/xpending/ +
+ https://redis.io/docs/latest/commands/xreadgroup/
- +
foreach ($redis->keys('*') as $key) {
- echo "$key => " . $redis->type($key) . "\n";
-}
$redis->xpending('mystream', 'workers', '-', '+', 10);
@@ -15251,48 +21412,56 @@

Examples

-

+ + Redis|array|bool xrange(string $key, string $start, string $end, int $count = -1)

-

Delete one or more keys from the Redis database. Unlike this operation, the actual -deletion is asynchronous, meaning it is safe to delete large keys without fear of -Redis blocking for a long period of time.

+

Get a range of entries from a STREAM key.

Parameters

- + - + - - + + + + + + + + + + + +
array|stringstring $key

The stream key name to list.

string...$other_keys

If the first argument passed to this method was a string -you may pass any number of additional key names.

$start

The minimum ID to return.

string$end

The maximum ID to return.

int$count

An optional maximum number of entries to return.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The number of keys deleted or false on failure.

Redis|array|bool

The entries in the stream within the requested range or false on failure.

+

See also

@@ -15300,20 +21469,7 @@

See also

- - - - - - - - @@ -15324,10 +21480,10 @@

Examples

- https://redis.io/commands/unlink -
- https://redis.io/commands/del -
- -Redis::del + https://redis.io/docs/latest/commands/xrange/
- + - +
$redis->unlink('key1', 'key2', 'key3');
$redis->xRange('stream', '0-1', '0-2');
$redis->unlink(['key1', 'key2', 'key3']);
$redis->xRange('stream', '-', '+');
@@ -15336,18 +21492,17 @@

Examples

-

- - Redis|array|bool - unsubscribe(array $channels) - + +

+ + Redis|array|bool xread(array $streams, int $count = -1, int $block = -1)

-

Unsubscribe from one or more subscribed channels.

+

Consume one or more unconsumed elements in one or more streams.

Parameters

@@ -15355,21 +21510,34 @@

Parameters

- - + + + + + + + + + + + +
array$channels

One or more channels to unsubscribe from.

$streams

An associative array with stream name keys and minimum id values.

int$count

An optional limit to how many entries are returned per stream

int$block

An optional maximum number of milliseconds to block the caller if no +data is available on any of the provided streams.

-

Return Value

+

Return Value

- +
+
- +
Redis|array|bool

The array of unsubscribed channels.

An array of read elements or false if there aren't any.

+

See also

@@ -15377,14 +21545,7 @@

See also

- - - - @@ -15395,16 +21556,14 @@

Examples

- https://redis.io/commands/unsubscribe -
- -Redis::subscribe + https://redis.io/docs/latest/commands/xread/
- +
$redis->subscribe(['channel-1', 'channel-2'], function ($redis, $channel, $message) {
- if ($message == 'quit') {
- echo "$channel => 'quit' detected, unsubscribing!\n";
- $redis->unsubscribe([$channel]);
- } else {
- echo "$channel => $message\n";
- }
-});
-
-echo "We've unsubscribed from both channels, exiting\n";
$redis->xAdd('s03', '3-1', ['title' => 'The Search, Part I']);
+$redis->xAdd('s03', '3-2', ['title' => 'The Search, Part II']);
+$redis->xAdd('s03', '3-3', ['title' => 'The House Of Quark']);
+$redis->xAdd('s04', '4-1', ['title' => 'The Way of the Warrior']);
+$redis->xAdd('s04', '4-3', ['title' => 'The Visitor']);
+$redis->xAdd('s04', '4-4', ['title' => 'Hippocratic Oath']);
+
+$redis->xRead(['s03' => '3-2', 's04' => '4-1']);
@@ -15413,30 +21572,61 @@

Examples

-

- - Redis|bool - unwatch() - + +

+ + Redis|array|bool xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1)

-

Remove any previously WATCH'ed keys in a transaction.

+

Read one or more messages using a consumer group.

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + +
string$group

The consumer group to use.

string$consumer

The consumer to use.

array$streams

An array of stream names and message IDs

int$count

Optional maximum number of messages to return

int$block

How long to block if there are no messages available.

+ + +

Return Value

+ +
+ - - + +
Redis|bool

on success and false on failure.

Redis|array|bool

Zero or more unread messages or false on failure.

+

See also

@@ -15444,71 +21634,93 @@

See also

- - - - +
- https://redis.io/commands/unwatch -
- https://redis.io/commands/unwatch + https://redis.io/docs/latest/commands/xreadgroup/
+ + +

Examples

+ + - - +
- -Redis::watch -
$redis->xGroup('CREATE', 'episodes', 'ds9', '0-0', true);
+
+$redis->xAdd('episodes', '1-1', ['title' => 'Emissary: Part 1']);
+$redis->xAdd('episodes', '1-2', ['title' => 'A Man Alone']);
+
+$messages = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
+
+// After having read the two messages, add another
+$redis->xAdd('episodes', '1-3', ['title' => 'Emissary: Part 2']);
+
+// Acknowledge the first two read messages
+foreach ($messages as $stream => $stream_messages) {
+    $ids = array_keys($stream_messages);
+    $redis->xAck('stream', 'ds9', $ids);
+}
+
+// We can now pick up where we left off, and will only get the final message
+$msgs = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
-
-

- - Redis|bool - watch(array|string $key, string ...$other_keys) - + +

+ + Redis|array|bool xrevrange(string $key, string $end, string $start, int $count = -1)

-

Watch one or more keys for conditional execution of a transaction.

+

Get a range of entries from a STREAM key in reverse chronological order.

Parameters

- + - + - - + + + + + + + + + + + +
array|stringstring $key

The stream key to query.

string...$other_keys

If the first argument was passed as a string, any number of additional -string key names may be passed variadically.

$end

The maximum message ID to include.

string$start

The minimum message ID to include.

int$count

An optional maximum number of messages to include.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|boolRedis|array|bool

The entries within the requested range, from newest to oldest.

+

See also

@@ -15516,13 +21728,13 @@

See also

@@ -15533,30 +21745,10 @@

Examples

- https://redis.io/commands/watch + https://redis.io/docs/latest/commands/xrevrange/
- https://redis.io/commands/unwatch + https://redis.io/docs/latest/commands/xrange/
- + + + +
$redis1 = new Redis(['host' => 'localhost']);
-$redis2 = new Redis(['host' => 'localhost']);
-
-// Start watching 'incr-key'
-$redis1->watch('incr-key');
-
-// Retrieve its value.
-$val = $redis1->get('incr-key');
-
-// A second client modifies 'incr-key' after we read it.
-$redis2->set('incr-key', 0);
-
-// Because another client changed the value of 'incr-key' after we read it, this
-// is no longer a proper increment operation, but because we are `WATCH`ing the
-// key, this transaction will fail and we can try again.
-//
-// If were to comment out the above `$redis2->set('incr-key', 0)` line the
-// transaction would succeed.
-$redis1->multi();
-$redis1->set('incr-key', $val + 1);
-$res = $redis1->exec();
-
-// bool(false)
-var_dump($res);
$redis->xRevRange('stream', '0-2', '0-1');
$redis->xRevRange('stream', '+', '-');
@@ -15565,46 +21757,56 @@

Examples

-

- - int|false - wait(int $numreplicas, int $timeout) - + +

+ + Redis|int|false vadd(string $key, array $values, mixed $element, array|null $options = null)

-

Block the client up to the provided timeout until a certain number of replicas have confirmed -recieving them.

+

Add to a vector set

Parameters

- - - + + + - - - + + + + + + + + + + + + +
int$numreplicas

The number of replicas we want to confirm write operaions

string$key

The vector set to add to.

int$timeout

How long to wait (zero meaning forever).

array$values

A non-empty array of floating point values

mixed$element

The element to add to the vector set.

array|null$options

An optional options array

-

Return Value

+

Return Value

- +
+
- - + +
int|false

The number of replicas that have confirmed or false on failure.

Redis|int|false

One if the key was added zero if not.

+

See also

@@ -15612,31 +21814,37 @@

See also

- https://redis.io/commands/wait + https://redis.io/docs/latest/commands/vadd/
+

Examples

+ + + + + +
$redis->vadd('embeddings', [0.12, 0.04, 0.88], 'doc:1');
+
-

- - int|false - xack(string $key, string $group, array $ids) - + +

+ + Redis|array|false vsim(string $key, mixed $member, array|null $options = null)

-

Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but -not yet acknowledged by XACK.)

+

Query similarity of a vector by element or scores

Parameters

@@ -15645,30 +21853,37 @@

Parameters

string $key -

The stream to query.

+

The vector set to query.

- string - $group -

The consumer group to use.

+ mixed + $member +

Either an element or array of scores. PhpRedis +will attempt to infer which it is, but since +there can be some ambiguity here due to +serialization you can also explicitly specify +ELE, VALUES, or FP32 in the options +array.

- array - $ids -

An array of stream entry IDs.

+ array|null + $options +

An optional options array

-

Return Value

+

Return Value

- +
+
- - + +
int|false

The number of acknowledged messages

Redis|array|false

An array of elements and their similarity scores, or false on failure.

+

See also

@@ -15676,20 +21891,7 @@

See also

- - - - - - - - @@ -15700,29 +21902,7 @@

Examples

- https://redis.io/commands/xack -
- https://redis.io/commands/xreadgroup -
- -Redis::xack + https://redis.io/docs/latest/commands/vsim/
- +
$redis->xAdd('ships', '*', ['name' => 'Enterprise']);
-$redis->xAdd('ships', '*', ['name' => 'Defiant']);
-
-$redis->xGroup('CREATE', 'ships', 'Federation', '0-0');
-
-// Consume a single message with the consumer group 'Federation'
-$ship = $redis->xReadGroup('Federation', 'Picard', ['ships' => '>'], 1);
-
-/* Retrieve the ID of the message we read.
-assert(isset($ship['ships']));
-$id = key($ship['ships']);
-
-// The message we just read is now pending.
-$res = $redis->xPending('ships', 'Federation'));
-var_dump($res);
-
-// We can tell Redis we were able to process the message by using XACK
-$res = $redis->xAck('ships', 'Federation', [$id]);
-assert($res === 1);
-
-// The message should no longer be pending.
-$res = $redis->xPending('ships', 'Federation');
-var_dump($res);
$redis->vsim('embeddings', 'doc:1', ['COUNT' => 3]);
@@ -15731,18 +21911,17 @@

Examples

-

- - Redis|string|false - xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false) - + +

+ + Redis|int|false vcard(string $key)

-

Append a message to a stream.

+

Get the length of a vector set

Parameters

@@ -15751,52 +21930,22 @@

Parameters

string $key -

The stream name.

- - - string - $id -

The ID for the message we want to add. This can be the special value '' -which means Redis will generate the ID that appends the message to the -end of the stream. It can also be a value in the form - which will -generate an ID that appends to the end ot entries with the same value -(if any exist).

- - - array - $values - - - - int - $maxlen -

If specified Redis will append the new message but trim any number of the -oldest messages in the stream until the length is <= $maxlen.

- - - bool - $approx -

Used in conjunction with $maxlen, this flag tells Redis to trim the stream -but in a more efficient way, meaning the trimming may not be exactly to -$maxlen values.

- - - bool - $nomkstream -

If passed as TRUE, the stream must exist for Redis to append the message.

+

The vector set to query.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|string|falseRedis|int|false

The number of elements in the vector set or false on failure.

+

See also

@@ -15804,7 +21953,7 @@

See also

@@ -15815,10 +21964,7 @@

Examples

- https://redis.io/commands/xadd + https://redis.io/docs/latest/commands/vcard/
- - - - +
$redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']);
$redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']);
$redis->vcard('embeddings');
@@ -15827,18 +21973,17 @@

Examples

-

- - Redis|bool|array - xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) - + +

+ + Redis|int|false vdim(string $key)

-

This command allows a consumer to claim pending messages that have been idle for a specified period of time.

Its purpose is to provide a mechanism for picking up messages that may have had a failed consumer.

+

Get the dimensions of a vector set

Parameters

@@ -15847,50 +21992,22 @@

Parameters

string $key -

The stream to check.

- - - string - $group -

The consumer group to query.

- - - string - $consumer -

Which consumer to check.

- - - int - $min_idle -

The minimum time in milliseconds for the message to have been pending.

- - - string - $start -

The minimum message id to check.

- - - int - $count -

An optional limit on how many messages are returned.

- - - bool - $justid -

If the client only wants message IDs and not all of their data.

+

The vector set to query.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|bool|array

An array of pending IDs or false if there are none, or on failure.

Redis|int|false

The number of dimensions in the vector set or false on failure.

+

See also

@@ -15898,19 +22015,7 @@

See also

- - - - - - - - @@ -15921,23 +22026,7 @@

Examples

- https://redis.io/commands/xautoclaim -
- https://redis.io/commands/xclaim -
- https://redis.io/docs/data-types/streams-tutorial/ + https://redis.io/docs/latest/commands/vdim/
- +
$redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
-
-$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
-
-// Consume the ['name' => 'Defiant'] message
-$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
-
-// The "Jem'Hadar" consumer has the message presently
-$pending = $redis->xPending('ships', 'combatants');
-var_dump($pending);
-
-// Asssume control of the pending message with a different consumer.
-$res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0');
-
-// Now the 'Sisko' consumer owns the message
-$pending = $redis->xPending('ships', 'combatants');
-var_dump($pending);
$redis->vdim('embeddings');
@@ -15946,81 +22035,41 @@

Examples

-

- - Redis|array|bool - xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) - + +

+ + Redis|array|false vinfo(string $key)

-

This method allows a consumer to take ownership of pending stream entries, by ID. Another -command that does much the same thing but does not require passing specific IDs is Redis::xAutoClaim.

+

Get various bits of information about a vector set

Parameters

- - - - - - - - - - - +
string$key

The stream we wish to claim messages for.

string$group

Our consumer group.

- - - - - - - - - - - - - - - - - + +
string$consumer

Our consumer.

int$min_idle
array$ids
array$options

An options array that modifies how the command operates.

-
# Following is an options array describing every option you can pass.  Note that
-# 'IDLE', and 'TIME' are mutually exclusive.
-$options = [
-    'IDLE'       => 3            # Set the idle time of the message to a 3.  By default
-                                 # the idle time is set to zero.
-    'TIME'       => 1000*time()  # Same as IDLE except it takes a unix timestamp in
-                                 # milliseconds.
-    'RETRYCOUNT' => 0            # Set the retry counter to zero.  By default XCLAIM
-                                 # doesn't modify the counter.
-    'FORCE'                      # Creates the pending message entry even if IDs are
-                                 # not already
-                                 # in the PEL with another client.
-    'JUSTID'                     # Return only an array of IDs rather than the messages
-                                 # themselves.
-];
$key

The vector set to query.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|bool

An array of claimed messags or false on failure.

Redis|array|false

An array of information about the vector set or false on failure.

+

See also

@@ -16028,13 +22077,7 @@

See also

- - - - @@ -16045,26 +22088,7 @@

Examples

- https://redis.io/commands/xclaim -
- https://redis.io/commands/xautoclaim. + https://redis.io/docs/latest/commands/vinfo/
- +
$redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true);
-
-$redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']);
-
-// Consume the ['name' => 'Defiant'] message
-$msgs = $redis->xReadGroup('combatants', "Jem'Hadar", ['ships' => '>'], 1);
-
-// The "Jem'Hadar" consumer has the message presently
-$pending = $redis->xPending('ships', 'combatants');
-var_dump($pending);
-
-assert($pending && isset($pending[1]));
-
-// Claim the message by ID.
-$claimed = $redis->xClaim('ships', 'combatants', 'Sisko', 0, [$pending[1]], ['JUSTID']);
-var_dump($claimed);
-
-// Now the 'Sisko' consumer owns the message
-$pending = $redis->xPending('ships', 'combatants');
-var_dump($pending);
$redis->vinfo('embeddings');
@@ -16073,18 +22097,17 @@

Examples

-

- - Redis|int|false - xdel(string $key, array $ids) - + +

+ + Redis|bool vismember(string $key, mixed $member)

-

Remove one or more specific IDs from a stream.

+

Check if an element is a member of a vectorset

Parameters

@@ -16093,33 +22116,46 @@

Parameters

string $key -

The stream to modify.

+

The vector set to query.

- array - $ids -

One or more message IDs to remove.

+ mixed + $member +

The member to check for.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The number of messages removed or false on failure.

Redis|bool

true if the member exists, false if it does not.

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/vismember/ +
+

Examples

- +
$redis->xDel('stream', ['1-1', '2-1', '3-1']);
$redis->vismember('embeddings', 'doc:1');
@@ -16128,19 +22164,17 @@

Examples

-

- - mixed - xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) - + +

+ + Redis|array|false vemb(string $key, mixed $member, bool $raw = false)

-

XGROUP

Perform various operation on consumer groups for a particular Redis STREAM. What the command does -is primarily based on which operation is passed.

+

Get the embeddings for a specific member

Parameters

@@ -16148,62 +22182,33 @@

Parameters

- - - - - - - - - - - + - - - + + + - - - - - - - + +
string$operation

The subcommand you intend to execute. Valid options are as follows -'HELP' - Redis will return information about the command -Requires: none -'CREATE' - Create a consumer group. -Requires: Key, group, consumer. -'SETID' - Set the ID of an existing consumer group for the stream. -Requires: Key, group, id. -'CREATECONSUMER' - Create a new consumer group for the stream. You must -also pass key, group, and the consumer name you wish to -create. -Requires: Key, group, consumer. -'DELCONSUMER' - Delete a consumer from group attached to the stream. -Requires: Key, group, consumer. -'DESTROY' - Delete a consumer group from a stream. -Requires: Key, group.

string $key

The STREAM we're operating on.

string$group

The consumer group we want to create/modify/delete.

The vector set to query.

string$id_or_consumer

The STREAM id (e.g. '$') or consumer group. See the operation section -for information about which to send.

mixed$member

The member to query.

bool$mkstream

This flag may be sent in combination with the 'CREATE' operation, and -cause Redis to also create the STREAM if it doesn't currently exist.

int$entries_read$raw

If set to true, the raw embeddings will be returned

-

Return Value

+

Return Value

- +
+
- - + +
mixed

This command return various results depending on the operation performed.

Redis|array|false

An array of embeddings for the member or false on failure.

+

See also

@@ -16211,30 +22216,37 @@

See also

- https://redis.io/commands/xgroup/ + https://redis.io/docs/latest/commands/vemb/
+

Examples

+ + + + + +
$redis->vemb('embeddings', 'doc:1');
+
-

- - mixed - xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) - + +

+ + Redis|array|string|false vrandmember(string $key, int $count = 0)

-

Retrieve information about a stream key.

+

Get one or more random members from a vector set

Parameters

@@ -16242,36 +22254,28 @@

Parameters

- - - - - - - - - - - - + + - +
string$operation

The specific info operation to perform.

string|null$arg1

The first argument (depends on operation)

string|null$arg2

The second argument

$key

The vector set to query.

int $count

The COUNT argument to XINFO STREAM

The number of random members to return.

-

Return Value

+

Return Value

- +
+
- - + +
mixed

This command can return different things depending on the operation being called.

Redis|array|string|false
+

See also

@@ -16279,7 +22283,7 @@

See also

@@ -16290,13 +22294,7 @@

Examples

- https://redis.io/commands/xinfo + https://redis.io/docs/latest/commands/vrandmember/
- - - - - - - +
$redis->xInfo('CONSUMERS', 'stream');
$redis->xInfo('GROUPS', 'stream');
$redis->xInfo('STREAM', 'stream');
$redis->vrandmember('embeddings', 2);
@@ -16305,18 +22303,17 @@

Examples

-

- - Redis|int|false - xlen(string $key) - + +

+ + Redis|array|false vrange(string $key, string $min, string $max, int $count = -1)

-

Get the number of messages in a Redis STREAM key.

+

Retreive a lexographical range of elements from a vector set

Parameters

@@ -16325,20 +22322,37 @@

Parameters

string $key -

The Stream to check.

+

The vector set to query.

+ + + string + $min +

The minimum element to return.

+ + + string + $max +

The maximum element to return.

+ + + int + $count +

An optional maximum number of elements to return.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|int|false

The number of messages or false on failure.

Redis|array|false

An array of elements in the requested range or false on failure.

+

See also

@@ -16346,7 +22360,7 @@

See also

@@ -16357,7 +22371,7 @@

Examples

- https://redis.io/commands/xlen + https://redis.io/docs/latest/commands/vrange/
- +
$redis->xLen('stream');
$redis->vrange('embeddings', '-', '+', 5);
@@ -16366,19 +22380,17 @@

Examples

-

- - Redis|array|false - xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) - + +

+ + Redis|int|false vrem(string $key, mixed $member)

-

Interact with stream messages that have been consumed by a consumer group but not yet -acknowledged with XACK.

+

Remove an element from a vector set

Parameters

@@ -16387,45 +22399,27 @@

Parameters

string $key -

The stream to inspect.

- - - string - $group -

The user group we want to see pending messages from.

- - - string|null - $start -

The minimum ID to consider.

- - - string|null - $end - - - - int - $count -

Optional maximum number of messages to return.

+

The vector set to remove from.

- string|null - $consumer -

If provided, limit the returned messages to a specific consumer.

+ mixed + $member +

The member to remove.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|false

The pending messages belonging to the stream or false on failure.

Redis|int|false

1 if the member was removed, 0 if it was not.

+

See also

@@ -16433,36 +22427,37 @@

See also

+
- https://redis.io/commands/xpending + https://redis.io/docs/latest/commands/vrem/
+ + +

Examples

+ + - - +
- https://redis.io/commands/xreadgroup -
$redis->vrem('embeddings', 'doc:1');
-
-

- - Redis|array|bool - xrange(string $key, string $start, string $end, int $count = -1) - + +

+ + Redis|int|false vsetattr(string $key, mixed $member, array|string $attributes)

-

Get a range of entries from a STREAM key.

+

Set the attributes of a vector set element

Parameters

@@ -16471,35 +22466,34 @@

Parameters

string $key -

The stream key name to list.

+

The vector set to modify.

- string - $start -

The minimum ID to return.

- - - string - $end -

The maximum ID to return.

+ mixed + $member +

The member to modify.

- int - $count -

An optional maximum number of entries to return.

+ array|string + $attributes +

The attributes to set. This should either +be a json encoded string or an array which +will be json encoded.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|bool

The entries in the stream within the requested range or false on failure.

Redis|int|false

1 if the attributes were set, 0 if they were not.

+

See also

@@ -16507,7 +22501,7 @@

See also

@@ -16518,10 +22512,7 @@

Examples

- https://redis.io/commands/xrange + https://redis.io/docs/latest/commands/vsetattr/
- - - - +
$redis->xRange('stream', '0-1', '0-2');
$redis->xRange('stream', '-', '+');
$redis->vsetattr('embeddings', 'doc:1', ['topic' => 'tech']);
@@ -16530,51 +22521,51 @@

Examples

-

- - Redis|array|bool - xread(array $streams, int $count = -1, int $block = -1) - + +

+ + Redis|array|string|false vgetattr(string $key, mixed $member, bool $decode = true)

-

Consume one or more unconsumed elements in one or more streams.

+

Get the attributes of a vector set element

Parameters

- - - + + + - - - + + + - - - + + +
array$streams

An associative array with stream name keys and minimum id values.

string$key

The vector set to query.

int$count

An optional limit to how many entries are returnd per stream

mixed$member

The member to query.

int$block

An optional maximum number of milliseconds to block the caller if no -data is available on any of the provided streams.

bool$decode

Whether to automatically deserialize any returned json.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|bool

An array of read elements or false if there aren't any.

Redis|array|string|false

An array of attributes for the member or false on failure.

+

See also

@@ -16582,7 +22573,7 @@

See also

@@ -16593,14 +22584,7 @@

Examples

- https://redis.io/commands/xread + https://redis.io/docs/latest/commands/vgetattr/
- +
$redis->xAdd('s03', '3-1', ['title' => 'The Search, Part I']);
-$redis->xAdd('s03', '3-2', ['title' => 'The Search, Part II']);
-$redis->xAdd('s03', '3-3', ['title' => 'The House Of Quark']);
-$redis->xAdd('s04', '4-1', ['title' => 'The Way of the Warrior']);
-$redis->xAdd('s04', '4-3', ['title' => 'The Visitor']);
-$redis->xAdd('s04', '4-4', ['title' => 'Hippocratic Oath']);
-
-$redis->xRead(['s03' => '3-2', 's04' => '4-1']);
$redis->vgetattr('embeddings', 'doc:1');
@@ -16609,18 +22593,17 @@

Examples

-

- - Redis|array|bool - xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) - + +

-

Read one or more messages using a consumer group.

+

Get any adajcent values for a member of a vector set.

Parameters

@@ -16628,41 +22611,33 @@

Parameters

- - - - - - - - - - - - + + - - - + + + - - - + + +
string$group

The consumer group to use.

string$consumer

The consumer to use.

array$streams

An array of stream names and message IDs

$key

The vector set to query.

int$count

Optional maximum number of messages to return

mixed$member

The member to query.

int$block

How long to block if there are no messages available.

bool$withscores

If set to true, the scores of the adjacent values will be returned.

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|bool

Zero or more unread messages or false on failure.

Redis|array|false

An array of adjacent values and their scores, or false on failure.

+

See also

@@ -16670,7 +22645,7 @@

See also

@@ -16681,24 +22656,7 @@

Examples

- https://redis.io/commands/xreadgroup + https://redis.io/docs/latest/commands/vlinks/
- +
$redis->xGroup('CREATE', 'episodes', 'ds9', '0-0', true);
-
-$redis->xAdd('episodes', '1-1', ['title' => 'Emissary: Part 1']);
-$redis->xAdd('episodes', '1-2', ['title' => 'A Man Alone']);
-
-$messages = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
-
-// After having read the two messages, add another
-$redis->xAdd('episodes', '1-3', ['title' => 'Emissary: Part 2']);
-
-// Acknowledge the first two read messages
-foreach ($messages as $stream => $stream_messages) {
- $ids = array_keys($stream_messages);
- $redis->xAck('stream', 'ds9', $ids);
-}
-
-// We can now pick up where we left off, and will only get the final message
-$msgs = $redis->xReadGroup('ds9', 'sisko', ['episodes' => '>']);
$redis->vlinks('embeddings', 'doc:1', true);
@@ -16707,18 +22665,17 @@

Examples

-

- - Redis|array|bool - xrevrange(string $key, string $end, string $start, int $count = -1) - + +

+ + Redis|array|false gcra(string $key, int $maxBurst, int $requestsPerPeriod, int $period, int $tokens = 0)

-

Get a range of entries from a STREAM ke in reverse cronological order.

+

Get rate limiting information

Parameters

@@ -16727,35 +22684,42 @@

Parameters

string $key -

The stream key to query.

+ - string - $end -

The maximum message ID to include.

+ int + $maxBurst + - string - $start -

The minimum message ID to include.

+ int + $requestsPerPeriod + int - $count -

An optional maximum number of messages to include.

+ $period + + + + int + $tokens +

= 0

-

Return Value

+

Return Value

- +
+
- - + +
Redis|array|bool

The entries within the requested range, from newest to oldest.

Redis|array|false
+

See also

@@ -16763,13 +22727,7 @@

See also

- - - - @@ -16780,10 +22738,7 @@

Examples

- https://redis.io/commands/xrevrange -
- https://redis.io/commands/xrange + https://redis.io/docs/latest/commands/gcra/
- - - - +
$redis->xRevRange('stream', '0-2', '0-1');
$redis->xRevRange('stream', '+', '-');
$redis->gcra('user:123', 10, 100, 3600);
@@ -16792,11 +22747,10 @@

Examples

-

- - Redis|int|false - xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) - + +

+ + Redis|int|false xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1)

@@ -16841,15 +22795,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of entries deleted from the stream.

+

See also

@@ -16857,7 +22813,7 @@

See also

@@ -16880,11 +22836,10 @@

Examples

-

- - Redis|int|false - zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) - + +

+ + Redis|int|float|false zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems)

@@ -16906,8 +22861,7 @@

Parameters

+ ]; +

Note: 'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis +will send whichever one is last in the options array.

@@ -16937,16 +22890,18 @@

Parameters

- https://redis.io/commands/xtrim + https://redis.io/docs/latest/commands/xtrim/
array|float $score_or_options

Either the score for the first element, or an array of options.

-

- $options = [
+
 $options = [
      'NX',       # Only update elements that already exist
      'NX',       # Only add new elements but don't update existing ones.
 
@@ -16924,10 +22878,9 @@ 

Parameters

# increment the element by the # provided score, much like ZINCRBY. When this option # is passed, you may only send a single score and member. - ]; - - Note: 'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis - will send whichever one is last in the options array.
mixed
-

Return Value

+

Return Value

- +
+
- +
Redis|int|falseRedis|int|float|false

The return value varies depending on the options passed.

Following is information about the options that may be passed as the second argument:

+

See also

@@ -16954,7 +22909,7 @@

See also

@@ -16965,10 +22920,8 @@

Examples

- https://redis.io/commands/zadd + https://redis.io/docs/latest/commands/zadd/
- - - - +
$redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third');
$redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element');
$redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third');
+$redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element');
@@ -16977,11 +22930,10 @@

Examples

-

- - Redis|int|false - zCard(string $key) - + +

+ + Redis|int|false zCard(string $key)

@@ -16997,20 +22949,22 @@

Parameters

string $key -

The sorted set to retreive cardinality from.

+

The sorted set to retrieve cardinality from.

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of elements in the set or false on failure

+

See also

@@ -17018,7 +22972,7 @@

See also

@@ -17038,11 +22992,10 @@

Examples

-

- - Redis|int|false - zCount(string $key, string $start, string $end) - + +

+ + Redis|int|false zCount(string $key, int|string $start, int|string $end)

@@ -17061,27 +23014,31 @@

Parameters

- + - + - + - +
- https://redis.io/commands/zcard + https://redis.io/docs/latest/commands/zcard/

The sorted set to check.

stringint|string $start

The minimum score to include in the count

stringint|string $end

The maximum score to include in the count

+

NOTE: In addition to a floating point score you may pass the special values of '-inf' and +'+inf' meaning negative and positive infinity, respectively.

-

Return Value

+

Return Value

- +
+
Redis|int|false
+

See also

@@ -17089,7 +23046,7 @@

See also

@@ -17100,13 +23057,9 @@

Examples

- https://redis.io/commands/zcount + https://redis.io/docs/latest/commands/zcount/
- - - - - - - +
$redis->zCount('fruit-rankings', '0', '+inf');
$redis->zCount('fruit-rankings', 50, 60);
$redis->zCount('fruit-rankings', '-inf', 0);
$redis->zCount('fruit-rankings', '0', '+inf');
+$redis->zCount('fruit-rankings', 50, 60);
+$redis->zCount('fruit-rankings', '-inf', 0);
@@ -17115,11 +23068,10 @@

Examples

-

- - Redis|float|false - zIncrBy(string $key, float $value, mixed $member) - + +

+ + Redis|float|false zIncrBy(string $key, float $value, mixed $member)

@@ -17150,15 +23102,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|float|false

The new score of the member or false on failure.

+

See also

@@ -17166,7 +23120,7 @@

See also

@@ -17177,10 +23131,8 @@

Examples

- https://redis.io/commands/zincrby + https://redis.io/docs/latest/commands/zincrby/
- - - - +
$redis->zIncrBy('zs', 5.0, 'bananas');
$redis->zIncrBy('zs', 2.0, 'eggplants');
$redis->zIncrBy('zs', 5.0, 'bananas');
+$redis->zIncrBy('zs', 2.0, 'eggplants');
@@ -17189,18 +23141,17 @@

Examples

-

- - Redis|int|false - zLexCount(string $key, string $min, string $max) - + +

+ + Redis|int|false zLexCount(string $key, string $min, string $max)

-

Count the number of elements in a sorted set whos members fall within the provided +

Count the number of elements in a sorted set whose members fall within the provided lexographical range.

@@ -17225,15 +23176,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of members that fall within the range or false on failure.

+

See also

@@ -17241,7 +23194,7 @@

See also

@@ -17252,7 +23205,7 @@

Examples

- https://redis.io/commands/zlexcount + https://redis.io/docs/latest/commands/zlexcount/
-
$redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
+
$redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
 $redis->zLexCount('captains', '[A', '[S');
@@ -17262,11 +23215,10 @@

Examples

-

- - Redis|array|false - zMscore(string $key, mixed $member, mixed ...$other_members) - + +

+ + Redis|array|false zMscore(string $key, mixed $member, mixed ...$other_members)

@@ -17297,15 +23249,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

An array of the scores of the requested elements.

+

See also

@@ -17313,7 +23267,7 @@

See also

@@ -17324,9 +23278,9 @@

Examples

- https://redis.io/commands/zmscore + https://redis.io/docs/latest/commands/zmscore/
-
$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
-
-$redis->zMScore('zs', 'zero', 'two');
+
$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
+
+$redis->zMScore('zs', 'zero', 'two');
 $redis->zMScore('zs', 'one', 'not-a-member');
@@ -17336,11 +23290,10 @@

Examples

-

- - Redis|array|false - zPopMax(string $key, int $count = null) - + +

+ + Redis|array|false zPopMax(string $key, int|null $count = null)

@@ -17359,22 +23312,24 @@

Parameters

The sorted set to pop elements from.

- int + int|null $count

An optional count of elements to pop.

-

Return Value

+

Return Value

- +
+
- +
Redis|array|false

All of the popped elements with scores or false on fialure.

All of the popped elements with scores or false on failure

+

See also

@@ -17382,7 +23337,7 @@

See also

@@ -17393,9 +23348,9 @@

Examples

- https://redis.io/commands/zpopmax + https://redis.io/docs/latest/commands/zpopmax/
-
$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
-
-$redis->zPopMax('zs');
+
$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
+
+$redis->zPopMax('zs');
 $redis->zPopMax('zs', 2);.
@@ -17405,11 +23360,10 @@

Examples

-

- - Redis|array|false - zPopMin(string $key, int $count = null) - + +

+ + Redis|array|false zPopMin(string $key, int|null $count = null)

@@ -17428,22 +23382,24 @@

Parameters

The sorted set to pop elements from.

- int + int|null $count

An optional count of elements to pop.

-

Return Value

+

Return Value

- +
+
Redis|array|false

The popped elements with their scores or false on failure.

+

See also

@@ -17451,7 +23407,7 @@

See also

@@ -17462,9 +23418,9 @@

Examples

- https://redis.io/commands/zpopmin + https://redis.io/docs/latest/commands/zpopmin/
-
$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
-
-$redis->zPopMin('zs');
+
$redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three');
+
+$redis->zPopMin('zs');
 $redis->zPopMin('zs', 2);
@@ -17474,11 +23430,10 @@

Examples

-

- - Redis|array|false - zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null) - + +

+ + Redis|array|false zRange(string $key, string|int $start, string|int $end, array|bool|null $options = null)

@@ -17498,12 +23453,12 @@

Parameters

The sorted set in question.

- mixed + string|int $start

The starting index we want to return.

- mixed + string|int $end

The final index we want to return.

@@ -17513,27 +23468,29 @@

Parameters

This value may either be an array of options to pass to the command, or for historical purposes a boolean which controls just the 'WITHSCORES' option.

-
$options = [
+
$options = [
     'WITHSCORES' => true,     # Return both scores and members.
     'LIMIT'      => [10, 10], # Start at offset 10 and return 10 elements.
     'REV'                     # Return the elements in reverse order
     'BYSCORE',                # Treat `start` and `end` as scores instead
     'BYLEX'                   # Treat `start` and `end` as lexicographical values.
 ];
-

Note: 'BYLEX' and 'BYSCORE' are mutually exclusive.

+

Note: BYLEX and BYSCORE are mutually exclusive.

-

Return Value

+

Return Value

- +
+
Redis|array|false

An array with matching elements or false on failure.

+

See also

@@ -17541,7 +23498,7 @@

See also

@@ -17552,10 +23509,8 @@

Examples

- https://redis.io/commands/zrange/ + https://redis.io/docs/latest/commands/zrange/
- - - - +
$redis->zRange('zset', 0, -1);
$redis->zRange('zset', '-inf', 'inf', ['byscore' => true]);
$redis->zRange('zset', 0, -1);
+$redis->zRange('zset', '-inf', 'inf', ['byscore']);
@@ -17564,11 +23519,10 @@

Examples

-

- - Redis|array|false - zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) - + +

+ + Redis|array|false zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1)

@@ -17584,7 +23538,7 @@

Parameters

string $key -

The sorted set to retreive elements from

+

The sorted set to retrieve elements from

string @@ -17609,15 +23563,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

An array of matching elements or false on failure.

+

See also

@@ -17625,7 +23581,7 @@

See also

@@ -17636,10 +23592,10 @@

Examples

- https://redis.io/commands/zrangebylex + https://redis.io/docs/latest/commands/zrangebylex/
-
$redis = new Redis(['host' => 'localhost']);
-$redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
-
-$redis->zRangeByLex('captains', '[A', '[S');
+
$redis = new Redis(['host' => 'localhost']);
+$redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer');
+
+$redis->zRangeByLex('captains', '[A', '[S');
 $redis->zRangeByLex('captains', '[A', '[S', 2, 2);
@@ -17649,11 +23605,10 @@

Examples

-

- - Redis|array|false - zRangeByScore(string $key, string $start, string $end, array $options = []) - + +

+ + Redis|array|false zRangeByScore(string $key, string $start, string $end, array $options = [])

@@ -17692,15 +23647,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

The number of matching elements or false on failure.

+

See also

@@ -17708,7 +23665,7 @@

See also

@@ -17719,10 +23676,8 @@

Examples

- https://redis.io/commands/zrangebyscore + https://redis.io/docs/latest/commands/zrangebyscore/
- - - - +
$redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]);
$redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]);
$redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]);
+$redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]);
@@ -17731,11 +23686,10 @@

Examples

-

- - Redis|int|false - zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = NULL) - + +

+ + Redis|int|false zrangestore(string $dstkey, string $srckey, string $start, string $end, array|bool|null $options = null)

@@ -17777,15 +23731,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of elements stored in $dstkey or false on failure.

+

See also

@@ -17793,7 +23749,7 @@

See also

@@ -17806,16 +23762,23 @@

See also

- https://redis.io/commands/zrange/ + https://redis.io/docs/latest/commands/zrange/
+

Examples

+ + + + + +
$redis->zrangestore('recent:leaders', 'leaders', '0', '9');
+
-

- - Redis|string|array - zRandMember(string $key, array $options = null) - + +

+ + Redis|string|array zRandMember(string $key, array|null $options = null)

@@ -17834,25 +23797,27 @@

Parameters

The sorted set to pull random members from.

- array + array|null $options -

One or more options that determine exactly how the command operates.

-

OPTION TYPE MEANING -'COUNT' int The number of random members to return. -'WITHSCORES' bool Whether to return scores and members instead of

+

One or more options that determine exactly how the command operates. +OPTION TYPE MEANING +'COUNT' int The number of random members to return. +'WITHSCORES' bool Whether to return scores and members instead of

-

Return Value

+

Return Value

- +
+
- +
Redis|string|array

One ore more random elements.

One or more random elements.

+

See also

@@ -17860,7 +23825,7 @@

See also

@@ -17880,11 +23845,10 @@

Examples

-

- - Redis|int|false - zRank(string $key, mixed $member) - + +

+ + Redis|int|false zRank(string $key, mixed $member)

@@ -17905,20 +23869,22 @@

Parameters

- +
- https://redis.io/commands/zrandmember + https://redis.io/docs/latest/commands/zrandmember/
mixed $member

The member to test.

-

Return Value

+

Return Value

- +
+
Redis|int|false

The rank of the requested member.

+

See also

@@ -17926,7 +23892,7 @@

See also

@@ -17937,10 +23903,8 @@

Examples

- https://redis.io/commands/zrank + https://redis.io/docs/latest/commands/zrank/
- - - - +
$redis->zRank('zs', 'zero');
$redis->zRank('zs', 'three');
$redis->zRank('zs', 'zero');
+$redis->zRank('zs', 'three');
@@ -17949,11 +23913,10 @@

Examples

-

- - Redis|int|false - zRem(mixed $key, mixed $member, mixed ...$other_members) - + +

+ + Redis|int|false zRem(mixed $key, mixed $member, mixed ...$other_members)

@@ -17984,15 +23947,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of members that were actually removed or false on failure.

+

See also

@@ -18000,7 +23965,7 @@

See also

@@ -18020,11 +23985,10 @@

Examples

-

- - Redis|int|false - zRemRangeByLex(string $key, string $min, string $max) - + +

+ + Redis|int|false zRemRangeByLex(string $key, string $min, string $max)

@@ -18055,15 +24019,17 @@

Parameters

- https://redis.io/commands/zrem + https://redis.io/docs/latest/commands/zrem/
-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of elements removed from the set or false on failure.

+

See also

@@ -18071,7 +24037,7 @@

See also

@@ -18088,10 +24054,8 @@

Examples

- https://redis.io/commands/zremrangebylex + https://redis.io/docs/latest/commands/zremrangebylex/
- - - - +
$redis->zRemRangeByLex('zs', '[a', '(b');
$redis->zRemRangeByLex('zs', '(banana', '(eggplant');
$redis->zRemRangeByLex('zs', '[a', '(b');
+$redis->zRemRangeByLex('zs', '(banana', '(eggplant');
@@ -18100,11 +24064,10 @@

Examples

-

- - Redis|int|false - zRemRangeByRank(string $key, int $start, int $end) - + +

+ + Redis|int|false zRemRangeByRank(string $key, int $start, int $end)

@@ -18120,7 +24083,7 @@

Parameters

string $key -

The sorted set where we wnat to remove members.

+

The sorted set where we want to remove members.

int @@ -18135,15 +24098,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of members removed from the set or false on failure.

+

See also

@@ -18151,7 +24116,7 @@

See also

@@ -18171,11 +24136,10 @@

Examples

-

- - Redis|int|false - zRemRangeByScore(string $key, string $start, string $end) - + +

+ + Redis|int|false zRemRangeByScore(string $key, string $start, string $end)

@@ -18191,7 +24155,7 @@

Parameters

- + @@ -18206,15 +24170,17 @@

Parameters

- https://redis.io/commands/zremrangebyrank + https://redis.io/docs/latest/commands/zremrangebyrank/
string $key

The sorted set where we wnat to remove members.

The sorted set where we want to remove members.

string
-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of members removed from the set or false on failure.

+

See also

@@ -18222,7 +24188,7 @@

See also

@@ -18233,7 +24199,7 @@

Examples

- https://redis.io/commands/zremrangebyrank + https://redis.io/docs/latest/commands/zremrangebyrank/
-
$redis->zAdd('zs', 2, 'two', 4, 'four', 6, 'six');
+
$redis->zAdd('zs', 2, 'two', 4, 'four', 6, 'six');
 $redis->zRemRangeByScore('zs', 2, 4);
@@ -18243,11 +24209,10 @@

Examples

-

- - Redis|array|false - zRevRange(string $key, int $start, int $end, mixed $scores = null) - + +

+ + Redis|array|false zRevRange(string $key, int $start, int $end, mixed $scores = null)

@@ -18278,14 +24243,16 @@

Parameters

mixed $scores - +

Whether or not Redis should also return each members score. See +the example below demonstrating how it may be used.

-

Return Value

+

Return Value

- +
+
Redis|array|false

The members (and possibly scores) of the matching elements or false @@ -18293,6 +24260,7 @@

Return Value

+

See also

@@ -18300,7 +24268,7 @@

See also

@@ -18311,16 +24279,10 @@

Examples

- https://redis.io/commands/zrevrange + https://redis.io/docs/latest/commands/zrevrange/
- - - - - - - - - - +
$redis->zRevRange('zs', 0, -1);
$redis->zRevRange('zs', 2, 3);
$redis->zRevRange('zs', 0, -1, true);
$redis->zRevRange('zs', 0, -1, ['withscores' => true]);
$redis->zRevRange('zs', 0, -1);
+$redis->zRevRange('zs', 2, 3);
+$redis->zRevRange('zs', 0, -1, true);
+$redis->zRevRange('zs', 0, -1, ['withscores' => true]);
@@ -18329,11 +24291,10 @@

Examples

-

- - Redis|array|false - zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) - + +

+ + Redis|array|false zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1)

@@ -18354,12 +24315,12 @@

Parameters

string $max - +

The maximum legographical element to include in the result.

string $min -

The maximum legographical element to include in the result.

+

The minimum lexographical element to include in the result.

int @@ -18374,15 +24335,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

The matching members or false on failure.

+

See also

@@ -18390,7 +24353,7 @@

See also

@@ -18407,10 +24370,8 @@

Examples

- https://redis.io/commands/zrevrangebylex + https://redis.io/docs/latest/commands/zrevrangebylex/
- - - - +
$redis->zRevRangeByLex('captains', '[Q', '[J');
$redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2);
$redis->zRevRangeByLex('captains', '[Q', '[J');
+$redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2);
@@ -18419,11 +24380,10 @@

Examples

-

- - Redis|array|false - zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) - + +

+ + Redis|array|false zRevRangeByScore(string $key, string $max, string $min, array|bool $options = [])

@@ -18455,25 +24415,27 @@

Parameters

array|bool $options

An options array that modifies how the command executes.

-
$options = [
+
$options = [
     'WITHSCORES' => true|false # Whether or not to return scores
     'LIMIT' => [offset, count] # Return a subset of the matching members
 ];
-

NOTE: For legacy reason, you may also simply pass true for the +

NOTE: For legacy reason, you may also simply pass true for the options argument, to mean WITHSCORES.

-

Return Value

+

Return Value

- +
+
Redis|array|false

The matching members in reverse order of score or false on failure.

+

See also

@@ -18481,7 +24443,7 @@

See also

@@ -18492,14 +24454,14 @@

Examples

- https://redis.io/commands/zrevrangebyscore + https://redis.io/docs/latest/commands/zrevrangebyscore/
-
$redis->zadd('oldest-people', 122.4493, 'Jeanne Calment', 119.2932, 'Kane Tanaka',
- 119.2658, 'Sarah Knauss', 118.7205, 'Lucile Randon',
- 117.7123, 'Nabi Tajima', 117.6301, 'Marie-Louise Meilleur',
- 117.5178, 'Violet Brown', 117.3753, 'Emma Morano',
- 117.2219, 'Chiyo Miyako', 117.0740, 'Misao Okawa');
-
-$redis->zRevRangeByScore('oldest-people', 122, 119);
-$redis->zRevRangeByScore('oldest-people', 'inf', 118);
+
$redis->zadd('oldest-people', 122.4493, 'Jeanne Calment', 119.2932, 'Kane Tanaka',
+                              119.2658, 'Sarah Knauss',   118.7205, 'Lucile Randon',
+                              117.7123, 'Nabi Tajima',    117.6301, 'Marie-Louise Meilleur',
+                              117.5178, 'Violet Brown',   117.3753, 'Emma Morano',
+                              117.2219, 'Chiyo Miyako',   117.0740, 'Misao Okawa');
+
+$redis->zRevRangeByScore('oldest-people', 122, 119);
+$redis->zRevRangeByScore('oldest-people', 'inf', 118);
 $redis->zRevRangeByScore('oldest-people', '117.5', '-inf', ['LIMIT' => [0, 1]]);
@@ -18509,11 +24471,10 @@

Examples

-

- - Redis|int|false - zRevRank(string $key, mixed $member) - + +

+ + Redis|int|false zRevRank(string $key, mixed $member)

@@ -18539,9 +24500,10 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The reverse rank (the rank if counted high to low) of the member or @@ -18549,6 +24511,7 @@

Return Value

+

See also

@@ -18556,7 +24519,7 @@

See also

@@ -18567,9 +24530,9 @@

Examples

- https://redis.io/commands/zrevrank + https://redis.io/docs/latest/commands/zrevrank/
-
$redis->zAdd('ds9-characters', 10, 'Sisko', 9, 'Garak', 8, 'Dax', 7, 'Odo');
-
-$redis->zrevrank('ds9-characters', 'Sisko');
+
$redis->zAdd('ds9-characters', 10, 'Sisko', 9, 'Garak', 8, 'Dax', 7, 'Odo');
+
+$redis->zrevrank('ds9-characters', 'Sisko');
 $redis->zrevrank('ds9-characters', 'Garak');
@@ -18579,11 +24542,10 @@

Examples

-

- - Redis|float|false - zScore(string $key, mixed $member) - + +

+ + Redis|float|false zScore(string $key, mixed $member)

@@ -18609,15 +24571,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
- +
Redis|float|false

score of the requested element or false if it is not found.

The score of the requested element or false if it is not found.

+

See also

@@ -18625,7 +24589,7 @@

See also

@@ -18636,7 +24600,7 @@

Examples

- https://redis.io/commands/zscore + https://redis.io/docs/latest/commands/zscore/
-
$redis->zAdd('telescopes', 11.9, 'LBT', 10.4, 'GTC', 10, 'HET');
+
$redis->zAdd('telescopes', 11.9, 'LBT', 10.4, 'GTC', 10, 'HET');
 $redis->zScore('telescopes', 'LBT');
@@ -18646,11 +24610,10 @@

Examples

-

- - Redis|array|false - zdiff(array $keys, array $options = null) - + +

+ + Redis|array|false zdiff(array $keys, array|null $options = null)

@@ -18667,10 +24630,10 @@

Parameters

array $keys -

One ore more sorted sets.

+

One or more sorted sets.

- array + array|null $options

An array which can contain ['WITHSCORES' => true] if you want Redis to return members and scores.

@@ -18678,15 +24641,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

An array of members or false on failure.

+

See also

@@ -18694,7 +24659,7 @@

See also

@@ -18705,10 +24670,10 @@

Examples

- https://redis.io/commands/zdiff + https://redis.io/docs/latest/commands/zdiff/
-
$redis->zAdd('primes', 1, 'one', 3, 'three', 5, 'five');
-$redis->zAdd('evens', 2, 'two', 4, 'four');
-$redis->zAdd('mod3', 3, 'three', 6, 'six');
-
+
$redis->zAdd('primes', 1, 'one', 3, 'three', 5, 'five');
+$redis->zAdd('evens', 2, 'two', 4, 'four');
+$redis->zAdd('mod3', 3, 'three', 6, 'six');
+
 $redis->zDiff(['primes', 'evens', 'mod3']);
@@ -18718,11 +24683,10 @@

Examples

-

- - Redis|int|false - zdiffstore(string $dst, array $keys) - + +

+ + Redis|int|false zdiffstore(string $dst, array $keys)

@@ -18738,7 +24702,7 @@

Parameters

string $dst - +

The destination set name.

array @@ -18748,9 +24712,10 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of elements stored in the destination set or false on @@ -18758,6 +24723,7 @@

Return Value

+

See also

@@ -18765,7 +24731,7 @@

See also

@@ -18779,16 +24745,23 @@

See also

- https://redis.io/commands/zdiff + https://redis.io/docs/latest/commands/zdiff/
+

Examples

+ + + + + +
$redis->zdiffstore('only:new', ['all:users', 'inactive:users']);
+
-

- - Redis|array|false - zinter(array $keys, array|null $weights = null, array|null $options = null) - + +

+ + Redis|array|false zinter(array $keys, array|null $weights = null, array|null $options = null)

@@ -18804,7 +24777,7 @@

Parameters

array $keys -

One ore more sorted sets.

+

One or more sorted sets.

array|null @@ -18821,15 +24794,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

All of the members that exist in every set.

+

See also

@@ -18837,7 +24812,7 @@

See also

@@ -18848,11 +24823,11 @@

Examples

- https://redis.io/commands/zinter + https://redis.io/docs/latest/commands/zinter/
-
$redis->zAdd('TNG', 2, 'Worf', 2.5, 'Data', 4.0, 'Picard');
-$redis->zAdd('DS9', 2.5, 'Worf', 3.0, 'Kira', 4.0, 'Sisko');
-
-$redis->zInter(['TNG', 'DS9']);
-$redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true]);
+
$redis->zAdd('TNG', 2, 'Worf', 2.5, 'Data', 4.0, 'Picard');
+$redis->zAdd('DS9', 2.5, 'Worf', 3.0, 'Kira', 4.0, 'Sisko');
+
+$redis->zInter(['TNG', 'DS9']);
+$redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true]);
 $redis->zInter(['TNG', 'DS9'], NULL, ['withscores' => true, 'aggregate' => 'max']);
@@ -18862,11 +24837,10 @@

Examples

-

- - Redis|int|false - zintercard(array $keys, int $limit = -1) - + +

+ + Redis|int|false zintercard(array $keys, int $limit = -1)

@@ -18883,7 +24857,7 @@

Parameters

array $keys -

One ore more sorted set key names.

+

One or more sorted set key names.

int @@ -18895,15 +24869,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The cardinality of the intersection or false on failure.

+

See also

@@ -18911,13 +24887,13 @@

See also

@@ -18935,9 +24911,9 @@

Examples

- https://redis.io/commands/zintercard + https://redis.io/docs/latest/commands/zintercard/
- https://redis.io/commands/zinter + https://redis.io/docs/latest/commands/zinter/
-
$redis->zAdd('zs1', 1, 'one', 2, 'two', 3, 'three', 4, 'four');
-$redis->zAdd('zs2', 2, 'two', 4, 'four');
-
+
$redis->zAdd('zs1', 1, 'one', 2, 'two', 3, 'three', 4, 'four');
+$redis->zAdd('zs2', 2, 'two', 4, 'four');
+
 $redis->zInterCard(['zs1', 'zs2']);
@@ -18947,18 +24923,17 @@

Examples

-

- - Redis|int|false - zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) - + +

+ + Redis|int|false zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null)

-

Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

+

Compute the intersection of one or more sorted sets storing the result in a new sorted set.

Parameters

@@ -18972,7 +24947,7 @@

Parameters

array $keys -

One ore more sorted set key names.

+

One or more sorted set key names.

array|null @@ -18982,23 +24957,25 @@

Parameters

string|null $aggregate -

An optional aggregation method to use.

-

'SUM' - Store sum of all intersected members (this is the default). +

An optional aggregation method to use. +'SUM' - Store sum of all intersected members (this is the default). 'MIN' - Store minimum value for each intersected member. 'MAX' - Store maximum value for each intersected member.

-

Return Value

+

Return Value

- +
+
Redis|int|false

The total number of members writtern to the destination set or false on failure.

+

See also

@@ -19006,13 +24983,13 @@

See also

@@ -19023,11 +25000,11 @@

Examples

- https://redis.io/commands/zinterstore + https://redis.io/docs/latest/commands/zinterstore/
- https://redis.io/commands/zinter + https://redis.io/docs/latest/commands/zinter/
-
$redis->zAdd('zs1', 3, 'apples', 2, 'pears');
-$redis->zAdd('zs2', 4, 'pears', 3, 'bananas');
-$redis->zAdd('zs3', 2, 'figs', 3, 'pears');
-
-$redis->zInterStore('fruit-sum', ['zs1', 'zs2', 'zs3']);
+
$redis->zAdd('zs1', 3, 'apples', 2, 'pears');
+$redis->zAdd('zs2', 4, 'pears', 3, 'bananas');
+$redis->zAdd('zs3', 2, 'figs', 3, 'pears');
+
+$redis->zInterStore('fruit-sum', ['zs1', 'zs2', 'zs3']);
 $redis->zInterStore('fruit-max', ['zs1', 'zs2', 'zs3'], NULL, 'MAX');
@@ -19037,11 +25014,10 @@

Examples

-

- - Redis|array|false - zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) - + +

+ + Redis|array|false zscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

@@ -19060,7 +25036,7 @@

Parameters

The sorted set to scan.

- int|null + null|int|string $iterator

A reference to an iterator that should be initialized to NULL initially, that will be updated after each subsequent call to ZSCAN. Once the iterator @@ -19082,15 +25058,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

An array of elements or false on failure.

+

See also

@@ -19098,13 +25076,13 @@

See also

@@ -19118,16 +25096,28 @@

See also

- https://redis.io/commands/zscan + https://redis.io/docs/latest/commands/zscan/
- https://redis.io/commands/scan + https://redis.io/docs/latest/commands/scan/
+

Examples

+ + + + + +
$it = null;
+while ($members = $redis->zscan('leaders', $it)) {
+    foreach ($members as $member => $score) {
+        printf('%s => %s' . PHP_EOL, $member, $score);
+    }
+}
+
-

- - Redis|array|false - zunion(array $keys, array|null $weights = null, array|null $options = null) - + +

+ + Redis|array|false zunion(array $keys, array|null $weights = null, array|null $options = null)

@@ -19143,7 +25133,7 @@

Parameters

array $keys -

One ore more sorted set key names

+

One or more sorted set key names

array|null @@ -19156,7 +25146,7 @@

Parameters

array|null $options

An array that modifies how this command functions.

-
$options = [
+
$options = [
     # By default when members exist in more than one set Redis will SUM
     # total score for each match.  Instead, it can return the AVG, MIN,
     # or MAX value based on this option.
@@ -19169,29 +25159,42 @@ 

Parameters

-

Return Value

+

Return Value

- +
+
Redis|array|false

The union of each sorted set or false on failure

+
+

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/zunion/ +
+

Examples

-
$redis->del('store1', 'store2', 'store3');
-$redis->zAdd('store1', 1, 'apples', 3, 'pears', 6, 'bananas');
-$redis->zAdd('store2', 3, 'apples', 5, 'coconuts', 2, 'bananas');
-$redis->zAdd('store3', 2, 'bananas', 6, 'apples', 4, 'figs');
-
-$redis->zUnion(['store1', 'store2', 'store3'], NULL, ['withscores' => true]);
-$redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true]);
+
$redis->del('store1', 'store2', 'store3');
+$redis->zAdd('store1', 1, 'apples', 3, 'pears', 6, 'bananas');
+$redis->zAdd('store2', 3, 'apples', 5, 'coconuts', 2, 'bananas');
+$redis->zAdd('store3', 2, 'bananas', 6, 'apples', 4, 'figs');
+
+$redis->zUnion(['store1', 'store2', 'store3'], NULL, ['withscores' => true]);
+$redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true]);
 $redis->zUnion(['store1', 'store3'], [2, .5], ['withscores' => true, 'aggregate' => 'MIN']);
@@ -19201,11 +25204,10 @@

Examples

-

- - Redis|int|false - zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) - + +

+ + Redis|int|false zunionstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null)

@@ -19242,15 +25244,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
Redis|int|false

The number of members stored in the destination set or false on failure.

+

See also

@@ -19258,7 +25262,7 @@

See also

@@ -19276,10 +25280,10 @@

Examples

- https://redis.io/commands/zunionstore + https://redis.io/docs/latest/commands/zunionstore/
-
$redis->zAdd('zs1', 1, 'one', 3, 'three');
-$redis->zAdd('zs1', 2, 'two', 4, 'four');
-$redis->zadd('zs3', 1, 'one', 7, 'five');
-
+
$redis->zAdd('zs1', 1, 'one', 3, 'three');
+$redis->zAdd('zs1', 2, 'two', 4, 'four');
+$redis->zadd('zs3', 1, 'one', 7, 'five');
+
 $redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']);
@@ -19287,6 +25291,68 @@

Examples

+
+
+ +

+ + Redis|string|false digest(string $key) +

+
+ + + +
+

Ask the server for the XXH3 digest of a given key's value

+
+
+

Parameters

+ + + + + + + +
string$key

The key to retrieve the digest for.

+ + +

Return Value

+ +
+ + + + + +
Redis|string|false

The XXH3 digest as a string or false on failure.

+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/docs/latest/commands/digest/ +
+ + +

Examples

+ + + + + +
$redis->digest('session:42');
+ +
+
+
diff --git a/docs/RedisArray.html b/docs/RedisArray.html index 2a8ffb4de1..c8d5b9b629 100644 --- a/docs/RedisArray.html +++ b/docs/RedisArray.html @@ -5,7 +5,7 @@ RedisArray | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> @@ -83,7 +472,7 @@

RedisArray

class - RedisArray (View source) + RedisArray (View source)

@@ -113,7 +502,7 @@

Methods

- __construct(string|array $name_or_hosts, array $options = NULL) + __construct(string|array $name_or_hosts, array|null $options = null)

No description

@@ -179,7 +568,7 @@

Methods

bool|null
- _rehash(callable $fn = NULL) + _rehash(callable|null $fn = null)

No description

@@ -231,7 +620,7 @@

Methods

- bool|null + bool|null|array
exec() @@ -278,7 +667,7 @@

Methods

bool|array
- hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + hscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

No description

@@ -333,7 +722,7 @@

Methods

bool|RedisArray
- multi(string $host, int $mode = NULL) + multi(string $host, int|null $mode = null)

No description

@@ -366,7 +755,7 @@

Methods

bool|array
- scan(int|null $iterator, string $node, string|null $pattern = null, int $count = 0) + scan(null|int|string $iterator, string $node, string|null $pattern = null, int $count = 0)

No description

@@ -399,7 +788,7 @@

Methods

bool|array
- sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + sscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

No description

@@ -432,24 +821,15252 @@

Methods

bool|array
- zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + zscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

No description

+
+
+ mixed +
+
+ acl(string $subcmd, string $args) + +

No description

+
+
+
+
+ RedisArray|int|false +
+
+ append(string $key, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ auth(SensitiveParameter] $ixed $credentials) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ bgrewriteaof() + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ waitaof(int $numlocal, int $numreplicas, int $timeout) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ bitcount(string $key, int $start, int $end = -1, bool $bybit = false) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ bitop(string $operation, string $deskey, string $srckey, string $other_keys) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ bitpos(string $key, bool $bit, int $start, int $end = -1, bool $bybit = false) + +

No description

+
+
+
+
+
+ RedisArray|array|null|false +
+
+ blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed $extra_args) + +

No description

+
+
+
+
+
+ RedisArray|array|null|false +
+
+ brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed $extra_args) + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ brpoplpush(string $src, string $dst, int|float $timeout) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ bzPopMax(string|array $key, string|int $timeout_or_key, mixed $extra_args) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ bzPopMin(string|array $key, string|int $timeout_or_key, mixed $extra_args) + +

No description

+
+
+
+
+
+ RedisArray|array|null|false +
+
+ bzmpop(float $timeout, array $keys, string $from, int $count = 1) + +

No description

+
+
+
+
+
+ RedisArray|array|null|false +
+
+ zmpop(array $keys, string $from, int $count = 1) + +

No description

+
+
+
+
+
+ RedisArray|array|null|false +
+
+ blmpop(float $timeout, array $keys, string $from, int $count = 1) + +

No description

+
+
+
+
+
+ RedisArray|array|null|false +
+
+ lmpop(array $keys, string $from, int $count = 1) + +

No description

+
+
+
+
+
+ bool +
+
+ clearLastError() + +

No description

+
+
+
+
+
+ mixed +
+
+ client(string $opt, mixed $args) + +

No description

+
+
+
+
+
+ bool +
+
+ close() + +

No description

+
+
+
+
+
+ mixed +
+
+ command(?string $opt = null, mixed $args) + +

No description

+
+
+
+
+
+ mixed +
+
+ config(string $operation, array|string|null $key_or_settings = null, ?string $value = null) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ copy(string $src, string $dst, ?array $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ dbSize() + +

No description

+
+
+
+
+
+ RedisArray|string +
+
+ debug(string $key) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ decr(string $key, int $by = 1) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ decrBy(string $key, int $value) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ delex(string $key, ?array $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ delifeq(string $key, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ delete(array|string $key, string $other_keys) + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ dump(string $key) + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ echo(string $str) + +

No description

+
+
+
+
+
+ mixed +
+
+ eval(string $script, array $args = [], int $num_keys) + +

No description

+
+
+
+
+
+ mixed +
+
+ eval_ro(string $script_sha, array $args = [], int $num_keys) + +

No description

+
+
+
+
+
+ mixed +
+
+ evalsha(string $sha1, array $args = [], int $num_keys) + +

No description

+
+
+
+
+
+ mixed +
+
+ evalsha_ro(string $sha1, array $args = [], int $num_keys) + +

No description

+
+
+
+
+
+ RedisArray|int|bool +
+
+ exists(mixed $key, mixed $other_keys) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ expire(string $key, int $timeout, ?string $mode = null) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ expireAt(string $key, int $timestamp, ?string $mode = null) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ failover(?array $to = null, bool $abort = false, int $timeout) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ expiretime(string $key) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ pexpiretime(string $key) + +

No description

+
+
+
+
+
+ mixed +
+
+ fcall(string $fn, array $keys = [], array $args = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ fcall_ro(string $fn, array $keys = [], array $args = []) + +

No description

+
+
+
+
+
+ RedisArray|bool|string|array +
+
+ function(string $operation, mixed $args) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ geoadd(string $key, float $lng, float $lat, string $member, mixed $other_triples_and_options) + +

No description

+
+
+
+
+
+ RedisArray|float|false +
+
+ geodist(string $key, string $src, string $dst, ?string $unit = null) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ geohash(string $key, string $member, string $other_members) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ geopos(string $key, string $member, string $other_members) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ array +
+
+ geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ RedisArray|array|int|false +
+
+ geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) + +

No description

+
+
+
+
+
+ mixed +
+
+ get(string $key) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ getWithMeta(string $key) + +

No description

+
+
+
+
+
+ mixed +
+
+ getAuth() + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ getBit(string $key, int $idx) + +

No description

+
+
+
+
+
+ RedisArray|string|bool +
+
+ getEx(string $key, array $options = []) + +

No description

+
+
+
+
+
+ int +
+
+ getDBNum() + +

No description

+
+
+
+
+
+ RedisArray|string|bool +
+
+ getDel(string $key) + +

No description

+
+
+
+
+
+ string +
+
+ getHost() + +

No description

+
+
+
+
+
+ string|null +
+
+ getLastError() + +

No description

+
+
+
+
+
+ int +
+
+ getMode() + +

No description

+
+
+
+
+
+ string|null +
+
+ getPersistentID() + +

No description

+
+
+
+
+
+ int +
+
+ getPort() + +

No description

+
+
+
+
+
+ string|false +
+
+ serverName() + +

No description

+
+
+
+
+
+ string|false +
+
+ serverVersion() + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ getRange(string $key, int $start, int $end) + +

No description

+
+
+
+
+
+ RedisArray|string|array|int|false +
+
+ lcs(string $key1, string $key2, ?array $options = null) + +

No description

+
+
+
+
+
+ float +
+
+ getReadTimeout() + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ getset(string $key, mixed $value) + +

No description

+
+
+
+
+
+ float|false +
+
+ getTimeout() + +

No description

+
+
+
+
+
+ array +
+
+ getTransferredBytes() + +

No description

+
+
+
+
+
+ void +
+
+ clearTransferredBytes() + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ hDel(string $key, string $field, string $other_fields) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ hExists(string $key, string $field) + +

No description

+
+
+
+
+
+ mixed +
+
+ hGet(string $key, string $member) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hGetAll(string $key) + +

No description

+
+
+
+
+
+ mixed +
+
+ hGetWithMeta(string $key, string $member) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ hIncrBy(string $key, string $field, int $value) + +

No description

+
+
+
+
+
+ RedisArray|float|false +
+
+ hIncrByFloat(string $key, string $field, float $value) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hKeys(string $key) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ hLen(string $key) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hMget(string $key, array $fields) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hgetex(string $key, array $fields, string|array|null $expiry = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ hsetex(string $key, array $fields, ?array $expiry = null) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hgetdel(string $key, array $fields) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ hMset(string $key, array $fieldvals) + +

No description

+
+
+
+
+
+ RedisArray|string|array|false +
+
+ hRandField(string $key, ?array $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ hSet(string $key, mixed $fields_and_vals) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ hSetNx(string $key, string $field, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ hStrLen(string $key, string $field) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hVals(string $key) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ httl(string $key, array $fields) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hpttl(string $key, array $fields) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hexpiretime(string $key, array $fields) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hpexpiretime(string $key, array $fields) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ hpersist(string $key, array $fields) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ expiremember(string $key, string $field, int $ttl, ?string $unit = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ expirememberat(string $key, string $field, int $timestamp) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ incr(string $key, int $by = 1) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ incrBy(string $key, int $value) + +

No description

+
+
+
+
+
+ RedisArray|float|false +
+
+ incrByFloat(string $key, float $value) + +

No description

+
+
+
+
+
+ bool +
+
+ isConnected() + +

No description

+
+
+
+
+
+ void +
+
+ lInsert(string $key, string $pos, mixed $pivot, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ lLen(string $key) + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ lMove(string $src, string $dst, string $wherefrom, string $whereto) + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout) + +

No description

+
+
+
+
+
+ RedisArray|bool|string|array +
+
+ lPop(string $key, int $count) + +

No description

+
+
+
+
+
+ RedisArray|null|bool|int|array +
+
+ lPos(string $key, mixed $value, ?array $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ lPush(string $key, mixed $elements) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ rPush(string $key, mixed $elements) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ lPushx(string $key, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ rPushx(string $key, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ lSet(string $key, int $index, mixed $value) + +

No description

+
+
+
+
+
+ int +
+
+ lastSave() + +

No description

+
+
+
+
+
+ mixed +
+
+ lindex(string $key, int $index) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ lrange(string $key, int $start, int $end) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ lrem(string $key, mixed $value, int $count) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ ltrim(string $key, int $start, int $end) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ move(string $key, int $index) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ msetex(array $key_vals, int|float|array|null $expiry = null) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ msetnx(array $key_values) + +

No description

+
+
+
+
+
+ RedisArray|int|string|false +
+
+ object(string $subcommand, string $key) + +

No description

+
+
+
+
+
+ bool +
+
+ open(string $host, int $port = 6379, float $timeout, ?string $persistent_id = null, int $retry_interval, float $read_timeout, ?array $context = null) + +

No description

+
+
+
+
+
+ bool +
+
+ pconnect(string $host, int $port = 6379, float $timeout, ?string $persistent_id = null, int $retry_interval, float $read_timeout, ?array $context = null) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ persist(string $key) + +

No description

+
+
+
+
+
+ bool +
+
+ pexpire(string $key, int $timeout, ?string $mode = null) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ pexpireAt(string $key, int $timestamp, ?string $mode = null) + +

No description

+
+
+
+
+
+ RedisArray|int +
+
+ pfadd(string $key, array $elements) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ pfcount(array|string $key_or_keys) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ pfmerge(string $dst, array $srckeys) + +

No description

+
+
+
+
+
+ RedisArray +
+
+ pipeline() + +

No description

+
+
+
+
+
+ bool +
+
+ popen(string $host, int $port = 6379, float $timeout, ?string $persistent_id = null, int $retry_interval, float $read_timeout, ?array $context = null) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ psetex(string $key, int $expire, mixed $value) + +

No description

+
+
+
+
+
+ bool +
+
+ psubscribe(array $patterns, callable $cb) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ pttl(string $key) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ publish(string $channel, string $message) + +

No description

+
+
+
+
+
+ mixed +
+
+ pubsub(string $command, mixed $arg = null) + +

No description

+
+
+
+
+
+ RedisArray|array|bool +
+
+ punsubscribe(array $patterns) + +

No description

+
+
+
+
+
+ RedisArray|array|string|bool +
+
+ rPop(string $key, int $count) + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ randomKey() + +

No description

+
+
+
+
+
+ mixed +
+
+ rawcommand(string $command, mixed $args) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ rename(string $old_name, string $new_name) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ renameNx(string $key_src, string $key_dst) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ reset() + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ restore(string $key, int $ttl, string $value, ?array $options = null) + +

No description

+
+
+
+
+
+ mixed +
+
+ role() + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ rpoplpush(string $srckey, string $dstkey) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ sAdd(string $key, mixed $value, mixed $other_values) + +

No description

+
+
+
+
+
+ int +
+
+ sAddArray(string $key, array $values) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ sDiff(string $key, string $other_keys) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ sDiffStore(string $dst, string $key, string $other_keys) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ sInter(array|string $key, string $other_keys) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ sintercard(array $keys, int $limit = -1) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ sInterStore(array|string $key, string $other_keys) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ sMembers(string $key) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ sMisMember(string $key, string $member, string $other_members) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ sMove(string $src, string $dst, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|string|array|false +
+
+ sPop(string $key, int $count) + +

No description

+
+
+
+
+
+ mixed +
+
+ sRandMember(string $key, int $count) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ sUnion(string $key, string $other_keys) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ sUnionStore(string $dst, string $key, string $other_keys) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ scard(string $key) + +

No description

+
+
+
+
+
+ mixed +
+
+ script(string $command, mixed $args) + +

No description

+
+
+
+
+
+ RedisArray|string|bool +
+
+ set(string $key, mixed $value, mixed $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ setBit(string $key, int $idx, bool $value) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ setRange(string $key, int $index, string $value) + +

No description

+
+
+
+
+
+ void +
+
+ setex(string $key, int $expire, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ setnx(string $key, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ sismember(string $key, mixed $value) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ slaveof(?string $host = null, int $port = 6379) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ replicaof(?string $host = null, int $port = 6379) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ touch(array|string $key_or_array, string $more_keys) + +

No description

+
+
+
+
+
+ mixed +
+
+ slowlog(string $operation, int $length) + +

No description

+
+
+
+
+
+ mixed +
+
+ sort(string $key, ?array $options = null) + +

No description

+
+
+
+
+
+ mixed +
+
+ sort_ro(string $key, ?array $options = null) + +

No description

+
+
+
+
+
+ array +
+
+ sortAsc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) + +

No description

+
+
+
+
+
+ array +
+
+ sortAscAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) + +

No description

+
+
+
+
+
+ array +
+
+ sortDesc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) + +

No description

+
+
+
+
+
+ array +
+
+ sortDescAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ srem(string $key, mixed $value, mixed $other_values) + +

No description

+
+
+
+
+
+ bool +
+
+ ssubscribe(array $channels, callable $cb) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ strlen(string $key) + +

No description

+
+
+
+
+
+ bool +
+
+ subscribe(array $channels, callable $cb) + +

No description

+
+
+
+
+
+ RedisArray|array|bool +
+
+ sunsubscribe(array $channels) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ swapdb(int $src, int $dst) + +

No description

+
+
+
+
+
+ RedisArray|array +
+
+ time() + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ ttl(string $key) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ type(string $key) + +

No description

+
+
+
+
+
+ RedisArray|array|bool +
+
+ unsubscribe(array $channels) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ watch(array|string $key, string $other_keys) + +

No description

+
+
+
+
+
+ int|false +
+
+ wait(int $numreplicas, int $timeout) + +

No description

+
+
+
+
+
+ int|false +
+
+ xack(string $key, string $group, array $ids) + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ xadd(string $key, string $id, array $values, int $maxlen, bool $approx = false, bool $nomkstream = false) + +

No description

+
+
+
+
+
+ RedisArray|bool|array +
+
+ xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) + +

No description

+
+
+
+
+
+ RedisArray|array|bool +
+
+ xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ xdel(string $key, array $ids) + +

No description

+
+
+
+
+
+ Relay|array|false +
+
+ xdelex(string $key, array $ids, ?string $mode = null) + +

No description

+
+
+
+
+
+ mixed +
+
+ xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ xlen(string $key) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null) + +

No description

+
+
+
+
+
+ RedisArray|array|bool +
+
+ xrange(string $key, string $start, string $end, int $count = -1) + +

No description

+
+
+
+
+
+ RedisArray|array|bool +
+
+ xread(array $streams, int $count = -1, int $block = -1) + +

No description

+
+
+
+
+
+ RedisArray|array|bool +
+
+ xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) + +

No description

+
+
+
+
+
+ RedisArray|array|bool +
+
+ xrevrange(string $key, string $end, string $start, int $count = -1) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ vadd(string $key, array $values, mixed $element, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ vsim(string $key, mixed $member, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ vcard(string $key) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ vdim(string $key) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ vinfo(string $key) + +

No description

+
+
+
+
+
+ RedisArray|bool +
+
+ vismember(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ vemb(string $key, mixed $member, bool $raw = false) + +

No description

+
+
+
+
+
+ RedisArray|array|string|false +
+
+ vrandmember(string $key, int $count) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ vrange(string $key, string $min, string $max, int $count = -1) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ vrem(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ vsetattr(string $key, mixed $member, array|string $attributes) + +

No description

+
+
+
+
+
+ RedisArray|array|string|false +
+
+ vgetattr(string $key, mixed $member, bool $decode = true) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ vlinks(string $key, mixed $member, bool $withscores = false) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) + +

No description

+
+
+
+
+
+ RedisArray|int|float|false +
+
+ zAdd(string $key, array|float $score_or_options, mixed $more_scores_and_mems) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zCard(string $key) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zCount(string $key, int|string $start, int|string $end) + +

No description

+
+
+
+
+
+ RedisArray|float|false +
+
+ zIncrBy(string $key, float $value, mixed $member) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zLexCount(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zMscore(string $key, mixed $member, mixed $other_members) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zPopMax(string $key, ?int $count = null) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zPopMin(string $key, ?int $count = null) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zRange(string $key, string|int $start, string|int $end, array|bool|null $options = null) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zRangeByScore(string $key, string $start, string $end, array $options = []) + +

No description

+
+
+
+
+
+ RedisArray|string|array +
+
+ zRandMember(string $key, ?array $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zRank(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zRem(mixed $key, mixed $member, mixed $other_members) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zRemRangeByLex(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zRemRangeByRank(string $key, int $start, int $end) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zRemRangeByScore(string $key, string $start, string $end) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zRevRange(string $key, int $start, int $end, mixed $scores = null) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zRevRank(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisArray|float|false +
+
+ zScore(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zdiff(array $keys, ?array $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zdiffstore(string $dst, array $keys) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zinter(array $keys, ?array $weights = null, ?array $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zintercard(array $keys, int $limit = -1) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null) + +

No description

+
+
+
+
+
+ RedisArray|array|false +
+
+ zunion(array $keys, ?array $weights = null, ?array $options = null) + +

No description

+
+
+
+
+
+ RedisArray|int|false +
+
+ zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null) + +

No description

+
+
+
+
+
+ RedisArray|string|false +
+
+ digest(string $key) + +

No description

+
+
+
+
+ + +

Details

+ +
+
+ +

+ + mixed __call(string $function_name, array $arguments) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$function_name
array$arguments
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + __construct(string|array $name_or_hosts, array|null $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$name_or_hosts
array|null$options
+ + + + + +
+
+ +
+
+ +

+ + bool|array _continuum() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|callable _distributor() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|callable
+ +
+ + + +
+
+ +
+
+ +

+ + bool|callable _function() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|callable
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array _hosts() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|null|Redis _instance(string $host) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$host
+ + +

Return Value

+ +
+ + + + + +
bool|null|Redis
+ +
+ + + +
+
+ +
+
+ +

+ + bool|null _rehash(callable|null $fn = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
callable|null$fn
+ + +

Return Value

+ +
+ + + + + +
bool|null
+ +
+ + + +
+
+ +
+
+ +

+ + bool|string|null _target(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
bool|string|null
+ +
+ + + +
+
+ +
+
+ +

+ + array bgsave() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|int del(string|array $key, string ...$otherkeys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key
string...$otherkeys
+ + +

Return Value

+ +
+ + + + + +
bool|int
+ +
+ + + +
+
+ +
+
+ +

+ + bool|null discard() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|null
+ +
+ + + +
+
+ +
+
+ +

+ + bool|null|array exec() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|null|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array flushall() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array flushdb() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array getOption(int $opt) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
int$opt
+ + +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array hscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
null|int|string$iterator
string|null$pattern
int$count
+ + +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array info() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array keys(string $pattern) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$pattern
+ + +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array mget(array $keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$keys
+ + +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool mset(array $pairs) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$pairs
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + bool|RedisArray multi(string $host, int|null $mode = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$host
int|null$mode
+ + +

Return Value

+ +
+ + + + + +
bool|RedisArray
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array ping() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array save() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array scan(null|int|string $iterator, string $node, string|null $pattern = null, int $count = 0) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
null|int|string$iterator
string$node
string|null$pattern
int$count
+ + +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array select(int $index) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
int$index
+ + +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array setOption(int $opt, string $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
int$opt
string$value
+ + +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array sscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
null|int|string$iterator
string|null$pattern
int$count
+ + +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ + +
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key
string...$otherkeys
+ + +

Return Value

+ +
+ + + + + +
bool|int
+ +
+ + + +
+
+ +
+
+ +

+ + bool|null unwatch() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool|null
+ +
+ + + +
+
+ +
+
+ +

+ + bool|array zscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
null|int|string$iterator
string|null$pattern
int$count
+ + +

Return Value

+ +
+ + + + + +
bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + mixed acl(string $subcmd, string $args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$subcmd
string$args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false append(string $key, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool auth(SensitiveParameter] $ixed $credentials) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
SensitiveParameter]$ixed $credentials
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool bgrewriteaof() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false waitaof(int $numlocal, int $numreplicas, int $timeout) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
int$numlocal
int$numreplicas
int$timeout
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false bitcount(string $key, int $start, int $end = -1, bool $bybit = false) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
bool$bybit
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false bitop(string $operation, string $deskey, string $srckey, string $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$operation
string$deskey
string$srckey
string$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false bitpos(string $key, bool $bit, int $start, int $end = -1, bool $bybit = false) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
bool$bit
int$start
int$end
bool$bybit
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|null|false blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed $extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_keys
string|float|int$timeout_or_key
mixed$extra_args
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|null|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|null|false brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed $extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_keys
string|float|int$timeout_or_key
mixed$extra_args
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|null|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false brpoplpush(string $src, string $dst, int|float $timeout) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$src
string$dst
int|float$timeout
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false bzPopMax(string|array $key, string|int $timeout_or_key, mixed $extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|int$timeout_or_key
mixed$extra_args
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false bzPopMin(string|array $key, string|int $timeout_or_key, mixed $extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|int$timeout_or_key
mixed$extra_args
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|null|false bzmpop(float $timeout, array $keys, string $from, int $count = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
float$timeout
array$keys
string$from
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|null|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|null|false zmpop(array $keys, string $from, int $count = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys
string$from
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|null|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|null|false blmpop(float $timeout, array $keys, string $from, int $count = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
float$timeout
array$keys
string$from
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|null|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|null|false lmpop(array $keys, string $from, int $count = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys
string$from
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|null|false
+ +
+ + + +
+
+ +
+
+ +

+ + bool clearLastError() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + mixed client(string $opt, mixed $args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$opt
mixed$args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + bool close() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + mixed command(?string $opt = null, mixed $args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
?string$opt
mixed$args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed config(string $operation, array|string|null $key_or_settings = null, ?string $value = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$operation
array|string|null$key_or_settings
?string$value
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool copy(string $src, string $dst, ?array $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$src
string$dst
?array$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false dbSize() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string debug(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false decr(string $key, int $by = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$by
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false decrBy(string $key, int $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false delex(string $key, ?array $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
?array$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false delifeq(string $key, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false delete(array|string $key, string $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false dump(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false echo(string $str) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$str
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed eval(string $script, array $args = [], int $num_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script
array$args
int$num_keys
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed eval_ro(string $script_sha, array $args = [], int $num_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script_sha
array$args
int$num_keys
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed evalsha(string $sha1, array $args = [], int $num_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$sha1
array$args
int$num_keys
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed evalsha_ro(string $sha1, array $args = [], int $num_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$sha1
array$args
int$num_keys
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|bool exists(mixed $key, mixed $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
mixed$key
mixed$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool expire(string $key, int $timeout, ?string $mode = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timeout
?string$mode
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool expireAt(string $key, int $timestamp, ?string $mode = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timestamp
?string$mode
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool failover(?array $to = null, bool $abort = false, int $timeout) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
?array$to
bool$abort
int$timeout
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false expiretime(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false pexpiretime(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed fcall(string $fn, array $keys = [], array $args = []) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$fn
array$keys
array$args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed fcall_ro(string $fn, array $keys = [], array $args = []) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$fn
array$keys
array$args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool|string|array function(string $operation, mixed $args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$operation
mixed$args
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool|string|array
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false geoadd(string $key, float $lng, float $lat, string $member, mixed $other_triples_and_options) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
float$lng
float$lat
string$member
mixed$other_triples_and_options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|float|false geodist(string $key, string $src, string $dst, ?string $unit = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$src
string$dst
?string$unit
+ + +

Return Value

+ +
+ + + + + +
RedisArray|float|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false geohash(string $key, string $member, string $other_members) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
string$other_members
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false geopos(string $key, string $member, string $other_members) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
string$other_members
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
float$lng
float$lat
float$radius
string$unit
array$options
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
float$lng
float$lat
float$radius
string$unit
array$options
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$member
float$radius
string$unit
array$options
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$member
float$radius
string$unit
array$options
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + array geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
array|string$position
array|int|float$shape
string$unit
array$options
+ + +

Return Value

+ +
+ + + + + +
array
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|int|false geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$dst
string$src
array|string$position
array|int|float$shape
string$unit
array$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed get(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false getWithMeta(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed getAuth() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false getBit(string $key, int $idx) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$idx
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|bool getEx(string $key, array $options = []) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|bool
+ +
+ + + +
+
+ +
+
+ +

+ + int getDBNum() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
int
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|bool getDel(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|bool
+ +
+ + + +
+
+ +
+
+ +

+ + string getHost() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
string
+ +
+ + + +
+
+ +
+
+ +

+ + string|null getLastError() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
string|null
+ +
+ + + +
+
+ +
+
+ +

+ + int getMode() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
int
+ +
+ + + +
+
+ +
+
+ +

+ + string|null getPersistentID() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
string|null
+ +
+ + + +
+
+ +
+
+ +

+ + int getPort() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
int
+ +
+ + + +
+
+ +
+
+ +

+ + string|false serverName() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
string|false
+ +
+ + + +
+
+ +
+
+ +

+ + string|false serverVersion() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
string|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false getRange(string $key, int $start, int $end) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|array|int|false lcs(string $key1, string $key2, ?array $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key1
string$key2
?array$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|array|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + float getReadTimeout() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
float
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false getset(string $key, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + float|false getTimeout() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
float|false
+ +
+ + + +
+
+ +
+
+ +

+ + array getTransferredBytes() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
array
+ +
+ + + +
+
+ +
+
+ +

+ + void clearTransferredBytes() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
void
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false hDel(string $key, string $field, string $other_fields) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$field
string$other_fields
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool hExists(string $key, string $field) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$field
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + mixed hGet(string $key, string $member) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$member
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hGetAll(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed hGetWithMeta(string $key, string $member) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$member
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false hIncrBy(string $key, string $field, int $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$field
int$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|float|false hIncrByFloat(string $key, string $field, float $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$field
float$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|float|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hKeys(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false hLen(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hMget(string $key, array $fields) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$fields
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hgetex(string $key, array $fields, string|array|null $expiry = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
array$fields
string|array|null$expiry
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false hsetex(string $key, array $fields, ?array $expiry = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
array$fields
?array$expiry
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hgetdel(string $key, array $fields) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$fields
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool hMset(string $key, array $fieldvals) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$fieldvals
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|array|false hRandField(string $key, ?array $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
?array$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false hSet(string $key, mixed $fields_and_vals) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$fields_and_vals
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool hSetNx(string $key, string $field, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$field
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false hStrLen(string $key, string $field) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$field
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hVals(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false httl(string $key, array $fields) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$fields
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hpttl(string $key, array $fields) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$fields
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hexpiretime(string $key, array $fields) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$fields
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hpexpiretime(string $key, array $fields) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$fields
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false hpersist(string $key, array $fields) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$fields
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false expiremember(string $key, string $field, int $ttl, ?string $unit = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$field
int$ttl
?string$unit
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false expirememberat(string $key, string $field, int $timestamp) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$field
int$timestamp
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false incr(string $key, int $by = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$by
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false incrBy(string $key, int $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|float|false incrByFloat(string $key, float $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
float$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|float|false
+ +
+ + + +
+
+ +
+
+ +

+ + bool isConnected() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + void lInsert(string $key, string $pos, mixed $pivot, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$pos
mixed$pivot
mixed$value
+ + +

Return Value

+ +
+ + + + + +
void
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false lLen(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false lMove(string $src, string $dst, string $wherefrom, string $whereto) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$src
string$dst
string$wherefrom
string$whereto
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$src
string$dst
string$wherefrom
string$whereto
float$timeout
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool|string|array lPop(string $key, int $count) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool|string|array
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|null|bool|int|array lPos(string $key, mixed $value, ?array $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
?array$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|null|bool|int|array
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false lPush(string $key, mixed $elements) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$elements
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false rPush(string $key, mixed $elements) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$elements
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false lPushx(string $key, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false rPushx(string $key, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool lSet(string $key, int $index, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$index
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + int lastSave() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
int
+ +
+ + + +
+
+ +
+
+ +

+ + mixed lindex(string $key, int $index) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$index
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false lrange(string $key, int $start, int $end) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false lrem(string $key, mixed $value, int $count) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool ltrim(string $key, int $start, int $end) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool move(string $key, int $index) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$index
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false msetex(array $key_vals, int|float|array|null $expiry = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array$key_vals
int|float|array|null$expiry
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool msetnx(array $key_values) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$key_values
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|string|false object(string $subcommand, string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$subcommand
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + bool open(string $host, int $port = 6379, float $timeout, ?string $persistent_id = null, int $retry_interval, float $read_timeout, ?array $context = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
?string$persistent_id
int$retry_interval
float$read_timeout
?array$context
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + bool pconnect(string $host, int $port = 6379, float $timeout, ?string $persistent_id = null, int $retry_interval, float $read_timeout, ?array $context = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
?string$persistent_id
int$retry_interval
float$read_timeout
?array$context
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool persist(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + bool pexpire(string $key, int $timeout, ?string $mode = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timeout
?string$mode
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool pexpireAt(string $key, int $timestamp, ?string $mode = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$timestamp
?string$mode
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int pfadd(string $key, array $elements) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$elements
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false pfcount(array|string $key_or_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array|string$key_or_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool pfmerge(string $dst, array $srckeys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$dst
array$srckeys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray pipeline() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
RedisArray
+ +
+ + + +
+
+ +
+
+ +

+ + bool popen(string $host, int $port = 6379, float $timeout, ?string $persistent_id = null, int $retry_interval, float $read_timeout, ?array $context = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$host
int$port
float$timeout
?string$persistent_id
int$retry_interval
float$read_timeout
?array$context
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool psetex(string $key, int $expire, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$expire
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + bool psubscribe(array $patterns, callable $cb) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array$patterns
callable$cb
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false pttl(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false publish(string $channel, string $message) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$channel
string$message
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed pubsub(string $command, mixed $arg = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$command
mixed$arg
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|bool punsubscribe(array $patterns) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$patterns
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|string|bool rPop(string $key, int $count) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|string|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false randomKey() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed rawcommand(string $command, mixed $args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$command
mixed$args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool rename(string $old_name, string $new_name) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$old_name
string$new_name
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool renameNx(string $key_src, string $key_dst) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key_src
string$key_dst
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool reset() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool restore(string $key, int $ttl, string $value, ?array $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int$ttl
string$value
?array$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + mixed role() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false rpoplpush(string $srckey, string $dstkey) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$srckey
string$dstkey
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false sAdd(string $key, mixed $value, mixed $other_values) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
mixed$other_values
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + int sAddArray(string $key, array $values) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$values
+ + +

Return Value

+ +
+ + + + + +
int
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false sDiff(string $key, string $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false sDiffStore(string $dst, string $key, string $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$dst
string$key
string$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false sInter(array|string $key, string $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false sintercard(array $keys, int $limit = -1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array$keys
int$limit
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false sInterStore(array|string $key, string $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false sMembers(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false sMisMember(string $key, string $member, string $other_members) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$member
string$other_members
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool sMove(string $src, string $dst, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$src
string$dst
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|array|false sPop(string $key, int $count) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed sRandMember(string $key, int $count) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false sUnion(string $key, string $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
string$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false sUnionStore(string $dst, string $key, string $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$dst
string$key
string$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false scard(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed script(string $command, mixed $args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$command
mixed$args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|bool set(string $key, mixed $value, mixed $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
mixed$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false setBit(string $key, int $idx, bool $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$idx
bool$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false setRange(string $key, int $index, string $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$index
string$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + void setex(string $key, int $expire, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
int$expire
mixed$value
+ + +

Return Value

+ +
+ + + + + +
void
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool setnx(string $key, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool sismember(string $key, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool slaveof(?string $host = null, int $port = 6379) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
?string$host
int$port
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool replicaof(?string $host = null, int $port = 6379) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
?string$host
int$port
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false touch(array|string $key_or_array, string $more_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key_or_array
string$more_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed slowlog(string $operation, int $length) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$operation
int$length
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed sort(string $key, ?array $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
?array$options
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + mixed sort_ro(string $key, ?array $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
?array$options
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + array sortAsc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
?string$pattern
mixed$get
int$offset
int$count
?string$store
+ + +

Return Value

+ +
+ + + + + +
array
+ +
+ + + +
+
+ +
+
+ +

+ + array sortAscAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
?string$pattern
mixed$get
int$offset
int$count
?string$store
+ + +

Return Value

+ +
+ + + + + +
array
+ +
+ + + +
+
+ +
+
+ +

+ + array sortDesc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
?string$pattern
mixed$get
int$offset
int$count
?string$store
+ + +

Return Value

+ +
+ + + + + +
array
+ +
+ + + +
+
+ +
+
+ +

+ + array sortDescAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
?string$pattern
mixed$get
int$offset
int$count
?string$store
+ + +

Return Value

+ +
+ + + + + +
array
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false srem(string $key, mixed $value, mixed $other_values) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$value
mixed$other_values
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + bool ssubscribe(array $channels, callable $cb) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array$channels
callable$cb
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false strlen(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + bool subscribe(array $channels, callable $cb) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array$channels
callable$cb
+ + +

Return Value

+ +
+ + + + + +
bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|bool sunsubscribe(array $channels) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$channels
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool swapdb(int $src, int $dst) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
int$src
int$dst
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array time() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
RedisArray|array
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false ttl(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false type(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|bool unsubscribe(array $channels) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
array$channels
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool watch(array|string $key, string $other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + int|false wait(int $numreplicas, int $timeout) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
int$numreplicas
int$timeout
+ + +

Return Value

+ +
+ + + + + +
int|false
+ +
+ + + +
+
+ +
+
+ +

+ + int|false xack(string $key, string $group, array $ids) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
string$group
array$ids
+ + +

Return Value

+ +
+ + + + + +
int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|string|false xadd(string $key, string $id, array $values, int $maxlen, bool $approx = false, bool $nomkstream = false) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$id
array$values
int$maxlen
bool$approx
bool$nomkstream
+ + +

Return Value

+ +
+ + + + + +
RedisArray|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool|array xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$group
string$consumer
int$min_idle
string$start
int$count
bool$justid
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool|array
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|bool xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$group
string$consumer
int$min_idle
array$ids
array$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false xdel(string $key, array $ids) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
array$ids
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + Relay|array|false xdelex(string $key, array $ids, ?string $mode = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
array$ids
?string$mode
+ + +

Return Value

+ +
+ + + + + +
Relay|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + mixed xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$operation
?string$arg1
?string$arg2
int$count
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false xlen(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$group
?string$start
?string$end
int$count
?string$consumer
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|bool xrange(string $key, string $start, string $end, int $count = -1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$start
string$end
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|bool xread(array $streams, int $count = -1, int $block = -1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$streams
int$count
int$block
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|bool xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$group
string$consumer
array$streams
int$count
int$block
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|bool xrevrange(string $key, string $end, string $start, int $count = -1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$end
string$start
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false vadd(string $key, array $values, mixed $element, array|null $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
array$values
mixed$element
array|null$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false vsim(string $key, mixed $member, array|null $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$member
array|null$options
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false vcard(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false vdim(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false vinfo(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|bool vismember(string $key, mixed $member) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$member
+ + +

Return Value

+ +
+ + + + + +
RedisArray|bool
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false vemb(string $key, mixed $member, bool $raw = false) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$member
bool$raw
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|string|false vrandmember(string $key, int $count) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|string|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|false vrange(string $key, string $min, string $max, int $count = -1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
string$min
string$max
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false vrem(string $key, mixed $member) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$member
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|int|false vsetattr(string $key, mixed $member, array|string $attributes) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$member
array|string$attributes
+ + +

Return Value

+ +
+ + + + + +
RedisArray|int|false
+ +
+ + + +
+
+ +
+
+ +

+ + RedisArray|array|string|false vgetattr(string $key, mixed $member, bool $decode = true) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$member
bool$decode
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|string|false
+ +
+ + + +
+
+ +
+
+ + +
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$key
mixed$member
bool$withscores
+ + +

Return Value

+ +
+ + + + + +
RedisArray|array|false
+ +
+ + + +
+
- -

Details

- -
+
-

- - mixed - __call(string $function_name, array $arguments) - + +

+ + RedisArray|int|false xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1)

@@ -465,26 +16082,43 @@

Parameters

- + - - + + + + + + + + + + + + + + + + +
string$function_name$key
array$argumentsstring$threshold
bool$approx
bool$minid
int$limit
-

Return Value

+

Return Value

- +
+
- +
mixedRedisArray|int|false
+
@@ -493,11 +16127,10 @@

Return Value

-

- - - __construct(string|array $name_or_hosts, array $options = NULL) - + +

+ + RedisArray|int|float|false zAdd(string $key, array|float $score_or_options, mixed $more_scores_and_mems)

@@ -512,18 +16145,34 @@

Parameters

- - + + - - + + + + + + +
string|array$name_or_hostsstring$key
array$optionsarray|float$score_or_options
mixed$more_scores_and_mems
+

Return Value

+ +
+ + + + + +
RedisArray|int|float|false
+ +
@@ -532,11 +16181,10 @@

Parameters

-

- - bool|array - _continuum() - + +

+ + RedisArray|int|false zCard(string $key)

@@ -547,16 +16195,28 @@

- -

Return Value

+

Parameters

+ + + + + +
string$key
+ + +

Return Value

+ +
+ - +
bool|arrayRedisArray|int|false
+
@@ -565,11 +16225,10 @@

Return Value

-

- - bool|callable - _distributor() - + +

+ + RedisArray|int|false zCount(string $key, int|string $start, int|string $end)

@@ -580,16 +16239,38 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + +
string$key
int|string$start
int|string$end
+ + +

Return Value

+ +
+ - +
bool|callableRedisArray|int|false
+
@@ -598,11 +16279,10 @@

Return Value

-

- - bool|callable - _function() - + +

+ + RedisArray|float|false zIncrBy(string $key, float $value, mixed $member)

@@ -613,16 +16293,38 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + +
string$key
float$value
mixed$member
+ + +

Return Value

+ +
+ - +
bool|callableRedisArray|float|false
+
@@ -631,11 +16333,10 @@

Return Value

-

- - bool|array - _hosts() - + +

+ + RedisArray|int|false zLexCount(string $key, string $min, string $max)

@@ -646,16 +16347,38 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + +
string$key
string$min
string$max
+ + +

Return Value

+ +
+ - +
bool|arrayRedisArray|int|false
+
@@ -664,11 +16387,10 @@

Return Value

-

- - bool|null|Redis - _instance(string $host) - + +

+ + RedisArray|array|false zMscore(string $key, mixed $member, mixed $other_members)

@@ -684,21 +16406,33 @@

Parameters

- + + + + + + + + + + +
string$host$key
mixed$member
mixed$other_members
-

Return Value

+

Return Value

- +
+
- +
bool|null|RedisRedisArray|array|false
+
@@ -707,11 +16441,10 @@

Return Value

-

- - bool|null - _rehash(callable $fn = NULL) - + +

+ + RedisArray|array|false zPopMax(string $key, ?int $count = null)

@@ -726,22 +16459,29 @@

Parameters

- - + + + + + + +
callable$fnstring$key
?int$count
-

Return Value

+

Return Value

- +
+
- +
bool|nullRedisArray|array|false
+
@@ -750,11 +16490,10 @@

Return Value

-

- - bool|string|null - _target(string $key) - + +

+ + RedisArray|array|false zPopMin(string $key, ?int $count = null)

@@ -772,19 +16511,26 @@

Parameters

string $key + + + ?int + $count + -

Return Value

+

Return Value

- +
+
- +
bool|string|nullRedisArray|array|false
+
@@ -793,11 +16539,10 @@

Return Value

-

- - array - bgsave() - + +

+ + RedisArray|array|false zRange(string $key, string|int $start, string|int $end, array|bool|null $options = null)

@@ -808,16 +16553,43 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + + + + + + +
string$key
string|int$start
string|int$end
array|bool|null$options
+ + +

Return Value

+ +
+ - +
arrayRedisArray|array|false
+
@@ -826,11 +16598,10 @@

Return Value

-

- - bool|int - del(string|array $key, string ...$otherkeys) - + +

+ + RedisArray|array|false zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1)

@@ -845,27 +16616,44 @@

Parameters

- + - + + + + + + + + + + + + + + + +
string|arraystring $key
string...$otherkeys$min
string$max
int$offset
int$count
-

Return Value

+

Return Value

- +
+
- +
bool|intRedisArray|array|false
+
@@ -874,11 +16662,10 @@

Return Value

-

- - bool|null - discard() - + +

+ + RedisArray|array|false zRangeByScore(string $key, string $start, string $end, array $options = [])

@@ -889,16 +16676,43 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + + + + + + +
string$key
string$start
string$end
array$options
+ + +

Return Value

+ +
+ - +
bool|nullRedisArray|array|false
+
@@ -907,11 +16721,10 @@

Return Value

-

- - bool|null - exec() - + +

+ + RedisArray|string|array zRandMember(string $key, ?array $options = null)

@@ -922,16 +16735,33 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + +
string$key
?array$options
+ + +

Return Value

+ +
+ - +
bool|nullRedisArray|string|array
+
@@ -940,11 +16770,10 @@

Return Value

-

- - bool|array - flushall() - + +

+ + RedisArray|int|false zRank(string $key, mixed $member)

@@ -955,16 +16784,33 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + +
string$key
mixed$member
+ + +

Return Value

+ +
+ - +
bool|arrayRedisArray|int|false
+
@@ -973,11 +16819,10 @@

Return Value

-

- - bool|array - flushdb() - + +

+ + RedisArray|int|false zRem(mixed $key, mixed $member, mixed $other_members)

@@ -988,16 +16833,38 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + +
mixed$key
mixed$member
mixed$other_members
+ + +

Return Value

+ +
+ - +
bool|arrayRedisArray|int|false
+
@@ -1006,11 +16873,10 @@

Return Value

-

- - bool|array - getOption(int $opt) - + +

+ + RedisArray|int|false zRemRangeByLex(string $key, string $min, string $max)

@@ -1025,22 +16891,34 @@

Parameters

- - + + + + + + + + + + + +
int$optstring$key
string$min
string$max
-

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisArray|int|false
+
@@ -1049,11 +16927,10 @@

Return Value

-

- - bool|array - hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) - + +

+ + RedisArray|int|false zRemRangeByRank(string $key, int $start, int $end)

@@ -1073,32 +16950,29 @@

Parameters

- int|null - $iterator - - - - string|null - $pattern + int + $start int - $count + $end -

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisArray|int|false
+
@@ -1107,11 +16981,10 @@

Return Value

-

- - bool|array - info() - + +

+ + RedisArray|int|false zRemRangeByScore(string $key, string $start, string $end)

@@ -1122,16 +16995,38 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + +
string$key
string$start
string$end
+ + +

Return Value

+ +
+ - +
bool|arrayRedisArray|int|false
+
@@ -1140,11 +17035,10 @@

Return Value

-

- - bool|array - keys(string $pattern) - + +

+ + RedisArray|array|false zRevRange(string $key, int $start, int $end, mixed $scores = null)

@@ -1160,21 +17054,38 @@

Parameters

- + + + + + + + + + + + + + + + +
string$pattern$key
int$start
int$end
mixed$scores
-

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisArray|array|false
+
@@ -1183,11 +17094,10 @@

Return Value

-

- - bool|array - mget(array $keys) - + +

+ + RedisArray|array|false zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1)

@@ -1202,22 +17112,44 @@

Parameters

- - + + + + + + + + + + + + + + + + + + + + + +
array$keysstring$key
string$max
string$min
int$offset
int$count
-

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisArray|array|false
+
@@ -1226,11 +17158,10 @@

Return Value

-

- - bool - mset(array $pairs) - + +

+ + RedisArray|array|false zRevRangeByScore(string $key, string $max, string $min, array|bool $options = [])

@@ -1245,22 +17176,39 @@

Parameters

- - + + + + + + + + + + + + + + + + +
array$pairsstring$key
string$max
string$min
array|bool$options
-

Return Value

+

Return Value

- +
+
- +
boolRedisArray|array|false
+
@@ -1269,11 +17217,10 @@

Return Value

-

- - bool|RedisArray - multi(string $host, int $mode = NULL) - + +

+ + RedisArray|int|false zRevRank(string $key, mixed $member)

@@ -1289,26 +17236,28 @@

Parameters

- + - - + +
string$host$key
int$modemixed$member
-

Return Value

+

Return Value

- +
+
- +
bool|RedisArrayRedisArray|int|false
+
@@ -1317,11 +17266,10 @@

Return Value

-

- - bool|array - ping() - + +

+ + RedisArray|float|false zScore(string $key, mixed $member)

@@ -1332,16 +17280,33 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + +
string$key
mixed$member
+ + +

Return Value

+ +
+ - +
bool|arrayRedisArray|float|false
+
@@ -1350,11 +17315,10 @@

Return Value

-

- - bool|array - save() - + +

+ + RedisArray|array|false zdiff(array $keys, ?array $options = null)

@@ -1365,16 +17329,33 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + +
array$keys
?array$options
+ + +

Return Value

+ +
+ - +
bool|arrayRedisArray|array|false
+
@@ -1383,11 +17364,10 @@

Return Value

-

- - bool|array - scan(int|null $iterator, string $node, string|null $pattern = null, int $count = 0) - + +

+ + RedisArray|int|false zdiffstore(string $dst, array $keys)

@@ -1402,37 +17382,29 @@

Parameters

- - - - - - - - - - - + - - + +
int|null$iterator
string$node
string|null$pattern$dst
int$countarray$keys
-

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisArray|int|false
+
@@ -1441,11 +17413,10 @@

Return Value

-

- - bool|array - select(int $index) - + +

+ + RedisArray|array|false zinter(array $keys, ?array $weights = null, ?array $options = null)

@@ -1460,22 +17431,34 @@

Parameters

- - + + + + + + + + + + + +
int$indexarray$keys
?array$weights
?array$options
-

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisArray|array|false
+
@@ -1484,11 +17467,10 @@

Return Value

-

- - bool|array - setOption(int $opt, string $value) - + +

+ + RedisArray|int|false zintercard(array $keys, int $limit = -1)

@@ -1503,27 +17485,29 @@

Parameters

- - + + - - + +
int$optarray$keys
string$valueint$limit
-

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisArray|int|false
+
@@ -1532,11 +17516,10 @@

Return Value

-

- - bool|array - sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) - + +

+ + RedisArray|int|false zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null)

@@ -1552,36 +17535,38 @@

Parameters

- + - - + + - - + + - - + +
string$key$dst
int|null$iteratorarray$keys
string|null$pattern?array$weights
int$count?string$aggregate
-

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisArray|int|false
+
@@ -1590,11 +17575,10 @@

Return Value

-

+ + RedisArray|array|false zunion(array $keys, ?array $weights = null, ?array $options = null)

@@ -1609,27 +17593,34 @@

Parameters

- - + + - - + + + + + + +
string|array$keyarray$keys
string...$otherkeys?array$weights
?array$options
-

Return Value

+

Return Value

- +
+
- +
bool|intRedisArray|array|false
+
@@ -1638,11 +17629,10 @@

Return Value

-

- - bool|null - unwatch() - + +

+ + RedisArray|int|false zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null)

@@ -1653,16 +17643,43 @@

- -

Return Value

+

Parameters

+ + + + + + + + + + + + + + + + + + + + +
string$dst
array$keys
?array$weights
?string$aggregate
+ + +

Return Value

+ +
+ - +
bool|nullRedisArray|int|false
+
@@ -1671,11 +17688,10 @@

Return Value

-

- - bool|array - zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) - + +

+ + RedisArray|string|false digest(string $key)

@@ -1693,34 +17709,21 @@

Parameters

string $key - - - int|null - $iterator - - - - string|null - $pattern - - - - int - $count - -

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisArray|string|false
+
diff --git a/docs/RedisCluster.html b/docs/RedisCluster.html index 4e9fb9fa9e..3187136751 100644 --- a/docs/RedisCluster.html +++ b/docs/RedisCluster.html @@ -5,7 +5,7 @@ RedisCluster | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> @@ -83,7 +472,7 @@

RedisCluster

class - RedisCluster (View source) + RedisCluster (View source)

@@ -92,6 +481,56 @@

RedisCluster +

Constants

+ + + + + + + + + + + + + + + + + + + + +
+ OPT_SLAVE_FAILOVER + +

Used to configure how PhpRedis will failover to replica nodes when a +primary node fails to respond.

+

+
+ FAILOVER_NONE + +

Never read from replicas.

+

+
+ FAILOVER_ERROR + +

Attempt to read from replicas when the primary errors out or is down.

+

+
+ FAILOVER_DISTRIBUTE + +

Distribute readonly commands at random between the primary and +replica(s).

+

+
+ FAILOVER_DISTRIBUTE_SLAVES + +

Distribute readonly commands between the replicas only.

+

+
+

Methods

@@ -102,7 +541,7 @@

Methods

- __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, mixed $auth = NULL, array $context = NULL) + __construct(string|null $name, array|null $seeds = null, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, mixed $auth = null, array|null $context = null)

No description

@@ -115,8 +554,7 @@

Methods

_compress(string $value) -

No description

-
+

{\Redis::_compress()}

@@ -164,6 +602,17 @@

Methods

+
+ string +
+
+ _digest(mixed $value) + +

No description

+
+
+
+
mixed
@@ -241,6 +690,28 @@

Methods

+
+ RedisCluster|int|false +
+
+ wait(string|array $key_or_address, int $numreplicas, int $timeout) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ waitaof(string|array $key_or_address, int $numlocal, int $numreplicas, int $timeout) + +

No description

+
+
+
+
RedisCluster|bool
@@ -290,7 +761,8 @@

Methods

blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) -

See Redis::blpop()

+

No description

+
@@ -300,7 +772,8 @@

Methods

brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) -

See Redis::brpop()

+

No description

+
@@ -310,7 +783,28 @@

Methods

brpoplpush(string $srckey, string $deskey, int $timeout) -

See Redis::brpoplpush()

+

No description

+
+
+
+
+
+ RedisCluster|string|false +
+
+ lmove(string $src, string $dst, string $wherefrom, string $whereto) + +

Move an element from one list into another.

+
+
+
+
+ RedisCluster|string|false +
+
+ blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout) + +

Move an element from one list to another, blocking up to a timeout until an element is available.

@@ -395,7 +889,7 @@

Methods

array|string|bool
- client(string|array $key_or_address, string $subcommand, string|null $arg = NULL) + client(string|array $key_or_address, string $subcommand, string|null $arg = null)

No description

@@ -457,6 +951,17 @@

Methods

+
+ RedisCluster|bool +
+
+ copy(string $src, string $dst, array|null $options = null) + +

No description

+
+
+
+
RedisCluster|int|false
@@ -499,6 +1004,27 @@

Methods

No description

+
+
+
+ RedisCluster|int|false +
+
+ delex(string $key, array|null $options = null) + +

Delete a key conditionally based on its value or hash digest

+
+
+
+
+ RedisCluster|int|false +
+
+ delifeq(string $key, mixed $value) + +

Delete a key if it's equal to the specified value. This command is +specific to Valkey >= 9.0

+
@@ -615,7 +1141,7 @@

Methods

RedisCluster|bool
- expire(string $key, int $timeout, string|null $mode = NULL) + expire(string $key, int $timeout, string|null $mode = null)

No description

@@ -626,7 +1152,7 @@

Methods

RedisCluster|bool
- expireat(string $key, int $timestamp, string|null $mode = NULL) + expireat(string $key, int $timestamp, string|null $mode = null)

No description

@@ -766,10 +1292,10 @@

Methods

- mixed + RedisCluster|array
- get(string $key) + geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = [])

No description

@@ -777,10 +1303,10 @@

Methods

- RedisCluster|int|false + RedisCluster|array|int|false
- getbit(string $key, int $value) + geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = [])

No description

@@ -788,10 +1314,10 @@

Methods

- string|null + mixed
- getlasterror() + get(string $key)

No description

@@ -799,10 +1325,10 @@

Methods

- int + mixed
- getmode() + getdel(string $key)

No description

@@ -810,10 +1336,10 @@

Methods

- mixed + RedisCluster|array|false
- getoption(int $option) + getWithMeta(string $key)

No description

@@ -824,7 +1350,7 @@

Methods

RedisCluster|string|false
- getrange(string $key, int $start, int $end) + getex(string $key, array $options = [])

No description

@@ -832,10 +1358,10 @@

Methods

- RedisCluster|string|array|int|false + RedisCluster|int|false
- lcs(string $key1, string $key2, array|null $options = NULL) + getbit(string $key, int $value)

No description

@@ -843,10 +1369,10 @@

Methods

- RedisCluster|string|bool + string|null
- getset(string $key, mixed $value) + getlasterror()

No description

@@ -854,10 +1380,10 @@

Methods

- int|false + int
- gettransferredbytes() + getmode()

No description

@@ -865,10 +1391,10 @@

Methods

- RedisCluster|int|false + mixed
- hdel(string $key, string $member, string ...$other_members) + getoption(int $option)

No description

@@ -876,10 +1402,10 @@

Methods

- RedisCluster|bool + RedisCluster|string|false
- hexists(string $key, string $member) + getrange(string $key, int $start, int $end)

No description

@@ -887,10 +1413,10 @@

Methods

- mixed + RedisCluster|string|array|int|false
- hget(string $key, string $member) + lcs(string $key1, string $key2, array|null $options = null)

No description

@@ -898,10 +1424,10 @@

Methods

- RedisCluster|array|false + RedisCluster|string|bool
- hgetall(string $key) + getset(string $key, mixed $value)

No description

@@ -909,10 +1435,10 @@

Methods

- RedisCluster|int|false + array|false
- hincrby(string $key, string $member, int $value) + gettransferredbytes()

No description

@@ -920,10 +1446,10 @@

Methods

- RedisCluster|float|false + void
- hincrbyfloat(string $key, string $member, float $value) + cleartransferredbytes()

No description

@@ -931,10 +1457,10 @@

Methods

- RedisCluster|array|false + RedisCluster|int|false
- hkeys(string $key) + hdel(string $key, string $member, string ...$other_members)

No description

@@ -942,10 +1468,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool
- hlen(string $key) + hexists(string $key, string $member)

No description

@@ -953,10 +1479,10 @@

Methods

- RedisCluster|array|false + mixed
- hmget(string $key, array $keys) + hget(string $key, string $member)

No description

@@ -964,10 +1490,10 @@

Methods

- RedisCluster|bool + RedisCluster|array|false
- hmset(string $key, array $key_values) + hgetall(string $key)

No description

@@ -975,10 +1501,10 @@

Methods

- array|bool + mixed
- hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + hgetWithMeta(string $key, string $member)

No description

@@ -989,7 +1515,7 @@

Methods

RedisCluster|int|false
- hset(string $key, string $member, mixed $value) + hincrby(string $key, string $member, int $value)

No description

@@ -997,10 +1523,10 @@

Methods

- RedisCluster|bool + RedisCluster|float|false
- hsetnx(string $key, string $member, mixed $value) + hincrbyfloat(string $key, string $member, float $value)

No description

@@ -1008,10 +1534,10 @@

Methods

- RedisCluster|int|false + RedisCluster|array|false
- hstrlen(string $key, string $field) + hkeys(string $key)

No description

@@ -1019,10 +1545,10 @@

Methods

- RedisCluster|array|false + RedisCluster|int|false
- hvals(string $key) + hlen(string $key)

No description

@@ -1030,10 +1556,10 @@

Methods

- RedisCluster|int|false + RedisCluster|array|false
- incr(string $key, int $by = 1) + hmget(string $key, array $keys)

No description

@@ -1041,10 +1567,10 @@

Methods

- RedisCluster|int|false + RedisCluster|array|false
- incrby(string $key, int $value) + hgetex(string $key, array $fields, string|array|null $expiry = null)

No description

@@ -1052,10 +1578,10 @@

Methods

- RedisCluster|float|false + RedisCluster|int|false
- incrbyfloat(string $key, float $value) + hsetex(string $key, array $fields, array|null $expiry = null)

No description

@@ -1066,20 +1592,18 @@

Methods

RedisCluster|array|false
- info(string|array $key_or_address, string ...$sections) + hgetdel(string $key, array $fields) -

Retrieve information about the connected redis-server. If no arguments are passed to -this function, redis will return every info field. Alternatively you may pass a specific -section you want returned (e.g. 'server', or 'memory') to receive only information pertaining -to that section.

+

No description

+
- RedisCluster|array|false + RedisCluster|bool
- keys(string $pattern) + hmset(string $key, array $key_values)

No description

@@ -1087,10 +1611,10 @@

Methods

- RedisCluster|int|false + array|bool
- lastsave(string|array $key_or_address) + hscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

No description

@@ -1098,10 +1622,10 @@

Methods

- RedisCluster|string|bool + RedisCluster|int|false
- lget(string $key, int $index) + expiremember(string $key, string $field, int $ttl, string|null $unit = null)

No description

@@ -1109,10 +1633,10 @@

Methods

- mixed + RedisCluster|int|false
- lindex(string $key, int $index) + expirememberat(string $key, string $field, int $timestamp)

No description

@@ -1120,10 +1644,10 @@

Methods

- RedisCluster|int|false + RedisCluster|string|array
- linsert(string $key, string $pos, mixed $pivot, mixed $value) + hrandfield(string $key, array|null $options = null)

No description

@@ -1131,10 +1655,10 @@

Methods

- RedisCluster|int|bool + RedisCluster|int|false
- llen(string $key) + hset(string $key, string $member, mixed $value)

No description

@@ -1142,10 +1666,10 @@

Methods

- RedisCluster|bool|string|array + RedisCluster|bool
- lpop(string $key, int $count = 0) + hsetnx(string $key, string $member, mixed $value)

No description

@@ -1153,10 +1677,10 @@

Methods

- RedisCluster|int|bool + RedisCluster|int|false
- lpush(string $key, mixed $value, mixed ...$other_values) + hstrlen(string $key, string $field)

No description

@@ -1164,10 +1688,10 @@

Methods

- RedisCluster|int|bool + RedisCluster|array|false
- lpushx(string $key, mixed $value) + hexpire(string $key, int $ttl, array $fields, string|null $mode = NULL)

No description

@@ -1178,7 +1702,7 @@

Methods

RedisCluster|array|false
- lrange(string $key, int $start, int $end) + hpexpire(string $key, int $ttl, array $fields, string|null $mode = NULL)

No description

@@ -1186,10 +1710,10 @@

Methods

- RedisCluster|int|bool + RedisCluster|array|false
- lrem(string $key, mixed $value, int $count = 0) + hexpireat(string $key, int $time, array $fields, string|null $mode = NULL)

No description

@@ -1197,10 +1721,10 @@

Methods

- RedisCluster|bool + RedisCluster|array|false
- lset(string $key, int $index, mixed $value) + hpexpireat(string $key, int $mstime, array $fields, string|null $mode = NULL)

No description

@@ -1208,10 +1732,10 @@

Methods

- RedisCluster|bool + RedisCluster|array|false
- ltrim(string $key, int $start, int $end) + httl(string $key, array $fields)

No description

@@ -1222,7 +1746,7 @@

Methods

RedisCluster|array|false
- mget(array $keys) + hpttl(string $key, array $fields)

No description

@@ -1230,10 +1754,10 @@

Methods

- RedisCluster|bool + RedisCluster|array|false
- mset(array $key_values) + hexpiretime(string $key, array $fields)

No description

@@ -1244,7 +1768,7 @@

Methods

RedisCluster|array|false
- msetnx(array $key_values) + hpexpiretime(string $key, array $fields)

No description

@@ -1252,10 +1776,10 @@

Methods

- RedisCluster|bool + RedisCluster|array|false
- multi(int $value = Redis::MULTI) + hpersist(string $key, array $fields)

No description

@@ -1263,10 +1787,10 @@

Methods

- RedisCluster|int|string|false + RedisCluster|array|false
- object(string $subcommand, string $key) + hvals(string $key)

No description

@@ -1274,10 +1798,10 @@

Methods

- RedisCluster|bool + RedisCluster|int|false
- persist(string $key) + incr(string $key, int $by = 1)

No description

@@ -1285,10 +1809,10 @@

Methods

- RedisCluster|bool + RedisCluster|int|false
- pexpire(string $key, int $timeout, string|null $mode = NULL) + incrby(string $key, int $value)

No description

@@ -1296,10 +1820,10 @@

Methods

- RedisCluster|bool + RedisCluster|float|false
- pexpireat(string $key, int $timestamp, string|null $mode = NULL) + incrbyfloat(string $key, float $value)

No description

@@ -1307,10 +1831,23 @@

Methods

- RedisCluster|bool + RedisCluster|array|false
- pfadd(string $key, array $elements) + info(string|array $key_or_address, string ...$sections) + +

Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

+
+
+
+
+ RedisCluster|array|false +
+
+ keys(string $pattern)

No description

@@ -1321,7 +1858,7 @@

Methods

RedisCluster|int|false
- pfcount(string $key) + lastsave(string|array $key_or_address)

No description

@@ -1329,10 +1866,10 @@

Methods

- RedisCluster|bool + RedisCluster|string|bool
- pfmerge(string $key, array $keys) + lget(string $key, int $index)

No description

@@ -1343,17 +1880,18 @@

Methods

mixed
- ping(string|array $key_or_address, string|null $message = NULL) + lindex(string $key, int $index) -

PING an instance in the redis cluster.

+

No description

+
- RedisCluster|bool + RedisCluster|int|false
- psetex(string $key, int $timeout, string $value) + linsert(string $key, string $pos, mixed $pivot, mixed $value)

No description

@@ -1361,10 +1899,10 @@

Methods

- void + RedisCluster|int|bool
- psubscribe(array $patterns, callable $callback) + llen(string $key)

No description

@@ -1372,10 +1910,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool|string|array
- pttl(string $key) + lpop(string $key, int $count = 0)

No description

@@ -1383,10 +1921,10 @@

Methods

- RedisCluster|bool + RedisCluster|null|bool|int|array
- publish(string $channel, string $message) + lpos(string $key, mixed $value, array|null $options = null)

No description

@@ -1394,10 +1932,10 @@

Methods

- mixed + RedisCluster|int|bool
- pubsub(string|array $key_or_address, string ...$values) + lpush(string $key, mixed $value, mixed ...$other_values)

No description

@@ -1405,10 +1943,10 @@

Methods

- bool|array + RedisCluster|int|bool
- punsubscribe(string $pattern, string ...$other_patterns) + lpushx(string $key, mixed $value)

No description

@@ -1416,10 +1954,10 @@

Methods

- RedisCluster|bool|string + RedisCluster|array|false
- randomkey(string|array $key_or_address) + lrange(string $key, int $start, int $end)

No description

@@ -1427,10 +1965,10 @@

Methods

- mixed + RedisCluster|int|bool
- rawcommand(string|array $key_or_address, string $command, mixed ...$args) + lrem(string $key, mixed $value, int $count = 0)

No description

@@ -1441,7 +1979,7 @@

Methods

RedisCluster|bool
- rename(string $key_src, string $key_dst) + lset(string $key, int $index, mixed $value)

No description

@@ -1452,7 +1990,7 @@

Methods

RedisCluster|bool
- renamenx(string $key, string $newkey) + ltrim(string $key, int $start, int $end)

No description

@@ -1460,10 +1998,10 @@

Methods

- RedisCluster|bool + RedisCluster|array|false
- restore(string $key, int $timeout, string $value, array|null $options = NULL) + mget(array $keys)

No description

@@ -1471,10 +2009,10 @@

Methods

- mixed + RedisCluster|bool
- role(string|array $key_or_address) + mset(array $key_values)

No description

@@ -1482,10 +2020,10 @@

Methods

- RedisCluster|bool|string|array + RedisCluster|array|false
- rpop(string $key, int $count = 0) + msetnx(array $key_values)

No description

@@ -1493,10 +2031,10 @@

Methods

- RedisCluster|bool|string + Redis|int|false
- rpoplpush(string $src, string $dst) + msetex(array $key_vals, int|float|array|null $expiry = null)

No description

@@ -1504,10 +2042,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool
- rpush(string $key, mixed ...$elements) + multi(int $value = Redis::MULTI)

No description

@@ -1515,10 +2053,10 @@

Methods

- RedisCluster|bool|int + RedisCluster|int|string|false
- rpushx(string $key, string $value) + object(string $subcommand, string $key)

No description

@@ -1526,10 +2064,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool
- sadd(string $key, mixed $value, mixed ...$other_values) + persist(string $key)

No description

@@ -1537,10 +2075,10 @@

Methods

- RedisCluster|bool|int + RedisCluster|bool
- saddarray(string $key, array $values) + pexpire(string $key, int $timeout, string|null $mode = null)

No description

@@ -1551,7 +2089,7 @@

Methods

RedisCluster|bool
- save(string|array $key_or_address) + pexpireat(string $key, int $timestamp, string|null $mode = null)

No description

@@ -1559,10 +2097,10 @@

Methods

- bool|array + RedisCluster|bool
- scan(int|null $iterator, string|array $key_or_address, string|null $pattern = null, int $count = 0) + pfadd(string $key, array $elements)

No description

@@ -1573,7 +2111,7 @@

Methods

RedisCluster|int|false
- scard(string $key) + pfcount(string $key)

No description

@@ -1581,10 +2119,10 @@

Methods

- mixed + RedisCluster|bool
- script(string|array $key_or_address, mixed ...$args) + pfmerge(string $key, array $keys)

No description

@@ -1592,21 +2130,20 @@

Methods

- RedisCluster|array|false + mixed
- sdiff(string $key, string ...$other_keys) + ping(string|array $key_or_address, string|null $message = null) -

No description

-
+

PING an instance in the redis cluster.

- RedisCluster|int|false + RedisCluster|bool
- sdiffstore(string $dst, string $key, string ...$other_keys) + psetex(string $key, int $timeout, string $value)

No description

@@ -1614,10 +2151,10 @@

Methods

- RedisCluster|string|bool + void
- set(string $key, mixed $value, mixed $options = NULL) + psubscribe(array $patterns, callable $callback)

No description

@@ -1628,7 +2165,7 @@

Methods

RedisCluster|int|false
- setbit(string $key, int $offset, bool $onoff) + pttl(string $key)

No description

@@ -1636,10 +2173,10 @@

Methods

- RedisCluster|bool + RedisCluster|bool|int
- setex(string $key, int $expire, mixed $value) + publish(string $channel, string $message)

No description

@@ -1647,10 +2184,10 @@

Methods

- RedisCluster|bool + mixed
- setnx(string $key, mixed $value) + pubsub(string|array $key_or_address, string ...$values)

No description

@@ -1658,10 +2195,10 @@

Methods

- bool + bool|array
- setoption(int $option, mixed $value) + punsubscribe(string $pattern, string ...$other_patterns)

No description

@@ -1669,10 +2206,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool|string
- setrange(string $key, int $offset, string $value) + randomkey(string|array $key_or_address)

No description

@@ -1680,10 +2217,10 @@

Methods

- RedisCluster|array|false + mixed
- sinter(array|string $key, string ...$other_keys) + rawcommand(string|array $key_or_address, string $command, mixed ...$args)

No description

@@ -1691,10 +2228,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool
- sintercard(array $keys, int $limit = -1) + rename(string $key_src, string $key_dst)

No description

@@ -1702,10 +2239,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool
- sinterstore(array|string $key, string ...$other_keys) + renamenx(string $key, string $newkey)

No description

@@ -1716,7 +2253,7 @@

Methods

RedisCluster|bool
- sismember(string $key, mixed $value) + restore(string $key, int $timeout, string $value, array|null $options = null)

No description

@@ -1727,7 +2264,7 @@

Methods

mixed
- slowlog(string|array $key_or_address, mixed ...$args) + role(string|array $key_or_address)

No description

@@ -1735,10 +2272,10 @@

Methods

- RedisCluster|array|false + RedisCluster|bool|string|array
- smembers(string $key) + rpop(string $key, int $count = 0)

No description

@@ -1746,10 +2283,10 @@

Methods

- RedisCluster|bool + RedisCluster|bool|string
- smove(string $src, string $dst, string $member) + rpoplpush(string $src, string $dst)

No description

@@ -1757,10 +2294,10 @@

Methods

- RedisCluster|array|bool|int|string + RedisCluster|int|false
- sort(string $key, array|null $options = NULL) + rpush(string $key, mixed ...$elements)

No description

@@ -1768,10 +2305,10 @@

Methods

- RedisCluster|array|bool|int|string + RedisCluster|bool|int
- sort_ro(string $key, array|null $options = NULL) + rpushx(string $key, string $value)

No description

@@ -1779,10 +2316,10 @@

Methods

- RedisCluster|string|array|false + RedisCluster|int|false
- spop(string $key, int $count = 0) + sadd(string $key, mixed $value, mixed ...$other_values)

No description

@@ -1790,10 +2327,10 @@

Methods

- RedisCluster|string|array|false + RedisCluster|bool|int
- srandmember(string $key, int $count = 0) + saddarray(string $key, array $values)

No description

@@ -1801,10 +2338,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool
- srem(string $key, mixed $value, mixed ...$other_values) + save(string|array $key_or_address)

No description

@@ -1812,10 +2349,10 @@

Methods

- array|false + bool|array
- sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + scan(null|int|string $iterator, string|array $key_or_address, string|null $pattern = null, int $count = 0)

No description

@@ -1826,7 +2363,7 @@

Methods

RedisCluster|int|false
- strlen(string $key) + scard(string $key)

No description

@@ -1834,10 +2371,10 @@

Methods

- void + mixed
- subscribe(array $channels, callable $cb) + script(string|array $key_or_address, mixed ...$args)

No description

@@ -1845,10 +2382,10 @@

Methods

- RedisCluster|bool|array + RedisCluster|array|false
- sunion(string $key, string ...$other_keys) + sdiff(string $key, string ...$other_keys)

No description

@@ -1859,7 +2396,7 @@

Methods

RedisCluster|int|false
- sunionstore(string $dst, string $key, string ...$other_keys) + sdiffstore(string $dst, string $key, string ...$other_keys)

No description

@@ -1867,10 +2404,10 @@

Methods

- RedisCluster|bool|array + RedisCluster|string|bool
- time(string|array $key_or_address) + set(string $key, mixed $value, mixed $options = null)

No description

@@ -1881,7 +2418,7 @@

Methods

RedisCluster|int|false
- ttl(string $key) + setbit(string $key, int $offset, bool $onoff)

No description

@@ -1889,10 +2426,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool
- type(string $key) + setex(string $key, int $expire, mixed $value)

No description

@@ -1900,10 +2437,10 @@

Methods

- bool|array + RedisCluster|bool
- unsubscribe(array $channels) + setnx(string $key, mixed $value)

No description

@@ -1911,10 +2448,10 @@

Methods

- RedisCluster|int|false + bool
- unlink(array|string $key, string ...$other_keys) + setoption(int $option, mixed $value)

No description

@@ -1922,10 +2459,10 @@

Methods

- bool + RedisCluster|int|false
- unwatch() + setrange(string $key, int $offset, string $value)

No description

@@ -1933,10 +2470,10 @@

Methods

- RedisCluster|bool + RedisCluster|array|false
- watch(string $key, string ...$other_keys) + sinter(array|string $key, string ...$other_keys)

No description

@@ -1947,7 +2484,7 @@

Methods

RedisCluster|int|false
- xack(string $key, string $group, array $ids) + sintercard(array $keys, int $limit = -1)

No description

@@ -1955,10 +2492,10 @@

Methods

- RedisCluster|string|false + RedisCluster|int|false
- xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false) + sinterstore(array|string $key, string ...$other_keys)

No description

@@ -1966,10 +2503,10 @@

Methods

- RedisCluster|string|array|false + RedisCluster|bool
- xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options) + sismember(string $key, mixed $value)

No description

@@ -1977,10 +2514,10 @@

Methods

- RedisCluster|int|false + RedisCluster|array|false
- xdel(string $key, array $ids) + smismember(string $key, string $member, string ...$other_members)

No description

@@ -1991,7 +2528,7 @@

Methods

mixed
- xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) + slowlog(string|array $key_or_address, mixed ...$args)

No description

@@ -1999,10 +2536,10 @@

Methods

- Redis|bool|array + RedisCluster|array|false
- xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) + smembers(string $key)

No description

@@ -2010,10 +2547,10 @@

Methods

- mixed + RedisCluster|bool
- xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) + smove(string $src, string $dst, string $member)

No description

@@ -2021,10 +2558,10 @@

Methods

- RedisCluster|int|false + RedisCluster|array|bool|int|string
- xlen(string $key) + sort(string $key, array|null $options = null)

No description

@@ -2032,10 +2569,10 @@

Methods

- RedisCluster|array|false + RedisCluster|array|bool|int|string
- xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) + sort_ro(string $key, array|null $options = null)

No description

@@ -2043,10 +2580,10 @@

Methods

- RedisCluster|bool|array + RedisCluster|string|array|false
- xrange(string $key, string $start, string $end, int $count = -1) + spop(string $key, int $count = 0)

No description

@@ -2054,10 +2591,10 @@

Methods

- RedisCluster|bool|array + RedisCluster|string|array|false
- xread(array $streams, int $count = -1, int $block = -1) + srandmember(string $key, int $count = 0)

No description

@@ -2065,10 +2602,10 @@

Methods

- RedisCluster|bool|array + RedisCluster|int|false
- xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) + srem(string $key, mixed $value, mixed ...$other_values)

No description

@@ -2076,10 +2613,10 @@

Methods

- RedisCluster|bool|array + array|false
- xrevrange(string $key, string $start, string $end, int $count = -1) + sscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

No description

@@ -2090,7 +2627,7 @@

Methods

RedisCluster|int|false
- xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1) + strlen(string $key)

No description

@@ -2098,10 +2635,10 @@

Methods

- RedisCluster|int|false + void
- zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) + subscribe(array $channels, callable $cb)

No description

@@ -2109,10 +2646,10 @@

Methods

- RedisCluster|int|false + RedisCluster|bool|array
- zcard(string $key) + sunion(string $key, string ...$other_keys)

No description

@@ -2123,7 +2660,7 @@

Methods

RedisCluster|int|false
- zcount(string $key, string $start, string $end) + sunionstore(string $dst, string $key, string ...$other_keys)

No description

@@ -2131,10 +2668,10 @@

Methods

- RedisCluster|float|false + RedisCluster|bool|array
- zincrby(string $key, float $value, string $member) + time(string|array $key_or_address)

No description

@@ -2145,7 +2682,7 @@

Methods

RedisCluster|int|false
- zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) + ttl(string $key)

No description

@@ -2156,7 +2693,18 @@

Methods

RedisCluster|int|false
- zintercard(array $keys, int $limit = -1) + type(string $key) + +

No description

+
+
+
+
+
+ bool|array +
+
+ unsubscribe(array $channels)

No description

@@ -2167,7 +2715,7 @@

Methods

RedisCluster|int|false
- zlexcount(string $key, string $min, string $max) + unlink(array|string $key, string ...$other_keys)

No description

@@ -2175,10 +2723,10 @@

Methods

- RedisCluster|bool|array + bool
- zpopmax(string $key, int $value = null) + unwatch()

No description

@@ -2186,10 +2734,10 @@

Methods

- RedisCluster|bool|array + RedisCluster|bool
- zpopmin(string $key, int $value = null) + watch(string $key, string ...$other_keys)

No description

@@ -2197,10 +2745,21 @@

Methods

- RedisCluster|array|bool + RedisCluster|int|false
- zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null) + vadd(string $key, array $values, mixed $element, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ vsim(string $key, mixed $member, array|null $options = null)

No description

@@ -2211,7 +2770,18 @@

Methods

RedisCluster|int|false
- zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null) + vcard(string $key) + +

No description

+
+
+ +
+
+ RedisCluster|int|false +
+
+ vdim(string $key)

No description

@@ -2222,29 +2792,82 @@

Methods

RedisCluster|array|false
- zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1) + vinfo(string $key)

No description

+ +
+
+ RedisCluster|bool +
+
+ vismember(string $key, mixed $member) + +

Check if an element is a member of a vectorset

+
RedisCluster|array|false
- zrangebyscore(string $key, string $start, string $end, array $options = []) + vemb(string $key, mixed $member, bool $raw = false) + +

No description

+
+
+
+
+
+ RedisCluster|array|string|false +
+
+ vrandmember(string $key, int $count = 0)

No description

+
+
+
+ RedisCluster|array|false +
+
+ vrange(string $key, string $min, string $max, int $count = -1) + +

Retreive a lexographical range of elements from a vector set

+
RedisCluster|int|false
- zrank(string $key, mixed $member) + vrem(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ vlinks(string $key, mixed $member, bool $withscores = false) + +

No description

+
+
+
+
+
+ RedisCluster|array|string|false +
+
+ vgetattr(string $key, mixed $member, bool $decode = true)

No description

@@ -2255,7 +2878,18 @@

Methods

RedisCluster|int|false
- zrem(string $key, string $value, string ...$other_values) + vsetattr(string $key, mixed $member, array|string $attributes) + +

No description

+
+
+ +
+
+ RedisCluster|array|false +
+
+ gcra(string $key, int $maxBurst, int $requestsPerPeriod, int $period, int $tokens = 0)

No description

@@ -2266,7 +2900,29 @@

Methods

RedisCluster|int|false
- zremrangebylex(string $key, string $min, string $max) + xack(string $key, string $group, array $ids) + +

No description

+
+
+ +
+
+ RedisCluster|string|false +
+
+ xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false) + +

No description

+
+
+
+
+
+ RedisCluster|string|array|false +
+
+ xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options)

No description

@@ -2277,7 +2933,51 @@

Methods

RedisCluster|int|false
- zremrangebyrank(string $key, string $min, string $max) + xdel(string $key, array $ids) + +

No description

+
+
+ +
+
+ RedisCluster|array|false +
+
+ xdelex(string $key, array $ids, string|null $mode = null) + +

No description

+
+
+
+
+
+ mixed +
+
+ xgroup(string $operation, string|null $key = null, string|null $group = null, string|null $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) + +

No description

+
+
+
+
+
+ mixed +
+
+ xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1)

No description

@@ -2288,7 +2988,29 @@

Methods

RedisCluster|int|false
- zremrangebyscore(string $key, string $min, string $max) + xlen(string $key) + +

No description

+
+
+ +
+
+ RedisCluster|array|false +
+
+ xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ xrange(string $key, string $start, string $end, int $count = -1)

No description

@@ -2299,7 +3021,7 @@

Methods

RedisCluster|bool|array
- zrevrange(string $key, string $min, string $max, array $options = null) + xread(array $streams, int $count = -1, int $block = -1)

No description

@@ -2310,7 +3032,7 @@

Methods

RedisCluster|bool|array
- zrevrangebylex(string $key, string $min, string $max, array $options = null) + xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1)

No description

@@ -2321,7 +3043,7 @@

Methods

RedisCluster|bool|array
- zrevrangebyscore(string $key, string $min, string $max, array $options = null) + xrevrange(string $key, string $start, string $end, int $count = -1)

No description

@@ -2332,7 +3054,7 @@

Methods

RedisCluster|int|false
- zrevrank(string $key, mixed $member) + xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1)

No description

@@ -2340,10 +3062,32 @@

Methods

- RedisCluster|bool|array + RedisCluster|int|float|false +
+
+ zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zcard(string $key) + +

No description

+
+
+
+
+
+ RedisCluster|int|false
- zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) + zcount(string $key, string $start, string $end)

No description

@@ -2354,7 +3098,7 @@

Methods

RedisCluster|float|false
- zscore(string $key, mixed $member) + zincrby(string $key, float $value, string $member)

No description

@@ -2365,24 +3109,3507 @@

Methods

RedisCluster|int|false
- zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) + zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null)

No description

+
+
+ RedisCluster|int|false +
+
+ zintercard(array $keys, int $limit = -1) + +

No description

+
+
+
+
+ RedisCluster|int|false +
+
+ zlexcount(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zpopmax(string $key, int|null $value = null) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zpopmin(string $key, int|null $value = null) + +

No description

+
+
+
+
+
+ RedisCluster|array|bool +
+
+ zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|string|array +
+
+ zrandmember(string $key, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ zrangebyscore(string $key, string $start, string $end, array $options = []) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zrank(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zrem(string $key, string $value, string ...$other_values) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zremrangebylex(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zremrangebyrank(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zremrangebyscore(string $key, string $min, string $max) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zrevrange(string $key, string $min, string $max, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zrevrangebylex(string $key, string $min, string $max, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zrevrangebyscore(string $key, string $min, string $max, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zrevrank(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisCluster|bool|array +
+
+ zscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0) + +

No description

+
+
+
+
+
+ RedisCluster|float|false +
+
+ zscore(string $key, mixed $member) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ zmscore(string $key, mixed $member, mixed ...$other_members) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zunionstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ zinter(array $keys, array|null $weights = null, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|int|false +
+
+ zdiffstore(string $dst, array $keys) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ zunion(array $keys, array|null $weights = null, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|array|false +
+
+ zdiff(array $keys, array|null $options = null) + +

No description

+
+
+
+
+
+ RedisCluster|string|false +
+
+ digest(string $key) + +

No description

+
+
+
+ + + +

Details

+ +
+
+ +

+ + __construct(string|null $name, array|null $seeds = null, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, mixed $auth = null, array|null $context = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string|null$name
array|null$seeds
int|float$timeout
int|float$read_timeout
bool$persistent
mixed$auth
array|null$context
+ + + + + +
+
+ +
+
+ +

+ + string _compress(string $value) +

+
+ + + + +
+

Parameters

+ + + + + + + +
string$value
+ + +

Return Value

+ +
+ + + + + +
string
+ +
+ + + +
+
+ +
+
+ +

+ + string _uncompress(string $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$value
+ + +

Return Value

+ +
+ + + + + +
string
+ +
+ + +

See also

+ + + + + + +
+ +Redis::_uncompress +
+ + +
+
+ +
+
+ +

+ + bool|string _serialize(mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
mixed$value
+ + +

Return Value

+ +
+ + + + + +
bool|string
+ +
+ + +

See also

+ + + + + + +
+ +Redis::_serialize +
+ + +
+
+ +
+
+ +

+ + mixed _unserialize(string $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$value
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ +Redis::_unserialize +
+ + +
+
+ +
+
+ +

+ + string _pack(mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
mixed$value
+ + +

Return Value

+ +
+ + + + + +
string
+ +
+ + +

See also

+ + + + + + +
+ +Redis::_pack +
+ + +
+
+ +
+
+ +

+ + string _digest(mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
mixed$value
+ + +

Return Value

+ +
+ + + + + +
string
+ +
+ + +

See also

+ + + + + + +
+ +Redis::_digest +
+ + +
+
+ +
+
+ +

+ + mixed _unpack(string $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$value
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ +Redis::_unpack +
+ + +
+
+ +
+
+ +

+ + bool|string _prefix(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
bool|string
+ +
+ + +

See also

+ + + + + + +
+ +Redis::_prefix +
+ + +
+
+ +
+
+ +

+ + array _masters() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
array
+ +
+ + + +
+
+ +
+
+ +

+ + string|null _redir() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
string|null
+ +
+ + + +
+
+ +
+
+ +

+ + mixed acl(string|array $key_or_address, string $subcmd, string ...$args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
string$subcmd
string...$args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ +Redis::acl +
+ + +
+
+ +
+
+ +

+ + RedisCluster|bool|int append(string $key, mixed $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
mixed$value
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|bool|int
+ +
+ + +

See also

+ + + + + + +
+ +Redis::append +
+ + +
+
+ +
+
+ +

+ + RedisCluster|bool bgrewriteaof(string|array $key_or_address) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|bool
+ +
+ + +

See also

+ + + + + + +
+ +Redis::bgrewriteaof +
+ + +
+
+ +
+
+ +

+ + RedisCluster|int|false wait(string|array $key_or_address, int $numreplicas, int $timeout) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
int$numreplicas
int$timeout
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::wait +
+ + +
+
+ +
+
+ +

+ + RedisCluster|array|false waitaof(string|array $key_or_address, int $numlocal, int $numreplicas, int $timeout) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string|array$key_or_address
int$numlocal
int$numreplicas
int$timeout
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|array|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::waitaof +
+ + +
+
+ +
+
+ +

+ + RedisCluster|bool bgsave(string|array $key_or_address) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|bool
+ +
+ + +

See also

+ + + + + + +
+ +Redis::bgSave +
+ + +
+
+ +
+
+ +

+ + RedisCluster|bool|int bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$key
int$start
int$end
bool$bybit
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|bool|int
+ +
+ + +

See also

+ + + + + + +
+ +Redis::bitcount +
+ + +
+
+ +
+
+ +

+ + RedisCluster|bool|int bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$operation
string$deskey
string$srckey
string...$otherkeys
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|bool|int
+ +
+ + +

See also

+ + + + + + +
+ +Redis::bitop +
+ + +
+
+ +
+
+ +

+ + RedisCluster|int|false bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) +

+
+ + + +
+

Return the position of the first bit set to 0 or 1 in a string.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key

The key to check (must be a string)

bool$bit

Whether to look for an unset (0) or set (1) bit.

int$start

Where in the string to start looking.

int$end

Where in the string to stop looking.

bool$bybit

If true, Redis will treat $start and $end as BIT values and not bytes, so if start +was 0 and end was 2, Redis would only search the first two bits.

+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int|false
+ +
+ + +

See also

+ + + + + + +
+ https://https://redis.io/commands/bitpos/ +
+ + +
+
+ +
+
+ +

+ + RedisCluster|array|null|false blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|float|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|array|null|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::blPop +
+ + +
+
+ +
+
+ +

+ + RedisCluster|array|null|false brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|float|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|array|null|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::brPop +
+ + +
+
+ +
+
+ +

+ + mixed brpoplpush(string $srckey, string $deskey, int $timeout) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$srckey
string$deskey
int$timeout
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ +Redis::brpoplpush +
+ + +
+
+ +
+
+ +

+ + RedisCluster|string|false lmove(string $src, string $dst, string $wherefrom, string $whereto) +

+
+ + + +
+

Move an element from one list into another.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
string$src
string$dst
string$wherefrom
string$whereto
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|string|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::lMove +
+ + +
+
+ +
+
+ +

+ + RedisCluster|string|false blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout) +

+
+ + + +
+

Move an element from one list to another, blocking up to a timeout until an element is available.

+
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$src
string$dst
string$wherefrom
string$whereto
float$timeout
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|string|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::blmove +
+ + +
+
+ +
+
+ +

+ + array bzpopmax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ +
+ + + + + +
array
+ +
+ + +

See also

+ + + + + + +
+ +Redis::bzPopMax +
+ + +
+
+ +
+
+ +

+ + array bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key
string|int$timeout_or_key
mixed...$extra_args
+ + +

Return Value

+ +
+ + + + + +
array
+ +
+ + +

See also

+ + + + + + +
+ +Redis::bzPopMin +
+ + +
+
+ +
+
+ +

+ + RedisCluster|array|null|false bzmpop(float $timeout, array $keys, string $from, int $count = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
float$timeout
array$keys
string$from
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|array|null|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::bzmpop +
+ + +
+
+ +
+
+ +

+ + RedisCluster|array|null|false zmpop(array $keys, string $from, int $count = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys
string$from
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|array|null|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::zmpop +
+ + +
+
+ +
+
+ +

+ + RedisCluster|array|null|false blmpop(float $timeout, array $keys, string $from, int $count = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
float$timeout
array$keys
string$from
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|array|null|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::blmpop +
+ + +
+
+ +
+
+ +

+ + RedisCluster|array|null|false lmpop(array $keys, string $from, int $count = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
array$keys
string$from
int$count
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|array|null|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::lmpop +
+ + +
+
+ +
+
+ +

+ + bool clearlasterror() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool
+ +
+ + +

See also

+ + + + + + +
+ +Redis::clearLastError +
+ + +
+
+ +
+
+ +

+ + array|string|bool client(string|array $key_or_address, string $subcommand, string|null $arg = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
string$subcommand
string|null$arg
+ + +

Return Value

+ +
+ + + + + +
array|string|bool
+ +
+ + +

See also

+ + + + + + +
+ +Redis::client +
+ + +
+
+ +
+
+ +

+ + bool close() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool
+ +
+ + +

See also

+ + + + + + +
+ +Redis::close +
+ + +
+
+ +
+
+ +

+ + mixed cluster(string|array $key_or_address, string $command, mixed ...$extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
string$command
mixed...$extra_args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ \Redis::cluster() +
+ + +
+
+ +
+
+ +

+ + mixed command(mixed ...$extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
mixed...$extra_args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ +Redis::command +
+ + +
+
+ +
+
+ +

+ + mixed config(string|array $key_or_address, string $subcommand, mixed ...$extra_args) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string|array$key_or_address
string$subcommand
mixed...$extra_args
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ +Redis::config +
+ + +
+
+ +
+
+ +

+ + RedisCluster|int dbsize(string|array $key_or_address) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string|array$key_or_address
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int
+ +
+ + +

See also

+ + + + + + +
+ +Redis::dbSize +
+ + +
+
+ +
+
+ +

+ + RedisCluster|bool copy(string $src, string $dst, array|null $options = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$src
string$dst
array|null$options
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|bool
+ +
+ + +

See also

+ + + + + + +
+ https://redis.io/commands/copy +
+ + +
+
+ +
+
+ +

+ + RedisCluster|int|false decr(string $key, int $by = 1) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$by
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::decr +
+ + +
+
+ +
+
+ +

+ + RedisCluster|int|false decrby(string $key, int $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
int$value
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::decrBy +
+ + +
+
+ +
+
+ +

+ + float decrbyfloat(string $key, float $value) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string$key
float$value
+ + +

Return Value

+ +
+ + + + + +
float
+ +
+ + +

See also

+ + + + + + +
+ \Redis::decrbyfloat() +
+ + +
+
+ +
+
+ +

+ + RedisCluster|int|false del(array|string $key, string ...$other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
array|string$key
string...$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::del +
+ + +
+
+ +
+
+ +

+ + RedisCluster|int|false delex(string $key, array|null $options = null) +

+
+ + + +
+

Delete a key conditionally based on its value or hash digest

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to delete

array|null$options

An array with options to modify how DELX works.

+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int|false

Returns 1 if the key was deleted, 0 if it was not.

+ +
+ + + +
+
+ +
+
+ +

+ + RedisCluster|int|false delifeq(string $key, mixed $value) +

+
+ + + +
+

Delete a key if it's equal to the specified value. This command is +specific to Valkey >= 9.0

+
+
+

Parameters

+ + + + + + + + + + + + +
string$key

The key to delete

mixed$value

The value to compare against the key's value.

+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int|false

Returns 1 if the key was deleted, 0 if it was not.

+ +
+ + + +
+
+ +
+
+ +

+ + bool discard() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
bool
+ +
+ + +

See also

+ + + + + + +
+ +Redis::discard +
+ + +
+
+ +
+
+ +

+ + RedisCluster|string|false dump(string $key) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + +
string$key
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|string|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::dump +
+ + +
+
+ +
+
+ +

+ + RedisCluster|string|false echo(string|array $key_or_address, string $msg) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
string|array$key_or_address
string$msg
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|string|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::echo +
+ + +
+
+ +
+
+ +

+ + mixed eval(string $script, array $args = [], int $num_keys = 0) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script
array$args
int$num_keys
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ +Redis::eval +
+ + +
+
+ +
+
+ +

+ + mixed eval_ro(string $script, array $args = [], int $num_keys = 0) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script
array$args
int$num_keys
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ +Redis::eval_ro +
+ + +
+
+ +
+
+ +

+ + mixed evalsha(string $script_sha, array $args = [], int $num_keys = 0) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script_sha
array$args
int$num_keys
+ + +

Return Value

+ +
+ + + + + +
mixed
+ +
+ + +

See also

+ + + + + + +
+ +Redis::evalsha +
+ + +
+
+ +
+
+ +

+ + mixed evalsha_ro(string $script_sha, array $args = [], int $num_keys = 0) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + + + + + + +
string$script_sha
array$args
int$num_keys
+ +

Return Value

-

Details

+
+ + + + + +
mixed
-
+
+ + +

See also

+ + + + + + +
+ +Redis::evalsha_ro +
+ + +
+
+ +
+
+ +

+ + array|false exec() +

+
+ + + +
+

No description

+ +
+
+ +

Return Value

+ +
+ + + + + +
array|false
+ +
+ + +

See also

+ + + + + + +
+ +Redis::exec +
+ + +
+
+ +
-

- - - __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, mixed $auth = NULL, array $context = NULL) - + +

+ + RedisCluster|int|bool exists(mixed $key, mixed ...$other_keys)

@@ -2397,56 +6624,246 @@

Parameters

- - + + - - + + +
string|null$namemixed$key
array$seedsmixed...$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int|bool
+ +
+ + +

See also

+ + - + + + +
int|float + +Redis::exists +
+ + +
+
+ +
+
+ +

+ + RedisCluster|int|bool touch(mixed $key, mixed ...$other_keys) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + + + + +
mixed$key
mixed...$other_keys
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|int|bool
+ +
+ + +

See also

+ + + + + + +
+ +Redis::touch +
+ + +
+
+ +
+
+ +

+ + RedisCluster|bool expire(string $key, int $timeout, string|null $mode = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + + + + + - - + + + + +
string$key
int $timeout
int|float$read_timeoutstring|null$mode
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|bool
+ +
+ + +

See also

+ + + + + + +
+ +Redis::expire +
+ + +
+
+ +
+
+ +

+ + RedisCluster|bool expireat(string $key, int $timestamp, string|null $mode = null) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + + + + - - + + - - + + +
string$key
bool$persistentint$timestamp
mixed$authstring|null$mode
+ + +

Return Value

+ +
+ + + + + +
RedisCluster|bool
+ +
+ + +

See also

+ + - - +
array$context + +Redis::expireAt +
- - -
-

- - string - _compress(string $value) - + +

+ + RedisCluster|int|false expiretime(string $key)

@@ -2462,21 +6879,23 @@

Parameters

- +
string$value$key
-

Return Value

+

Return Value

- +
+
- +
stringRedisCluster|int|false
+

See also

@@ -2484,8 +6903,8 @@

See also

@@ -2497,11 +6916,10 @@

See also

-

- - string - _uncompress(string $value) - + +

+ + RedisCluster|int|false pexpiretime(string $key)

@@ -2517,21 +6935,23 @@

Parameters

- -Redis::_compress + +Redis::expiretime
- +
string$value$key
-

Return Value

+

Return Value

- +
+
- +
stringRedisCluster|int|false
+

See also

@@ -2539,8 +6959,8 @@

See also

@@ -2552,11 +6972,10 @@

See also

-

- - bool|string - _serialize(mixed $value) - + +

+ + RedisCluster|bool flushall(string|array $key_or_address, bool $async = false)

@@ -2571,22 +6990,29 @@

Parameters

- -Redis::_uncompress + +Redis::pexpiretime
- - + + + + + + +
mixed$valuestring|array$key_or_address
bool$async
-

Return Value

+

Return Value

- +
+
- +
bool|stringRedisCluster|bool
+

See also

@@ -2594,8 +7020,8 @@

See also

@@ -2607,11 +7033,10 @@

See also

-

- - mixed - _unserialize(string $value) - + +

+ + RedisCluster|bool flushdb(string|array $key_or_address, bool $async = false)

@@ -2626,22 +7051,29 @@

Parameters

- -Redis::_serialize + +Redis::flushAll
- - + + + + + + +
string$valuestring|array$key_or_address
bool$async
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|bool
+

See also

@@ -2649,8 +7081,8 @@

See also

@@ -2662,11 +7094,10 @@

See also

-

- - string - _pack(mixed $value) - + +

+ + RedisCluster|int|false geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options)

@@ -2681,22 +7112,44 @@

Parameters

- -Redis::_unserialize + +Redis::flushDB
+ + + + + + + + + + + + + + + + + + + + - +
string$key
float$lng
float$lat
string$member
mixed$value...$other_triples_and_options
-

Return Value

+

Return Value

- +
+
- +
stringRedisCluster|int|false
+

See also

@@ -2704,8 +7157,8 @@

See also

@@ -2717,11 +7170,10 @@

See also

-

- - mixed - _unpack(string $value) - + +

+ + RedisCluster|float|false geodist(string $key, string $src, string $dest, string|null $unit = null)

@@ -2737,21 +7189,38 @@

Parameters

- -Redis::_pack + +Redis::geoadd
- + + + + + + + + + + + + + + + +
string$value$key
string$src
string$dest
string|null$unit
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|float|false
+

See also

@@ -2759,8 +7228,8 @@

See also

@@ -2772,11 +7241,10 @@

See also

-

- - bool|string - _prefix(string $key) - + +

+ + RedisCluster|array|false geohash(string $key, string $member, string ...$other_members)

@@ -2794,19 +7262,31 @@

Parameters

+ + + + + + + + + +
- -Redis::_unpack + +Redis::geodist
string $key
string$member
string...$other_members
-

Return Value

+

Return Value

- +
+
- +
bool|stringRedisCluster|array|false
+

See also

@@ -2814,8 +7294,8 @@

See also

@@ -2827,11 +7307,10 @@

See also

-

- - array - _masters() - + +

+ + RedisCluster|array|false geopos(string $key, string $member, string ...$other_members)

@@ -2842,62 +7321,62 @@

- -

Return Value

+

Parameters

- -Redis::_prefix + +Redis::geohash
- - - - -
array
- - - - - - - - -
-

- - string|null - _redir() - -

-
- - + + string + $key + + + + string + $member + + + + string + ...$other_members + + + -
-

No description

- -
-
-

Return Value

+

Return Value

- +
+
- +
string|nullRedisCluster|array|false
+
+

See also

+ + + + + + +
+ +Redis::geopos +
+
-

- - mixed - acl(string|array $key_or_address, string $subcmd, string ...$args) - + +

+ + mixed georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = [])

@@ -2912,32 +7391,49 @@

Parameters

- - + + - - + + + + + + + + + + + + - + + + + + +
string|array$key_or_addressstring$key
string$subcmdfloat$lng
float$lat
float$radius
string...$args$unit
array$options
-

Return Value

+

Return Value

- +
+
mixed
+

See also

@@ -2945,7 +7441,8 @@

See also

@@ -2957,11 +7454,10 @@

See also

-

- - RedisCluster|bool|int - append(string $key, mixed $value) - + +

+ + mixed georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = [])

@@ -2981,22 +7477,44 @@

Parameters

- - + + + + + + + + + + + + + + + + + + + + + +
- Redis::acl + +Redis::georadius
mixed$valuefloat$lng
float$lat
float$radius
string$unit
array$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|intmixed
+

See also

@@ -3004,8 +7522,8 @@

See also

@@ -3017,11 +7535,10 @@

See also

-

- - RedisCluster|bool - bgrewriteaof(string|array $key_or_address) - + +

+ + mixed georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = [])

@@ -3036,22 +7553,44 @@

Parameters

- -Redis::append + +Redis::georadius_ro
- - + + + + + + + + + + + + + + + + + + + + + +
string|array$key_or_addressstring$key
string$member
float$radius
string$unit
array$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolmixed
+

See also

@@ -3059,7 +7598,8 @@

See also

@@ -3071,11 +7611,10 @@

See also

-

- - RedisCluster|bool - bgsave(string|array $key_or_address) - + +

+ + mixed georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = [])

@@ -3090,22 +7629,44 @@

Parameters

- Redis::bgrewriteaof + +Redis::georadiusbymember
- - + + + + + + + + + + + + + + + + + + + + + +
string|array$key_or_addressstring$key
string$member
float$radius
string$unit
array$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolmixed
+

See also

@@ -3113,7 +7674,8 @@

See also

@@ -3125,11 +7687,10 @@

See also

-

- - RedisCluster|bool|int - bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) - + +

+ + RedisCluster|array geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = [])

@@ -3149,32 +7710,39 @@

Parameters

- - + + - - + + - - + + + + + + +
- Redis::bgsave + +Redis::georadiusbymember_ro
int$startarray|string$position
int$endarray|int|float$shape
bool$bybitstring$unit
array$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|intRedisCluster|array
+

See also

@@ -3182,7 +7750,7 @@

See also

@@ -3194,11 +7762,10 @@

See also

-

- - RedisCluster|bool|int - bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys) - + +

+ + RedisCluster|array|int|false geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = [])

@@ -3214,36 +7781,48 @@

Parameters

- Redis::bitcount + https://redis.io/commands/geosearch
- + - + - - + + + + + + + - + + + + + +
string$operation$dst
string$deskey$src
string$srckeyarray|string$position
array|int|float$shape
string...$otherkeys$unit
array$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|intRedisCluster|array|int|false
+

See also

@@ -3251,7 +7830,7 @@

See also

@@ -3263,18 +7842,18 @@

See also

-

- - RedisCluster|int|false - bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) - + +

+ + mixed get(string $key)

-

Return the position of the first bit set to 0 or 1 in a string.

+

No description

+

Parameters

@@ -3283,41 +7862,22 @@

Parameters

- - - - - - - - - - - - - - - - - - - - - +
- Redis::bitop + https://redis.io/commands/geosearchstore
string $key

The key to check (must be a string)

bool$bit

Whether to look for an unset (0) or set (1) bit.

int$start

Where in the string to start looking.

int$end

Where in the string to stop looking.

bool$bybit

If true, Redis will treat $start and $end as BIT values and not bytes, so if start -was 0 and end was 2, Redis would only search the first two bits.

-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falsemixed
+

See also

@@ -3325,7 +7885,8 @@

See also

@@ -3337,122 +7898,130 @@

See also

-

- - RedisCluster|array|null|false - blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) - + +

+ + mixed getdel(string $key)

-

See Redis::blpop()

+

No description

+

Parameters

- https://https://redis.io/commands/bitpos/ + +Redis::get
- + - - - - - - - - - -
string|arraystring $key
string|float|int$timeout_or_key
mixed...$extra_args
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|null|falsemixed
+ +

See also

+ + + + + + +
+ +Redis::getDel +
+
-

- - RedisCluster|array|null|false - brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args) - + +

+ + RedisCluster|array|false getWithMeta(string $key)

-

See Redis::brpop()

+

No description

+

Parameters

- + - - - - - - - - - -
string|arraystring $key
string|float|int$timeout_or_key
mixed...$extra_args
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|null|falseRedisCluster|array|false
+
+

See also

+ + + + + + +
+ +Redis::getWithMeta +
+
-

- - mixed - brpoplpush(string $srckey, string $deskey, int $timeout) - + +

+ + RedisCluster|string|false getex(string $key, array $options = [])

-

See Redis::brpoplpush()

+

No description

+

Parameters

@@ -3460,44 +8029,52 @@

Parameters

- - - - - - + - - + +
string$srckey
string$deskey$key
int$timeoutarray$options
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|string|false
+
+

See also

+ + + + + + +
+ +Redis::getEx +
+
-

- - array - bzpopmax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) - + +

+ + RedisCluster|int|false getbit(string $key, int $value)

@@ -3512,32 +8089,29 @@

Parameters

- + - - - - - - - + +
string|arraystring $key
string|int$timeout_or_key
mixed...$extra_argsint$value
-

Return Value

+

Return Value

- +
+
- +
arrayRedisCluster|int|false
+

See also

@@ -3545,7 +8119,8 @@

See also

@@ -3557,11 +8132,10 @@

See also

-

- - array - bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) - + +

+ + string|null getlasterror()

@@ -3572,36 +8146,18 @@

-

Parameters

- -
- Redis::bzpopmax + +Redis::getBit
- - - - - - - - - - - - - - - -
string|array$key
string|int$timeout_or_key
mixed...$extra_args
- -

Return Value

+

Return Value

- +
+
- +
arraystring|null
+

See also

@@ -3609,7 +8165,8 @@

See also

@@ -3621,11 +8178,10 @@

See also

-

- - RedisCluster|array|null|false - bzmpop(float $timeout, array $keys, string $from, int $count = 1) - + +

+ + int getmode()

@@ -3636,41 +8192,74 @@

-

Parameters

+ +

Return Value

+ +
+
- Redis::bzpopmin + +Redis::getLastError
+ + + + +
int
+ + + + +

See also

- - - - - - - - - - - - + +
float$timeout
array$keys
string$from + +Redis::getMode +
+ + + + + + +
+ +

+ + mixed getoption(int $option) +

+
+ + + +
+

No description

+ +
+
+

Parameters

+ + - +
int$count$option
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|null|falsemixed
+

See also

@@ -3678,7 +8267,8 @@

See also

@@ -3690,11 +8280,10 @@

See also

-

- - RedisCluster|array|null|false - zmpop(array $keys, string $from, int $count = 1) - + +

+ + RedisCluster|string|false getrange(string $key, int $start, int $end)

@@ -3709,32 +8298,34 @@

Parameters

- Redis::bzmpop + +Redis::getOption
- - + + - - + + - +
array$keysstring$key
string$fromint$start
int$count$end
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|null|falseRedisCluster|string|false
+

See also

@@ -3742,7 +8333,8 @@

See also

@@ -3754,11 +8346,10 @@

See also

-

- - RedisCluster|array|null|false - blmpop(float $timeout, array $keys, string $from, int $count = 1) - + +

+ + RedisCluster|string|array|int|false lcs(string $key1, string $key2, array|null $options = null)

@@ -3773,37 +8364,34 @@

Parameters

- Redis::zmpop + +Redis::getRange
- - - - - - - + + - + - - + +
float$timeout
array$keysstring$key1
string$from$key2
int$countarray|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|null|falseRedisCluster|string|array|int|false
+

See also

@@ -3811,8 +8399,8 @@

See also

@@ -3824,11 +8412,10 @@

See also

-

- - RedisCluster|array|null|false - lmpop(array $keys, string $from, int $count = 1) - + +

+ + RedisCluster|string|bool getset(string $key, mixed $value)

@@ -3843,32 +8430,29 @@

Parameters

- -Redis::blmpop + +Redis::lcs
- - - - - - + - - + +
array$keys
string$from$key
int$countmixed$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|null|falseRedisCluster|string|bool
+

See also

@@ -3876,8 +8460,8 @@

See also

@@ -3889,11 +8473,10 @@

See also

-

- - bool - clearlasterror() - + +

+ + array|false gettransferredbytes()

@@ -3905,15 +8488,17 @@

-

Return Value

+

Return Value

-
- -Redis::lmpop + +Redis::getset
+
+
- +
boolarray|false
+

See also

@@ -3921,7 +8506,8 @@

See also

@@ -3933,11 +8519,10 @@

See also

-

- - array|string|bool - client(string|array $key_or_address, string $subcommand, string|null $arg = NULL) - + +

+ + void cleartransferredbytes()

@@ -3948,36 +8533,18 @@

-

Parameters

- -
- \Redis::clearlasterror() + +Redis::getTransferredBytes
- - - - - - - - - - - - - - - -
string|array$key_or_address
string$subcommand
string|null$arg
- -

Return Value

+

Return Value

- +
+
- +
array|string|boolvoid
+

See also

@@ -3985,7 +8552,8 @@

See also

@@ -3997,11 +8565,10 @@

See also

-

- - bool - close() - + +

+ + RedisCluster|int|false hdel(string $key, string $member, string ...$other_members)

@@ -4012,16 +8579,38 @@

- -

Return Value

+

Parameters

- Redis::client + +Redis::clearTransferredBytes
+ + + + + + + + + + + + + + + +
string$key
string$member
string...$other_members
+ + +

Return Value

+ +
+ - +
boolRedisCluster|int|false
+

See also

@@ -4029,7 +8618,8 @@

See also

@@ -4041,11 +8631,10 @@

See also

-

- - mixed - cluster(string|array $key_or_address, string $command, mixed ...$extra_args) - + +

+ + RedisCluster|bool hexists(string $key, string $member)

@@ -4060,32 +8649,29 @@

Parameters

- Redis::close + +Redis::hDel
- - - - - - + - - + +
string|array$key_or_address
string$command$key
mixed...$extra_argsstring$member
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|bool
+

See also

@@ -4093,7 +8679,8 @@

See also

@@ -4105,11 +8692,10 @@

See also

-

- - mixed - command(mixed ...$extra_args) - + +

+ + mixed hget(string $key, string $member)

@@ -4124,22 +8710,29 @@

Parameters

- Redis::cluster + +Redis::hExists
- - + + + + + + +
mixed...$extra_argsstring$key
string$member
-

Return Value

+

Return Value

- +
+
mixed
+

See also

@@ -4147,7 +8740,8 @@

See also

@@ -4159,11 +8753,10 @@

See also

-

- - mixed - config(string|array $key_or_address, string $subcommand, mixed ...$extra_args) - + +

+ + RedisCluster|array|false hgetall(string $key)

@@ -4178,32 +8771,24 @@

Parameters

- Redis::command + +Redis::hGet
- - - - - - - - - - - +
string|array$key_or_address
string$subcommand
mixed...$extra_args$key
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|array|false
+

See also

@@ -4211,8 +8796,8 @@

See also

@@ -4224,11 +8809,10 @@

See also

-

- - RedisCluster|int - dbsize(string|array $key_or_address) - + +

+ + mixed hgetWithMeta(string $key, string $member)

@@ -4243,22 +8827,29 @@

Parameters

- -Redis::config + +Redis::hGetAll
- - + + + + + + +
string|array$key_or_addressstring$key
string$member
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|intmixed
+

See also

@@ -4266,7 +8857,8 @@

See also

@@ -4278,11 +8870,10 @@

See also

-

- - RedisCluster|int|false - decr(string $key, int $by = 1) - + +

+ + RedisCluster|int|false hincrby(string $key, string $member, int $value)

@@ -4300,24 +8891,31 @@

Parameters

+ + + + + - +
- \Redis::dbsize() + +Redis::hGetWithMeta
string $key
string$member
int$by$value
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -4325,8 +8923,8 @@

See also

@@ -4338,11 +8936,10 @@

See also

-

- - RedisCluster|int|false - decrby(string $key, int $value) - + +

+ + RedisCluster|float|false hincrbyfloat(string $key, string $member, float $value)

@@ -4362,22 +8959,29 @@

Parameters

- + + + + + +
- -Redis::decr + +Redis::hIncrBy
intstring$member
float $value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|float|false
+

See also

@@ -4385,7 +8989,8 @@

See also

@@ -4397,11 +9002,10 @@

See also

-

- - float - decrbyfloat(string $key, float $value) - + +

+ + RedisCluster|array|false hkeys(string $key)

@@ -4419,24 +9023,21 @@

Parameters

- - - - -
- \Redis::decrby() + +Redis::hIncrByFloat
string $key
float$value
-

Return Value

+

Return Value

- +
+
- +
floatRedisCluster|array|false
+

See also

@@ -4444,7 +9045,8 @@

See also

@@ -4456,11 +9058,10 @@

See also

-

- - RedisCluster|int|false - del(array|string $key, string ...$other_keys) - + +

+ + RedisCluster|int|false hlen(string $key)

@@ -4475,27 +9076,24 @@

Parameters

- Redis::decrbyfloat + +Redis::hKeys
- - - - - - +
array|string$key
string...$other_keys$key
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -4503,8 +9101,8 @@

See also

@@ -4516,11 +9114,10 @@

See also

-

- - bool - discard() - + +

+ + RedisCluster|array|false hmget(string $key, array $keys)

@@ -4531,16 +9128,33 @@

- -

Return Value

+

Parameters

- -Redis::del + +Redis::hLen
+ + + + + + + + + + +
string$key
array$keys
+ + +

Return Value

+ +
+ - +
boolRedisCluster|array|false
+

See also

@@ -4548,7 +9162,8 @@

See also

@@ -4560,11 +9175,10 @@

See also

-

- - RedisCluster|string|false - dump(string $key) - + +

+ + RedisCluster|array|false hgetex(string $key, array $fields, string|array|null $expiry = null)

@@ -4582,19 +9196,31 @@

Parameters

+ + + + + + + + + +
- Redis::discard + +Redis::hMget
string $key
array$fields
string|array|null$expiry
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|falseRedisCluster|array|false
+

See also

@@ -4602,7 +9228,8 @@

See also

@@ -4614,11 +9241,10 @@

See also

-

- - RedisCluster|string|false - echo(string|array $key_or_address, string $msg) - + +

+ + RedisCluster|int|false hsetex(string $key, array $fields, array|null $expiry = null)

@@ -4633,27 +9259,34 @@

Parameters

- Redis::dump + +Redis::hgetex
- - + + + + + + + - - + +
string|array$key_or_addressstring$key
array$fields
string$msgarray|null$expiry
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|falseRedisCluster|int|false
+

See also

@@ -4661,8 +9294,8 @@

See also

@@ -4674,11 +9307,10 @@

See also

-

- - mixed - eval(string $script, array $args = [], int $num_keys = 0) - + +

+ + RedisCluster|array|false hgetdel(string $key, array $fields)

@@ -4694,31 +9326,28 @@

Parameters

- -Redis::echo + +Redis::hsetex
- + - - - - - - +
string$script$key
array$args
int$num_keys$fields
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|array|false
+

See also

@@ -4726,7 +9355,8 @@

See also

@@ -4738,11 +9368,10 @@

See also

-

- - mixed - eval_ro(string $script, array $args = [], int $num_keys = 0) - + +

+ + RedisCluster|bool hmset(string $key, array $key_values)

@@ -4758,31 +9387,28 @@

Parameters

- Redis::eval + +Redis::hgetdel
- + - - - - - - +
string$script$key
array$args
int$num_keys$key_values
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|bool
+

See also

@@ -4790,7 +9416,8 @@

See also

@@ -4802,11 +9429,10 @@

See also

-

- - mixed - evalsha(string $script_sha, array $args = [], int $num_keys = 0) - + +

+ + array|bool hscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

@@ -4822,31 +9448,38 @@

Parameters

- Redis::eval_ro + +Redis::hMset
- + - - + + + + + + + - +
string$script_sha$key
array$argsnull|int|string$iterator
string|null$pattern
int$num_keys$count
-

Return Value

+

Return Value

- +
+
- +
mixedarray|bool
+

See also

@@ -4854,7 +9487,8 @@

See also

@@ -4866,11 +9500,10 @@

See also

-

- - mixed - evalsha_ro(string $script_sha, array $args = [], int $num_keys = 0) - + +

+ + RedisCluster|int|false expiremember(string $key, string $field, int $ttl, string|null $unit = null)

@@ -4886,31 +9519,38 @@

Parameters

- Redis::evalsha + +Redis::hscan
- + - - + + - + + + + + +
string$script_sha$key
array$argsstring$field
int$num_keys$ttl
string|null$unit
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|int|false
+

See also

@@ -4918,7 +9558,8 @@

See also

@@ -4930,11 +9571,10 @@

See also

-

- - array|false - exec() - + +

+ + RedisCluster|int|false expirememberat(string $key, string $field, int $timestamp)

@@ -4945,16 +9585,38 @@

- -

Return Value

+

Parameters

- Redis::evalsha_ro + +Redis::expiremember
+ + + + + + + + + + + + + + + +
string$key
string$field
int$timestamp
+ + +

Return Value

+ +
+ - +
array|falseRedisCluster|int|false
+

See also

@@ -4962,8 +9624,8 @@

See also

@@ -4975,11 +9637,10 @@

See also

-

- - RedisCluster|int|bool - exists(mixed $key, mixed ...$other_keys) - + +

+ + RedisCluster|string|array hrandfield(string $key, array|null $options = null)

@@ -4994,27 +9655,29 @@

Parameters

- -Redis::exec + +Redis::expirememberat
- + - - + +
mixedstring $key
mixed...$other_keysarray|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|boolRedisCluster|string|array
+

See also

@@ -5022,7 +9685,7 @@

See also

@@ -5034,11 +9697,10 @@

See also

-

- - RedisCluster|int|bool - touch(mixed $key, mixed ...$other_keys) - + +

+ + RedisCluster|int|false hset(string $key, string $member, mixed $value)

@@ -5053,27 +9715,34 @@

Parameters

- Redis::exists + https://redis.io/commands/hrandfield
- + + + + + + - +
mixedstring $key
string$member
mixed...$other_keys$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|boolRedisCluster|int|false
+

See also

@@ -5081,8 +9750,8 @@

See also

@@ -5094,11 +9763,10 @@

See also

-

- - RedisCluster|bool - expire(string $key, int $timeout, string|null $mode = NULL) - + +

+ + RedisCluster|bool hsetnx(string $key, string $member, mixed $value)

@@ -5118,27 +9786,29 @@

Parameters

- - + + - - + +
- -Redis::touch + +Redis::hSet
int$timeoutstring$member
string|null$modemixed$value
-

Return Value

+

Return Value

- +
+
RedisCluster|bool
+

See also

@@ -5146,7 +9816,8 @@

See also

@@ -5158,11 +9829,10 @@

See also

-

- - RedisCluster|bool - expireat(string $key, int $timestamp, string|null $mode = NULL) - + +

+ + RedisCluster|int|false hstrlen(string $key, string $field)

@@ -5182,27 +9852,24 @@

Parameters

- - - - - - - + +
- Redis::expire + +Redis::hSetNx
int$timestamp
string|null$modestring$field
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|int|false
+

See also

@@ -5210,7 +9877,8 @@

See also

@@ -5222,11 +9890,10 @@

See also

-

- - RedisCluster|int|false - expiretime(string $key) - + +

+ + RedisCluster|array|false hexpire(string $key, int $ttl, array $fields, string|null $mode = NULL)

@@ -5244,19 +9911,36 @@

Parameters

+ + + + + + + + + + + + + + +
- Redis::expireat + +Redis::hStrLen
string $key
int$ttl
array$fields
string|null$mode
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|false
+

See also

@@ -5264,8 +9948,8 @@

See also

@@ -5277,11 +9961,10 @@

See also

-

- - RedisCluster|int|false - pexpiretime(string $key) - + +

+ + RedisCluster|array|false hpexpire(string $key, int $ttl, array $fields, string|null $mode = NULL)

@@ -5299,19 +9982,36 @@

Parameters

+ + + + + + + + + + + + + + +
- -Redis::expiretime + +Redis::hexpire
string $key
int$ttl
array$fields
string|null$mode
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|false
+

See also

@@ -5319,8 +10019,8 @@

See also

@@ -5332,11 +10032,10 @@

See also

-

- - RedisCluster|bool - flushall(string|array $key_or_address, bool $async = false) - + +

+ + RedisCluster|array|false hexpireat(string $key, int $time, array $fields, string|null $mode = NULL)

@@ -5351,27 +10050,39 @@

Parameters

- -Redis::pexpiretime + +Redis::hpexpire
- - + + - - + + + + + + + + + + + +
string|array$key_or_addressstring$key
bool$asyncint$time
array$fields
string|null$mode
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|array|false
+

See also

@@ -5379,7 +10090,8 @@

See also

@@ -5391,11 +10103,10 @@

See also

-

- - RedisCluster|bool - flushdb(string|array $key_or_address, bool $async = false) - + +

+ + RedisCluster|array|false hpexpireat(string $key, int $mstime, array $fields, string|null $mode = NULL)

@@ -5410,27 +10121,39 @@

Parameters

- Redis::flushall + +Redis::hexpireat
- - + + - - + + + + + + + + + + + +
string|array$key_or_addressstring$key
bool$asyncint$mstime
array$fields
string|null$mode
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|array|false
+

See also

@@ -5438,7 +10161,8 @@

See also

@@ -5450,11 +10174,10 @@

See also

-

- - RedisCluster|int|false - geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) - + +

+ + RedisCluster|array|false httl(string $key, array $fields)

@@ -5474,37 +10197,24 @@

Parameters

- - - - - - - - - - - - - - - - - + +
- Redis::flushdb + +Redis::hpexpireat
float$lng
float$lat
string$member
mixed...$other_triples_and_optionsarray$fields
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|false
+

See also

@@ -5512,7 +10222,8 @@

See also

@@ -5524,11 +10235,10 @@

See also

-

- - RedisCluster|float|false - geodist(string $key, string $src, string $dest, string|null $unit = null) - + +

+ + RedisCluster|array|false hpttl(string $key, array $fields)

@@ -5548,32 +10258,24 @@

Parameters

- - - - - - - - - - - - + +
- Redis::geoadd + +Redis::httl
string$src
string$dest
string|null$unitarray$fields
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|float|falseRedisCluster|array|false
+

See also

@@ -5581,7 +10283,8 @@

See also

@@ -5593,11 +10296,10 @@

See also

-

- - RedisCluster|array|false - geohash(string $key, string $member, string ...$other_members) - + +

+ + RedisCluster|array|false hexpiretime(string $key, array $fields)

@@ -5617,27 +10319,24 @@

Parameters

- - - - - - - + +
- Redis::geodist + +Redis::hpttl
string$member
string...$other_membersarray$fields
-

Return Value

+

Return Value

- +
+
RedisCluster|array|false
+

See also

@@ -5645,7 +10344,8 @@

See also

@@ -5657,11 +10357,10 @@

See also

-

- - RedisCluster|array|false - geopos(string $key, string $member, string ...$other_members) - + +

+ + RedisCluster|array|false hpexpiretime(string $key, array $fields)

@@ -5681,27 +10380,24 @@

Parameters

- - - - - - - + +
- Redis::geohash + +Redis::hexpiretime
string$member
string...$other_membersarray$fields
-

Return Value

+

Return Value

- +
+
RedisCluster|array|false
+

See also

@@ -5709,7 +10405,8 @@

See also

@@ -5721,11 +10418,10 @@

See also

-

- - mixed - georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) - + +

+ + RedisCluster|array|false hpersist(string $key, array $fields)

@@ -5743,44 +10439,26 @@

Parameters

- - - - - - - - - - - - - - - - - - - - - +
- Redis::geopos + +Redis::hpexpiretime
string $key
float$lng
float$lat
float$radius
string$unit
array$options$fields
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|array|false
+

See also

@@ -5788,7 +10466,8 @@

See also

@@ -5800,11 +10479,10 @@

See also

-

- - mixed - georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) - + +

+ + RedisCluster|array|false hvals(string $key)

@@ -5822,44 +10500,21 @@

Parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
- Redis::georadius + +Redis::hpexpiretime
string $key
float$lng
float$lat
float$radius
string$unit
array$options
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|array|false
+

See also

@@ -5867,7 +10522,8 @@

See also

@@ -5879,11 +10535,10 @@

See also

-

- - mixed - georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) - + +

+ + RedisCluster|int|false incr(string $key, int $by = 1)

@@ -5903,37 +10558,24 @@

Parameters

- - - - - - - - - - - - - - - - - + +
- Redis::georadius_ro + +Redis::hVals
string$member
float$radius
string$unit
array$optionsint$by
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|int|false
+

See also

@@ -5941,7 +10583,8 @@

See also

@@ -5953,11 +10596,10 @@

See also

-

- - mixed - georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) - + +

+ + RedisCluster|int|false incrby(string $key, int $value)

@@ -5977,37 +10619,24 @@

Parameters

- - - - - - - - - - - - - - - - - + +
- Redis::georadiusbymember + +Redis::incr
string$member
float$radius
string$unit
array$optionsint$value
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|int|false
+

See also

@@ -6015,7 +10644,8 @@

See also

@@ -6027,11 +10657,10 @@

See also

-

- - mixed - get(string $key) - + +

+ + RedisCluster|float|false incrbyfloat(string $key, float $value)

@@ -6049,19 +10678,26 @@

Parameters

+ + + + +
- Redis::georadiusbymember_ro + +Redis::incrBy
string $key
float$value
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|float|false
+

See also

@@ -6069,7 +10705,8 @@

See also

@@ -6081,46 +10718,50 @@

See also

-

- - RedisCluster|int|false - getbit(string $key, int $value) - + +

+ + RedisCluster|array|false info(string|array $key_or_address, string ...$sections)

-

No description

- +

Retrieve information about the connected redis-server. If no arguments are passed to +this function, redis will return every info field. Alternatively you may pass a specific +section you want returned (e.g. 'server', or 'memory') to receive only information pertaining +to that section.

If connected to Redis server >= 7.0.0 you may pass multiple optional sections.

Parameters

- Redis::get + +Redis::incrByFloat
- - - + + + - - - + + +
string$keystring|array$key_or_address

Either a key name or array with host and port indicating +which cluster node we want to send the command to.

int$valuestring...$sections

Optional section(s) you wish Redis server to return.

-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|false
+

See also

@@ -6128,7 +10769,7 @@

See also

@@ -6140,11 +10781,10 @@

See also

-

- - string|null - getlasterror() - + +

+ + RedisCluster|array|false keys(string $pattern)

@@ -6155,16 +10795,28 @@

- -

Return Value

+

Parameters

- Redis::getbit + https://redis.io/commands/info/
+ + + + + +
string$pattern
+ + +

Return Value

+ +
+ - +
string|nullRedisCluster|array|false
+

See also

@@ -6172,7 +10824,8 @@

See also

@@ -6184,11 +10837,10 @@

See also

-

- - int - getmode() - + +

+ + RedisCluster|int|false lastsave(string|array $key_or_address)

@@ -6199,16 +10851,28 @@

- -

Return Value

+

Parameters

- Redis::getlasterror + +Redis::keys
+ + + + + +
string|array$key_or_address
+ + +

Return Value

+ +
+ - +
intRedisCluster|int|false
+

See also

@@ -6216,7 +10880,8 @@

See also

@@ -6228,11 +10893,10 @@

See also

-

- - mixed - getoption(int $option) - + +

+ + RedisCluster|string|bool lget(string $key, int $index)

@@ -6247,22 +10911,29 @@

Parameters

- Redis::getmode + +Redis::lastSave
+ + + + + - +
string$key
int$option$index
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|string|bool
+

See also

@@ -6270,7 +10941,7 @@

See also

@@ -6282,11 +10953,10 @@

See also

-

- - RedisCluster|string|false - getrange(string $key, int $start, int $end) - + +

+ + mixed lindex(string $key, int $index)

@@ -6307,26 +10977,23 @@

Parameters

- - - - - - +
- Redis::getoption + \Redis::lget()
int$start
int$end$index
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|falsemixed
+

See also

@@ -6334,7 +11001,8 @@

See also

@@ -6346,11 +11014,10 @@

See also

-

- - RedisCluster|string|array|int|false - lcs(string $key1, string $key2, array|null $options = NULL) - + +

+ + RedisCluster|int|false linsert(string $key, string $pos, mixed $pivot, mixed $value)

@@ -6366,31 +11033,38 @@

Parameters

- Redis::getrange + +Redis::lindex
- + - + - - + + + + + + +
string$key1$key
string$key2$pos
array|null$optionsmixed$pivot
mixed$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|array|int|falseRedisCluster|int|false
+

See also

@@ -6398,7 +11072,8 @@

See also

@@ -6410,11 +11085,10 @@

See also

-

- - RedisCluster|string|bool - getset(string $key, mixed $value) - + +

+ + RedisCluster|int|bool llen(string $key)

@@ -6432,24 +11106,21 @@

Parameters

- - - - -
- Redis::lcs + +Redis::lInsert
string $key
mixed$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|boolRedisCluster|int|bool
+

See also

@@ -6457,7 +11128,8 @@

See also

@@ -6469,11 +11141,10 @@

See also

-

- - int|false - gettransferredbytes() - + +

+ + RedisCluster|bool|string|array lpop(string $key, int $count = 0)

@@ -6484,16 +11155,33 @@

- -

Return Value

+

Parameters

- Redis::getset + +Redis::lLen
+ + + + + + + + + + +
string$key
int$count
+ + +

Return Value

+ +
+ - +
int|falseRedisCluster|bool|string|array
+

See also

@@ -6501,7 +11189,8 @@

See also

@@ -6513,11 +11202,10 @@

See also

-

- - RedisCluster|int|false - hdel(string $key, string $member, string ...$other_members) - + +

+ + RedisCluster|null|bool|int|array lpos(string $key, mixed $value, array|null $options = null)

@@ -6537,27 +11225,29 @@

Parameters

- - + + - - + +
- Redis::gettransferredbytes + +Redis::lPop
string$membermixed$value
string...$other_membersarray|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|null|bool|int|array
+

See also

@@ -6565,7 +11255,8 @@

See also

@@ -6577,11 +11268,10 @@

See also

-

- - RedisCluster|bool - hexists(string $key, string $member) - + +

+ + RedisCluster|int|bool lpush(string $key, mixed $value, mixed ...$other_values)

@@ -6601,22 +11291,29 @@

Parameters

- - + + + + + + +
- Redis::hdel + +Redis::lPos
string$membermixed$value
mixed...$other_values
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|int|bool
+

See also

@@ -6624,7 +11321,8 @@

See also

@@ -6636,11 +11334,10 @@

See also

-

- - mixed - hget(string $key, string $member) - + +

+ + RedisCluster|int|bool lpushx(string $key, mixed $value)

@@ -6660,22 +11357,24 @@

Parameters

- - + +
- Redis::hexists + +Redis::lPush
string$membermixed$value
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|int|bool
+

See also

@@ -6683,7 +11382,8 @@

See also

@@ -6695,11 +11395,10 @@

See also

-

- - RedisCluster|array|false - hgetall(string $key) - + +

+ + RedisCluster|array|false lrange(string $key, int $start, int $end)

@@ -6717,19 +11416,31 @@

Parameters

+ + + + + + + + + +
- Redis::hget + +Redis::lPushx
string $key
int$start
int$end
-

Return Value

+

Return Value

- +
+
RedisCluster|array|false
+

See also

@@ -6737,7 +11448,8 @@

See also

@@ -6749,11 +11461,10 @@

See also

-

- - RedisCluster|int|false - hincrby(string $key, string $member, int $value) - + +

+ + RedisCluster|int|bool lrem(string $key, mixed $value, int $count = 0)

@@ -6773,27 +11484,29 @@

Parameters

- - + + - +
- Redis::hgetall + +Redis::lrange
string$membermixed$value
int$value$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|int|bool
+

See also

@@ -6801,7 +11514,8 @@

See also

@@ -6813,11 +11527,10 @@

See also

-

- - RedisCluster|float|false - hincrbyfloat(string $key, string $member, float $value) - + +

+ + RedisCluster|bool lset(string $key, int $index, mixed $value)

@@ -6837,27 +11550,29 @@

Parameters

- - + + - +
- Redis::hincrby + +Redis::lrem
string$memberint$index
floatmixed $value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|float|falseRedisCluster|bool
+

See also

@@ -6865,7 +11580,8 @@

See also

@@ -6877,11 +11593,10 @@

See also

-

- - RedisCluster|array|false - hkeys(string $key) - + +

+ + RedisCluster|bool ltrim(string $key, int $start, int $end)

@@ -6899,19 +11614,31 @@

Parameters

+ + + + + + + + + +
- Redis::hincrbyfloat + +Redis::lSet
string $key
int$start
int$end
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|bool
+

See also

@@ -6919,7 +11646,8 @@

See also

@@ -6931,11 +11659,10 @@

See also

-

- - RedisCluster|int|false - hlen(string $key) - + +

+ + RedisCluster|array|false mget(array $keys)

@@ -6950,22 +11677,24 @@

Parameters

- Redis::hkeys + +Redis::ltrim
- - + +
string$keyarray$keys
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|false
+

See also

@@ -6973,7 +11702,8 @@

See also

@@ -6985,11 +11715,10 @@

See also

-

- - RedisCluster|array|false - hmget(string $key, array $keys) - + +

+ + RedisCluster|bool mset(array $key_values)

@@ -7004,27 +11733,24 @@

Parameters

- Redis::hlen + +Redis::mget
- - - - - - +
string$key
array$keys$key_values
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|bool
+

See also

@@ -7032,7 +11758,8 @@

See also

@@ -7044,11 +11771,10 @@

See also

-

- - RedisCluster|bool - hmset(string $key, array $key_values) - + +

+ + RedisCluster|array|false msetnx(array $key_values)

@@ -7063,11 +11789,6 @@

Parameters

- Redis::hmget + +Redis::mset
- - - - - @@ -7075,15 +11796,17 @@

Parameters

string$key
array $key_values
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|array|false
+

See also

@@ -7091,7 +11814,8 @@

See also

@@ -7103,11 +11827,10 @@

See also

-

- - array|bool - hscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) - + +

+ + Redis|int|false msetex(array $key_vals, int|float|array|null $expiry = null)

@@ -7122,37 +11845,29 @@

Parameters

- Redis::hmset + +Redis::msetnx
- - - - - - - - - - - - + + - - + +
string$key
int|null$iterator
string|null$patternarray$key_vals
int$countint|float|array|null$expiry
-

Return Value

+

Return Value

- +
+
- +
array|boolRedis|int|false
+

See also

@@ -7160,7 +11875,8 @@

See also

@@ -7172,11 +11888,10 @@

See also

-

- - RedisCluster|int|false - hset(string $key, string $member, mixed $value) - + +

+ + RedisCluster|bool multi(int $value = Redis::MULTI)

@@ -7191,56 +11906,36 @@

Parameters

- Redis::hscan + +Redis::msetex
- - - - - - - - - - - +
string$key
string$member
mixedint $value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool
+ -

See also

- - - - - - -
- Redis::hset -
-
-

- - RedisCluster|bool - hsetnx(string $key, string $member, mixed $value) - + +

+ + RedisCluster|int|string|false object(string $subcommand, string $key)

@@ -7256,31 +11951,28 @@

Parameters

- + - - - - - - +
string$key$subcommand
string$member
mixed$value$key
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|int|string|false
+

See also

@@ -7288,7 +11980,8 @@

See also

@@ -7300,11 +11993,10 @@

See also

-

- - RedisCluster|int|false - hstrlen(string $key, string $field) - + +

+ + RedisCluster|bool persist(string $key)

@@ -7322,24 +12014,21 @@

Parameters

- - - - -
- Redis::hsetnx + +Redis::object
string $key
string$field
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool
+

See also

@@ -7347,7 +12036,8 @@

See also

@@ -7359,11 +12049,10 @@

See also

-

- - RedisCluster|array|false - hvals(string $key) - + +

+ + RedisCluster|bool pexpire(string $key, int $timeout, string|null $mode = null)

@@ -7381,19 +12070,31 @@

Parameters

+ + + + + + + + + +
- Redis::hstrlen + +Redis::persist
string $key
int$timeout
string|null$mode
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|bool
+

See also

@@ -7401,7 +12102,8 @@

See also

@@ -7413,11 +12115,10 @@

See also

-

- - RedisCluster|int|false - incr(string $key, int $by = 1) - + +

+ + RedisCluster|bool pexpireat(string $key, int $timestamp, string|null $mode = null)

@@ -7438,21 +12139,28 @@

Parameters

- + + + + + +
- Redis::hvals + +Redis::pexpire
int$by$timestamp
string|null$mode
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool
+

See also

@@ -7460,7 +12168,8 @@

See also

@@ -7472,11 +12181,10 @@

See also

-

- - RedisCluster|int|false - incrby(string $key, int $value) - + +

+ + RedisCluster|bool pfadd(string $key, array $elements)

@@ -7496,22 +12204,24 @@

Parameters

- - + +
- Redis::incr + +Redis::pexpireAt
int$valuearray$elements
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool
+

See also

@@ -7519,7 +12229,8 @@

See also

@@ -7531,11 +12242,10 @@

See also

-

- - RedisCluster|float|false - incrbyfloat(string $key, float $value) - + +

+ + RedisCluster|int|false pfcount(string $key)

@@ -7553,24 +12263,21 @@

Parameters

- - - - -
- Redis::incrby + +Redis::pfadd
string $key
float$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|float|falseRedisCluster|int|false
+

See also

@@ -7578,7 +12285,8 @@

See also

@@ -7590,49 +12298,47 @@

See also

-

- - RedisCluster|array|false - info(string|array $key_or_address, string ...$sections) - + +

+ + RedisCluster|bool pfmerge(string $key, array $keys)

-

Retrieve information about the connected redis-server. If no arguments are passed to -this function, redis will return every info field. Alternatively you may pass a specific -section you want returned (e.g. 'server', or 'memory') to receive only information pertaining -to that section.

If connected to Redis server >= 7.0.0 you may pass multiple optional sections.

+

No description

+

Parameters

- Redis::incrbyfloat + +Redis::pfcount
- - - + + + - - - + + +
string|array$key_or_address

Either a key name or array with host and port indicating -which cluster node we want to send the command to.

string$key
string...$sections

Optional section(s) you wish Redis server to return.

array$keys
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|bool
+

See also

@@ -7640,7 +12346,8 @@

See also

@@ -7652,41 +12359,48 @@

See also

-

- - RedisCluster|array|false - keys(string $pattern) - + +

+ + mixed ping(string|array $key_or_address, string|null $message = null)

-

No description

- +

PING an instance in the redis cluster.

Parameters

- https://redis.io/commands/info/ + +Redis::pfmerge
- - - + + + + + + + +
string$patternstring|array$key_or_address

Either a key name or a two element array with host and +address, informing RedisCluster which node to ping.

string|null$message

An optional message to send.

-

Return Value

+

Return Value

- +
+
- - + +
RedisCluster|array|falsemixed

This method always returns true if no message was sent, and the message itself +if one was.

+

See also

@@ -7694,7 +12408,8 @@

See also

@@ -7706,11 +12421,10 @@

See also

-

- - RedisCluster|int|false - lastsave(string|array $key_or_address) - + +

+ + RedisCluster|bool psetex(string $key, int $timeout, string $value)

@@ -7725,22 +12439,34 @@

Parameters

- Redis::keys + +Redis::ping
- - + + + + + + + + + + + +
string|array$key_or_addressstring$key
int$timeout
string$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool
+

See also

@@ -7748,7 +12474,8 @@

See also

@@ -7760,11 +12487,10 @@

See also

-

- - RedisCluster|string|bool - lget(string $key, int $index) - + +

+ + void psubscribe(array $patterns, callable $callback)

@@ -7779,27 +12505,29 @@

Parameters

- Redis::lastsave + +Redis::psetex
- - + + - - + +
string$keyarray$patterns
int$indexcallable$callback
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|boolvoid
+

See also

@@ -7807,7 +12535,8 @@

See also

@@ -7819,11 +12548,10 @@

See also

-

- - mixed - lindex(string $key, int $index) - + +

+ + RedisCluster|int|false pttl(string $key)

@@ -7841,24 +12569,21 @@

Parameters

- - - - -
- Redis::lget + +Redis::psubscribe
string $key
int$index
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|int|false
+

See also

@@ -7866,7 +12591,8 @@

See also

@@ -7878,11 +12604,10 @@

See also

-

- - RedisCluster|int|false - linsert(string $key, string $pos, mixed $pivot, mixed $value) - + +

+ + RedisCluster|bool|int publish(string $channel, string $message)

@@ -7898,36 +12623,28 @@

Parameters

- Redis::lindex + +Redis::pttl
- + - - - - - - - - - - - +
string$key$channel
string$pos
mixed$pivot
mixed$value$message
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool|int
+

See also

@@ -7935,7 +12652,8 @@

See also

@@ -7947,11 +12665,10 @@

See also

-

- - RedisCluster|int|bool - llen(string $key) - + +

+ + mixed pubsub(string|array $key_or_address, string ...$values)

@@ -7966,22 +12683,29 @@

Parameters

- Redis::linsert + +Redis::publish
+ + + + + - +
string|array$key_or_address
string$key...$values
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|boolmixed
+

See also

@@ -7989,7 +12713,8 @@

See also

@@ -8001,11 +12726,10 @@

See also

-

- - RedisCluster|bool|string|array - lpop(string $key, int $count = 0) - + +

+ + bool|array punsubscribe(string $pattern, string ...$other_patterns)

@@ -8021,26 +12745,28 @@

Parameters

- Redis::llen + +Redis::pubsub
- + - - + +
string$key$pattern
int$countstring...$other_patterns
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|string|arraybool|array
+

See also

@@ -8048,7 +12774,8 @@

See also

@@ -8060,11 +12787,10 @@

See also

-

- - RedisCluster|int|bool - lpush(string $key, mixed $value, mixed ...$other_values) - + +

+ + RedisCluster|bool|string randomkey(string|array $key_or_address)

@@ -8079,32 +12805,24 @@

Parameters

- Redis::lpop + +Redis::punsubscribe
- - - - - - - - - - - - + +
string$key
mixed$value
mixed...$other_valuesstring|array$key_or_address
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|boolRedisCluster|bool|string
+

See also

@@ -8112,7 +12830,8 @@

See also

@@ -8124,11 +12843,10 @@

See also

-

- - RedisCluster|int|bool - lpushx(string $key, mixed $value) - + +

+ + mixed rawcommand(string|array $key_or_address, string $command, mixed ...$args)

@@ -8143,27 +12861,34 @@

Parameters

- Redis::lpush + +Redis::randomKey
+ + + + + - + - +
string|array$key_or_address
string$key$command
mixed$value...$args
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|boolmixed
+

See also

@@ -8171,7 +12896,8 @@

See also

@@ -8183,11 +12909,10 @@

See also

-

- - RedisCluster|array|false - lrange(string $key, int $start, int $end) - + +

+ + RedisCluster|bool rename(string $key_src, string $key_dst)

@@ -8203,31 +12928,28 @@

Parameters

- Redis::lpushx + +Redis::rawcommand
- - - - - - + - - + +
string$key
int$start$key_src
int$endstring$key_dst
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|bool
+

See also

@@ -8235,7 +12957,8 @@

See also

@@ -8247,11 +12970,10 @@

See also

-

- - RedisCluster|int|bool - lrem(string $key, mixed $value, int $count = 0) - + +

+ + RedisCluster|bool renamenx(string $key, string $newkey)

@@ -8271,27 +12993,24 @@

Parameters

- - - - - - - + +
- Redis::lrange + +Redis::rename
mixed$value
int$countstring$newkey
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|boolRedisCluster|bool
+

See also

@@ -8299,7 +13018,8 @@

See also

@@ -8311,11 +13031,10 @@

See also

-

- - RedisCluster|bool - lset(string $key, int $index, mixed $value) - + +

+ + RedisCluster|bool restore(string $key, int $timeout, string $value, array|null $options = null)

@@ -8336,26 +13055,33 @@

Parameters

- + - + + + + + +
- Redis::lrem + +Redis::renameNx
int$index$timeout
mixedstring $value
array|null$options
-

Return Value

+

Return Value

- +
+
RedisCluster|bool
+

See also

@@ -8363,7 +13089,8 @@

See also

@@ -8375,11 +13102,10 @@

See also

-

- - RedisCluster|bool - ltrim(string $key, int $start, int $end) - + +

+ + mixed role(string|array $key_or_address)

@@ -8394,32 +13120,24 @@

Parameters

- Redis::lset + +Redis::restore
- - - - - - - - - - - - + +
string$key
int$start
int$endstring|array$key_or_address
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolmixed
+

See also

@@ -8427,7 +13145,8 @@

See also

@@ -8439,11 +13158,10 @@

See also

-

- - RedisCluster|array|false - mget(array $keys) - + +

+ + RedisCluster|bool|string|array rpop(string $key, int $count = 0)

@@ -8458,22 +13176,29 @@

Parameters

- Redis::ltrim + +Redis::role
- - + + + + + + +
array$keysstring$key
int$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|bool|string|array
+

See also

@@ -8481,7 +13206,8 @@

See also

@@ -8493,11 +13219,10 @@

See also

-

- - RedisCluster|bool - mset(array $key_values) - + +

+ + RedisCluster|bool|string rpoplpush(string $src, string $dst)

@@ -8512,22 +13237,29 @@

Parameters

- Redis::mget + +Redis::rPop
- - + + + + + + +
array$key_valuesstring$src
string$dst
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|bool|string
+

See also

@@ -8535,7 +13267,8 @@

See also

@@ -8547,11 +13280,10 @@

See also

-

- - RedisCluster|array|false - msetnx(array $key_values) - + +

+ + RedisCluster|int|false rpush(string $key, mixed ...$elements)

@@ -8566,22 +13298,29 @@

Parameters

- Redis::mset + +Redis::rpoplpush
- - + + + + + + +
array$key_valuesstring$key
mixed...$elements
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|int|false
+

See also

@@ -8589,7 +13328,8 @@

See also

@@ -8601,11 +13341,10 @@

See also

-

- - RedisCluster|bool - multi(int $value = Redis::MULTI) - + +

+ + RedisCluster|bool|int rpushx(string $key, string $value)

@@ -8620,35 +13359,53 @@

Parameters

- Redis::msetnx + +Redis::rPush
- + + + + + +
intstring$key
string $value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|bool|int
+ +

See also

+ + + + + + +
+ +Redis::rPushx +
+
-

- - RedisCluster|int|string|false - object(string $subcommand, string $key) - + +

+ + RedisCluster|int|false sadd(string $key, mixed $value, mixed ...$other_values)

@@ -8664,26 +13421,33 @@

Parameters

- + - - + + + + + + +
string$subcommand$key
string$keymixed$value
mixed...$other_values
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|string|falseRedisCluster|int|false
+

See also

@@ -8691,7 +13455,8 @@

See also

@@ -8703,11 +13468,10 @@

See also

-

- - RedisCluster|bool - persist(string $key) - + +

+ + RedisCluster|bool|int saddarray(string $key, array $values)

@@ -8725,19 +13489,26 @@

Parameters

+ + + + +
- Redis::object + +Redis::sAdd
string $key
array$values
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|bool|int
+

See also

@@ -8745,7 +13516,8 @@

See also

@@ -8757,11 +13529,10 @@

See also

-

- - RedisCluster|bool - pexpire(string $key, int $timeout, string|null $mode = NULL) - + +

+ + RedisCluster|bool save(string|array $key_or_address)

@@ -8776,32 +13547,24 @@

Parameters

- Redis::persist + +Redis::sAddArray
- - - - - - - - - - - - + +
string$key
int$timeout
string|null$modestring|array$key_or_address
-

Return Value

+

Return Value

- +
+
RedisCluster|bool
+

See also

@@ -8809,7 +13572,8 @@

See also

@@ -8821,11 +13585,10 @@

See also

-

- - RedisCluster|bool - pexpireat(string $key, int $timestamp, string|null $mode = NULL) - + +

+ + bool|array scan(null|int|string $iterator, string|array $key_or_address, string|null $pattern = null, int $count = 0)

@@ -8840,32 +13603,39 @@

Parameters

- Redis::pexpire + +Redis::save
- - + + - - + + - + + + + + +
string$keynull|int|string$iterator
int$timestampstring|array$key_or_address
string|null$mode$pattern
int$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolbool|array
+

See also

@@ -8873,7 +13643,8 @@

See also

@@ -8885,11 +13656,10 @@

See also

-

- - RedisCluster|bool - pfadd(string $key, array $elements) - + +

+ + RedisCluster|int|false scard(string $key)

@@ -8907,24 +13677,21 @@

Parameters

- - - - -
- Redis::pexpireat + +Redis::scan
string $key
array$elements
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|int|false
+

See also

@@ -8932,8 +13699,8 @@

See also

@@ -8945,11 +13712,10 @@

See also

-

- - RedisCluster|int|false - pfcount(string $key) - + +

+ + mixed script(string|array $key_or_address, mixed ...$args)

@@ -8964,22 +13730,29 @@

Parameters

- -Redis::pfadd + +Redis::scard
- - + + + + + + +
string$keystring|array$key_or_address
mixed...$args
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falsemixed
+

See also

@@ -8987,8 +13760,8 @@

See also

@@ -9000,11 +13773,10 @@

See also

-

- - RedisCluster|bool - pfmerge(string $key, array $keys) - + +

+ + RedisCluster|array|false sdiff(string $key, string ...$other_keys)

@@ -9024,22 +13796,24 @@

Parameters

- - + +
- -Redis::pfcount + +Redis::script
array$keysstring...$other_keys
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|array|false
+

See also

@@ -9047,8 +13821,8 @@

See also

@@ -9060,47 +13834,52 @@

See also

-

- - mixed - ping(string|array $key_or_address, string|null $message = NULL) - + +

+ + RedisCluster|int|false sdiffstore(string $dst, string $key, string ...$other_keys)

-

PING an instance in the redis cluster.

+

No description

+

Parameters

- -Redis::pfmerge + +Redis::sDiff
- - - + + + - - - + + + + + + + +
string|array$key_or_address

Either a key name or a two element array with host and -address, informing RedisCluster which node to ping.

string$dst
string|null$message

An optional message to send.

string$key
string...$other_keys
-

Return Value

+

Return Value

- +
+
- - + +
mixed

This method always returns true if no message was sent, and the message itself -if one was.

RedisCluster|int|false
+

See also

@@ -9108,8 +13887,8 @@

See also

@@ -9121,11 +13900,10 @@

See also

-

- - RedisCluster|bool - psetex(string $key, int $timeout, string $value) - + +

+ + RedisCluster|string|bool set(string $key, mixed $value, mixed $options = null)

@@ -9145,27 +13923,29 @@

Parameters

- - + + - - + +
- -Redis::ping + +Redis::sDiffStore
int$timeoutmixed$value
string$valuemixed$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|string|bool
+

See also

@@ -9173,7 +13953,7 @@

See also

@@ -9185,11 +13965,10 @@

See also

-

- - void - psubscribe(array $patterns, callable $callback) - + +

+ + RedisCluster|int|false setbit(string $key, int $offset, bool $onoff)

@@ -9204,27 +13983,34 @@

Parameters

- Redis::psetex + https://redis.io/commands/set
- - + + - - + + + + + + +
array$patternsstring$key
callable$callbackint$offset
bool$onoff
-

Return Value

+

Return Value

- +
+
- +
voidRedisCluster|int|false
+

See also

@@ -9232,7 +14018,8 @@

See also

@@ -9244,11 +14031,10 @@

See also

-

- - RedisCluster|int|false - pttl(string $key) - + +

+ + RedisCluster|bool setex(string $key, int $expire, mixed $value)

@@ -9266,19 +14052,31 @@

Parameters

+ + + + + + + + + +
- Redis::psubscribe + +Redis::setBit
string $key
int$expire
mixed$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool
+

See also

@@ -9286,7 +14084,8 @@

See also

@@ -9298,11 +14097,10 @@

See also

-

- - RedisCluster|bool - publish(string $channel, string $message) - + +

+ + RedisCluster|bool setnx(string $key, mixed $value)

@@ -9318,26 +14116,28 @@

Parameters

- Redis::pttl + +Redis::setex
- + - - + +
string$channel$key
string$messagemixed$value
-

Return Value

+

Return Value

- +
+
RedisCluster|bool
+

See also

@@ -9345,7 +14145,8 @@

See also

@@ -9357,11 +14158,10 @@

See also

-

- - mixed - pubsub(string|array $key_or_address, string ...$values) - + +

+ + bool setoption(int $option, mixed $value)

@@ -9376,27 +14176,29 @@

Parameters

- Redis::publish + +Redis::setnx
- - + + - - + +
string|array$key_or_addressint$option
string...$valuesmixed$value
-

Return Value

+

Return Value

- +
+
- +
mixedbool
+

See also

@@ -9404,7 +14206,8 @@

See also

@@ -9416,11 +14219,10 @@

See also

-

- - bool|array - punsubscribe(string $pattern, string ...$other_patterns) - + +

+ + RedisCluster|int|false setrange(string $key, int $offset, string $value)

@@ -9436,26 +14238,33 @@

Parameters

- Redis::pubsub + +Redis::setOption
- + + + + + + - +
string$pattern$key
int$offset
string...$other_patterns$value
-

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisCluster|int|false
+

See also

@@ -9463,7 +14272,8 @@

See also

@@ -9475,11 +14285,10 @@

See also

-

- - RedisCluster|bool|string - randomkey(string|array $key_or_address) - + +

+ + RedisCluster|array|false sinter(array|string $key, string ...$other_keys)

@@ -9494,22 +14303,29 @@

Parameters

- Redis::punsubscribe + +Redis::setRange
- - + + + + + + +
string|array$key_or_addressarray|string$key
string...$other_keys
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|stringRedisCluster|array|false
+

See also

@@ -9517,7 +14333,8 @@

See also

@@ -9529,11 +14346,10 @@

See also

-

- - mixed - rawcommand(string|array $key_or_address, string $command, mixed ...$args) - + +

+ + RedisCluster|int|false sintercard(array $keys, int $limit = -1)

@@ -9548,32 +14364,29 @@

Parameters

- Redis::randomkey + +Redis::sInter
- - - - - - - + + - - + +
string|array$key_or_address
string$commandarray$keys
mixed...$argsint$limit
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|int|false
+

See also

@@ -9581,7 +14394,8 @@

See also

@@ -9593,11 +14407,10 @@

See also

-

- - RedisCluster|bool - rename(string $key_src, string $key_dst) - + +

+ + RedisCluster|int|false sinterstore(array|string $key, string ...$other_keys)

@@ -9612,27 +14425,29 @@

Parameters

- Redis::rawcommand + +Redis::sintercard
- - + + - +
string$key_srcarray|string$key
string$key_dst...$other_keys
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|int|false
+

See also

@@ -9640,7 +14455,8 @@

See also

@@ -9652,11 +14468,10 @@

See also

-

- - RedisCluster|bool - renamenx(string $key, string $newkey) - + +

+ + RedisCluster|bool sismember(string $key, mixed $value)

@@ -9676,22 +14491,24 @@

Parameters

- - + +
- Redis::rename + +Redis::sInterStore
string$newkeymixed$value
-

Return Value

+

Return Value

- +
+
RedisCluster|bool
+

See also

@@ -9699,7 +14516,8 @@

See also

@@ -9711,11 +14529,10 @@

See also

-

- - RedisCluster|bool - restore(string $key, int $timeout, string $value, array|null $options = NULL) - + +

+ + RedisCluster|array|false smismember(string $key, string $member, string ...$other_members)

@@ -9733,34 +14550,31 @@

Parameters

- - - - - - + - - + +
- Redis::renamenx + +Redis::sismember
string $key
int$timeout
string$value$member
array|null$optionsstring...$other_members
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|array|false
+

See also

@@ -9768,7 +14582,8 @@

See also

@@ -9780,11 +14595,10 @@

See also

-

- - mixed - role(string|array $key_or_address) - + +

+ + mixed slowlog(string|array $key_or_address, mixed ...$args)

@@ -9802,19 +14616,26 @@

Parameters

+ + + + +
- Redis::restore + +Redis::sMisMember
string|array $key_or_address
mixed...$args
-

Return Value

+

Return Value

- +
+
mixed
+

See also

@@ -9822,7 +14643,8 @@

See also

@@ -9834,11 +14656,10 @@

See also

-

- - RedisCluster|bool|string|array - rpop(string $key, int $count = 0) - + +

+ + RedisCluster|array|false smembers(string $key)

@@ -9856,24 +14677,21 @@

Parameters

- - - - -
- Redis::role + +Redis::slowlog
string $key
int$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|string|arrayRedisCluster|array|false
+

See also

@@ -9881,7 +14699,8 @@

See also

@@ -9893,11 +14712,10 @@

See also

-

- - RedisCluster|bool|string - rpoplpush(string $src, string $dst) - + +

+ + RedisCluster|bool smove(string $src, string $dst, string $member)

@@ -9920,19 +14738,26 @@

Parameters

+ + + + +
- \Redis::rpop() + +Redis::sMembers
string $dst
string$member
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|stringRedisCluster|bool
+

See also

@@ -9940,8 +14765,8 @@

See also

@@ -9953,11 +14778,10 @@

See also

-

- - RedisCluster|int|false - rpush(string $key, mixed ...$elements) - + +

+ + RedisCluster|array|bool|int|string sort(string $key, array|null $options = null)

@@ -9977,22 +14801,24 @@

Parameters

- - + +
- -Redis::rpoplpush + +Redis::sMove
mixed...$elementsarray|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|bool|int|string
+

See also

@@ -10000,7 +14826,8 @@

See also

@@ -10012,11 +14839,10 @@

See also

-

- - RedisCluster|bool|int - rpushx(string $key, string $value) - + +

+ + RedisCluster|array|bool|int|string sort_ro(string $key, array|null $options = null)

@@ -10036,22 +14862,24 @@

Parameters

- - + +
- Redis::rpush + +Redis::sort
string$valuearray|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|intRedisCluster|array|bool|int|string
+

See also

@@ -10059,7 +14887,8 @@

See also

@@ -10071,11 +14900,10 @@

See also

-

- - RedisCluster|int|false - sadd(string $key, mixed $value, mixed ...$other_values) - + +

+ + RedisCluster|string|array|false spop(string $key, int $count = 0)

@@ -10095,27 +14923,24 @@

Parameters

- - - - - - - + +
- Redis::rpushx + +Redis::sort_ro
mixed$value
mixed...$other_valuesint$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|string|array|false
+

See also

@@ -10123,7 +14948,8 @@

See also

@@ -10135,11 +14961,10 @@

See also

-

- - RedisCluster|bool|int - saddarray(string $key, array $values) - + +

+ + RedisCluster|string|array|false srandmember(string $key, int $count = 0)

@@ -10159,22 +14984,24 @@

Parameters

- - + +
- \Redis::sadd() + +Redis::sPop
array$valuesint$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|intRedisCluster|string|array|false
+

See also

@@ -10182,7 +15009,8 @@

See also

@@ -10194,11 +15022,10 @@

See also

-

- - RedisCluster|bool - save(string|array $key_or_address) - + +

+ + RedisCluster|int|false srem(string $key, mixed $value, mixed ...$other_values)

@@ -10213,22 +15040,34 @@

Parameters

- \Redis::saddarray() + +Redis::sRandMember
- - + + + + + + + + + + + +
string|array$key_or_addressstring$key
mixed$value
mixed...$other_values
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|int|false
+

See also

@@ -10236,7 +15075,8 @@

See also

@@ -10248,11 +15088,10 @@

See also

-

- - bool|array - scan(int|null $iterator, string|array $key_or_address, string|null $pattern = null, int $count = 0) - + +

+ + array|false sscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

@@ -10267,13 +15106,13 @@

Parameters

- Redis::save + +Redis::srem
- - + + - - + + @@ -10289,15 +15128,17 @@

Parameters

int|null$iteratorstring$key
string|array$key_or_addressnull|int|string$iterator
-

Return Value

+

Return Value

- +
+
- +
bool|arrayarray|false
+

See also

@@ -10305,7 +15146,8 @@

See also

@@ -10317,11 +15159,10 @@

See also

-

- - RedisCluster|int|false - scard(string $key) - + +

+ + RedisCluster|int|false strlen(string $key)

@@ -10343,15 +15184,17 @@

Parameters

- Redis::scan + +Redis::sscan
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -10359,7 +15202,8 @@

See also

@@ -10371,11 +15215,10 @@

See also

-

- - mixed - script(string|array $key_or_address, mixed ...$args) - + +

+ + void subscribe(array $channels, callable $cb)

@@ -10390,27 +15233,29 @@

Parameters

- Redis::scard + +Redis::strlen
- - + + - - + +
string|array$key_or_addressarray$channels
mixed...$argscallable$cb
-

Return Value

+

Return Value

- +
+
- +
mixedvoid
+

See also

@@ -10418,7 +15263,8 @@

See also

@@ -10430,11 +15276,10 @@

See also

-

- - RedisCluster|array|false - sdiff(string $key, string ...$other_keys) - + +

+ + RedisCluster|bool|array sunion(string $key, string ...$other_keys)

@@ -10461,15 +15306,17 @@

Parameters

- Redis::script + +Redis::subscribe
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|bool|array
+

See also

@@ -10477,7 +15324,8 @@

See also

@@ -10489,11 +15337,10 @@

See also

-

- - RedisCluster|int|false - sdiffstore(string $dst, string $key, string ...$other_keys) - + +

+ + RedisCluster|int|false sunionstore(string $dst, string $key, string ...$other_keys)

@@ -10525,15 +15372,17 @@

Parameters

- \Redis::sdiff() + +Redis::sUnion
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -10541,7 +15390,8 @@

See also

@@ -10553,11 +15403,10 @@

See also

-

- - RedisCluster|string|bool - set(string $key, mixed $value, mixed $options = NULL) - + +

+ + RedisCluster|bool|array time(string|array $key_or_address)

@@ -10572,32 +15421,24 @@

Parameters

- \Redis::sdiffstore() + +Redis::sUnionStore
- - - - - - - - - - - - + +
string$key
mixed$value
mixed$optionsstring|array$key_or_address
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|boolRedisCluster|bool|array
+

See also

@@ -10605,7 +15446,8 @@

See also

@@ -10617,11 +15459,10 @@

See also

-

- - RedisCluster|int|false - setbit(string $key, int $offset, bool $onoff) - + +

+ + RedisCluster|int|false ttl(string $key)

@@ -10639,29 +15480,21 @@

Parameters

- - - - - - - - - -
- https://redis.io/commands/set + +Redis::time
string $key
int$offset
bool$onoff
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -10669,7 +15502,8 @@

See also

@@ -10681,11 +15515,10 @@

See also

-

- - RedisCluster|bool - setex(string $key, int $expire, mixed $value) - + +

+ + RedisCluster|int|false type(string $key)

@@ -10703,29 +15536,21 @@

Parameters

- - - - - - - - - -
- Redis::setbit + +Redis::ttl
string $key
int$expire
mixed$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|int|false
+

See also

@@ -10733,7 +15558,8 @@

See also

@@ -10745,11 +15571,10 @@

See also

-

- - RedisCluster|bool - setnx(string $key, mixed $value) - + +

+ + bool|array unsubscribe(array $channels)

@@ -10764,27 +15589,24 @@

Parameters

- Redis::setex + +Redis::type
- - - - - - - + +
string$key
mixed$valuearray$channels
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolbool|array
+

See also

@@ -10792,7 +15614,8 @@

See also

@@ -10804,11 +15627,10 @@

See also

-

- - bool - setoption(int $option, mixed $value) - + +

@@ -10823,27 +15645,29 @@

Parameters

- Redis::setnx + +Redis::unsubscribe
- - + + - - + +
int$optionarray|string$key
mixed$valuestring...$other_keys
-

Return Value

+

Return Value

- +
+
- +
boolRedisCluster|int|false
+

See also

@@ -10851,7 +15675,8 @@

See also

@@ -10863,11 +15688,10 @@

See also

-

- - RedisCluster|int|false - setrange(string $key, int $offset, string $value) - + +

+ + bool unwatch()

@@ -10878,36 +15702,18 @@

-

Parameters

- -
- Redis::setoption + +Redis::unlink
- - - - - - - - - - - - - - - -
string$key
int$offset
string$value
- -

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falsebool
+

See also

@@ -10915,7 +15721,8 @@

See also

@@ -10927,11 +15734,10 @@

See also

-

- - RedisCluster|array|false - sinter(array|string $key, string ...$other_keys) - + +

+ + RedisCluster|bool watch(string $key, string ...$other_keys)

@@ -10946,7 +15752,7 @@

Parameters

- Redis::setrange + +Redis::unwatch
- + @@ -10958,15 +15764,17 @@

Parameters

array|stringstring $key
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|bool
+

See also

@@ -10974,7 +15782,8 @@

See also

@@ -10986,11 +15795,10 @@

See also

-

- - RedisCluster|int|false - sintercard(array $keys, int $limit = -1) - + +

+ + RedisCluster|int|false vadd(string $key, array $values, mixed $element, array|null $options = null)

@@ -11005,86 +15813,39 @@

Parameters

- \Redis::sinter() + +Redis::watch
- - - - - - - + + -
array$keys
int$limitstring$key
- - -

Return Value

- - - - - - -
RedisCluster|int|false
- - - -

See also

- - - + + -
- Redis::sintercard - array$values
- - - - - - -
-

- - RedisCluster|int|false - sinterstore(array|string $key, string ...$other_keys) - -

-
- - - -
-

No description

- -
-
-

Parameters

- - - - - + + + - - + +
array|string$key
mixed$element
string...$other_keysarray|null$options
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -11092,7 +15853,8 @@

See also

@@ -11104,11 +15866,10 @@

See also

-

- - RedisCluster|bool - sismember(string $key, mixed $value) - + +

+ + RedisCluster|array|false vsim(string $key, mixed $member, array|null $options = null)

@@ -11129,21 +15890,28 @@

Parameters

- + + + + + +
- \Redis::sinterstore() + +Redis::vadd
mixed$value$member
array|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|array|false
+

See also

@@ -11151,7 +15919,8 @@

See also

@@ -11163,11 +15932,10 @@

See also

-

- - mixed - slowlog(string|array $key_or_address, mixed ...$args) - + +

+ + RedisCluster|int|false vcard(string $key)

@@ -11182,27 +15950,24 @@

Parameters

- Redis::sismember + +Redis::vsim
- - - - - - - + +
string|array$key_or_address
mixed...$argsstring$key
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|int|false
+

See also

@@ -11210,7 +15975,8 @@

See also

@@ -11222,11 +15988,10 @@

See also

-

- - RedisCluster|array|false - smembers(string $key) - + +

+ + RedisCluster|int|false vdim(string $key)

@@ -11248,15 +16013,17 @@

Parameters

- Redis::slowlog + +Redis::vcard
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|int|false
+

See also

@@ -11264,7 +16031,8 @@

See also

@@ -11276,11 +16044,10 @@

See also

-

- - RedisCluster|bool - smove(string $src, string $dst, string $member) - + +

+ + RedisCluster|array|false vinfo(string $key)

@@ -11296,31 +16063,23 @@

Parameters

- \Redis::smembers() + +Redis::vdim
- - - - - - - - - - - +
string$src
string$dst
string$member$key
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolRedisCluster|array|false
+

See also

@@ -11328,7 +16087,8 @@

See also

@@ -11340,19 +16100,17 @@

See also

-

- - RedisCluster|array|bool|int|string - sort(string $key, array|null $options = NULL) - + +

+ + RedisCluster|bool vismember(string $key, mixed $member)

-

No description

- +

Check if an element is a member of a vectorset

Parameters

@@ -11361,50 +16119,39 @@

Parameters

- + - - - + + +
- \Redis::smove() + +Redis::vinfo
string $key

The vector set to query.

array|null$optionsmixed$member

The member to check for.

-

Return Value

+

Return Value

- +
+
- - + +
RedisCluster|array|bool|int|stringRedisCluster|bool

true if the member exists, false if it does not.

+ -

See also

- - - - - - -
- -Redis::sort -
-
-

- - RedisCluster|array|bool|int|string - sort_ro(string $key, array|null $options = NULL) - + +

+ + RedisCluster|array|false vemb(string $key, mixed $member, bool $raw = false)

@@ -11424,22 +16171,29 @@

Parameters

- array|null - $options + mixed + $member + + + + bool + $raw -

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|bool|int|stringRedisCluster|array|false
+

See also

@@ -11447,8 +16201,8 @@

See also

@@ -11460,11 +16214,10 @@

See also

-

- - RedisCluster|string|array|false - spop(string $key, int $count = 0) - + +

+ + RedisCluster|array|string|false vrandmember(string $key, int $count = 0)

@@ -11491,15 +16244,17 @@

Parameters

- -Redis::sort_ro + +Redis::vemb
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|array|falseRedisCluster|array|string|false
+

See also

@@ -11507,7 +16262,8 @@

See also

@@ -11519,19 +16275,17 @@

See also

-

- - RedisCluster|string|array|false - srandmember(string $key, int $count = 0) - + +

+ + RedisCluster|array|false vrange(string $key, string $min, string $max, int $count = -1)

-

No description

- +

Retreive a lexographical range of elements from a vector set

Parameters

@@ -11540,49 +16294,49 @@

Parameters

- + + + + + + + + + + + - +
- Redis::spop + +Redis::vrandmember
string $key

The vector set to query.

string$min

The minimum element to return.

string$max

The maximum element to return.

int $count

An optional maximum number of elements to return.

-

Return Value

+

Return Value

- +
+
- - + +
RedisCluster|string|array|falseRedisCluster|array|false

An array of elements in the specified range.`

+ -

See also

- - - - - - -
- Redis::srandmember -
-
-

- - RedisCluster|int|false - srem(string $key, mixed $value, mixed ...$other_values) - + +

+ + RedisCluster|int|false vrem(string $key, mixed $member)

@@ -11603,26 +16357,23 @@

Parameters

mixed - $value - - - - mixed - ...$other_values + $member -

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -11630,7 +16381,8 @@

See also

@@ -11642,11 +16394,10 @@

See also

-

- - array|false - sscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) - + +

@@ -11666,32 +16417,29 @@

Parameters

- - - - - - - + + - - + +
- Redis::srem + +Redis::vrem
int|null$iterator
string|null$patternmixed$member
int$countbool$withscores
-

Return Value

+

Return Value

- +
+
- +
array|falseRedisCluster|array|false
+

See also

@@ -11699,7 +16447,8 @@

See also

@@ -11711,11 +16460,10 @@

See also

-

- - RedisCluster|int|false - strlen(string $key) - + +

+ + RedisCluster|array|string|false vgetattr(string $key, mixed $member, bool $decode = true)

@@ -11733,19 +16481,31 @@

Parameters

+ + + + + + + + + +
- Redis::sscan + +Redis::vlinks
string $key
mixed$member
bool$decode
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|string|false
+

See also

@@ -11753,7 +16513,8 @@

See also

@@ -11765,11 +16526,10 @@

See also

-

- - void - subscribe(array $channels, callable $cb) - + +

+ + RedisCluster|int|false vsetattr(string $key, mixed $member, array|string $attributes)

@@ -11784,27 +16544,34 @@

Parameters

- Redis::strlen + +Redis::vgetattr
- - + + - - + + + + + + +
array$channelsstring$key
callable$cbmixed$member
array|string$attributes
-

Return Value

+

Return Value

- +
+
- +
voidRedisCluster|int|false
+

See also

@@ -11812,7 +16579,8 @@

See also

@@ -11824,11 +16592,10 @@

See also

-

- - RedisCluster|bool|array - sunion(string $key, string ...$other_keys) - + +

+ + RedisCluster|array|false gcra(string $key, int $maxBurst, int $requestsPerPeriod, int $period, int $tokens = 0)

@@ -11848,22 +16615,39 @@

Parameters

- - + + + + + + + + + + + + + + + + +
- Redis::subscribe + +Redis::vsetattr
string...$other_keysint$maxBurst
int$requestsPerPeriod
int$period
int$tokens
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|array|false
+

See also

@@ -11871,7 +16655,8 @@

See also

@@ -11883,11 +16668,10 @@

See also

-

- - RedisCluster|int|false - sunionstore(string $dst, string $key, string ...$other_keys) - + +

+ + RedisCluster|int|false xack(string $key, string $group, array $ids)

@@ -11903,31 +16687,33 @@

Parameters

- \Redis::sunion() + +Redis::gcra
- + - + - - + +
string$dst$key
string$key$group
string...$other_keysarray$ids
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -11935,7 +16721,8 @@

See also

@@ -11947,11 +16734,10 @@

See also

-

- - RedisCluster|bool|array - time(string|array $key_or_address) - + +

+ + RedisCluster|string|false xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false)

@@ -11966,22 +16752,44 @@

Parameters

- \Redis::sunionstore() + +Redis::xack
- - + + + + + + + + + + + + + + + + + + + + + +
string|array$key_or_addressstring$key
string$id
array$values
int$maxlen
bool$approx
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|string|false
+

See also

@@ -11989,7 +16797,8 @@

See also

@@ -12001,11 +16810,10 @@

See also

-

- - RedisCluster|int|false - ttl(string $key) - + +

+ + RedisCluster|string|array|false xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options)

@@ -12023,19 +16831,46 @@

Parameters

+ + + + + + + + + + + + + + + + + + + + + + + + +
- Redis::time + +Redis::xadd
string $key
string$group
string$consumer
int$min_iddle
array$ids
array$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|string|array|false
+

See also

@@ -12043,7 +16878,8 @@

See also

@@ -12055,11 +16891,10 @@

See also

-

- - RedisCluster|int|false - type(string $key) - + +

+ + RedisCluster|int|false xdel(string $key, array $ids)

@@ -12077,19 +16912,26 @@

Parameters

+ + + + +
- Redis::ttl + +Redis::xclaim
string $key
array$ids
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -12097,7 +16939,8 @@

See also

@@ -12109,11 +16952,10 @@

See also

-

- - bool|array - unsubscribe(array $channels) - + +

+ + RedisCluster|array|false xdelex(string $key, array $ids, string|null $mode = null)

@@ -12128,22 +16970,34 @@

Parameters

- Redis::type + +Redis::xdel
+ + + + + - + + + + + +
string$key
array$channels$ids
string|null$mode
-

Return Value

+

Return Value

- +
+
- +
bool|arrayRedisCluster|array|false
+

See also

@@ -12151,7 +17005,8 @@

See also

@@ -12163,11 +17018,10 @@

See also

-

+ + mixed xgroup(string $operation, string|null $key = null, string|null $group = null, string|null $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2)

@@ -12182,27 +17036,49 @@

Parameters

- Redis::unsubscribe + +Redis::xdelex
- + + + + + + - - + + + + + + + + + + + + + + + + +
array|stringstring$operation
string|null $key
string...$other_keysstring|null$group
string|null$id_or_consumer
bool$mkstream
int$entries_read
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falsemixed
+

See also

@@ -12210,7 +17086,8 @@

See also

@@ -12222,11 +17099,10 @@

See also

-

- - bool - unwatch() - + +

+ + RedisCluster|bool|array xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false)

@@ -12237,16 +17113,58 @@

- -

Return Value

+

Parameters

- Redis::unlink + +Redis::xgroup
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
string$key
string$group
string$consumer
int$min_idle
string$start
int$count
bool$justid
+ + +

Return Value

+ +
+ - +
boolRedisCluster|bool|array
+

See also

@@ -12254,7 +17172,8 @@

See also

@@ -12266,11 +17185,10 @@

See also

-

- - RedisCluster|bool - watch(string $key, string ...$other_keys) - + +

+ + mixed xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1)

@@ -12286,26 +17204,38 @@

Parameters

- Redis::unwatch + +Redis::xautoclaim
- + - - + + + + + + + + + + + +
string$key$operation
string...$other_keysstring|null$arg1
string|null$arg2
int$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|boolmixed
+

See also

@@ -12313,7 +17243,8 @@

See also

@@ -12325,11 +17256,10 @@

See also

-

- - RedisCluster|int|false - xack(string $key, string $group, array $ids) - + +

+ + RedisCluster|int|false xlen(string $key)

@@ -12347,29 +17277,21 @@

Parameters

- - - - - - - - - -
- Redis::watch + +Redis::xinfo
string $key
string$group
array$ids
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -12377,7 +17299,8 @@

See also

@@ -12389,11 +17312,10 @@

See also

-

- - RedisCluster|string|false - xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false) - + +

+ + RedisCluster|array|false xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null)

@@ -12414,36 +17336,43 @@

Parameters

- + - - + + + + + + + - + - - + +
- Redis::xack + +Redis::xlen
string$id$group
array$valuesstring|null$start
string|null$end
int$maxlen$count
bool$approxstring|null$consumer
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|falseRedisCluster|array|false
+

See also

@@ -12451,7 +17380,8 @@

See also

@@ -12463,11 +17393,10 @@

See also

-

- - RedisCluster|string|array|false - xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options) - + +

+ + RedisCluster|bool|array xrange(string $key, string $start, string $end, int $count = -1)

@@ -12488,41 +17417,33 @@

Parameters

- + - + - - - - - - - - - - - +
- Redis::xadd + +Redis::xpending
string$group$start
string$consumer$end
int$min_iddle
array$ids
array$options$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|string|array|falseRedisCluster|bool|array
+

See also

@@ -12530,7 +17451,8 @@

See also

@@ -12542,11 +17464,10 @@

See also

-

- - RedisCluster|int|false - xdel(string $key, array $ids) - + +

+ + RedisCluster|bool|array xread(array $streams, int $count = -1, int $block = -1)

@@ -12561,27 +17482,34 @@

Parameters

- Redis::xclaim + +Redis::xrange
- - + + - - + + + + + + +
string$keyarray$streams
array$idsint$count
int$block
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool|array
+

See also

@@ -12589,7 +17517,8 @@

See also

@@ -12601,11 +17530,10 @@

See also

-

- - mixed - xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2) - + +

+ + RedisCluster|bool|array xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1)

@@ -12621,46 +17549,43 @@

Parameters

- Redis::xdel + +Redis::xread
- - - - - - + - + - - + + - - + + - +
string$operation
string$key$group
string$group$consumer
string$id_or_consumerarray$streams
bool$mkstreamint$count
int$entries_read$block
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|bool|array
+

See also

@@ -12668,7 +17593,8 @@

See also

@@ -12680,11 +17606,10 @@

See also

-

- - Redis|bool|array - xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) - + +

+ + RedisCluster|bool|array xrevrange(string $key, string $start, string $end, int $count = -1)

@@ -12705,46 +17630,33 @@

Parameters

- - - - - - - - - - - + - + - - - - -
- Redis::xgroup + +Redis::xreadgroup
string$group
string$consumer
int$min_idle$start
string$start$end
int $count
bool$justid
-

Return Value

+

Return Value

- +
+
- +
Redis|bool|arrayRedisCluster|bool|array
+

See also

@@ -12752,7 +17664,8 @@

See also

@@ -12764,11 +17677,10 @@

See also

-

- - mixed - xinfo(string $operation, string|null $arg1 = null, string|null $arg2 = null, int $count = -1) - + +

+ + RedisCluster|int|false xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1)

@@ -12784,36 +17696,43 @@

Parameters

- Redis::xautoclaim + +Redis::xrevrange
- + - - + + - - + + + + + + + - +
string$operation$key
string|null$arg1int$maxlen
string|null$arg2bool$approx
bool$minid
int$count$limit
-

Return Value

+

Return Value

- +
+
- +
mixedRedisCluster|int|false
+

See also

@@ -12821,7 +17740,8 @@

See also

@@ -12833,11 +17753,10 @@

See also

-

- - RedisCluster|int|false - xlen(string $key) - + +

+ + RedisCluster|int|float|false zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems)

@@ -12855,19 +17774,31 @@

Parameters

+ + + + + + + + + +
- Redis::xinfo + +Redis::xtrim
string $key
array|float$score_or_options
mixed...$more_scores_and_mems
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|int|float|false
+

See also

@@ -12875,7 +17806,8 @@

See also

@@ -12887,11 +17819,10 @@

See also

-

- - RedisCluster|array|false - xpending(string $key, string $group, string|null $start = null, string|null $end = null, int $count = -1, string|null $consumer = null) - + +

+ + RedisCluster|int|false zcard(string $key)

@@ -12909,44 +17840,21 @@

Parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
- Redis::xlen + +Redis::zAdd
string $key
string$group
string|null$start
string|null$end
int$count
string|null$consumer
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|int|false
+

See also

@@ -12954,7 +17862,8 @@

See also

@@ -12966,11 +17875,10 @@

See also

-

- - RedisCluster|bool|array - xrange(string $key, string $start, string $end, int $count = -1) - + +

+ + RedisCluster|int|false zcount(string $key, string $start, string $end)

@@ -12998,24 +17906,21 @@

Parameters

- - - - -
- Redis::xpending + +Redis::zCard
string $end
int$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|int|false
+

See also

@@ -13023,7 +17928,8 @@

See also

@@ -13035,11 +17941,10 @@

See also

-

- - RedisCluster|bool|array - xread(array $streams, int $count = -1, int $block = -1) - + +

+ + RedisCluster|float|false zincrby(string $key, float $value, string $member)

@@ -13054,32 +17959,34 @@

Parameters

- Redis::xrange + +Redis::zCount
- - + + - - + + - - + +
array$streamsstring$key
int$countfloat$value
int$blockstring$member
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|float|false
+

See also

@@ -13087,7 +17994,8 @@

See also

@@ -13099,11 +18007,10 @@

See also

-

- - RedisCluster|bool|array - xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) - + +

+ + RedisCluster|int|false zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null)

@@ -13119,41 +18026,38 @@

Parameters

- Redis::xread + +Redis::zIncrBy
- - - - - - + - + - - + + - - + +
string$group
string$consumer$dst
array$streams$keys
int$countarray|null$weights
int$blockstring|null$aggregate
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|int|false
+

See also

@@ -13161,7 +18065,8 @@

See also

@@ -13173,11 +18078,10 @@

See also

-

- - RedisCluster|bool|array - xrevrange(string $key, string $start, string $end, int $count = -1) - + +

+ + RedisCluster|int|false zintercard(array $keys, int $limit = -1)

@@ -13192,37 +18096,29 @@

Parameters

- Redis::xreadgroup + +Redis::zinterstore
- - - - - - - - - - - - + + - +
string$key
string$start
string$endarray$keys
int$count$limit
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|int|false
+

See also

@@ -13230,7 +18126,8 @@

See also

@@ -13242,11 +18139,10 @@

See also

-

- - RedisCluster|int|false - xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1) - + +

+ + RedisCluster|int|false zlexcount(string $key, string $min, string $max)

@@ -13266,37 +18162,29 @@

Parameters

- - - - - - - - - - - - + + - - + +
- Redis::xrevrange + +Redis::zintercard
int$maxlen
bool$approx
bool$minidstring$min
int$limitstring$max
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -13304,7 +18192,8 @@

See also

@@ -13316,11 +18205,10 @@

See also

-

- - RedisCluster|int|false - zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) - + +

+ + RedisCluster|bool|array zpopmax(string $key, int|null $value = null)

@@ -13340,27 +18228,24 @@

Parameters

- - - - - - - + +
- Redis::xtrim + +Redis::zLexCount
array|float$score_or_options
mixed...$more_scores_and_memsint|null$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool|array
+

See also

@@ -13368,7 +18253,8 @@

See also

@@ -13380,11 +18266,10 @@

See also

-

- - RedisCluster|int|false - zcard(string $key) - + +

+ + RedisCluster|bool|array zpopmin(string $key, int|null $value = null)

@@ -13402,19 +18287,26 @@

Parameters

+ + + + +
- Redis::zadd + +Redis::zPopMax
string $key
int|null$value
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool|array
+

See also

@@ -13422,7 +18314,8 @@

See also

@@ -13434,11 +18327,10 @@

See also

-

- - RedisCluster|int|false - zcount(string $key, string $start, string $end) - + +

+ + RedisCluster|array|bool zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null)

@@ -13458,27 +18350,34 @@

Parameters

- + - + + + + + +
- Redis::zcard + +Redis::zPopMin
stringmixed $start
stringmixed $end
array|bool|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|bool
+

See also

@@ -13486,7 +18385,8 @@

See also

@@ -13498,11 +18398,10 @@

See also

-

- - RedisCluster|float|false - zincrby(string $key, float $value, string $member) - + +

+ + RedisCluster|int|false zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null)

@@ -13518,31 +18417,43 @@

Parameters

- Redis::zcount + +Redis::zRange
- + - - + + - - + + + + + + + + + + + +
string$key$dstkey
float$valuestring$srckey
string$memberint$start
int$end
array|bool|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|float|falseRedisCluster|int|false
+

See also

@@ -13550,7 +18461,8 @@

See also

@@ -13562,11 +18474,10 @@

See also

-

- - RedisCluster|int|false - zinterstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null) - + +

+ + RedisCluster|string|array zrandmember(string $key, array|null $options = null)

@@ -13582,36 +18493,28 @@

Parameters

- Redis::zincrby + +Redis::zrangestore
- - - - - - + - - - - - - +
string$dst
array$keys$key
array|null$weights
string|null$aggregate$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|string|array
+

See also

@@ -13619,7 +18522,7 @@

See also

@@ -13631,11 +18534,10 @@

See also

-

- - RedisCluster|int|false - zintercard(array $keys, int $limit = -1) - + +

+ + RedisCluster|array|false zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1)

@@ -13650,27 +18552,44 @@

Parameters

- Redis::zinterstore + https://redis.io/commands/zrandmember
- - + + + + + + + + + + + + - + + + + + +
array$keysstring$key
string$min
string$max
int$limit$offset
int$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|false
+

See also

@@ -13678,7 +18597,8 @@

See also

@@ -13690,11 +18610,10 @@

See also

-

- - RedisCluster|int|false - zlexcount(string $key, string $min, string $max) - + +

+ + RedisCluster|array|false zrangebyscore(string $key, string $start, string $end, array $options = [])

@@ -13715,26 +18634,33 @@

Parameters

- + - + + + + + +
- Redis::zintercard + +Redis::zRangeByLex
string$min$start
string$max$end
array$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|array|false
+

See also

@@ -13742,7 +18668,8 @@

See also

@@ -13754,11 +18681,10 @@

See also

-

- - RedisCluster|bool|array - zpopmax(string $key, int $value = null) - + +

+ + RedisCluster|int|false zrank(string $key, mixed $member)

@@ -13778,22 +18704,24 @@

Parameters

- - + +
- Redis::zlexcount + +Redis::zRangeByScore
int$valuemixed$member
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|int|false
+

See also

@@ -13801,7 +18729,8 @@

See also

@@ -13813,11 +18742,10 @@

See also

-

- - RedisCluster|bool|array - zpopmin(string $key, int $value = null) - + +

+ + RedisCluster|int|false zrem(string $key, string $value, string ...$other_values)

@@ -13837,22 +18765,29 @@

Parameters

- - + + + + + + +
- Redis::zpopmax + +Redis::zRank
int$valuestring$value
string...$other_values
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|int|false
+

See also

@@ -13860,7 +18795,8 @@

See also

@@ -13872,11 +18808,10 @@

See also

-

- - RedisCluster|array|bool - zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null) - + +

+ + RedisCluster|int|false zremrangebylex(string $key, string $min, string $max)

@@ -13896,32 +18831,29 @@

Parameters

- - - - - - - + + - - + +
- Redis::zpopmin + +Redis::zRem
mixed$start
mixed$endstring$min
array|bool|null$optionsstring$max
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|boolRedisCluster|int|false
+

See also

@@ -13929,7 +18861,8 @@

See also

@@ -13941,11 +18874,10 @@

See also

-

- - RedisCluster|int|false - zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null) - + +

+ + RedisCluster|int|false zremrangebyrank(string $key, string $min, string $max)

@@ -13961,41 +18893,33 @@

Parameters

- Redis::zrange + +Redis::zRemRangeByLex
- + - - - - - - - - - - - + - - + +
string$dstkey$key
string$srckey
int$start
int$end$min
array|bool|null$optionsstring$max
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -14003,7 +18927,8 @@

See also

@@ -14015,11 +18940,10 @@

See also

-

- - RedisCluster|array|false - zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1) - + +

+ + RedisCluster|int|false zremrangebyscore(string $key, string $min, string $max)

@@ -14047,29 +18971,21 @@

Parameters

- - - - - - - - - -
- Redis::zrangestore + +Redis::zRemRangeByRank
string $max
int$offset
int$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|int|false
+

See also

@@ -14077,7 +18993,8 @@

See also

@@ -14089,11 +19006,10 @@

See also

-

- - RedisCluster|array|false - zrangebyscore(string $key, string $start, string $end, array $options = []) - + +

+ + RedisCluster|bool|array zrevrange(string $key, string $min, string $max, array|null $options = null)

@@ -14114,31 +19030,33 @@

Parameters

- + - + - +
- Redis::zrangebylex + +Redis::zRemRangeByScore
string$start$min
string$end$max
arrayarray|null $options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|array|falseRedisCluster|bool|array
+

See also

@@ -14146,7 +19064,8 @@

See also

@@ -14158,11 +19077,10 @@

See also

-

- - RedisCluster|int|false - zrank(string $key, mixed $member) - + +

+ + RedisCluster|bool|array zrevrangebylex(string $key, string $min, string $max, array|null $options = null)

@@ -14182,22 +19100,34 @@

Parameters

- - + + + + + + + + + + + +
- Redis::zrangebyscore + +Redis::zRevRange
mixed$memberstring$min
string$max
array|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool|array
+

See also

@@ -14205,7 +19135,8 @@

See also

@@ -14217,11 +19148,10 @@

See also

-

- - RedisCluster|int|false - zrem(string $key, string $value, string ...$other_values) - + +

+ + RedisCluster|bool|array zrevrangebyscore(string $key, string $min, string $max, array|null $options = null)

@@ -14242,26 +19172,33 @@

Parameters

- + - + + + + + +
- Redis::zrank + +Redis::zRevRangeByLex
string$value$min
string...$other_values$max
array|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool|array
+

See also

@@ -14269,7 +19206,8 @@

See also

@@ -14281,11 +19219,10 @@

See also

-

- - RedisCluster|int|false - zremrangebylex(string $key, string $min, string $max) - + +

+ + RedisCluster|int|false zrevrank(string $key, mixed $member)

@@ -14305,27 +19242,24 @@

Parameters

- - - - - - - + +
- Redis::zrem + +Redis::zRevRangeByScore
string$min
string$maxmixed$member
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -14333,7 +19267,8 @@

See also

@@ -14345,11 +19280,10 @@

See also

-

- - RedisCluster|int|false - zremrangebyrank(string $key, string $min, string $max) - + +

+ + RedisCluster|bool|array zscan(string $key, null|int|string $iterator, string|null $pattern = null, int $count = 0)

@@ -14369,27 +19303,34 @@

Parameters

- - + + - - + + + + + + +
- Redis::zremrangebylex + +Redis::zRevRank
string$minnull|int|string$iterator
string$maxstring|null$pattern
int$count
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|bool|array
+

See also

@@ -14397,7 +19338,8 @@

See also

@@ -14409,11 +19351,10 @@

See also

-

- - RedisCluster|int|false - zremrangebyscore(string $key, string $min, string $max) - + +

+ + RedisCluster|float|false zscore(string $key, mixed $member)

@@ -14433,27 +19374,24 @@

Parameters

- - - - - - - + +
- Redis::zremrangebyrank + +Redis::zscan
string$min
string$maxmixed$member
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|float|false
+

See also

@@ -14461,7 +19399,8 @@

See also

@@ -14473,11 +19412,10 @@

See also

-

- - RedisCluster|bool|array - zrevrange(string $key, string $min, string $max, array $options = null) - + +

+ + RedisCluster|array|false zmscore(string $key, mixed $member, mixed ...$other_members)

@@ -14497,32 +19435,29 @@

Parameters

- - - - - - - + + - - + +
- Redis::zremrangebyscore + +Redis::zScore
string$min
string$maxmixed$member
array$optionsmixed...$other_members
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|array|false
+

See also

@@ -14530,7 +19465,7 @@

See also

@@ -14542,11 +19477,10 @@

See also

-

- - RedisCluster|bool|array - zrevrangebylex(string $key, string $min, string $max, array $options = null) - + +

+ + RedisCluster|int|false zunionstore(string $dst, array $keys, array|null $weights = null, string|null $aggregate = null)

@@ -14562,36 +19496,38 @@

Parameters

- Redis::zrevrange + https://redis.io/commands/zmscore
- + - - + + - - + + - - + +
string$key$dst
string$minarray$keys
string$maxarray|null$weights
array$optionsstring|null$aggregate
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|int|false
+

See also

@@ -14599,7 +19535,8 @@

See also

@@ -14611,11 +19548,10 @@

See also

-

- - RedisCluster|bool|array - zrevrangebyscore(string $key, string $min, string $max, array $options = null) - + +

+ + RedisCluster|array|false zinter(array $keys, array|null $weights = null, array|null $options = null)

@@ -14630,37 +19566,34 @@

Parameters

- Redis::zrevrangebylex + +Redis::zunionstore
- - - - - - - + + - - + + - +
string$key
string$minarray$keys
string$maxarray|null$weights
arrayarray|null $options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|array|false
+

See also

@@ -14668,7 +19601,7 @@

See also

@@ -14680,11 +19613,10 @@

See also

-

- - RedisCluster|int|false - zrevrank(string $key, mixed $member) - + +

+ + RedisCluster|int|false zdiffstore(string $dst, array $keys)

@@ -14700,26 +19632,28 @@

Parameters

- Redis::zrevrangebyscore + https://redis.io/commands/zinter
- + - - + +
string$key$dst
mixed$memberarray$keys
-

Return Value

+

Return Value

- +
+
RedisCluster|int|false
+

See also

@@ -14727,7 +19661,7 @@

See also

@@ -14739,11 +19673,10 @@

See also

-

- - RedisCluster|bool|array - zscan(string $key, int|null $iterator, string|null $pattern = null, int $count = 0) - + +

+ + RedisCluster|array|false zunion(array $keys, array|null $weights = null, array|null $options = null)

@@ -14758,37 +19691,34 @@

Parameters

- Redis::zrevrank + https://redis.io/commands/zdiffstore
- - - - - - - + + - - + + - - + +
string$key
int|null$iteratorarray$keys
string|null$patternarray|null$weights
int$countarray|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|bool|arrayRedisCluster|array|false
+

See also

@@ -14796,7 +19726,7 @@

See also

@@ -14808,11 +19738,10 @@

See also

-

- - RedisCluster|float|false - zscore(string $key, mixed $member) - + +

+ + RedisCluster|array|false zdiff(array $keys, array|null $options = null)

@@ -14827,27 +19756,29 @@

Parameters

- Redis::zscan + https://redis.io/commands/zunion
- - + + - - + +
string$keyarray$keys
mixed$memberarray|null$options
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|float|falseRedisCluster|array|false
+

See also

@@ -14855,7 +19786,7 @@

See also

@@ -14867,11 +19798,10 @@

See also

-

- - RedisCluster|int|false - zunionstore(string $dst, array $keys, array|null $weights = NULL, string|null $aggregate = NULL) - + +

+ + RedisCluster|string|false digest(string $key)

@@ -14887,36 +19817,23 @@

Parameters

- Redis::zscore + https://redis.io/commands/zdiff
- - - - - - - - - - - - - - - - +
string$dst
array$keys
array|null$weights
string|null$aggregate$key
-

Return Value

+

Return Value

- +
+
- +
RedisCluster|int|falseRedisCluster|string|false
+

See also

@@ -14924,7 +19841,7 @@

See also

diff --git a/docs/RedisClusterException.html b/docs/RedisClusterException.html index aeda6a4c86..ad2467c649 100644 --- a/docs/RedisClusterException.html +++ b/docs/RedisClusterException.html @@ -5,7 +5,7 @@ RedisClusterException | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> @@ -83,7 +472,7 @@

RedisClusterException

class - RedisClusterException extends RuntimeException (View source) + RedisClusterException extends RuntimeException (View source)

diff --git a/docs/RedisException.html b/docs/RedisException.html index 66c373b690..45ddaad8fa 100644 --- a/docs/RedisException.html +++ b/docs/RedisException.html @@ -5,7 +5,7 @@ RedisException | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> @@ -83,7 +472,7 @@

RedisException

class - RedisException extends RuntimeException (View source) + RedisException extends RuntimeException (View source)

diff --git a/docs/RedisSentinel.html b/docs/RedisSentinel.html index ac95dd5c28..98f66de172 100644 --- a/docs/RedisSentinel.html +++ b/docs/RedisSentinel.html @@ -5,7 +5,7 @@ RedisSentinel | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> @@ -83,7 +472,7 @@

RedisSentinel

class - RedisSentinel (View source) + RedisSentinel (View source)

@@ -102,7 +491,7 @@

Methods

- __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = null, int $retry_interval = 0, float $read_timeout = 0, mixed $auth = null, array $context = null) + __construct(array|null $options = null)

No description

@@ -198,7 +587,7 @@

Methods

reset(string $pattern) @@ -236,11 +625,10 @@

Details

-

- - - __construct(string $host, int $port = 26379, float $timeout = 0, mixed $persistent = null, int $retry_interval = 0, float $read_timeout = 0, mixed $auth = null, array $context = null) - + +

+ + __construct(array|null $options = null)

@@ -255,43 +643,8 @@

Parameters

- Redis::zunionstore + https://redis.io/commands/digest
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
string$host
int$port
float$timeout
mixed$persistent
int$retry_interval
float$read_timeout
mixed$auth
array$contextarray|null$options
@@ -305,11 +658,10 @@

Parameters

-

- - bool|RedisSentinel - ckquorum(string $master) - + +

+ + bool|RedisSentinel ckquorum(string $master)

@@ -331,15 +683,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
bool|RedisSentinel
+
@@ -348,11 +702,10 @@

Return Value

-

- - bool|RedisSentinel - failover(string $master) - + +

+ + bool|RedisSentinel failover(string $master)

@@ -374,15 +727,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
bool|RedisSentinel
+
@@ -391,11 +746,10 @@

Return Value

-

- - bool|RedisSentinel - flushconfig() - + +

+ + bool|RedisSentinel flushconfig()

@@ -407,15 +761,17 @@

-

Return Value

+

Return Value

- +
+
bool|RedisSentinel
+
@@ -424,11 +780,10 @@

Return Value

-

- - array|bool|RedisSentinel - getMasterAddrByName(string $master) - + +

+ + array|bool|RedisSentinel getMasterAddrByName(string $master)

@@ -450,15 +805,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
array|bool|RedisSentinel
+
@@ -467,11 +824,10 @@

Return Value

-

- - array|bool|RedisSentinel - master(string $master) - + +

+ + array|bool|RedisSentinel master(string $master)

@@ -493,15 +849,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
array|bool|RedisSentinel
+
@@ -510,11 +868,10 @@

Return Value

-

- - array|bool|RedisSentinel - masters() - + +

+ + array|bool|RedisSentinel masters()

@@ -526,15 +883,17 @@

-

Return Value

+

Return Value

- +
+
array|bool|RedisSentinel
+
@@ -543,11 +902,10 @@

Return Value

-

- - string - myid() - + +

+ + string myid()

@@ -559,15 +917,17 @@

-

Return Value

+

Return Value

- +
+
string
+
@@ -576,11 +936,10 @@

Return Value

-

- - bool|RedisSentinel - ping() - + +

+ + bool|RedisSentinel ping()

@@ -592,15 +951,17 @@

-

Return Value

+

Return Value

- +
+
bool|RedisSentinel
+
@@ -609,11 +970,10 @@

Return Value

-

- - bool|RedisSentinel - reset(string $pattern) - + +

+ + int|RedisSentinel reset(string $pattern)

@@ -635,15 +995,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
- +
bool|RedisSentinelint|RedisSentinel
+
@@ -652,11 +1014,10 @@

Return Value

-

- - array|bool|RedisSentinel - sentinels(string $master) - + +

+ + array|bool|RedisSentinel sentinels(string $master)

@@ -678,15 +1039,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
array|bool|RedisSentinel
+
@@ -695,11 +1058,10 @@

Return Value

-

- - array|bool|RedisSentinel - slaves(string $master) - + +

+ + array|bool|RedisSentinel slaves(string $master)

@@ -721,15 +1083,17 @@

Parameters

-

Return Value

+

Return Value

- +
+
array|bool|RedisSentinel
+
diff --git a/docs/[Global_Namespace].html b/docs/[Global_Namespace].html index b30c8eb2e2..1993f6549a 100644 --- a/docs/[Global_Namespace].html +++ b/docs/[Global_Namespace].html @@ -5,7 +5,7 @@ [Global Namespace] | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> diff --git a/docs/classes.html b/docs/classes.html index f281726e73..99aeb1151b 100644 --- a/docs/classes.html +++ b/docs/classes.html @@ -5,7 +5,7 @@ All Classes | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> diff --git a/docs/css/highlight-github.min.css b/docs/css/highlight-github.min.css new file mode 100644 index 0000000000..7a9730c0dc --- /dev/null +++ b/docs/css/highlight-github.min.css @@ -0,0 +1,10 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! + Theme: GitHub + Description: Light theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Outdated base version: https://github.com/primer/github-syntax-light + Current colors taken from GitHub's CSS +*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#22863a}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0} diff --git a/docs/doc-index.html b/docs/doc-index.html index e475ef25be..905a5b9b56 100644 --- a/docs/doc-index.html +++ b/docs/doc-index.html @@ -5,7 +5,7 @@ Index | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> @@ -102,7 +491,7 @@

Index

  • S
  • T
  • U
  • -
  • V
  • +
  • V
  • W
  • X
  • Y
  • @@ -112,11 +501,17 @@

    Index

    A

    Redis::acl() — Method in class Redis
    -
    +

    Execute Redis ACL subcommands.

    Redis::append() — Method in class Redis

    Append data to a Redis STRING key.

    Redis::auth() — Method in class Redis
    -

    Authenticate a Redis connection after its been established.

    +

    Authenticate a Redis connection after its been established.

    +RedisArray::acl() — Method in class RedisArray
    +
    +RedisArray::append() — Method in class RedisArray
    +
    +RedisArray::auth() — Method in class RedisArray
    +
    RedisCluster::acl() — Method in class RedisCluster
    RedisCluster::append() — Method in class RedisCluster
    @@ -151,8 +546,34 @@

    A

    when no elements are available.

    Redis::blmpop() — Method in class Redis

    Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when -no elements are available.

    +no elements are available.

    +Redis::blmove() — Method in class Redis
    +
    RedisArray::bgsave() — Method in class RedisArray
    +
    +RedisArray::bgrewriteaof() — Method in class RedisArray
    +
    +RedisArray::bitcount() — Method in class RedisArray
    +
    +RedisArray::bitop() — Method in class RedisArray
    +
    +RedisArray::bitpos() — Method in class RedisArray
    +
    +RedisArray::blPop() — Method in class RedisArray
    +
    +RedisArray::brPop() — Method in class RedisArray
    +
    +RedisArray::brpoplpush() — Method in class RedisArray
    +
    +RedisArray::bzPopMax() — Method in class RedisArray
    +
    +RedisArray::bzPopMin() — Method in class RedisArray
    +
    +RedisArray::bzmpop() — Method in class RedisArray
    +
    +RedisArray::blmpop() — Method in class RedisArray
    +
    +RedisArray::blmove() — Method in class RedisArray
    RedisCluster::bgrewriteaof() — Method in class RedisCluster
    @@ -165,11 +586,13 @@

    A

    RedisCluster::bitpos
    () — Method in class RedisCluster

    Return the position of the first bit set to 0 or 1 in a string.

    RedisCluster::blpop() — Method in class RedisCluster
    -

    See Redis::blpop()

    +
    RedisCluster::brpop() — Method in class RedisCluster
    -

    See Redis::brpop()

    +
    RedisCluster::brpoplpush() — Method in class RedisCluster
    -

    See Redis::brpoplpush()

    +
    +RedisCluster::blmove() — Method in class RedisCluster
    +

    Move an element from one list to another, blocking up to a timeout until an element is available.

    RedisCluster::bzpopmax() — Method in class RedisCluster
    RedisCluster::bzpopmin() — Method in class RedisCluster
    @@ -182,17 +605,33 @@

    A

    Redis::clearLastError() — Method in class Redis

    Reset any last error on the connection to NULL

    Redis::client() — Method in class Redis
    -
    +

    Execute Redis CLIENT subcommands.

    Redis::close() — Method in class Redis
    -
    +

    Closes the connection to Redis

    Redis::command() — Method in class Redis
    -
    +

    Execute Redis COMMAND subcommands.

    Redis::config() — Method in class Redis

    Execute the Redis CONFIG command in a variety of ways.

    Redis::connect() — Method in class Redis
    -
    +

    Connect to a Redis server

    Redis::copy() — Method in class Redis
    -

    Make a copy of a key.

    +

    Make a copy of a key.

    +Redis::clearTransferredBytes() — Method in class Redis
    +

    Reset the number of bytes sent and received on the socket.

    +RedisArray::clearLastError() — Method in class RedisArray
    +
    +RedisArray::client() — Method in class RedisArray
    +
    +RedisArray::close() — Method in class RedisArray
    +
    +RedisArray::command() — Method in class RedisArray
    +
    +RedisArray::config() — Method in class RedisArray
    +
    +RedisArray::copy() — Method in class RedisArray
    +
    +RedisArray::clearTransferredBytes() — Method in class RedisArray
    +
    RedisCluster::clearlasterror() — Method in class RedisCluster
    RedisCluster::client() — Method in class RedisCluster
    @@ -204,6 +643,10 @@

    A

    RedisCluster::command() — Method in class RedisCluster
    RedisCluster::config() — Method in class RedisCluster
    +
    +RedisCluster::copy() — Method in class RedisCluster
    +
    +RedisCluster::cleartransferredbytes() — Method in class RedisCluster
    RedisSentinel::ckquorum() — Method in class RedisSentinel

    D

    @@ -211,22 +654,49 @@

    A

    Redis::dbSize() — Method in class Redis

    Return the number of keys in the currently selected Redis database.

    Redis::debug() — Method in class Redis
    -
    +

    Execute the Redis DEBUG command. Note that this is disabled by default +and can be very dangerous, even allowing you to crash the server. Use +with caution

    Redis::decr() — Method in class Redis

    Decrement a Redis integer by 1 or a provided value.

    Redis::decrBy() — Method in class Redis

    Decrement a redis integer by a value

    Redis::del() — Method in class Redis
    -

    Delete one or more keys from Redis.

    +

    Delete one or more keys from Redis.

    +Redis::delex() — Method in class Redis
    +

    Delete a key conditionally based on its value or hash digest

    +Redis::delifeq() — Method in class Redis
    +

    Delete a key if it's equal to the specified value. This command is +specific to Valkey >= 9.0

    Redis::delete() — Method in class Redis
    Redis::discard() — Method in class Redis

    Discard a transaction currently in progress.

    Redis::dump() — Method in class Redis
    -

    Dump Redis' internal binary representation of a key.

    +

    Dump Redis' internal binary representation of a key.

    +Redis::digest() — Method in class Redis
    +

    Ask the server for the XXH3 digest of a given key's value

    RedisArray::del() — Method in class RedisArray
    RedisArray::discard() — Method in class RedisArray
    +
    +RedisArray::dbSize() — Method in class RedisArray
    +
    +RedisArray::debug() — Method in class RedisArray
    +
    +RedisArray::decr() — Method in class RedisArray
    +
    +RedisArray::decrBy() — Method in class RedisArray
    +
    +RedisArray::delex() — Method in class RedisArray
    +
    +RedisArray::delifeq() — Method in class RedisArray
    +
    +RedisArray::delete() — Method in class RedisArray
    +
    +RedisArray::dump() — Method in class RedisArray
    +
    +RedisArray::digest() — Method in class RedisArray
    RedisCluster::dbsize() — Method in class RedisCluster
    @@ -237,10 +707,17 @@

    A

    RedisCluster::decrbyfloat
    () — Method in class RedisCluster
    RedisCluster::del() — Method in class RedisCluster
    -
    +
    +RedisCluster::delex() — Method in class RedisCluster
    +

    Delete a key conditionally based on its value or hash digest

    +RedisCluster::delifeq() — Method in class RedisCluster
    +

    Delete a key if it's equal to the specified value. This command is +specific to Valkey >= 9.0

    RedisCluster::discard() — Method in class RedisCluster
    RedisCluster::dump() — Method in class RedisCluster
    +
    +RedisCluster::digest() — Method in class RedisCluster

    E

    Redis::echo() — Method in class Redis
    @@ -267,8 +744,34 @@

    A

    Redis::expireAt() — Method in class Redis

    Set a key to expire at an exact unix timestamp.

    Redis::expiretime() — Method in class Redis
    -

    Get the expiration of a given key as a unix timestamp

    +

    Get the expiration of a given key as a unix timestamp

    +Redis::expiremember() — Method in class Redis
    +

    Set an expiration on a key member (KeyDB only).

    +Redis::expirememberat() — Method in class Redis
    +

    Set an expiration on a key membert to a specific unix timestamp (KeyDB only).

    RedisArray::exec() — Method in class RedisArray
    +
    +RedisArray::echo() — Method in class RedisArray
    +
    +RedisArray::eval() — Method in class RedisArray
    +
    +RedisArray::eval_ro() — Method in class RedisArray
    +
    +RedisArray::evalsha() — Method in class RedisArray
    +
    +RedisArray::evalsha_ro() — Method in class RedisArray
    +
    +RedisArray::exists() — Method in class RedisArray
    +
    +RedisArray::expire() — Method in class RedisArray
    +
    +RedisArray::expireAt() — Method in class RedisArray
    +
    +RedisArray::expiretime() — Method in class RedisArray
    +
    +RedisArray::expiremember() — Method in class RedisArray
    +
    +RedisArray::expirememberat() — Method in class RedisArray
    RedisCluster::echo() — Method in class RedisCluster
    @@ -289,17 +792,35 @@

    A

    RedisCluster::expireat
    () — Method in class RedisCluster
    RedisCluster::expiretime() — Method in class RedisCluster
    +
    +RedisCluster::expiremember() — Method in class RedisCluster
    +
    +RedisCluster::expirememberat() — Method in class RedisCluster

    F

    Redis::failover() — Method in class Redis
    -
    +
    +Redis::fcall() — Method in class Redis
    +

    Invoke a function.

    +Redis::fcall_ro() — Method in class Redis
    +

    This is a read-only variant of the FCALL command that cannot execute commands that modify data.

    Redis::flushAll() — Method in class Redis

    Deletes every key in all Redis databases

    Redis::flushDB() — Method in class Redis
    -

    Deletes all the keys of the currently selected database.

    +

    Deletes all the keys of the currently selected database.

    +Redis::function() — Method in class Redis
    +

    Functions is an API for managing code to be executed on the server.

    RedisArray::flushall() — Method in class RedisArray
    RedisArray::flushdb() — Method in class RedisArray
    +
    +RedisArray::failover() — Method in class RedisArray
    +
    +RedisArray::fcall() — Method in class RedisArray
    +
    +RedisArray::fcall_ro() — Method in class RedisArray
    +
    +RedisArray::function() — Method in class RedisArray
    RedisCluster::flushall() — Method in class RedisCluster
    @@ -317,7 +838,7 @@

    A

    Redis::geohash
    () — Method in class Redis

    Retrieve one or more GeoHash encoded strings for members of the set.

    Redis::geopos() — Method in class Redis
    -

    Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

    +

    Return the longitude and latitude for one or more members of a geospacially encoded sorted set.

    Redis::georadius() — Method in class Redis

    Retrieve members of a geospacially sorted set that are within a certain radius of a location.

    Redis::georadius_ro() — Method in class Redis
    @@ -332,7 +853,9 @@

    A

    Search a geospacial sorted set for members within a given area or range, storing the results into a new set.

    Redis::get() — Method in class Redis
    -

    Retrieve a string keys value.

    +

    Retrieve a string keys value.

    +Redis::getWithMeta() — Method in class Redis
    +

    Retrieve a value and metadata of key.

    Redis::getAuth() — Method in class Redis

    Get the authentication information on the connection, if any.

    Redis::getBit() — Method in class Redis
    @@ -364,8 +887,64 @@

    A

    Redis::getTimeout() — Method in class Redis

    Retrieve any set connection timeout

    Redis::getTransferredBytes() — Method in class Redis
    -
    +

    Get the number of bytes sent and received on the socket.

    +Redis::gcra() — Method in class Redis
    +

    Get rate limiting information

    RedisArray::getOption() — Method in class RedisArray
    +
    +RedisArray::geoadd() — Method in class RedisArray
    +
    +RedisArray::geodist() — Method in class RedisArray
    +
    +RedisArray::geohash() — Method in class RedisArray
    +
    +RedisArray::geopos() — Method in class RedisArray
    +
    +RedisArray::georadius() — Method in class RedisArray
    +
    +RedisArray::georadius_ro() — Method in class RedisArray
    +
    +RedisArray::georadiusbymember() — Method in class RedisArray
    +
    +RedisArray::georadiusbymember_ro() — Method in class RedisArray
    +
    +RedisArray::geosearch() — Method in class RedisArray
    +
    +RedisArray::geosearchstore() — Method in class RedisArray
    +
    +RedisArray::get() — Method in class RedisArray
    +
    +RedisArray::getWithMeta() — Method in class RedisArray
    +
    +RedisArray::getAuth() — Method in class RedisArray
    +
    +RedisArray::getBit() — Method in class RedisArray
    +
    +RedisArray::getEx() — Method in class RedisArray
    +
    +RedisArray::getDBNum() — Method in class RedisArray
    +
    +RedisArray::getDel() — Method in class RedisArray
    +
    +RedisArray::getHost() — Method in class RedisArray
    +
    +RedisArray::getLastError() — Method in class RedisArray
    +
    +RedisArray::getMode() — Method in class RedisArray
    +
    +RedisArray::getPersistentID() — Method in class RedisArray
    +
    +RedisArray::getPort() — Method in class RedisArray
    +
    +RedisArray::getRange() — Method in class RedisArray
    +
    +RedisArray::getReadTimeout() — Method in class RedisArray
    +
    +RedisArray::getset() — Method in class RedisArray
    +
    +RedisArray::getTimeout() — Method in class RedisArray
    +
    +RedisArray::getTransferredBytes() — Method in class RedisArray
    RedisCluster::geoadd() — Method in class RedisCluster
    @@ -382,8 +961,18 @@

    A

    RedisCluster::georadiusbymember
    () — Method in class RedisCluster
    RedisCluster::georadiusbymember_ro() — Method in class RedisCluster
    +
    +RedisCluster::geosearch() — Method in class RedisCluster
    +
    +RedisCluster::geosearchstore() — Method in class RedisCluster
    RedisCluster::get() — Method in class RedisCluster
    +
    +RedisCluster::getdel() — Method in class RedisCluster
    +
    +RedisCluster::getWithMeta() — Method in class RedisCluster
    +
    +RedisCluster::getex() — Method in class RedisCluster
    RedisCluster::getbit() — Method in class RedisCluster
    @@ -398,6 +987,8 @@

    A

    RedisCluster::getset
    () — Method in class RedisCluster
    RedisCluster::gettransferredbytes() — Method in class RedisCluster
    +
    +RedisCluster::gcra() — Method in class RedisCluster
    RedisSentinel::getMasterAddrByName() — Method in class RedisSentinel

    H

    @@ -409,7 +1000,9 @@

    A

    Redis::hGet() — Method in class Redis
    Redis::hGetAll() — Method in class Redis
    -

    Read every field and value from a hash.

    +

    Read every field and value from a hash.

    +Redis::hGetWithMeta() — Method in class Redis
    +

    Retrieve a value and metadata of hash field.

    Redis::hIncrBy() — Method in class Redis

    Increment a hash field's value by an integer

    Redis::hIncrByFloat() — Method in class Redis
    @@ -419,22 +1012,95 @@

    A

    Redis::hLen() — Method in class Redis

    Get the number of fields in a hash.

    Redis::hMget() — Method in class Redis
    -

    Get one or more fields from a hash.

    +

    Get one or more fields from a hash.

    +Redis::hgetex() — Method in class Redis
    +

    Get one or more fields of a hash while optionally setting expiration +information

    +Redis::hsetex() — Method in class Redis
    +

    Set one or more fields in a hash with optional expiration information.

    +Redis::hgetdel() — Method in class Redis
    +

    Get one or more fields and delete them

    Redis::hMset() — Method in class Redis

    Add or update one or more hash fields and values

    Redis::hRandField() — Method in class Redis

    Get one or more random field from a hash.

    Redis::hSet() — Method in class Redis
    -
    +

    Add or update one or more hash fields and values.

    Redis::hSetNx() — Method in class Redis

    Set a hash field and value, but only if that field does not exist

    Redis::hStrLen() — Method in class Redis

    Get the string length of a hash field

    Redis::hVals() — Method in class Redis
    -

    Get all of the values from a hash.

    +

    Get all of the values from a hash.

    +Redis::hexpire() — Method in class Redis
    +

    Set the expiration on one or more fields in a hash.

    +Redis::hpexpire() — Method in class Redis
    +

    Set the expiration on one or more fields in a hash in milliseconds.

    +Redis::hexpireat() — Method in class Redis
    +

    Set the expiration time on one or more fields of a hash.

    +Redis::hpexpireat() — Method in class Redis
    +

    Set the expiration time on one or more fields of a hash in milliseconds.

    +Redis::httl() — Method in class Redis
    +

    Get the TTL of one or more fields in a hash

    +Redis::hpttl() — Method in class Redis
    +

    Get the millisecond TTL of one or more fields in a hash

    +Redis::hexpiretime() — Method in class Redis
    +

    Get the expiration time of one or more fields in a hash

    +Redis::hpexpiretime() — Method in class Redis
    +

    Get the expiration time in milliseconds of one or more fields in a hash

    +Redis::hpersist() — Method in class Redis
    +

    Persist one or more hash fields

    Redis::hscan() — Method in class Redis

    Iterate over the fields and values of a hash in an incremental fashion.

    RedisArray::hscan() — Method in class RedisArray
    +
    +RedisArray::hDel() — Method in class RedisArray
    +
    +RedisArray::hExists() — Method in class RedisArray
    +
    +RedisArray::hGet() — Method in class RedisArray
    +
    +RedisArray::hGetAll() — Method in class RedisArray
    +
    +RedisArray::hGetWithMeta() — Method in class RedisArray
    +
    +RedisArray::hIncrBy() — Method in class RedisArray
    +
    +RedisArray::hIncrByFloat() — Method in class RedisArray
    +
    +RedisArray::hKeys() — Method in class RedisArray
    +
    +RedisArray::hLen() — Method in class RedisArray
    +
    +RedisArray::hMget() — Method in class RedisArray
    +
    +RedisArray::hgetex() — Method in class RedisArray
    +
    +RedisArray::hsetex() — Method in class RedisArray
    +
    +RedisArray::hgetdel() — Method in class RedisArray
    +
    +RedisArray::hMset() — Method in class RedisArray
    +
    +RedisArray::hRandField() — Method in class RedisArray
    +
    +RedisArray::hSet() — Method in class RedisArray
    +
    +RedisArray::hSetNx() — Method in class RedisArray
    +
    +RedisArray::hStrLen() — Method in class RedisArray
    +
    +RedisArray::hVals() — Method in class RedisArray
    +
    +RedisArray::httl() — Method in class RedisArray
    +
    +RedisArray::hpttl() — Method in class RedisArray
    +
    +RedisArray::hexpiretime() — Method in class RedisArray
    +
    +RedisArray::hpexpiretime() — Method in class RedisArray
    +
    +RedisArray::hpersist() — Method in class RedisArray
    RedisCluster::hdel() — Method in class RedisCluster
    @@ -443,6 +1109,8 @@

    A

    RedisCluster::hget
    () — Method in class RedisCluster
    RedisCluster::hgetall() — Method in class RedisCluster
    +
    +RedisCluster::hgetWithMeta() — Method in class RedisCluster
    RedisCluster::hincrby() — Method in class RedisCluster
    @@ -453,22 +1121,48 @@

    A

    RedisCluster::hlen
    () — Method in class RedisCluster
    RedisCluster::hmget() — Method in class RedisCluster
    +
    +RedisCluster::hgetex() — Method in class RedisCluster
    +
    +RedisCluster::hsetex() — Method in class RedisCluster
    +
    +RedisCluster::hgetdel() — Method in class RedisCluster
    RedisCluster::hmset() — Method in class RedisCluster
    RedisCluster::hscan() — Method in class RedisCluster
    +
    +RedisCluster::hrandfield() — Method in class RedisCluster
    RedisCluster::hset() — Method in class RedisCluster
    RedisCluster::hsetnx() — Method in class RedisCluster
    RedisCluster::hstrlen() — Method in class RedisCluster
    +
    +RedisCluster::hexpire() — Method in class RedisCluster
    +
    +RedisCluster::hpexpire() — Method in class RedisCluster
    +
    +RedisCluster::hexpireat() — Method in class RedisCluster
    +
    +RedisCluster::hpexpireat() — Method in class RedisCluster
    +
    +RedisCluster::httl() — Method in class RedisCluster
    +
    +RedisCluster::hpttl() — Method in class RedisCluster
    +
    +RedisCluster::hexpiretime() — Method in class RedisCluster
    +
    +RedisCluster::hpexpiretime() — Method in class RedisCluster
    +
    +RedisCluster::hpersist() — Method in class RedisCluster
    RedisCluster::hvals() — Method in class RedisCluster

    I

    Redis::incr() — Method in class Redis
    -

    Increment a key's value, optionally by a specifc amount.

    +

    Increment a key's value, optionally by a specific amount.

    Redis::incrBy() — Method in class Redis

    Increment a key by a specific integer value

    Redis::incrByFloat() — Method in class Redis
    @@ -481,6 +1175,14 @@

    A

    Redis::isConnected() — Method in class Redis

    Check if we are currently connected to a Redis instance.

    RedisArray::info() — Method in class RedisArray
    +
    +RedisArray::incr() — Method in class RedisArray
    +
    +RedisArray::incrBy() — Method in class RedisArray
    +
    +RedisArray::incrByFloat() — Method in class RedisArray
    +
    +RedisArray::isConnected() — Method in class RedisArray
    RedisCluster::incr() — Method in class RedisCluster
    @@ -508,7 +1210,7 @@

    A

    Redis::lInsert
    () — Method in class Redis
    Redis::lLen() — Method in class Redis
    -

    Retrieve the lenght of a list.

    +

    Retrieve the length of a list.

    Redis::lMove() — Method in class Redis

    Move an element from one list into another.

    Redis::lPop() — Method in class Redis
    @@ -530,7 +1232,39 @@

    A

    Redis::lrem() — Method in class Redis

    Remove one or more matching elements from a list.

    Redis::ltrim() — Method in class Redis
    -

    Trim a list to a subrange of elements.

    +

    Trim a list to a subrange of elements.

    +RedisArray::lmpop() — Method in class RedisArray
    +
    +RedisArray::lcs() — Method in class RedisArray
    +
    +RedisArray::lInsert() — Method in class RedisArray
    +
    +RedisArray::lLen() — Method in class RedisArray
    +
    +RedisArray::lMove() — Method in class RedisArray
    +
    +RedisArray::lPop() — Method in class RedisArray
    +
    +RedisArray::lPos() — Method in class RedisArray
    +
    +RedisArray::lPush() — Method in class RedisArray
    +
    +RedisArray::lPushx() — Method in class RedisArray
    +
    +RedisArray::lSet() — Method in class RedisArray
    +
    +RedisArray::lastSave() — Method in class RedisArray
    +
    +RedisArray::lindex() — Method in class RedisArray
    +
    +RedisArray::lrange() — Method in class RedisArray
    +
    +RedisArray::lrem() — Method in class RedisArray
    +
    +RedisArray::ltrim() — Method in class RedisArray
    +
    +RedisCluster::lmove() — Method in class RedisCluster
    +

    Move an element from one list into another.

    RedisCluster::lmpop() — Method in class RedisCluster
    RedisCluster::lcs() — Method in class RedisCluster
    @@ -546,6 +1280,8 @@

    A

    RedisCluster::llen() — Method in class RedisCluster
    RedisCluster::lpop() — Method in class RedisCluster
    +
    +RedisCluster::lpos() — Method in class RedisCluster
    RedisCluster::lpush() — Method in class RedisCluster
    @@ -561,15 +1297,17 @@

    A

    M

    Redis::mget() — Method in class Redis
    -

    Get one ore more string keys.

    +

    Get one or more string keys.

    Redis::migrate() — Method in class Redis
    -
    +

    Proxy for the Redis MIGRATE command.

    Redis::move() — Method in class Redis

    Move a key to a different database on the same redis instance.

    Redis::mset() — Method in class Redis
    -

    Set one ore more string keys.

    +

    Set one or more string keys.

    +Redis::msetex() — Method in class Redis
    +

    Set one or more keys and values with optional expiry information.

    Redis::msetnx() — Method in class Redis
    -

    Set one ore more string keys but only if none of the key exist.

    +

    Set one or more string keys but only if none of the key exist.

    Redis::multi() — Method in class Redis

    Begin a transaction.

    RedisArray::mget() — Method in class RedisArray
    @@ -577,12 +1315,20 @@

    A

    RedisArray::mset() — Method in class RedisArray
    RedisArray::multi() — Method in class RedisArray
    +
    +RedisArray::move() — Method in class RedisArray
    +
    +RedisArray::msetex() — Method in class RedisArray
    +
    +RedisArray::msetnx() — Method in class RedisArray
    RedisCluster::mget() — Method in class RedisCluster
    RedisCluster::mset() — Method in class RedisCluster
    RedisCluster::msetnx() — Method in class RedisCluster
    +
    +RedisCluster::msetex() — Method in class RedisCluster
    RedisCluster::multi() — Method in class RedisCluster
    @@ -594,16 +1340,20 @@

    A

    O

    Redis::object() — Method in class Redis
    -
    +

    Get encoding and other information about a key.

    Redis::open() — Method in class Redis
    +
    +RedisArray::object() — Method in class RedisArray
    +
    +RedisArray::open() — Method in class RedisArray
    RedisCluster::object() — Method in class RedisCluster

    P

    Redis::pexpiretime() — Method in class Redis
    -

    Get the expriation timestamp of a given Redis key but in milliseconds.

    +

    Get the expiration timestamp of a given Redis key but in milliseconds.

    Redis::pconnect() — Method in class Redis
    -
    +

    Connects to a Redis server creating or reusing a persistent connection.

    Redis::persist() — Method in class Redis

    Remove the expiration from a key.

    Redis::pexpire() — Method in class Redis
    @@ -633,10 +1383,42 @@

    A

    Redis::publish() — Method in class Redis

    Publish a message to a pubsub channel

    Redis::pubsub() — Method in class Redis
    -
    +

    Interact with the Redis PubSub subsystem.

    Redis::punsubscribe() — Method in class Redis

    Unsubscribe from one or more channels by pattern

    RedisArray::ping() — Method in class RedisArray
    +
    +RedisArray::pexpiretime() — Method in class RedisArray
    +
    +RedisArray::pconnect() — Method in class RedisArray
    +
    +RedisArray::persist() — Method in class RedisArray
    +
    +RedisArray::pexpire() — Method in class RedisArray
    +
    +RedisArray::pexpireAt() — Method in class RedisArray
    +
    +RedisArray::pfadd() — Method in class RedisArray
    +
    +RedisArray::pfcount() — Method in class RedisArray
    +
    +RedisArray::pfmerge() — Method in class RedisArray
    +
    +RedisArray::pipeline() — Method in class RedisArray
    +
    +RedisArray::popen() — Method in class RedisArray
    +
    +RedisArray::psetex() — Method in class RedisArray
    +
    +RedisArray::psubscribe() — Method in class RedisArray
    +
    +RedisArray::pttl() — Method in class RedisArray
    +
    +RedisArray::publish() — Method in class RedisArray
    +
    +RedisArray::pubsub() — Method in class RedisArray
    +
    +RedisArray::punsubscribe() — Method in class RedisArray
    RedisCluster::pexpiretime() — Method in class RedisCluster
    @@ -696,6 +1478,30 @@

    A

    Redis::replicaof
    () — Method in class Redis

    Used to turn a Redis instance into a replica of another, or to remove replica status promoting the instance to a primary.

    RedisArray
    +
    +RedisArray::rPush() — Method in class RedisArray
    +
    +RedisArray::rPushx() — Method in class RedisArray
    +
    +RedisArray::rPop() — Method in class RedisArray
    +
    +RedisArray::randomKey() — Method in class RedisArray
    +
    +RedisArray::rawcommand() — Method in class RedisArray
    +
    +RedisArray::rename() — Method in class RedisArray
    +
    +RedisArray::renameNx() — Method in class RedisArray
    +
    +RedisArray::reset() — Method in class RedisArray
    +
    +RedisArray::restore() — Method in class RedisArray
    +
    +RedisArray::role() — Method in class RedisArray
    +
    +RedisArray::rpoplpush() — Method in class RedisArray
    +
    +RedisArray::replicaof() — Method in class RedisArray
    RedisCluster
    RedisCluster::randomkey() — Method in class RedisCluster
    @@ -723,11 +1529,15 @@

    A

    RedisSentinel::reset() — Method in class RedisSentinel

    S

    -
    +
    +Redis::serverName() — Method in class Redis
    +

    Get the server name as reported by the HELLO response.

    +Redis::serverVersion() — Method in class Redis
    +

    Get the server version as reported by the HELLO response.

    Redis::sAdd() — Method in class Redis

    Add one or more values to a Redis SET key.

    Redis::sAddArray() — Method in class Redis
    -

    Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but +

    Add one or more values to a Redis SET key. This is an alternative to Redis::sadd() but instead of being variadic, takes a single array of values.

    Redis::sDiff() — Method in class Redis

    Given one or more Redis SETS, this command returns all of the members from the first @@ -804,11 +1614,16 @@

    A

    Redis::srem() — Method in class Redis

    Remove one or more values from a Redis SET key.

    Redis::sscan() — Method in class Redis
    -

    Scan the members of a redis SET key.

    +

    Scan the members of a redis SET key.

    +Redis::ssubscribe() — Method in class Redis
    +

    Subscribes the client to the specified shard channels.

    Redis::strlen() — Method in class Redis

    Retrieve the length of a Redis STRING key.

    Redis::subscribe() — Method in class Redis
    -

    Subscribe to one or more Redis pubsub channels.

    +

    Subscribe to one or more Redis pubsub channels.

    +Redis::sunsubscribe() — Method in class Redis
    +

    Unsubscribes the client from the given shard channels, +or from all of them if none is given.

    Redis::swapdb() — Method in class Redis

    Atomically swap two Redis databases so that all of the keys in the source database will now be in the destination database and vice-versa.

    @@ -821,6 +1636,82 @@

    A

    RedisArray::setOption
    () — Method in class RedisArray
    RedisArray::sscan() — Method in class RedisArray
    +
    +RedisArray::serverName() — Method in class RedisArray
    +
    +RedisArray::serverVersion() — Method in class RedisArray
    +
    +RedisArray::sAdd() — Method in class RedisArray
    +
    +RedisArray::sAddArray() — Method in class RedisArray
    +
    +RedisArray::sDiff() — Method in class RedisArray
    +
    +RedisArray::sDiffStore() — Method in class RedisArray
    +
    +RedisArray::sInter() — Method in class RedisArray
    +
    +RedisArray::sintercard() — Method in class RedisArray
    +
    +RedisArray::sInterStore() — Method in class RedisArray
    +
    +RedisArray::sMembers() — Method in class RedisArray
    +
    +RedisArray::sMisMember() — Method in class RedisArray
    +
    +RedisArray::sMove() — Method in class RedisArray
    +
    +RedisArray::sPop() — Method in class RedisArray
    +
    +RedisArray::sRandMember() — Method in class RedisArray
    +
    +RedisArray::sUnion() — Method in class RedisArray
    +
    +RedisArray::sUnionStore() — Method in class RedisArray
    +
    +RedisArray::scard() — Method in class RedisArray
    +
    +RedisArray::script() — Method in class RedisArray
    +
    +RedisArray::set() — Method in class RedisArray
    +
    +RedisArray::setBit() — Method in class RedisArray
    +
    +RedisArray::setRange() — Method in class RedisArray
    +
    +RedisArray::setex() — Method in class RedisArray
    +
    +RedisArray::setnx() — Method in class RedisArray
    +
    +RedisArray::sismember() — Method in class RedisArray
    +
    +RedisArray::slaveof() — Method in class RedisArray
    +
    +RedisArray::slowlog() — Method in class RedisArray
    +
    +RedisArray::sort() — Method in class RedisArray
    +
    +RedisArray::sort_ro() — Method in class RedisArray
    +
    +RedisArray::sortAsc() — Method in class RedisArray
    +
    +RedisArray::sortAscAlpha() — Method in class RedisArray
    +
    +RedisArray::sortDesc() — Method in class RedisArray
    +
    +RedisArray::sortDescAlpha() — Method in class RedisArray
    +
    +RedisArray::srem() — Method in class RedisArray
    +
    +RedisArray::ssubscribe() — Method in class RedisArray
    +
    +RedisArray::strlen() — Method in class RedisArray
    +
    +RedisArray::subscribe() — Method in class RedisArray
    +
    +RedisArray::sunsubscribe() — Method in class RedisArray
    +
    +RedisArray::swapdb() — Method in class RedisArray
    RedisCluster::sadd() — Method in class RedisCluster
    @@ -857,6 +1748,8 @@

    A

    RedisCluster::sinterstore
    () — Method in class RedisCluster
    RedisCluster::sismember() — Method in class RedisCluster
    +
    +RedisCluster::smismember() — Method in class RedisCluster
    RedisCluster::slowlog() — Method in class RedisCluster
    @@ -896,7 +1789,15 @@

    A

    Redis::ttl
    () — Method in class Redis

    Get the amount of time a Redis key has before it will expire, in seconds.

    Redis::type() — Method in class Redis
    -

    Get the type of a given Redis key.

    +

    Get the type of a given Redis key.

    +RedisArray::touch() — Method in class RedisArray
    +
    +RedisArray::time() — Method in class RedisArray
    +
    +RedisArray::ttl() — Method in class RedisArray
    +
    +RedisArray::type() — Method in class RedisArray
    +
    RedisCluster::touch() — Method in class RedisCluster
    RedisCluster::time() — Method in class RedisCluster
    @@ -917,24 +1818,117 @@

    A

    RedisArray::unlink() — Method in class RedisArray
    RedisArray::unwatch() — Method in class RedisArray
    +
    +RedisArray::unsubscribe() — Method in class RedisArray
    RedisCluster::unsubscribe() — Method in class RedisCluster
    RedisCluster::unlink() — Method in class RedisCluster
    RedisCluster::unwatch() — Method in class RedisCluster
    +

    V

    +
    +Redis::vadd() — Method in class Redis
    +

    Add to a vector set

    +Redis::vsim() — Method in class Redis
    +

    Query similarity of a vector by element or scores

    +Redis::vcard() — Method in class Redis
    +

    Get the length of a vector set

    +Redis::vdim() — Method in class Redis
    +

    Get the dimensions of a vector set

    +Redis::vinfo() — Method in class Redis
    +

    Get various bits of information about a vector set

    +Redis::vismember() — Method in class Redis
    +

    Check if an element is a member of a vectorset

    +Redis::vemb() — Method in class Redis
    +

    Get the embeddings for a specific member

    +Redis::vrandmember() — Method in class Redis
    +

    Get one or more random members from a vector set

    +Redis::vrange() — Method in class Redis
    +

    Retreive a lexographical range of elements from a vector set

    +Redis::vrem() — Method in class Redis
    +

    Remove an element from a vector set

    +Redis::vsetattr() — Method in class Redis
    +

    Set the attributes of a vector set element

    +Redis::vgetattr() — Method in class Redis
    +

    Get the attributes of a vector set element

    +Redis::vlinks() — Method in class Redis
    +

    Get any adajcent values for a member of a vector set.

    +RedisArray::vadd() — Method in class RedisArray
    +
    +RedisArray::vsim() — Method in class RedisArray
    +
    +RedisArray::vcard() — Method in class RedisArray
    +
    +RedisArray::vdim() — Method in class RedisArray
    +
    +RedisArray::vinfo() — Method in class RedisArray
    +
    +RedisArray::vismember() — Method in class RedisArray
    +
    +RedisArray::vemb() — Method in class RedisArray
    +
    +RedisArray::vrandmember() — Method in class RedisArray
    +
    +RedisArray::vrange() — Method in class RedisArray
    +
    +RedisArray::vrem() — Method in class RedisArray
    +
    +RedisArray::vsetattr() — Method in class RedisArray
    +
    +RedisArray::vgetattr() — Method in class RedisArray
    +
    +RedisArray::vlinks() — Method in class RedisArray
    +
    +RedisCluster::vadd() — Method in class RedisCluster
    +
    +RedisCluster::vsim() — Method in class RedisCluster
    +
    +RedisCluster::vcard() — Method in class RedisCluster
    +
    +RedisCluster::vdim() — Method in class RedisCluster
    +
    +RedisCluster::vinfo() — Method in class RedisCluster
    +
    +RedisCluster::vismember() — Method in class RedisCluster
    +

    Check if an element is a member of a vectorset

    +RedisCluster::vemb() — Method in class RedisCluster
    +
    +RedisCluster::vrandmember() — Method in class RedisCluster
    +
    +RedisCluster::vrange() — Method in class RedisCluster
    +

    Retreive a lexographical range of elements from a vector set

    +RedisCluster::vrem() — Method in class RedisCluster
    +
    +RedisCluster::vlinks() — Method in class RedisCluster
    +
    +RedisCluster::vgetattr() — Method in class RedisCluster
    +
    +RedisCluster::vsetattr() — Method in class RedisCluster

    W

    -
    +
    +Redis::waitaof() — Method in class Redis
    +
    Redis::watch() — Method in class Redis

    Watch one or more keys for conditional execution of a transaction.

    Redis::wait() — Method in class Redis

    Block the client up to the provided timeout until a certain number of replicas have confirmed -recieving them.

    +receiving them.

    +RedisArray::waitaof() — Method in class RedisArray
    +
    +RedisArray::watch() — Method in class RedisArray
    +
    +RedisArray::wait() — Method in class RedisArray
    +
    +RedisCluster::wait() — Method in class RedisCluster
    +
    +RedisCluster::waitaof() — Method in class RedisCluster
    +
    RedisCluster::watch() — Method in class RedisCluster

    X

    Redis::xack() — Method in class Redis
    -

    Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but +

    Acknowledge one or more messages that are pending (have been consumed using XREADGROUP but not yet acknowledged by XACK.)

    Redis::xadd() — Method in class Redis

    Append a message to a stream.

    @@ -944,7 +1938,9 @@

    A

    This method allows a consumer to take ownership of pending stream entries, by ID. Another command that does much the same thing but does not require passing specific IDs is Redis::xAutoClaim.

    Redis::xdel() — Method in class Redis
    -

    Remove one or more specific IDs from a stream.

    +

    Remove one or more specific IDs from a stream.

    +Redis::xdelex() — Method in class Redis
    +

    Remove one or more IDs from a stream with extended options.

    Redis::xgroup() — Method in class Redis
    XGROUP
    Redis::xinfo() — Method in class Redis
    @@ -961,9 +1957,37 @@

    A

    Redis::xreadgroup() — Method in class Redis

    Read one or more messages using a consumer group.

    Redis::xrevrange() — Method in class Redis
    -

    Get a range of entries from a STREAM ke in reverse cronological order.

    +

    Get a range of entries from a STREAM key in reverse chronological order.

    Redis::xtrim() — Method in class Redis
    -

    Truncate a STREAM key in various ways.

    +

    Truncate a STREAM key in various ways.

    +RedisArray::xack() — Method in class RedisArray
    +
    +RedisArray::xadd() — Method in class RedisArray
    +
    +RedisArray::xautoclaim() — Method in class RedisArray
    +
    +RedisArray::xclaim() — Method in class RedisArray
    +
    +RedisArray::xdel() — Method in class RedisArray
    +
    +RedisArray::xdelex() — Method in class RedisArray
    +
    +RedisArray::xinfo() — Method in class RedisArray
    +
    +RedisArray::xlen() — Method in class RedisArray
    +
    +RedisArray::xpending() — Method in class RedisArray
    +
    +RedisArray::xrange() — Method in class RedisArray
    +
    +RedisArray::xread() — Method in class RedisArray
    +
    +RedisArray::xreadgroup() — Method in class RedisArray
    +
    +RedisArray::xrevrange() — Method in class RedisArray
    +
    +RedisArray::xtrim() — Method in class RedisArray
    +
    RedisCluster::xack() — Method in class RedisCluster
    RedisCluster::xadd() — Method in class RedisCluster
    @@ -971,6 +1995,8 @@

    A

    RedisCluster::xclaim() — Method in class RedisCluster
    RedisCluster::xdel() — Method in class RedisCluster
    +
    +RedisCluster::xdelex() — Method in class RedisCluster
    RedisCluster::xgroup() — Method in class RedisCluster
    @@ -1004,7 +2030,7 @@

    A

    Redis::zIncrBy
    () — Method in class Redis

    Create or increment the score of a member in a Redis sorted set

    Redis::zLexCount() — Method in class Redis
    -

    Count the number of elements in a sorted set whos members fall within the provided +

    Count the number of elements in a sorted set whose members fall within the provided lexographical range.

    Redis::zMscore() — Method in class Redis

    Retrieve the score of one or more members in a sorted set.

    @@ -1054,7 +2080,7 @@

    A

    Similar to ZINTER but instead of returning the intersected values, this command returns the cardinality of the intersected set.

    Redis::zinterstore() — Method in class Redis
    -

    Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

    +

    Compute the intersection of one or more sorted sets storing the result in a new sorted set.

    Redis::zscan() — Method in class Redis

    Scan the members of a sorted set incrementally, using a cursor

    Redis::zunion() — Method in class Redis
    @@ -1062,6 +2088,66 @@

    A

    Redis::zunionstore() — Method in class Redis

    Perform a union on one or more Redis sets and store the result in a destination sorted set.

    RedisArray::zscan() — Method in class RedisArray
    +
    +RedisArray::zmpop() — Method in class RedisArray
    +
    +RedisArray::zAdd() — Method in class RedisArray
    +
    +RedisArray::zCard() — Method in class RedisArray
    +
    +RedisArray::zCount() — Method in class RedisArray
    +
    +RedisArray::zIncrBy() — Method in class RedisArray
    +
    +RedisArray::zLexCount() — Method in class RedisArray
    +
    +RedisArray::zMscore() — Method in class RedisArray
    +
    +RedisArray::zPopMax() — Method in class RedisArray
    +
    +RedisArray::zPopMin() — Method in class RedisArray
    +
    +RedisArray::zRange() — Method in class RedisArray
    +
    +RedisArray::zRangeByLex() — Method in class RedisArray
    +
    +RedisArray::zRangeByScore() — Method in class RedisArray
    +
    +RedisArray::zRandMember() — Method in class RedisArray
    +
    +RedisArray::zRank() — Method in class RedisArray
    +
    +RedisArray::zRem() — Method in class RedisArray
    +
    +RedisArray::zRemRangeByLex() — Method in class RedisArray
    +
    +RedisArray::zRemRangeByRank() — Method in class RedisArray
    +
    +RedisArray::zRemRangeByScore() — Method in class RedisArray
    +
    +RedisArray::zRevRange() — Method in class RedisArray
    +
    +RedisArray::zRevRangeByLex() — Method in class RedisArray
    +
    +RedisArray::zRevRangeByScore() — Method in class RedisArray
    +
    +RedisArray::zRevRank() — Method in class RedisArray
    +
    +RedisArray::zScore() — Method in class RedisArray
    +
    +RedisArray::zdiff() — Method in class RedisArray
    +
    +RedisArray::zdiffstore() — Method in class RedisArray
    +
    +RedisArray::zinter() — Method in class RedisArray
    +
    +RedisArray::zintercard() — Method in class RedisArray
    +
    +RedisArray::zinterstore() — Method in class RedisArray
    +
    +RedisArray::zunion() — Method in class RedisArray
    +
    +RedisArray::zunionstore() — Method in class RedisArray
    RedisCluster::zmpop() — Method in class RedisCluster
    @@ -1086,6 +2172,8 @@

    A

    RedisCluster::zrange
    () — Method in class RedisCluster
    RedisCluster::zrangestore() — Method in class RedisCluster
    +
    +RedisCluster::zrandmember() — Method in class RedisCluster
    RedisCluster::zrangebylex() — Method in class RedisCluster
    @@ -1112,8 +2200,18 @@

    A

    RedisCluster::zscan
    () — Method in class RedisCluster
    RedisCluster::zscore() — Method in class RedisCluster
    +
    +RedisCluster::zmscore() — Method in class RedisCluster
    RedisCluster::zunionstore() — Method in class RedisCluster
    +
    +RedisCluster::zinter() — Method in class RedisCluster
    +
    +RedisCluster::zdiffstore() — Method in class RedisCluster
    +
    +RedisCluster::zunion() — Method in class RedisCluster
    +
    +RedisCluster::zdiff() — Method in class RedisCluster

    _

    Redis::__construct() — Method in class Redis
    @@ -1121,13 +2219,13 @@

    A

    options array it is also possible to connect to an instance at the same time.

    Redis::__destruct() — Method in class Redis
    -
    +

    Destructor to clean up the Redis object.

    Redis::_compress() — Method in class Redis
    -

    Compress a value with the currently configured compressor as set with -Redis::setOption().

    +

    Compress a value with the currently configured compressor (Redis::OPT_COMPRESSION) +exactly the same way PhpRedis does before sending data to Redis.

    Redis::_uncompress() — Method in class Redis
    -

    Uncompress the provided argument that has been compressed with the -currently configured compressor as set with Redis::setOption().

    +

    Uncompress the provided argument using the compressor configured via +Redis::setOption() (Redis::OPT_COMPRESSION).

    Redis::_prefix() — Method in class Redis

    Prefix the passed argument with the currently set key prefix as set with Redis::setOption().

    @@ -1138,11 +2236,16 @@

    A

    Unserialize the passed argument with the currently set serializer as set with Redis::setOption().

    Redis::_pack() — Method in class Redis
    -

    Pack the provided value with the configured serializer and compressor -as set with Redis::setOption().

    +

    Pack the provided value by first serializing it (if Redis::OPT_SERIALIZER is set) +and then compressing the serialized payload (if Redis::OPT_COMPRESSION is set), +mirroring exactly what PhpRedis transmits to Redis.

    +Redis::_digest() — Method in class Redis
    +

    Compute the XXH3 digest of a PHP value after it has been _packed, producing +the same digest Redis' DIGEST command would return for the stored value.

    Redis::_unpack() — Method in class Redis
    -

    Unpack the provided value with the configured compressor and serializer -as set with Redis::setOption().

    +

    Unpack the provided value by first uncompressing it (if Redis::OPT_COMPRESSION +is set) and then unserializing it (if Redis::OPT_SERIALIZER is set) to recover +the original PHP value.

    RedisArray::__call() — Method in class RedisArray
    RedisArray::__construct() — Method in class RedisArray
    @@ -1164,7 +2267,7 @@

    A

    RedisCluster::__construct() — Method in class RedisCluster
    RedisCluster::_compress() — Method in class RedisCluster
    -
    +

    {\Redis::_compress()}

    RedisCluster::_uncompress() — Method in class RedisCluster
    RedisCluster::_serialize() — Method in class RedisCluster
    @@ -1172,6 +2275,8 @@

    A

    RedisCluster::_unserialize() — Method in class RedisCluster
    RedisCluster::_pack() — Method in class RedisCluster
    +
    +RedisCluster::_digest() — Method in class RedisCluster
    RedisCluster::_unpack() — Method in class RedisCluster
    diff --git a/docs/doctum-search.json b/docs/doctum-search.json index 20e66a1b42..1d0ec60f4b 100644 --- a/docs/doctum-search.json +++ b/docs/doctum-search.json @@ -1 +1 @@ -{"items":[{"t":"C","n":"Redis","p":"Redis.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisException","p":"RedisException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"Redis::__construct","p":"Redis.html#method___construct","d":"

    Create a new Redis instance. If passed sufficient information in the\noptions array it is also possible to connect to an instance at the same\ntime.

    "},{"t":"M","n":"Redis::__destruct","p":"Redis.html#method___destruct","d":null},{"t":"M","n":"Redis::_compress","p":"Redis.html#method__compress","d":"

    Compress a value with the currently configured compressor as set with\nRedis::setOption().

    "},{"t":"M","n":"Redis::_uncompress","p":"Redis.html#method__uncompress","d":"

    Uncompress the provided argument that has been compressed with the\ncurrently configured compressor as set with Redis::setOption().

    "},{"t":"M","n":"Redis::_prefix","p":"Redis.html#method__prefix","d":"

    Prefix the passed argument with the currently set key prefix as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_serialize","p":"Redis.html#method__serialize","d":"

    Serialize the provided value with the currently set serializer as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_unserialize","p":"Redis.html#method__unserialize","d":"

    Unserialize the passed argument with the currently set serializer as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_pack","p":"Redis.html#method__pack","d":"

    Pack the provided value with the configured serializer and compressor\nas set with Redis::setOption().

    "},{"t":"M","n":"Redis::_unpack","p":"Redis.html#method__unpack","d":"

    Unpack the provided value with the configured compressor and serializer\nas set with Redis::setOption().

    "},{"t":"M","n":"Redis::acl","p":"Redis.html#method_acl","d":null},{"t":"M","n":"Redis::append","p":"Redis.html#method_append","d":"

    Append data to a Redis STRING key.

    "},{"t":"M","n":"Redis::auth","p":"Redis.html#method_auth","d":"

    Authenticate a Redis connection after its been established.

    "},{"t":"M","n":"Redis::bgSave","p":"Redis.html#method_bgSave","d":"

    Execute a save of the Redis database in the background.

    "},{"t":"M","n":"Redis::bgrewriteaof","p":"Redis.html#method_bgrewriteaof","d":"

    Asynchronously rewrite Redis' append-only file

    "},{"t":"M","n":"Redis::bitcount","p":"Redis.html#method_bitcount","d":"

    Count the number of set bits in a Redis string.

    "},{"t":"M","n":"Redis::bitop","p":"Redis.html#method_bitop","d":null},{"t":"M","n":"Redis::bitpos","p":"Redis.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"Redis::blPop","p":"Redis.html#method_blPop","d":"

    Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified\ntimeout. This method may be called in two distinct ways, of which examples are provided below.

    "},{"t":"M","n":"Redis::brPop","p":"Redis.html#method_brPop","d":"

    Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

    "},{"t":"M","n":"Redis::brpoplpush","p":"Redis.html#method_brpoplpush","d":"

    Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,\noptionally blocking up to a specified timeout.

    "},{"t":"M","n":"Redis::bzPopMax","p":"Redis.html#method_bzPopMax","d":"

    POP the maximum scoring element off of one or more sorted sets, blocking up to a specified\ntimeout if no elements are available.

    "},{"t":"M","n":"Redis::bzPopMin","p":"Redis.html#method_bzPopMin","d":"

    POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout\nif no elements are available

    "},{"t":"M","n":"Redis::bzmpop","p":"Redis.html#method_bzmpop","d":"

    POP one or more elements from one or more sorted sets, blocking up to a specified amount of time\nwhen no elements are available.

    "},{"t":"M","n":"Redis::zmpop","p":"Redis.html#method_zmpop","d":"

    POP one or more of the highest or lowest scoring elements from one or more sorted sets.

    "},{"t":"M","n":"Redis::blmpop","p":"Redis.html#method_blmpop","d":"

    Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when\nno elements are available.

    "},{"t":"M","n":"Redis::lmpop","p":"Redis.html#method_lmpop","d":"

    Pop one or more elements off of one or more Redis LISTs.

    "},{"t":"M","n":"Redis::clearLastError","p":"Redis.html#method_clearLastError","d":"

    Reset any last error on the connection to NULL

    "},{"t":"M","n":"Redis::client","p":"Redis.html#method_client","d":null},{"t":"M","n":"Redis::close","p":"Redis.html#method_close","d":null},{"t":"M","n":"Redis::command","p":"Redis.html#method_command","d":null},{"t":"M","n":"Redis::config","p":"Redis.html#method_config","d":"

    Execute the Redis CONFIG command in a variety of ways.

    "},{"t":"M","n":"Redis::connect","p":"Redis.html#method_connect","d":null},{"t":"M","n":"Redis::copy","p":"Redis.html#method_copy","d":"

    Make a copy of a key.

    "},{"t":"M","n":"Redis::dbSize","p":"Redis.html#method_dbSize","d":"

    Return the number of keys in the currently selected Redis database.

    "},{"t":"M","n":"Redis::debug","p":"Redis.html#method_debug","d":null},{"t":"M","n":"Redis::decr","p":"Redis.html#method_decr","d":"

    Decrement a Redis integer by 1 or a provided value.

    "},{"t":"M","n":"Redis::decrBy","p":"Redis.html#method_decrBy","d":"

    Decrement a redis integer by a value

    "},{"t":"M","n":"Redis::del","p":"Redis.html#method_del","d":"

    Delete one or more keys from Redis.

    "},{"t":"M","n":"Redis::delete","p":"Redis.html#method_delete","d":""},{"t":"M","n":"Redis::discard","p":"Redis.html#method_discard","d":"

    Discard a transaction currently in progress.

    "},{"t":"M","n":"Redis::dump","p":"Redis.html#method_dump","d":"

    Dump Redis' internal binary representation of a key.

    "},{"t":"M","n":"Redis::echo","p":"Redis.html#method_echo","d":"

    Have Redis repeat back an arbitrary string to the client.

    "},{"t":"M","n":"Redis::eval","p":"Redis.html#method_eval","d":"

    Execute a LUA script on the redis server.

    "},{"t":"M","n":"Redis::eval_ro","p":"Redis.html#method_eval_ro","d":"

    This is simply the read-only variant of eval, meaning the underlying script\nmay not modify data in redis.

    "},{"t":"M","n":"Redis::evalsha","p":"Redis.html#method_evalsha","d":"

    Execute a LUA script on the server but instead of sending the script, send\nthe SHA1 hash of the script.

    "},{"t":"M","n":"Redis::evalsha_ro","p":"Redis.html#method_evalsha_ro","d":"

    This is simply the read-only variant of evalsha, meaning the underlying script\nmay not modify data in redis.

    "},{"t":"M","n":"Redis::exec","p":"Redis.html#method_exec","d":"

    Execute either a MULTI or PIPELINE block and return the array of replies.

    "},{"t":"M","n":"Redis::exists","p":"Redis.html#method_exists","d":"

    Test if one or more keys exist.

    "},{"t":"M","n":"Redis::expire","p":"Redis.html#method_expire","d":"

    Sets an expiration in seconds on the key in question. If connected to\nredis-server >= 7.0.0 you may send an additional "mode" argument which\nmodifies how the command will execute.

    "},{"t":"M","n":"Redis::expireAt","p":"Redis.html#method_expireAt","d":"

    Set a key to expire at an exact unix timestamp.

    "},{"t":"M","n":"Redis::failover","p":"Redis.html#method_failover","d":null},{"t":"M","n":"Redis::expiretime","p":"Redis.html#method_expiretime","d":"

    Get the expiration of a given key as a unix timestamp

    "},{"t":"M","n":"Redis::pexpiretime","p":"Redis.html#method_pexpiretime","d":"

    Get the expriation timestamp of a given Redis key but in milliseconds.

    "},{"t":"M","n":"Redis::flushAll","p":"Redis.html#method_flushAll","d":"

    Deletes every key in all Redis databases

    "},{"t":"M","n":"Redis::flushDB","p":"Redis.html#method_flushDB","d":"

    Deletes all the keys of the currently selected database.

    "},{"t":"M","n":"Redis::geoadd","p":"Redis.html#method_geoadd","d":"

    Add one or more members to a geospacial sorted set

    "},{"t":"M","n":"Redis::geodist","p":"Redis.html#method_geodist","d":"

    Get the distance between two members of a geospacially encoded sorted set.

    "},{"t":"M","n":"Redis::geohash","p":"Redis.html#method_geohash","d":"

    Retrieve one or more GeoHash encoded strings for members of the set.

    "},{"t":"M","n":"Redis::geopos","p":"Redis.html#method_geopos","d":"

    Return the longitude and lattitude for one or more members of a geospacially encoded sorted set.

    "},{"t":"M","n":"Redis::georadius","p":"Redis.html#method_georadius","d":"

    Retrieve members of a geospacially sorted set that are within a certain radius of a location.

    "},{"t":"M","n":"Redis::georadius_ro","p":"Redis.html#method_georadius_ro","d":"

    A readonly variant of GEORADIUS that may be executed on replicas.

    "},{"t":"M","n":"Redis::georadiusbymember","p":"Redis.html#method_georadiusbymember","d":"

    Similar to GEORADIUS except it uses a member as the center of the query.

    "},{"t":"M","n":"Redis::georadiusbymember_ro","p":"Redis.html#method_georadiusbymember_ro","d":"

    This is the read-only variant of GEORADIUSBYMEMBER that can be run on replicas.

    "},{"t":"M","n":"Redis::geosearch","p":"Redis.html#method_geosearch","d":"

    Search a geospacial sorted set for members in various ways.

    "},{"t":"M","n":"Redis::geosearchstore","p":"Redis.html#method_geosearchstore","d":"

    Search a geospacial sorted set for members within a given area or range, storing the results into\na new set.

    "},{"t":"M","n":"Redis::get","p":"Redis.html#method_get","d":"

    Retrieve a string keys value.

    "},{"t":"M","n":"Redis::getAuth","p":"Redis.html#method_getAuth","d":"

    Get the authentication information on the connection, if any.

    "},{"t":"M","n":"Redis::getBit","p":"Redis.html#method_getBit","d":"

    Get the bit at a given index in a string key.

    "},{"t":"M","n":"Redis::getEx","p":"Redis.html#method_getEx","d":"

    Get the value of a key and optionally set it's expiration.

    "},{"t":"M","n":"Redis::getDBNum","p":"Redis.html#method_getDBNum","d":"

    Get the database number PhpRedis thinks we're connected to.

    "},{"t":"M","n":"Redis::getDel","p":"Redis.html#method_getDel","d":"

    Get a key from Redis and delete it in an atomic operation.

    "},{"t":"M","n":"Redis::getHost","p":"Redis.html#method_getHost","d":"

    Return the host or Unix socket we are connected to.

    "},{"t":"M","n":"Redis::getLastError","p":"Redis.html#method_getLastError","d":"

    Get the last error returned to us from Redis, if any.

    "},{"t":"M","n":"Redis::getMode","p":"Redis.html#method_getMode","d":"

    Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

    "},{"t":"M","n":"Redis::getOption","p":"Redis.html#method_getOption","d":"

    Retrieve the value of a configuration setting as set by Redis::setOption()

    "},{"t":"M","n":"Redis::getPersistentID","p":"Redis.html#method_getPersistentID","d":"

    Get the persistent connection ID, if there is one.

    "},{"t":"M","n":"Redis::getPort","p":"Redis.html#method_getPort","d":"

    Get the port we are connected to. This number will be zero if we are connected to a unix socket.

    "},{"t":"M","n":"Redis::getRange","p":"Redis.html#method_getRange","d":"

    Retrieve a substring of a string by index.

    "},{"t":"M","n":"Redis::lcs","p":"Redis.html#method_lcs","d":"

    Get the longest common subsequence between two string keys.

    "},{"t":"M","n":"Redis::getReadTimeout","p":"Redis.html#method_getReadTimeout","d":"

    Get the currently set read timeout on the connection.

    "},{"t":"M","n":"Redis::getset","p":"Redis.html#method_getset","d":"

    Sets a key and returns any previously set value, if the key already existed.

    "},{"t":"M","n":"Redis::getTimeout","p":"Redis.html#method_getTimeout","d":"

    Retrieve any set connection timeout

    "},{"t":"M","n":"Redis::getTransferredBytes","p":"Redis.html#method_getTransferredBytes","d":null},{"t":"M","n":"Redis::hDel","p":"Redis.html#method_hDel","d":"

    Remove one or more fields from a hash.

    "},{"t":"M","n":"Redis::hExists","p":"Redis.html#method_hExists","d":"

    Checks whether a field exists in a hash.

    "},{"t":"M","n":"Redis::hGet","p":"Redis.html#method_hGet","d":null},{"t":"M","n":"Redis::hGetAll","p":"Redis.html#method_hGetAll","d":"

    Read every field and value from a hash.

    "},{"t":"M","n":"Redis::hIncrBy","p":"Redis.html#method_hIncrBy","d":"

    Increment a hash field's value by an integer

    "},{"t":"M","n":"Redis::hIncrByFloat","p":"Redis.html#method_hIncrByFloat","d":"

    Increment a hash field by a floating point value

    "},{"t":"M","n":"Redis::hKeys","p":"Redis.html#method_hKeys","d":"

    Retrieve all of the fields of a hash.

    "},{"t":"M","n":"Redis::hLen","p":"Redis.html#method_hLen","d":"

    Get the number of fields in a hash.

    "},{"t":"M","n":"Redis::hMget","p":"Redis.html#method_hMget","d":"

    Get one or more fields from a hash.

    "},{"t":"M","n":"Redis::hMset","p":"Redis.html#method_hMset","d":"

    Add or update one or more hash fields and values

    "},{"t":"M","n":"Redis::hRandField","p":"Redis.html#method_hRandField","d":"

    Get one or more random field from a hash.

    "},{"t":"M","n":"Redis::hSet","p":"Redis.html#method_hSet","d":null},{"t":"M","n":"Redis::hSetNx","p":"Redis.html#method_hSetNx","d":"

    Set a hash field and value, but only if that field does not exist

    "},{"t":"M","n":"Redis::hStrLen","p":"Redis.html#method_hStrLen","d":"

    Get the string length of a hash field

    "},{"t":"M","n":"Redis::hVals","p":"Redis.html#method_hVals","d":"

    Get all of the values from a hash.

    "},{"t":"M","n":"Redis::hscan","p":"Redis.html#method_hscan","d":"

    Iterate over the fields and values of a hash in an incremental fashion.

    "},{"t":"M","n":"Redis::incr","p":"Redis.html#method_incr","d":"

    Increment a key's value, optionally by a specifc amount.

    "},{"t":"M","n":"Redis::incrBy","p":"Redis.html#method_incrBy","d":"

    Increment a key by a specific integer value

    "},{"t":"M","n":"Redis::incrByFloat","p":"Redis.html#method_incrByFloat","d":"

    Increment a numeric key by a floating point value.

    "},{"t":"M","n":"Redis::info","p":"Redis.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"Redis::isConnected","p":"Redis.html#method_isConnected","d":"

    Check if we are currently connected to a Redis instance.

    "},{"t":"M","n":"Redis::keys","p":"Redis.html#method_keys","d":""},{"t":"M","n":"Redis::lInsert","p":"Redis.html#method_lInsert","d":""},{"t":"M","n":"Redis::lLen","p":"Redis.html#method_lLen","d":"

    Retrieve the lenght of a list.

    "},{"t":"M","n":"Redis::lMove","p":"Redis.html#method_lMove","d":"

    Move an element from one list into another.

    "},{"t":"M","n":"Redis::lPop","p":"Redis.html#method_lPop","d":"

    Pop one or more elements off a list.

    "},{"t":"M","n":"Redis::lPos","p":"Redis.html#method_lPos","d":"

    Retrieve the index of an element in a list.

    "},{"t":"M","n":"Redis::lPush","p":"Redis.html#method_lPush","d":"

    Prepend one or more elements to a list.

    "},{"t":"M","n":"Redis::rPush","p":"Redis.html#method_rPush","d":"

    Append one or more elements to a list.

    "},{"t":"M","n":"Redis::lPushx","p":"Redis.html#method_lPushx","d":"

    Prepend an element to a list but only if the list exists

    "},{"t":"M","n":"Redis::rPushx","p":"Redis.html#method_rPushx","d":"

    Append an element to a list but only if the list exists

    "},{"t":"M","n":"Redis::lSet","p":"Redis.html#method_lSet","d":"

    Set a list element at an index to a specific value.

    "},{"t":"M","n":"Redis::lastSave","p":"Redis.html#method_lastSave","d":"

    Retrieve the last time Redis' database was persisted to disk.

    "},{"t":"M","n":"Redis::lindex","p":"Redis.html#method_lindex","d":"

    Get the element of a list by its index.

    "},{"t":"M","n":"Redis::lrange","p":"Redis.html#method_lrange","d":"

    Retrieve elements from a list.

    "},{"t":"M","n":"Redis::lrem","p":"Redis.html#method_lrem","d":"

    Remove one or more matching elements from a list.

    "},{"t":"M","n":"Redis::ltrim","p":"Redis.html#method_ltrim","d":"

    Trim a list to a subrange of elements.

    "},{"t":"M","n":"Redis::mget","p":"Redis.html#method_mget","d":"

    Get one ore more string keys.

    "},{"t":"M","n":"Redis::migrate","p":"Redis.html#method_migrate","d":null},{"t":"M","n":"Redis::move","p":"Redis.html#method_move","d":"

    Move a key to a different database on the same redis instance.

    "},{"t":"M","n":"Redis::mset","p":"Redis.html#method_mset","d":"

    Set one ore more string keys.

    "},{"t":"M","n":"Redis::msetnx","p":"Redis.html#method_msetnx","d":"

    Set one ore more string keys but only if none of the key exist.

    "},{"t":"M","n":"Redis::multi","p":"Redis.html#method_multi","d":"

    Begin a transaction.

    "},{"t":"M","n":"Redis::object","p":"Redis.html#method_object","d":null},{"t":"M","n":"Redis::open","p":"Redis.html#method_open","d":""},{"t":"M","n":"Redis::pconnect","p":"Redis.html#method_pconnect","d":null},{"t":"M","n":"Redis::persist","p":"Redis.html#method_persist","d":"

    Remove the expiration from a key.

    "},{"t":"M","n":"Redis::pexpire","p":"Redis.html#method_pexpire","d":"

    Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0\nyou can pass an optional mode argument that modifies how the command will execute.

    "},{"t":"M","n":"Redis::pexpireAt","p":"Redis.html#method_pexpireAt","d":"

    Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to\nRedis >= 7.0.0 you can pass an optional 'mode' argument.

    "},{"t":"M","n":"Redis::pfadd","p":"Redis.html#method_pfadd","d":"

    Add one or more elements to a Redis HyperLogLog key

    "},{"t":"M","n":"Redis::pfcount","p":"Redis.html#method_pfcount","d":"

    Retrieve the cardinality of a Redis HyperLogLog key.

    "},{"t":"M","n":"Redis::pfmerge","p":"Redis.html#method_pfmerge","d":"

    Merge one or more source HyperLogLog sets into a destination set.

    "},{"t":"M","n":"Redis::ping","p":"Redis.html#method_ping","d":"

    PING the redis server with an optional string argument.

    "},{"t":"M","n":"Redis::pipeline","p":"Redis.html#method_pipeline","d":"

    Enter into pipeline mode.

    "},{"t":"M","n":"Redis::popen","p":"Redis.html#method_popen","d":""},{"t":"M","n":"Redis::psetex","p":"Redis.html#method_psetex","d":"

    Set a key with an expiration time in milliseconds

    "},{"t":"M","n":"Redis::psubscribe","p":"Redis.html#method_psubscribe","d":"

    Subscribe to one or more glob-style patterns

    "},{"t":"M","n":"Redis::pttl","p":"Redis.html#method_pttl","d":"

    Get a keys time to live in milliseconds.

    "},{"t":"M","n":"Redis::publish","p":"Redis.html#method_publish","d":"

    Publish a message to a pubsub channel

    "},{"t":"M","n":"Redis::pubsub","p":"Redis.html#method_pubsub","d":null},{"t":"M","n":"Redis::punsubscribe","p":"Redis.html#method_punsubscribe","d":"

    Unsubscribe from one or more channels by pattern

    "},{"t":"M","n":"Redis::rPop","p":"Redis.html#method_rPop","d":"

    Pop one or more elements from the end of a list.

    "},{"t":"M","n":"Redis::randomKey","p":"Redis.html#method_randomKey","d":"

    Return a random key from the current database

    "},{"t":"M","n":"Redis::rawcommand","p":"Redis.html#method_rawcommand","d":"

    Execute any arbitrary Redis command by name.

    "},{"t":"M","n":"Redis::rename","p":"Redis.html#method_rename","d":"

    Unconditionally rename a key from $old_name to $new_name

    "},{"t":"M","n":"Redis::renameNx","p":"Redis.html#method_renameNx","d":"

    Renames $key_src to $key_dst but only if newkey does not exist.

    "},{"t":"M","n":"Redis::reset","p":"Redis.html#method_reset","d":"

    Reset the state of the connection.

    "},{"t":"M","n":"Redis::restore","p":"Redis.html#method_restore","d":"

    Restore a key by the binary payload generated by the DUMP command.

    "},{"t":"M","n":"Redis::role","p":"Redis.html#method_role","d":"

    Query whether the connected instance is a primary or replica

    "},{"t":"M","n":"Redis::rpoplpush","p":"Redis.html#method_rpoplpush","d":"

    Atomically pop an element off the end of a Redis LIST and push it to the beginning of\nanother.

    "},{"t":"M","n":"Redis::sAdd","p":"Redis.html#method_sAdd","d":"

    Add one or more values to a Redis SET key.

    "},{"t":"M","n":"Redis::sAddArray","p":"Redis.html#method_sAddArray","d":"

    Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but\ninstead of being variadic, takes a single array of values.

    "},{"t":"M","n":"Redis::sDiff","p":"Redis.html#method_sDiff","d":"

    Given one or more Redis SETS, this command returns all of the members from the first\nset that are not in any subsequent set.

    "},{"t":"M","n":"Redis::sDiffStore","p":"Redis.html#method_sDiffStore","d":"

    This method performs the same operation as SDIFF except it stores the resulting diff\nvalues in a specified destination key.

    "},{"t":"M","n":"Redis::sInter","p":"Redis.html#method_sInter","d":"

    Given one or more Redis SET keys, this command will return all of the elements that are\nin every one.

    "},{"t":"M","n":"Redis::sintercard","p":"Redis.html#method_sintercard","d":"

    Compute the intersection of one or more sets and return the cardinality of the result.

    "},{"t":"M","n":"Redis::sInterStore","p":"Redis.html#method_sInterStore","d":"

    Perform the intersection of one or more Redis SETs, storing the result in a destination\nkey, rather than returning them.

    "},{"t":"M","n":"Redis::sMembers","p":"Redis.html#method_sMembers","d":"

    Retrieve every member from a set key.

    "},{"t":"M","n":"Redis::sMisMember","p":"Redis.html#method_sMisMember","d":"

    Check if one or more values are members of a set.

    "},{"t":"M","n":"Redis::sMove","p":"Redis.html#method_sMove","d":"

    Pop a member from one set and push it onto another. This command will create the\ndestination set if it does not currently exist.

    "},{"t":"M","n":"Redis::sPop","p":"Redis.html#method_sPop","d":"

    Remove one or more elements from a set.

    "},{"t":"M","n":"Redis::sRandMember","p":"Redis.html#method_sRandMember","d":"

    Retrieve one or more random members of a set.

    "},{"t":"M","n":"Redis::sUnion","p":"Redis.html#method_sUnion","d":"

    Returns the union of one or more Redis SET keys.

    "},{"t":"M","n":"Redis::sUnionStore","p":"Redis.html#method_sUnionStore","d":"

    Perform a union of one or more Redis SET keys and store the result in a new set

    "},{"t":"M","n":"Redis::save","p":"Redis.html#method_save","d":"

    Persist the Redis database to disk. This command will block the server until the save is\ncompleted. For a nonblocking alternative, see Redis::bgsave().

    "},{"t":"M","n":"Redis::scan","p":"Redis.html#method_scan","d":"

    Incrementally scan the Redis keyspace, with optional pattern and type matching.

    "},{"t":"M","n":"Redis::scard","p":"Redis.html#method_scard","d":"

    Retrieve the number of members in a Redis set.

    "},{"t":"M","n":"Redis::script","p":"Redis.html#method_script","d":"

    An administrative command used to interact with LUA scripts stored on the server.

    "},{"t":"M","n":"Redis::select","p":"Redis.html#method_select","d":"

    Select a specific Redis database.

    "},{"t":"M","n":"Redis::set","p":"Redis.html#method_set","d":"

    Create or set a Redis STRING key to a value.

    "},{"t":"M","n":"Redis::setBit","p":"Redis.html#method_setBit","d":"

    Set a specific bit in a Redis string to zero or one

    "},{"t":"M","n":"Redis::setRange","p":"Redis.html#method_setRange","d":"

    Update or append to a Redis string at a specific starting index

    "},{"t":"M","n":"Redis::setOption","p":"Redis.html#method_setOption","d":"

    Set a configurable option on the Redis object.

    "},{"t":"M","n":"Redis::setex","p":"Redis.html#method_setex","d":"

    Set a Redis STRING key with a specific expiration in seconds.

    "},{"t":"M","n":"Redis::setnx","p":"Redis.html#method_setnx","d":"

    Set a key to a value, but only if that key does not already exist.

    "},{"t":"M","n":"Redis::sismember","p":"Redis.html#method_sismember","d":"

    Check whether a given value is the member of a Redis SET.

    "},{"t":"M","n":"Redis::slaveof","p":"Redis.html#method_slaveof","d":"

    Turn a redis instance into a replica of another or promote a replica\nto a primary.

    "},{"t":"M","n":"Redis::replicaof","p":"Redis.html#method_replicaof","d":"

    Used to turn a Redis instance into a replica of another, or to remove\nreplica status promoting the instance to a primary.

    "},{"t":"M","n":"Redis::touch","p":"Redis.html#method_touch","d":"

    Update one or more keys last modified metadata.

    "},{"t":"M","n":"Redis::slowlog","p":"Redis.html#method_slowlog","d":"

    Interact with Redis' slowlog functionality in various ways, depending\non the value of 'operation'.

    "},{"t":"M","n":"Redis::sort","p":"Redis.html#method_sort","d":"

    Sort the contents of a Redis key in various ways.

    "},{"t":"M","n":"Redis::sort_ro","p":"Redis.html#method_sort_ro","d":"

    This is simply a read-only variant of the sort command

    "},{"t":"M","n":"Redis::sortAsc","p":"Redis.html#method_sortAsc","d":""},{"t":"M","n":"Redis::sortAscAlpha","p":"Redis.html#method_sortAscAlpha","d":""},{"t":"M","n":"Redis::sortDesc","p":"Redis.html#method_sortDesc","d":""},{"t":"M","n":"Redis::sortDescAlpha","p":"Redis.html#method_sortDescAlpha","d":""},{"t":"M","n":"Redis::srem","p":"Redis.html#method_srem","d":"

    Remove one or more values from a Redis SET key.

    "},{"t":"M","n":"Redis::sscan","p":"Redis.html#method_sscan","d":"

    Scan the members of a redis SET key.

    "},{"t":"M","n":"Redis::strlen","p":"Redis.html#method_strlen","d":"

    Retrieve the length of a Redis STRING key.

    "},{"t":"M","n":"Redis::subscribe","p":"Redis.html#method_subscribe","d":"

    Subscribe to one or more Redis pubsub channels.

    "},{"t":"M","n":"Redis::swapdb","p":"Redis.html#method_swapdb","d":"

    Atomically swap two Redis databases so that all of the keys in the source database will\nnow be in the destination database and vice-versa.

    "},{"t":"M","n":"Redis::time","p":"Redis.html#method_time","d":"

    Retrieve the server time from the connected Redis instance.

    "},{"t":"M","n":"Redis::ttl","p":"Redis.html#method_ttl","d":"

    Get the amount of time a Redis key has before it will expire, in seconds.

    "},{"t":"M","n":"Redis::type","p":"Redis.html#method_type","d":"

    Get the type of a given Redis key.

    "},{"t":"M","n":"Redis::unlink","p":"Redis.html#method_unlink","d":"

    Delete one or more keys from the Redis database. Unlike this operation, the actual\ndeletion is asynchronous, meaning it is safe to delete large keys without fear of\nRedis blocking for a long period of time.

    "},{"t":"M","n":"Redis::unsubscribe","p":"Redis.html#method_unsubscribe","d":"

    Unsubscribe from one or more subscribed channels.

    "},{"t":"M","n":"Redis::unwatch","p":"Redis.html#method_unwatch","d":"

    Remove any previously WATCH'ed keys in a transaction.

    "},{"t":"M","n":"Redis::watch","p":"Redis.html#method_watch","d":"

    Watch one or more keys for conditional execution of a transaction.

    "},{"t":"M","n":"Redis::wait","p":"Redis.html#method_wait","d":"

    Block the client up to the provided timeout until a certain number of replicas have confirmed\nrecieving them.

    "},{"t":"M","n":"Redis::xack","p":"Redis.html#method_xack","d":"

    Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but\nnot yet acknowledged by XACK.)

    "},{"t":"M","n":"Redis::xadd","p":"Redis.html#method_xadd","d":"

    Append a message to a stream.

    "},{"t":"M","n":"Redis::xautoclaim","p":"Redis.html#method_xautoclaim","d":"

    This command allows a consumer to claim pending messages that have been idle for a specified period of time.

    "},{"t":"M","n":"Redis::xclaim","p":"Redis.html#method_xclaim","d":"

    This method allows a consumer to take ownership of pending stream entries, by ID. Another\ncommand that does much the same thing but does not require passing specific IDs is Redis::xAutoClaim.

    "},{"t":"M","n":"Redis::xdel","p":"Redis.html#method_xdel","d":"

    Remove one or more specific IDs from a stream.

    "},{"t":"M","n":"Redis::xgroup","p":"Redis.html#method_xgroup","d":"XGROUP"},{"t":"M","n":"Redis::xinfo","p":"Redis.html#method_xinfo","d":"

    Retrieve information about a stream key.

    "},{"t":"M","n":"Redis::xlen","p":"Redis.html#method_xlen","d":"

    Get the number of messages in a Redis STREAM key.

    "},{"t":"M","n":"Redis::xpending","p":"Redis.html#method_xpending","d":"

    Interact with stream messages that have been consumed by a consumer group but not yet\nacknowledged with XACK.

    "},{"t":"M","n":"Redis::xrange","p":"Redis.html#method_xrange","d":"

    Get a range of entries from a STREAM key.

    "},{"t":"M","n":"Redis::xread","p":"Redis.html#method_xread","d":"

    Consume one or more unconsumed elements in one or more streams.

    "},{"t":"M","n":"Redis::xreadgroup","p":"Redis.html#method_xreadgroup","d":"

    Read one or more messages using a consumer group.

    "},{"t":"M","n":"Redis::xrevrange","p":"Redis.html#method_xrevrange","d":"

    Get a range of entries from a STREAM ke in reverse cronological order.

    "},{"t":"M","n":"Redis::xtrim","p":"Redis.html#method_xtrim","d":"

    Truncate a STREAM key in various ways.

    "},{"t":"M","n":"Redis::zAdd","p":"Redis.html#method_zAdd","d":"

    Add one or more elements and scores to a Redis sorted set.

    "},{"t":"M","n":"Redis::zCard","p":"Redis.html#method_zCard","d":"

    Return the number of elements in a sorted set.

    "},{"t":"M","n":"Redis::zCount","p":"Redis.html#method_zCount","d":"

    Count the number of members in a sorted set with scores inside a provided range.

    "},{"t":"M","n":"Redis::zIncrBy","p":"Redis.html#method_zIncrBy","d":"

    Create or increment the score of a member in a Redis sorted set

    "},{"t":"M","n":"Redis::zLexCount","p":"Redis.html#method_zLexCount","d":"

    Count the number of elements in a sorted set whos members fall within the provided\nlexographical range.

    "},{"t":"M","n":"Redis::zMscore","p":"Redis.html#method_zMscore","d":"

    Retrieve the score of one or more members in a sorted set.

    "},{"t":"M","n":"Redis::zPopMax","p":"Redis.html#method_zPopMax","d":"

    Pop one or more of the highest scoring elements from a sorted set.

    "},{"t":"M","n":"Redis::zPopMin","p":"Redis.html#method_zPopMin","d":"

    Pop one or more of the lowest scoring elements from a sorted set.

    "},{"t":"M","n":"Redis::zRange","p":"Redis.html#method_zRange","d":"

    Retrieve a range of elements of a sorted set between a start and end point.

    "},{"t":"M","n":"Redis::zRangeByLex","p":"Redis.html#method_zRangeByLex","d":"

    Retrieve a range of elements from a sorted set by legographical range.

    "},{"t":"M","n":"Redis::zRangeByScore","p":"Redis.html#method_zRangeByScore","d":"

    Retrieve a range of members from a sorted set by their score.

    "},{"t":"M","n":"Redis::zrangestore","p":"Redis.html#method_zrangestore","d":"

    This command is similar to ZRANGE except that instead of returning the values directly\nit will store them in a destination key provided by the user

    "},{"t":"M","n":"Redis::zRandMember","p":"Redis.html#method_zRandMember","d":"

    Retrieve one or more random members from a Redis sorted set.

    "},{"t":"M","n":"Redis::zRank","p":"Redis.html#method_zRank","d":"

    Get the rank of a member of a sorted set, by score.

    "},{"t":"M","n":"Redis::zRem","p":"Redis.html#method_zRem","d":"

    Remove one or more members from a Redis sorted set.

    "},{"t":"M","n":"Redis::zRemRangeByLex","p":"Redis.html#method_zRemRangeByLex","d":"

    Remove zero or more elements from a Redis sorted set by legographical range.

    "},{"t":"M","n":"Redis::zRemRangeByRank","p":"Redis.html#method_zRemRangeByRank","d":"

    Remove one or more members of a sorted set by their rank.

    "},{"t":"M","n":"Redis::zRemRangeByScore","p":"Redis.html#method_zRemRangeByScore","d":"

    Remove one or more members of a sorted set by their score.

    "},{"t":"M","n":"Redis::zRevRange","p":"Redis.html#method_zRevRange","d":"

    List the members of a Redis sorted set in reverse order

    "},{"t":"M","n":"Redis::zRevRangeByLex","p":"Redis.html#method_zRevRangeByLex","d":"

    List members of a Redis sorted set within a legographical range, in reverse order.

    "},{"t":"M","n":"Redis::zRevRangeByScore","p":"Redis.html#method_zRevRangeByScore","d":"

    List elements from a Redis sorted set by score, highest to lowest

    "},{"t":"M","n":"Redis::zRevRank","p":"Redis.html#method_zRevRank","d":"

    Retrieve a member of a sorted set by reverse rank.

    "},{"t":"M","n":"Redis::zScore","p":"Redis.html#method_zScore","d":"

    Get the score of a member of a sorted set.

    "},{"t":"M","n":"Redis::zdiff","p":"Redis.html#method_zdiff","d":"

    Given one or more sorted set key names, return every element that is in the first\nset but not any of the others.

    "},{"t":"M","n":"Redis::zdiffstore","p":"Redis.html#method_zdiffstore","d":"

    Store the difference of one or more sorted sets in a destination sorted set.

    "},{"t":"M","n":"Redis::zinter","p":"Redis.html#method_zinter","d":"

    Compute the intersection of one or more sorted sets and return the members

    "},{"t":"M","n":"Redis::zintercard","p":"Redis.html#method_zintercard","d":"

    Similar to ZINTER but instead of returning the intersected values, this command returns the\ncardinality of the intersected set.

    "},{"t":"M","n":"Redis::zinterstore","p":"Redis.html#method_zinterstore","d":"

    Compute the intersection of one ore more sorted sets storing the result in a new sorted set.

    "},{"t":"M","n":"Redis::zscan","p":"Redis.html#method_zscan","d":"

    Scan the members of a sorted set incrementally, using a cursor

    "},{"t":"M","n":"Redis::zunion","p":"Redis.html#method_zunion","d":"

    Retrieve the union of one or more sorted sets

    "},{"t":"M","n":"Redis::zunionstore","p":"Redis.html#method_zunionstore","d":"

    Perform a union on one or more Redis sets and store the result in a destination sorted set.

    "},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":""},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":"

    See Redis::blpop()

    "},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":"

    See Redis::brpop()

    "},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":"

    See Redis::brpoplpush()

    "},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

    PING an instance in the redis cluster.

    "},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xautoclaim","p":"RedisCluster.html#method_xautoclaim","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} +{"items":[{"t":"C","n":"Redis","p":"Redis.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisArray","p":"RedisArray.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisCluster","p":"RedisCluster.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisClusterException","p":"RedisClusterException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisException","p":"RedisException.html","d":null,"f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"C","n":"RedisSentinel","p":"RedisSentinel.html","d":"","f":{"n":"[Global Namespace]","p":"[Global_Namespace].html"}},{"t":"M","n":"Redis::__construct","p":"Redis.html#method___construct","d":"

    Create a new Redis instance. If passed sufficient information in the\noptions array it is also possible to connect to an instance at the same\ntime.

    "},{"t":"M","n":"Redis::__destruct","p":"Redis.html#method___destruct","d":"

    Destructor to clean up the Redis object.

    "},{"t":"M","n":"Redis::_compress","p":"Redis.html#method__compress","d":"

    Compress a value with the currently configured compressor (Redis::OPT_COMPRESSION)\nexactly the same way PhpRedis does before sending data to Redis.

    "},{"t":"M","n":"Redis::_uncompress","p":"Redis.html#method__uncompress","d":"

    Uncompress the provided argument using the compressor configured via\nRedis::setOption() (Redis::OPT_COMPRESSION).

    "},{"t":"M","n":"Redis::_prefix","p":"Redis.html#method__prefix","d":"

    Prefix the passed argument with the currently set key prefix as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_serialize","p":"Redis.html#method__serialize","d":"

    Serialize the provided value with the currently set serializer as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_unserialize","p":"Redis.html#method__unserialize","d":"

    Unserialize the passed argument with the currently set serializer as set\nwith Redis::setOption().

    "},{"t":"M","n":"Redis::_pack","p":"Redis.html#method__pack","d":"

    Pack the provided value by first serializing it (if Redis::OPT_SERIALIZER is set)\nand then compressing the serialized payload (if Redis::OPT_COMPRESSION is set),\nmirroring exactly what PhpRedis transmits to Redis.

    "},{"t":"M","n":"Redis::_digest","p":"Redis.html#method__digest","d":"

    Compute the XXH3 digest of a PHP value after it has been _packed, producing\nthe same digest Redis' DIGEST command would return for the stored value.

    "},{"t":"M","n":"Redis::_unpack","p":"Redis.html#method__unpack","d":"

    Unpack the provided value by first uncompressing it (if Redis::OPT_COMPRESSION\nis set) and then unserializing it (if Redis::OPT_SERIALIZER is set) to recover\nthe original PHP value.

    "},{"t":"M","n":"Redis::acl","p":"Redis.html#method_acl","d":"

    Execute Redis ACL subcommands.

    "},{"t":"M","n":"Redis::append","p":"Redis.html#method_append","d":"

    Append data to a Redis STRING key.

    "},{"t":"M","n":"Redis::auth","p":"Redis.html#method_auth","d":"

    Authenticate a Redis connection after its been established.

    "},{"t":"M","n":"Redis::bgSave","p":"Redis.html#method_bgSave","d":"

    Execute a save of the Redis database in the background.

    "},{"t":"M","n":"Redis::bgrewriteaof","p":"Redis.html#method_bgrewriteaof","d":"

    Asynchronously rewrite Redis' append-only file

    "},{"t":"M","n":"Redis::waitaof","p":"Redis.html#method_waitaof","d":""},{"t":"M","n":"Redis::bitcount","p":"Redis.html#method_bitcount","d":"

    Count the number of set bits in a Redis string.

    "},{"t":"M","n":"Redis::bitop","p":"Redis.html#method_bitop","d":null},{"t":"M","n":"Redis::bitpos","p":"Redis.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"Redis::blPop","p":"Redis.html#method_blPop","d":"

    Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified\ntimeout. This method may be called in two distinct ways, of which examples are provided below.

    "},{"t":"M","n":"Redis::brPop","p":"Redis.html#method_brPop","d":"

    Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout.

    "},{"t":"M","n":"Redis::brpoplpush","p":"Redis.html#method_brpoplpush","d":"

    Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list,\noptionally blocking up to a specified timeout.

    "},{"t":"M","n":"Redis::bzPopMax","p":"Redis.html#method_bzPopMax","d":"

    POP the maximum scoring element off of one or more sorted sets, blocking up to a specified\ntimeout if no elements are available.

    "},{"t":"M","n":"Redis::bzPopMin","p":"Redis.html#method_bzPopMin","d":"

    POP the minimum scoring element off of one or more sorted sets, blocking up to a specified timeout\nif no elements are available

    "},{"t":"M","n":"Redis::bzmpop","p":"Redis.html#method_bzmpop","d":"

    POP one or more elements from one or more sorted sets, blocking up to a specified amount of time\nwhen no elements are available.

    "},{"t":"M","n":"Redis::zmpop","p":"Redis.html#method_zmpop","d":"

    POP one or more of the highest or lowest scoring elements from one or more sorted sets.

    "},{"t":"M","n":"Redis::blmpop","p":"Redis.html#method_blmpop","d":"

    Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when\nno elements are available.

    "},{"t":"M","n":"Redis::lmpop","p":"Redis.html#method_lmpop","d":"

    Pop one or more elements off of one or more Redis LISTs.

    "},{"t":"M","n":"Redis::clearLastError","p":"Redis.html#method_clearLastError","d":"

    Reset any last error on the connection to NULL

    "},{"t":"M","n":"Redis::client","p":"Redis.html#method_client","d":"

    Execute Redis CLIENT subcommands.

    "},{"t":"M","n":"Redis::close","p":"Redis.html#method_close","d":"

    Closes the connection to Redis

    "},{"t":"M","n":"Redis::command","p":"Redis.html#method_command","d":"

    Execute Redis COMMAND subcommands.

    "},{"t":"M","n":"Redis::config","p":"Redis.html#method_config","d":"

    Execute the Redis CONFIG command in a variety of ways.

    "},{"t":"M","n":"Redis::connect","p":"Redis.html#method_connect","d":"

    Connect to a Redis server

    "},{"t":"M","n":"Redis::copy","p":"Redis.html#method_copy","d":"

    Make a copy of a key.

    "},{"t":"M","n":"Redis::dbSize","p":"Redis.html#method_dbSize","d":"

    Return the number of keys in the currently selected Redis database.

    "},{"t":"M","n":"Redis::debug","p":"Redis.html#method_debug","d":"

    Execute the Redis DEBUG command. Note that this is disabled by default\nand can be very dangerous, even allowing you to crash the server. Use\nwith caution

    "},{"t":"M","n":"Redis::decr","p":"Redis.html#method_decr","d":"

    Decrement a Redis integer by 1 or a provided value.

    "},{"t":"M","n":"Redis::decrBy","p":"Redis.html#method_decrBy","d":"

    Decrement a redis integer by a value

    "},{"t":"M","n":"Redis::del","p":"Redis.html#method_del","d":"

    Delete one or more keys from Redis.

    "},{"t":"M","n":"Redis::delex","p":"Redis.html#method_delex","d":"

    Delete a key conditionally based on its value or hash digest

    "},{"t":"M","n":"Redis::delifeq","p":"Redis.html#method_delifeq","d":"

    Delete a key if it's equal to the specified value. This command is\nspecific to Valkey >= 9.0

    "},{"t":"M","n":"Redis::delete","p":"Redis.html#method_delete","d":""},{"t":"M","n":"Redis::discard","p":"Redis.html#method_discard","d":"

    Discard a transaction currently in progress.

    "},{"t":"M","n":"Redis::dump","p":"Redis.html#method_dump","d":"

    Dump Redis' internal binary representation of a key.

    "},{"t":"M","n":"Redis::echo","p":"Redis.html#method_echo","d":"

    Have Redis repeat back an arbitrary string to the client.

    "},{"t":"M","n":"Redis::eval","p":"Redis.html#method_eval","d":"

    Execute a LUA script on the redis server.

    "},{"t":"M","n":"Redis::eval_ro","p":"Redis.html#method_eval_ro","d":"

    This is simply the read-only variant of eval, meaning the underlying script\nmay not modify data in redis.

    "},{"t":"M","n":"Redis::evalsha","p":"Redis.html#method_evalsha","d":"

    Execute a LUA script on the server but instead of sending the script, send\nthe SHA1 hash of the script.

    "},{"t":"M","n":"Redis::evalsha_ro","p":"Redis.html#method_evalsha_ro","d":"

    This is simply the read-only variant of evalsha, meaning the underlying script\nmay not modify data in redis.

    "},{"t":"M","n":"Redis::exec","p":"Redis.html#method_exec","d":"

    Execute either a MULTI or PIPELINE block and return the array of replies.

    "},{"t":"M","n":"Redis::exists","p":"Redis.html#method_exists","d":"

    Test if one or more keys exist.

    "},{"t":"M","n":"Redis::expire","p":"Redis.html#method_expire","d":"

    Sets an expiration in seconds on the key in question. If connected to\nredis-server >= 7.0.0 you may send an additional "mode" argument which\nmodifies how the command will execute.

    "},{"t":"M","n":"Redis::expireAt","p":"Redis.html#method_expireAt","d":"

    Set a key to expire at an exact unix timestamp.

    "},{"t":"M","n":"Redis::failover","p":"Redis.html#method_failover","d":null},{"t":"M","n":"Redis::expiretime","p":"Redis.html#method_expiretime","d":"

    Get the expiration of a given key as a unix timestamp

    "},{"t":"M","n":"Redis::pexpiretime","p":"Redis.html#method_pexpiretime","d":"

    Get the expiration timestamp of a given Redis key but in milliseconds.

    "},{"t":"M","n":"Redis::fcall","p":"Redis.html#method_fcall","d":"

    Invoke a function.

    "},{"t":"M","n":"Redis::fcall_ro","p":"Redis.html#method_fcall_ro","d":"

    This is a read-only variant of the FCALL command that cannot execute commands that modify data.

    "},{"t":"M","n":"Redis::flushAll","p":"Redis.html#method_flushAll","d":"

    Deletes every key in all Redis databases

    "},{"t":"M","n":"Redis::flushDB","p":"Redis.html#method_flushDB","d":"

    Deletes all the keys of the currently selected database.

    "},{"t":"M","n":"Redis::function","p":"Redis.html#method_function","d":"

    Functions is an API for managing code to be executed on the server.

    "},{"t":"M","n":"Redis::geoadd","p":"Redis.html#method_geoadd","d":"

    Add one or more members to a geospacial sorted set

    "},{"t":"M","n":"Redis::geodist","p":"Redis.html#method_geodist","d":"

    Get the distance between two members of a geospacially encoded sorted set.

    "},{"t":"M","n":"Redis::geohash","p":"Redis.html#method_geohash","d":"

    Retrieve one or more GeoHash encoded strings for members of the set.

    "},{"t":"M","n":"Redis::geopos","p":"Redis.html#method_geopos","d":"

    Return the longitude and latitude for one or more members of a geospacially encoded sorted set.

    "},{"t":"M","n":"Redis::georadius","p":"Redis.html#method_georadius","d":"

    Retrieve members of a geospacially sorted set that are within a certain radius of a location.

    "},{"t":"M","n":"Redis::georadius_ro","p":"Redis.html#method_georadius_ro","d":"

    A readonly variant of GEORADIUS that may be executed on replicas.

    "},{"t":"M","n":"Redis::georadiusbymember","p":"Redis.html#method_georadiusbymember","d":"

    Similar to GEORADIUS except it uses a member as the center of the query.

    "},{"t":"M","n":"Redis::georadiusbymember_ro","p":"Redis.html#method_georadiusbymember_ro","d":"

    This is the read-only variant of GEORADIUSBYMEMBER that can be run on replicas.

    "},{"t":"M","n":"Redis::geosearch","p":"Redis.html#method_geosearch","d":"

    Search a geospacial sorted set for members in various ways.

    "},{"t":"M","n":"Redis::geosearchstore","p":"Redis.html#method_geosearchstore","d":"

    Search a geospacial sorted set for members within a given area or range, storing the results into\na new set.

    "},{"t":"M","n":"Redis::get","p":"Redis.html#method_get","d":"

    Retrieve a string keys value.

    "},{"t":"M","n":"Redis::getWithMeta","p":"Redis.html#method_getWithMeta","d":"

    Retrieve a value and metadata of key.

    "},{"t":"M","n":"Redis::getAuth","p":"Redis.html#method_getAuth","d":"

    Get the authentication information on the connection, if any.

    "},{"t":"M","n":"Redis::getBit","p":"Redis.html#method_getBit","d":"

    Get the bit at a given index in a string key.

    "},{"t":"M","n":"Redis::getEx","p":"Redis.html#method_getEx","d":"

    Get the value of a key and optionally set it's expiration.

    "},{"t":"M","n":"Redis::getDBNum","p":"Redis.html#method_getDBNum","d":"

    Get the database number PhpRedis thinks we're connected to.

    "},{"t":"M","n":"Redis::getDel","p":"Redis.html#method_getDel","d":"

    Get a key from Redis and delete it in an atomic operation.

    "},{"t":"M","n":"Redis::getHost","p":"Redis.html#method_getHost","d":"

    Return the host or Unix socket we are connected to.

    "},{"t":"M","n":"Redis::getLastError","p":"Redis.html#method_getLastError","d":"

    Get the last error returned to us from Redis, if any.

    "},{"t":"M","n":"Redis::getMode","p":"Redis.html#method_getMode","d":"

    Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode

    "},{"t":"M","n":"Redis::getOption","p":"Redis.html#method_getOption","d":"

    Retrieve the value of a configuration setting as set by Redis::setOption()

    "},{"t":"M","n":"Redis::getPersistentID","p":"Redis.html#method_getPersistentID","d":"

    Get the persistent connection ID, if there is one.

    "},{"t":"M","n":"Redis::getPort","p":"Redis.html#method_getPort","d":"

    Get the port we are connected to. This number will be zero if we are connected to a unix socket.

    "},{"t":"M","n":"Redis::serverName","p":"Redis.html#method_serverName","d":"

    Get the server name as reported by the HELLO response.

    "},{"t":"M","n":"Redis::serverVersion","p":"Redis.html#method_serverVersion","d":"

    Get the server version as reported by the HELLO response.

    "},{"t":"M","n":"Redis::getRange","p":"Redis.html#method_getRange","d":"

    Retrieve a substring of a string by index.

    "},{"t":"M","n":"Redis::lcs","p":"Redis.html#method_lcs","d":"

    Get the longest common subsequence between two string keys.

    "},{"t":"M","n":"Redis::getReadTimeout","p":"Redis.html#method_getReadTimeout","d":"

    Get the currently set read timeout on the connection.

    "},{"t":"M","n":"Redis::getset","p":"Redis.html#method_getset","d":"

    Sets a key and returns any previously set value, if the key already existed.

    "},{"t":"M","n":"Redis::getTimeout","p":"Redis.html#method_getTimeout","d":"

    Retrieve any set connection timeout

    "},{"t":"M","n":"Redis::getTransferredBytes","p":"Redis.html#method_getTransferredBytes","d":"

    Get the number of bytes sent and received on the socket.

    "},{"t":"M","n":"Redis::clearTransferredBytes","p":"Redis.html#method_clearTransferredBytes","d":"

    Reset the number of bytes sent and received on the socket.

    "},{"t":"M","n":"Redis::hDel","p":"Redis.html#method_hDel","d":"

    Remove one or more fields from a hash.

    "},{"t":"M","n":"Redis::hExists","p":"Redis.html#method_hExists","d":"

    Checks whether a field exists in a hash.

    "},{"t":"M","n":"Redis::hGet","p":"Redis.html#method_hGet","d":null},{"t":"M","n":"Redis::hGetAll","p":"Redis.html#method_hGetAll","d":"

    Read every field and value from a hash.

    "},{"t":"M","n":"Redis::hGetWithMeta","p":"Redis.html#method_hGetWithMeta","d":"

    Retrieve a value and metadata of hash field.

    "},{"t":"M","n":"Redis::hIncrBy","p":"Redis.html#method_hIncrBy","d":"

    Increment a hash field's value by an integer

    "},{"t":"M","n":"Redis::hIncrByFloat","p":"Redis.html#method_hIncrByFloat","d":"

    Increment a hash field by a floating point value

    "},{"t":"M","n":"Redis::hKeys","p":"Redis.html#method_hKeys","d":"

    Retrieve all of the fields of a hash.

    "},{"t":"M","n":"Redis::hLen","p":"Redis.html#method_hLen","d":"

    Get the number of fields in a hash.

    "},{"t":"M","n":"Redis::hMget","p":"Redis.html#method_hMget","d":"

    Get one or more fields from a hash.

    "},{"t":"M","n":"Redis::hgetex","p":"Redis.html#method_hgetex","d":"

    Get one or more fields of a hash while optionally setting expiration\ninformation

    "},{"t":"M","n":"Redis::hsetex","p":"Redis.html#method_hsetex","d":"

    Set one or more fields in a hash with optional expiration information.

    "},{"t":"M","n":"Redis::hgetdel","p":"Redis.html#method_hgetdel","d":"

    Get one or more fields and delete them

    "},{"t":"M","n":"Redis::hMset","p":"Redis.html#method_hMset","d":"

    Add or update one or more hash fields and values

    "},{"t":"M","n":"Redis::hRandField","p":"Redis.html#method_hRandField","d":"

    Get one or more random field from a hash.

    "},{"t":"M","n":"Redis::hSet","p":"Redis.html#method_hSet","d":"

    Add or update one or more hash fields and values.

    "},{"t":"M","n":"Redis::hSetNx","p":"Redis.html#method_hSetNx","d":"

    Set a hash field and value, but only if that field does not exist

    "},{"t":"M","n":"Redis::hStrLen","p":"Redis.html#method_hStrLen","d":"

    Get the string length of a hash field

    "},{"t":"M","n":"Redis::hVals","p":"Redis.html#method_hVals","d":"

    Get all of the values from a hash.

    "},{"t":"M","n":"Redis::hexpire","p":"Redis.html#method_hexpire","d":"

    Set the expiration on one or more fields in a hash.

    "},{"t":"M","n":"Redis::hpexpire","p":"Redis.html#method_hpexpire","d":"

    Set the expiration on one or more fields in a hash in milliseconds.

    "},{"t":"M","n":"Redis::hexpireat","p":"Redis.html#method_hexpireat","d":"

    Set the expiration time on one or more fields of a hash.

    "},{"t":"M","n":"Redis::hpexpireat","p":"Redis.html#method_hpexpireat","d":"

    Set the expiration time on one or more fields of a hash in milliseconds.

    "},{"t":"M","n":"Redis::httl","p":"Redis.html#method_httl","d":"

    Get the TTL of one or more fields in a hash

    "},{"t":"M","n":"Redis::hpttl","p":"Redis.html#method_hpttl","d":"

    Get the millisecond TTL of one or more fields in a hash

    "},{"t":"M","n":"Redis::hexpiretime","p":"Redis.html#method_hexpiretime","d":"

    Get the expiration time of one or more fields in a hash

    "},{"t":"M","n":"Redis::hpexpiretime","p":"Redis.html#method_hpexpiretime","d":"

    Get the expiration time in milliseconds of one or more fields in a hash

    "},{"t":"M","n":"Redis::hpersist","p":"Redis.html#method_hpersist","d":"

    Persist one or more hash fields

    "},{"t":"M","n":"Redis::hscan","p":"Redis.html#method_hscan","d":"

    Iterate over the fields and values of a hash in an incremental fashion.

    "},{"t":"M","n":"Redis::expiremember","p":"Redis.html#method_expiremember","d":"

    Set an expiration on a key member (KeyDB only).

    "},{"t":"M","n":"Redis::expirememberat","p":"Redis.html#method_expirememberat","d":"

    Set an expiration on a key membert to a specific unix timestamp (KeyDB only).

    "},{"t":"M","n":"Redis::incr","p":"Redis.html#method_incr","d":"

    Increment a key's value, optionally by a specific amount.

    "},{"t":"M","n":"Redis::incrBy","p":"Redis.html#method_incrBy","d":"

    Increment a key by a specific integer value

    "},{"t":"M","n":"Redis::incrByFloat","p":"Redis.html#method_incrByFloat","d":"

    Increment a numeric key by a floating point value.

    "},{"t":"M","n":"Redis::info","p":"Redis.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"Redis::isConnected","p":"Redis.html#method_isConnected","d":"

    Check if we are currently connected to a Redis instance.

    "},{"t":"M","n":"Redis::keys","p":"Redis.html#method_keys","d":""},{"t":"M","n":"Redis::lInsert","p":"Redis.html#method_lInsert","d":""},{"t":"M","n":"Redis::lLen","p":"Redis.html#method_lLen","d":"

    Retrieve the length of a list.

    "},{"t":"M","n":"Redis::lMove","p":"Redis.html#method_lMove","d":"

    Move an element from one list into another.

    "},{"t":"M","n":"Redis::blmove","p":"Redis.html#method_blmove","d":null},{"t":"M","n":"Redis::lPop","p":"Redis.html#method_lPop","d":"

    Pop one or more elements off a list.

    "},{"t":"M","n":"Redis::lPos","p":"Redis.html#method_lPos","d":"

    Retrieve the index of an element in a list.

    "},{"t":"M","n":"Redis::lPush","p":"Redis.html#method_lPush","d":"

    Prepend one or more elements to a list.

    "},{"t":"M","n":"Redis::rPush","p":"Redis.html#method_rPush","d":"

    Append one or more elements to a list.

    "},{"t":"M","n":"Redis::lPushx","p":"Redis.html#method_lPushx","d":"

    Prepend an element to a list but only if the list exists

    "},{"t":"M","n":"Redis::rPushx","p":"Redis.html#method_rPushx","d":"

    Append an element to a list but only if the list exists

    "},{"t":"M","n":"Redis::lSet","p":"Redis.html#method_lSet","d":"

    Set a list element at an index to a specific value.

    "},{"t":"M","n":"Redis::lastSave","p":"Redis.html#method_lastSave","d":"

    Retrieve the last time Redis' database was persisted to disk.

    "},{"t":"M","n":"Redis::lindex","p":"Redis.html#method_lindex","d":"

    Get the element of a list by its index.

    "},{"t":"M","n":"Redis::lrange","p":"Redis.html#method_lrange","d":"

    Retrieve elements from a list.

    "},{"t":"M","n":"Redis::lrem","p":"Redis.html#method_lrem","d":"

    Remove one or more matching elements from a list.

    "},{"t":"M","n":"Redis::ltrim","p":"Redis.html#method_ltrim","d":"

    Trim a list to a subrange of elements.

    "},{"t":"M","n":"Redis::mget","p":"Redis.html#method_mget","d":"

    Get one or more string keys.

    "},{"t":"M","n":"Redis::migrate","p":"Redis.html#method_migrate","d":"

    Proxy for the Redis MIGRATE command.

    "},{"t":"M","n":"Redis::move","p":"Redis.html#method_move","d":"

    Move a key to a different database on the same redis instance.

    "},{"t":"M","n":"Redis::mset","p":"Redis.html#method_mset","d":"

    Set one or more string keys.

    "},{"t":"M","n":"Redis::msetex","p":"Redis.html#method_msetex","d":"

    Set one or more keys and values with optional expiry information.

    "},{"t":"M","n":"Redis::msetnx","p":"Redis.html#method_msetnx","d":"

    Set one or more string keys but only if none of the key exist.

    "},{"t":"M","n":"Redis::multi","p":"Redis.html#method_multi","d":"

    Begin a transaction.

    "},{"t":"M","n":"Redis::object","p":"Redis.html#method_object","d":"

    Get encoding and other information about a key.

    "},{"t":"M","n":"Redis::open","p":"Redis.html#method_open","d":""},{"t":"M","n":"Redis::pconnect","p":"Redis.html#method_pconnect","d":"

    Connects to a Redis server creating or reusing a persistent connection.

    "},{"t":"M","n":"Redis::persist","p":"Redis.html#method_persist","d":"

    Remove the expiration from a key.

    "},{"t":"M","n":"Redis::pexpire","p":"Redis.html#method_pexpire","d":"

    Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0\nyou can pass an optional mode argument that modifies how the command will execute.

    "},{"t":"M","n":"Redis::pexpireAt","p":"Redis.html#method_pexpireAt","d":"

    Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to\nRedis >= 7.0.0 you can pass an optional 'mode' argument.

    "},{"t":"M","n":"Redis::pfadd","p":"Redis.html#method_pfadd","d":"

    Add one or more elements to a Redis HyperLogLog key

    "},{"t":"M","n":"Redis::pfcount","p":"Redis.html#method_pfcount","d":"

    Retrieve the cardinality of a Redis HyperLogLog key.

    "},{"t":"M","n":"Redis::pfmerge","p":"Redis.html#method_pfmerge","d":"

    Merge one or more source HyperLogLog sets into a destination set.

    "},{"t":"M","n":"Redis::ping","p":"Redis.html#method_ping","d":"

    PING the redis server with an optional string argument.

    "},{"t":"M","n":"Redis::pipeline","p":"Redis.html#method_pipeline","d":"

    Enter into pipeline mode.

    "},{"t":"M","n":"Redis::popen","p":"Redis.html#method_popen","d":""},{"t":"M","n":"Redis::psetex","p":"Redis.html#method_psetex","d":"

    Set a key with an expiration time in milliseconds

    "},{"t":"M","n":"Redis::psubscribe","p":"Redis.html#method_psubscribe","d":"

    Subscribe to one or more glob-style patterns

    "},{"t":"M","n":"Redis::pttl","p":"Redis.html#method_pttl","d":"

    Get a keys time to live in milliseconds.

    "},{"t":"M","n":"Redis::publish","p":"Redis.html#method_publish","d":"

    Publish a message to a pubsub channel

    "},{"t":"M","n":"Redis::pubsub","p":"Redis.html#method_pubsub","d":"

    Interact with the Redis PubSub subsystem.

    "},{"t":"M","n":"Redis::punsubscribe","p":"Redis.html#method_punsubscribe","d":"

    Unsubscribe from one or more channels by pattern

    "},{"t":"M","n":"Redis::rPop","p":"Redis.html#method_rPop","d":"

    Pop one or more elements from the end of a list.

    "},{"t":"M","n":"Redis::randomKey","p":"Redis.html#method_randomKey","d":"

    Return a random key from the current database

    "},{"t":"M","n":"Redis::rawcommand","p":"Redis.html#method_rawcommand","d":"

    Execute any arbitrary Redis command by name.

    "},{"t":"M","n":"Redis::rename","p":"Redis.html#method_rename","d":"

    Unconditionally rename a key from $old_name to $new_name

    "},{"t":"M","n":"Redis::renameNx","p":"Redis.html#method_renameNx","d":"

    Renames $key_src to $key_dst but only if newkey does not exist.

    "},{"t":"M","n":"Redis::reset","p":"Redis.html#method_reset","d":"

    Reset the state of the connection.

    "},{"t":"M","n":"Redis::restore","p":"Redis.html#method_restore","d":"

    Restore a key by the binary payload generated by the DUMP command.

    "},{"t":"M","n":"Redis::role","p":"Redis.html#method_role","d":"

    Query whether the connected instance is a primary or replica

    "},{"t":"M","n":"Redis::rpoplpush","p":"Redis.html#method_rpoplpush","d":"

    Atomically pop an element off the end of a Redis LIST and push it to the beginning of\nanother.

    "},{"t":"M","n":"Redis::sAdd","p":"Redis.html#method_sAdd","d":"

    Add one or more values to a Redis SET key.

    "},{"t":"M","n":"Redis::sAddArray","p":"Redis.html#method_sAddArray","d":"

    Add one or more values to a Redis SET key. This is an alternative to Redis::sadd() but\ninstead of being variadic, takes a single array of values.

    "},{"t":"M","n":"Redis::sDiff","p":"Redis.html#method_sDiff","d":"

    Given one or more Redis SETS, this command returns all of the members from the first\nset that are not in any subsequent set.

    "},{"t":"M","n":"Redis::sDiffStore","p":"Redis.html#method_sDiffStore","d":"

    This method performs the same operation as SDIFF except it stores the resulting diff\nvalues in a specified destination key.

    "},{"t":"M","n":"Redis::sInter","p":"Redis.html#method_sInter","d":"

    Given one or more Redis SET keys, this command will return all of the elements that are\nin every one.

    "},{"t":"M","n":"Redis::sintercard","p":"Redis.html#method_sintercard","d":"

    Compute the intersection of one or more sets and return the cardinality of the result.

    "},{"t":"M","n":"Redis::sInterStore","p":"Redis.html#method_sInterStore","d":"

    Perform the intersection of one or more Redis SETs, storing the result in a destination\nkey, rather than returning them.

    "},{"t":"M","n":"Redis::sMembers","p":"Redis.html#method_sMembers","d":"

    Retrieve every member from a set key.

    "},{"t":"M","n":"Redis::sMisMember","p":"Redis.html#method_sMisMember","d":"

    Check if one or more values are members of a set.

    "},{"t":"M","n":"Redis::sMove","p":"Redis.html#method_sMove","d":"

    Pop a member from one set and push it onto another. This command will create the\ndestination set if it does not currently exist.

    "},{"t":"M","n":"Redis::sPop","p":"Redis.html#method_sPop","d":"

    Remove one or more elements from a set.

    "},{"t":"M","n":"Redis::sRandMember","p":"Redis.html#method_sRandMember","d":"

    Retrieve one or more random members of a set.

    "},{"t":"M","n":"Redis::sUnion","p":"Redis.html#method_sUnion","d":"

    Returns the union of one or more Redis SET keys.

    "},{"t":"M","n":"Redis::sUnionStore","p":"Redis.html#method_sUnionStore","d":"

    Perform a union of one or more Redis SET keys and store the result in a new set

    "},{"t":"M","n":"Redis::save","p":"Redis.html#method_save","d":"

    Persist the Redis database to disk. This command will block the server until the save is\ncompleted. For a nonblocking alternative, see Redis::bgsave().

    "},{"t":"M","n":"Redis::scan","p":"Redis.html#method_scan","d":"

    Incrementally scan the Redis keyspace, with optional pattern and type matching.

    "},{"t":"M","n":"Redis::scard","p":"Redis.html#method_scard","d":"

    Retrieve the number of members in a Redis set.

    "},{"t":"M","n":"Redis::script","p":"Redis.html#method_script","d":"

    An administrative command used to interact with LUA scripts stored on the server.

    "},{"t":"M","n":"Redis::select","p":"Redis.html#method_select","d":"

    Select a specific Redis database.

    "},{"t":"M","n":"Redis::set","p":"Redis.html#method_set","d":"

    Create or set a Redis STRING key to a value.

    "},{"t":"M","n":"Redis::setBit","p":"Redis.html#method_setBit","d":"

    Set a specific bit in a Redis string to zero or one

    "},{"t":"M","n":"Redis::setRange","p":"Redis.html#method_setRange","d":"

    Update or append to a Redis string at a specific starting index

    "},{"t":"M","n":"Redis::setOption","p":"Redis.html#method_setOption","d":"

    Set a configurable option on the Redis object.

    "},{"t":"M","n":"Redis::setex","p":"Redis.html#method_setex","d":"

    Set a Redis STRING key with a specific expiration in seconds.

    "},{"t":"M","n":"Redis::setnx","p":"Redis.html#method_setnx","d":"

    Set a key to a value, but only if that key does not already exist.

    "},{"t":"M","n":"Redis::sismember","p":"Redis.html#method_sismember","d":"

    Check whether a given value is the member of a Redis SET.

    "},{"t":"M","n":"Redis::slaveof","p":"Redis.html#method_slaveof","d":"

    Turn a redis instance into a replica of another or promote a replica\nto a primary.

    "},{"t":"M","n":"Redis::replicaof","p":"Redis.html#method_replicaof","d":"

    Used to turn a Redis instance into a replica of another, or to remove\nreplica status promoting the instance to a primary.

    "},{"t":"M","n":"Redis::touch","p":"Redis.html#method_touch","d":"

    Update one or more keys last modified metadata.

    "},{"t":"M","n":"Redis::slowlog","p":"Redis.html#method_slowlog","d":"

    Interact with Redis' slowlog functionality in various ways, depending\non the value of 'operation'.

    "},{"t":"M","n":"Redis::sort","p":"Redis.html#method_sort","d":"

    Sort the contents of a Redis key in various ways.

    "},{"t":"M","n":"Redis::sort_ro","p":"Redis.html#method_sort_ro","d":"

    This is simply a read-only variant of the sort command

    "},{"t":"M","n":"Redis::sortAsc","p":"Redis.html#method_sortAsc","d":""},{"t":"M","n":"Redis::sortAscAlpha","p":"Redis.html#method_sortAscAlpha","d":""},{"t":"M","n":"Redis::sortDesc","p":"Redis.html#method_sortDesc","d":""},{"t":"M","n":"Redis::sortDescAlpha","p":"Redis.html#method_sortDescAlpha","d":""},{"t":"M","n":"Redis::srem","p":"Redis.html#method_srem","d":"

    Remove one or more values from a Redis SET key.

    "},{"t":"M","n":"Redis::sscan","p":"Redis.html#method_sscan","d":"

    Scan the members of a redis SET key.

    "},{"t":"M","n":"Redis::ssubscribe","p":"Redis.html#method_ssubscribe","d":"

    Subscribes the client to the specified shard channels.

    "},{"t":"M","n":"Redis::strlen","p":"Redis.html#method_strlen","d":"

    Retrieve the length of a Redis STRING key.

    "},{"t":"M","n":"Redis::subscribe","p":"Redis.html#method_subscribe","d":"

    Subscribe to one or more Redis pubsub channels.

    "},{"t":"M","n":"Redis::sunsubscribe","p":"Redis.html#method_sunsubscribe","d":"

    Unsubscribes the client from the given shard channels,\nor from all of them if none is given.

    "},{"t":"M","n":"Redis::swapdb","p":"Redis.html#method_swapdb","d":"

    Atomically swap two Redis databases so that all of the keys in the source database will\nnow be in the destination database and vice-versa.

    "},{"t":"M","n":"Redis::time","p":"Redis.html#method_time","d":"

    Retrieve the server time from the connected Redis instance.

    "},{"t":"M","n":"Redis::ttl","p":"Redis.html#method_ttl","d":"

    Get the amount of time a Redis key has before it will expire, in seconds.

    "},{"t":"M","n":"Redis::type","p":"Redis.html#method_type","d":"

    Get the type of a given Redis key.

    "},{"t":"M","n":"Redis::unlink","p":"Redis.html#method_unlink","d":"

    Delete one or more keys from the Redis database. Unlike this operation, the actual\ndeletion is asynchronous, meaning it is safe to delete large keys without fear of\nRedis blocking for a long period of time.

    "},{"t":"M","n":"Redis::unsubscribe","p":"Redis.html#method_unsubscribe","d":"

    Unsubscribe from one or more subscribed channels.

    "},{"t":"M","n":"Redis::unwatch","p":"Redis.html#method_unwatch","d":"

    Remove any previously WATCH'ed keys in a transaction.

    "},{"t":"M","n":"Redis::watch","p":"Redis.html#method_watch","d":"

    Watch one or more keys for conditional execution of a transaction.

    "},{"t":"M","n":"Redis::wait","p":"Redis.html#method_wait","d":"

    Block the client up to the provided timeout until a certain number of replicas have confirmed\nreceiving them.

    "},{"t":"M","n":"Redis::xack","p":"Redis.html#method_xack","d":"

    Acknowledge one or more messages that are pending (have been consumed using XREADGROUP but\nnot yet acknowledged by XACK.)

    "},{"t":"M","n":"Redis::xadd","p":"Redis.html#method_xadd","d":"

    Append a message to a stream.

    "},{"t":"M","n":"Redis::xautoclaim","p":"Redis.html#method_xautoclaim","d":"

    This command allows a consumer to claim pending messages that have been idle for a specified period of time.

    "},{"t":"M","n":"Redis::xclaim","p":"Redis.html#method_xclaim","d":"

    This method allows a consumer to take ownership of pending stream entries, by ID. Another\ncommand that does much the same thing but does not require passing specific IDs is Redis::xAutoClaim.

    "},{"t":"M","n":"Redis::xdel","p":"Redis.html#method_xdel","d":"

    Remove one or more specific IDs from a stream.

    "},{"t":"M","n":"Redis::xdelex","p":"Redis.html#method_xdelex","d":"

    Remove one or more IDs from a stream with extended options.

    "},{"t":"M","n":"Redis::xgroup","p":"Redis.html#method_xgroup","d":"XGROUP"},{"t":"M","n":"Redis::xinfo","p":"Redis.html#method_xinfo","d":"

    Retrieve information about a stream key.

    "},{"t":"M","n":"Redis::xlen","p":"Redis.html#method_xlen","d":"

    Get the number of messages in a Redis STREAM key.

    "},{"t":"M","n":"Redis::xpending","p":"Redis.html#method_xpending","d":"

    Interact with stream messages that have been consumed by a consumer group but not yet\nacknowledged with XACK.

    "},{"t":"M","n":"Redis::xrange","p":"Redis.html#method_xrange","d":"

    Get a range of entries from a STREAM key.

    "},{"t":"M","n":"Redis::xread","p":"Redis.html#method_xread","d":"

    Consume one or more unconsumed elements in one or more streams.

    "},{"t":"M","n":"Redis::xreadgroup","p":"Redis.html#method_xreadgroup","d":"

    Read one or more messages using a consumer group.

    "},{"t":"M","n":"Redis::xrevrange","p":"Redis.html#method_xrevrange","d":"

    Get a range of entries from a STREAM key in reverse chronological order.

    "},{"t":"M","n":"Redis::vadd","p":"Redis.html#method_vadd","d":"

    Add to a vector set

    "},{"t":"M","n":"Redis::vsim","p":"Redis.html#method_vsim","d":"

    Query similarity of a vector by element or scores

    "},{"t":"M","n":"Redis::vcard","p":"Redis.html#method_vcard","d":"

    Get the length of a vector set

    "},{"t":"M","n":"Redis::vdim","p":"Redis.html#method_vdim","d":"

    Get the dimensions of a vector set

    "},{"t":"M","n":"Redis::vinfo","p":"Redis.html#method_vinfo","d":"

    Get various bits of information about a vector set

    "},{"t":"M","n":"Redis::vismember","p":"Redis.html#method_vismember","d":"

    Check if an element is a member of a vectorset

    "},{"t":"M","n":"Redis::vemb","p":"Redis.html#method_vemb","d":"

    Get the embeddings for a specific member

    "},{"t":"M","n":"Redis::vrandmember","p":"Redis.html#method_vrandmember","d":"

    Get one or more random members from a vector set

    "},{"t":"M","n":"Redis::vrange","p":"Redis.html#method_vrange","d":"

    Retreive a lexographical range of elements from a vector set

    "},{"t":"M","n":"Redis::vrem","p":"Redis.html#method_vrem","d":"

    Remove an element from a vector set

    "},{"t":"M","n":"Redis::vsetattr","p":"Redis.html#method_vsetattr","d":"

    Set the attributes of a vector set element

    "},{"t":"M","n":"Redis::vgetattr","p":"Redis.html#method_vgetattr","d":"

    Get the attributes of a vector set element

    "},{"t":"M","n":"Redis::vlinks","p":"Redis.html#method_vlinks","d":"

    Get any adajcent values for a member of a vector set.

    "},{"t":"M","n":"Redis::gcra","p":"Redis.html#method_gcra","d":"

    Get rate limiting information

    "},{"t":"M","n":"Redis::xtrim","p":"Redis.html#method_xtrim","d":"

    Truncate a STREAM key in various ways.

    "},{"t":"M","n":"Redis::zAdd","p":"Redis.html#method_zAdd","d":"

    Add one or more elements and scores to a Redis sorted set.

    "},{"t":"M","n":"Redis::zCard","p":"Redis.html#method_zCard","d":"

    Return the number of elements in a sorted set.

    "},{"t":"M","n":"Redis::zCount","p":"Redis.html#method_zCount","d":"

    Count the number of members in a sorted set with scores inside a provided range.

    "},{"t":"M","n":"Redis::zIncrBy","p":"Redis.html#method_zIncrBy","d":"

    Create or increment the score of a member in a Redis sorted set

    "},{"t":"M","n":"Redis::zLexCount","p":"Redis.html#method_zLexCount","d":"

    Count the number of elements in a sorted set whose members fall within the provided\nlexographical range.

    "},{"t":"M","n":"Redis::zMscore","p":"Redis.html#method_zMscore","d":"

    Retrieve the score of one or more members in a sorted set.

    "},{"t":"M","n":"Redis::zPopMax","p":"Redis.html#method_zPopMax","d":"

    Pop one or more of the highest scoring elements from a sorted set.

    "},{"t":"M","n":"Redis::zPopMin","p":"Redis.html#method_zPopMin","d":"

    Pop one or more of the lowest scoring elements from a sorted set.

    "},{"t":"M","n":"Redis::zRange","p":"Redis.html#method_zRange","d":"

    Retrieve a range of elements of a sorted set between a start and end point.

    "},{"t":"M","n":"Redis::zRangeByLex","p":"Redis.html#method_zRangeByLex","d":"

    Retrieve a range of elements from a sorted set by legographical range.

    "},{"t":"M","n":"Redis::zRangeByScore","p":"Redis.html#method_zRangeByScore","d":"

    Retrieve a range of members from a sorted set by their score.

    "},{"t":"M","n":"Redis::zrangestore","p":"Redis.html#method_zrangestore","d":"

    This command is similar to ZRANGE except that instead of returning the values directly\nit will store them in a destination key provided by the user

    "},{"t":"M","n":"Redis::zRandMember","p":"Redis.html#method_zRandMember","d":"

    Retrieve one or more random members from a Redis sorted set.

    "},{"t":"M","n":"Redis::zRank","p":"Redis.html#method_zRank","d":"

    Get the rank of a member of a sorted set, by score.

    "},{"t":"M","n":"Redis::zRem","p":"Redis.html#method_zRem","d":"

    Remove one or more members from a Redis sorted set.

    "},{"t":"M","n":"Redis::zRemRangeByLex","p":"Redis.html#method_zRemRangeByLex","d":"

    Remove zero or more elements from a Redis sorted set by legographical range.

    "},{"t":"M","n":"Redis::zRemRangeByRank","p":"Redis.html#method_zRemRangeByRank","d":"

    Remove one or more members of a sorted set by their rank.

    "},{"t":"M","n":"Redis::zRemRangeByScore","p":"Redis.html#method_zRemRangeByScore","d":"

    Remove one or more members of a sorted set by their score.

    "},{"t":"M","n":"Redis::zRevRange","p":"Redis.html#method_zRevRange","d":"

    List the members of a Redis sorted set in reverse order

    "},{"t":"M","n":"Redis::zRevRangeByLex","p":"Redis.html#method_zRevRangeByLex","d":"

    List members of a Redis sorted set within a legographical range, in reverse order.

    "},{"t":"M","n":"Redis::zRevRangeByScore","p":"Redis.html#method_zRevRangeByScore","d":"

    List elements from a Redis sorted set by score, highest to lowest

    "},{"t":"M","n":"Redis::zRevRank","p":"Redis.html#method_zRevRank","d":"

    Retrieve a member of a sorted set by reverse rank.

    "},{"t":"M","n":"Redis::zScore","p":"Redis.html#method_zScore","d":"

    Get the score of a member of a sorted set.

    "},{"t":"M","n":"Redis::zdiff","p":"Redis.html#method_zdiff","d":"

    Given one or more sorted set key names, return every element that is in the first\nset but not any of the others.

    "},{"t":"M","n":"Redis::zdiffstore","p":"Redis.html#method_zdiffstore","d":"

    Store the difference of one or more sorted sets in a destination sorted set.

    "},{"t":"M","n":"Redis::zinter","p":"Redis.html#method_zinter","d":"

    Compute the intersection of one or more sorted sets and return the members

    "},{"t":"M","n":"Redis::zintercard","p":"Redis.html#method_zintercard","d":"

    Similar to ZINTER but instead of returning the intersected values, this command returns the\ncardinality of the intersected set.

    "},{"t":"M","n":"Redis::zinterstore","p":"Redis.html#method_zinterstore","d":"

    Compute the intersection of one or more sorted sets storing the result in a new sorted set.

    "},{"t":"M","n":"Redis::zscan","p":"Redis.html#method_zscan","d":"

    Scan the members of a sorted set incrementally, using a cursor

    "},{"t":"M","n":"Redis::zunion","p":"Redis.html#method_zunion","d":"

    Retrieve the union of one or more sorted sets

    "},{"t":"M","n":"Redis::zunionstore","p":"Redis.html#method_zunionstore","d":"

    Perform a union on one or more Redis sets and store the result in a destination sorted set.

    "},{"t":"M","n":"Redis::digest","p":"Redis.html#method_digest","d":"

    Ask the server for the XXH3 digest of a given key's value

    "},{"t":"M","n":"RedisArray::__call","p":"RedisArray.html#method___call","d":null},{"t":"M","n":"RedisArray::__construct","p":"RedisArray.html#method___construct","d":null},{"t":"M","n":"RedisArray::_continuum","p":"RedisArray.html#method__continuum","d":null},{"t":"M","n":"RedisArray::_distributor","p":"RedisArray.html#method__distributor","d":null},{"t":"M","n":"RedisArray::_function","p":"RedisArray.html#method__function","d":null},{"t":"M","n":"RedisArray::_hosts","p":"RedisArray.html#method__hosts","d":null},{"t":"M","n":"RedisArray::_instance","p":"RedisArray.html#method__instance","d":null},{"t":"M","n":"RedisArray::_rehash","p":"RedisArray.html#method__rehash","d":null},{"t":"M","n":"RedisArray::_target","p":"RedisArray.html#method__target","d":null},{"t":"M","n":"RedisArray::bgsave","p":"RedisArray.html#method_bgsave","d":null},{"t":"M","n":"RedisArray::del","p":"RedisArray.html#method_del","d":null},{"t":"M","n":"RedisArray::discard","p":"RedisArray.html#method_discard","d":null},{"t":"M","n":"RedisArray::exec","p":"RedisArray.html#method_exec","d":null},{"t":"M","n":"RedisArray::flushall","p":"RedisArray.html#method_flushall","d":null},{"t":"M","n":"RedisArray::flushdb","p":"RedisArray.html#method_flushdb","d":null},{"t":"M","n":"RedisArray::getOption","p":"RedisArray.html#method_getOption","d":null},{"t":"M","n":"RedisArray::hscan","p":"RedisArray.html#method_hscan","d":null},{"t":"M","n":"RedisArray::info","p":"RedisArray.html#method_info","d":null},{"t":"M","n":"RedisArray::keys","p":"RedisArray.html#method_keys","d":null},{"t":"M","n":"RedisArray::mget","p":"RedisArray.html#method_mget","d":null},{"t":"M","n":"RedisArray::mset","p":"RedisArray.html#method_mset","d":null},{"t":"M","n":"RedisArray::multi","p":"RedisArray.html#method_multi","d":null},{"t":"M","n":"RedisArray::ping","p":"RedisArray.html#method_ping","d":null},{"t":"M","n":"RedisArray::save","p":"RedisArray.html#method_save","d":null},{"t":"M","n":"RedisArray::scan","p":"RedisArray.html#method_scan","d":null},{"t":"M","n":"RedisArray::select","p":"RedisArray.html#method_select","d":null},{"t":"M","n":"RedisArray::setOption","p":"RedisArray.html#method_setOption","d":null},{"t":"M","n":"RedisArray::sscan","p":"RedisArray.html#method_sscan","d":null},{"t":"M","n":"RedisArray::unlink","p":"RedisArray.html#method_unlink","d":null},{"t":"M","n":"RedisArray::unwatch","p":"RedisArray.html#method_unwatch","d":null},{"t":"M","n":"RedisArray::zscan","p":"RedisArray.html#method_zscan","d":null},{"t":"M","n":"RedisArray::acl","p":"RedisArray.html#method_acl","d":""},{"t":"M","n":"RedisArray::append","p":"RedisArray.html#method_append","d":""},{"t":"M","n":"RedisArray::auth","p":"RedisArray.html#method_auth","d":""},{"t":"M","n":"RedisArray::bgrewriteaof","p":"RedisArray.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisArray::waitaof","p":"RedisArray.html#method_waitaof","d":""},{"t":"M","n":"RedisArray::bitcount","p":"RedisArray.html#method_bitcount","d":""},{"t":"M","n":"RedisArray::bitop","p":"RedisArray.html#method_bitop","d":""},{"t":"M","n":"RedisArray::bitpos","p":"RedisArray.html#method_bitpos","d":""},{"t":"M","n":"RedisArray::blPop","p":"RedisArray.html#method_blPop","d":""},{"t":"M","n":"RedisArray::brPop","p":"RedisArray.html#method_brPop","d":""},{"t":"M","n":"RedisArray::brpoplpush","p":"RedisArray.html#method_brpoplpush","d":""},{"t":"M","n":"RedisArray::bzPopMax","p":"RedisArray.html#method_bzPopMax","d":""},{"t":"M","n":"RedisArray::bzPopMin","p":"RedisArray.html#method_bzPopMin","d":""},{"t":"M","n":"RedisArray::bzmpop","p":"RedisArray.html#method_bzmpop","d":""},{"t":"M","n":"RedisArray::zmpop","p":"RedisArray.html#method_zmpop","d":""},{"t":"M","n":"RedisArray::blmpop","p":"RedisArray.html#method_blmpop","d":""},{"t":"M","n":"RedisArray::lmpop","p":"RedisArray.html#method_lmpop","d":""},{"t":"M","n":"RedisArray::clearLastError","p":"RedisArray.html#method_clearLastError","d":""},{"t":"M","n":"RedisArray::client","p":"RedisArray.html#method_client","d":""},{"t":"M","n":"RedisArray::close","p":"RedisArray.html#method_close","d":""},{"t":"M","n":"RedisArray::command","p":"RedisArray.html#method_command","d":""},{"t":"M","n":"RedisArray::config","p":"RedisArray.html#method_config","d":""},{"t":"M","n":"RedisArray::copy","p":"RedisArray.html#method_copy","d":""},{"t":"M","n":"RedisArray::dbSize","p":"RedisArray.html#method_dbSize","d":""},{"t":"M","n":"RedisArray::debug","p":"RedisArray.html#method_debug","d":""},{"t":"M","n":"RedisArray::decr","p":"RedisArray.html#method_decr","d":""},{"t":"M","n":"RedisArray::decrBy","p":"RedisArray.html#method_decrBy","d":""},{"t":"M","n":"RedisArray::delex","p":"RedisArray.html#method_delex","d":""},{"t":"M","n":"RedisArray::delifeq","p":"RedisArray.html#method_delifeq","d":""},{"t":"M","n":"RedisArray::delete","p":"RedisArray.html#method_delete","d":""},{"t":"M","n":"RedisArray::dump","p":"RedisArray.html#method_dump","d":""},{"t":"M","n":"RedisArray::echo","p":"RedisArray.html#method_echo","d":""},{"t":"M","n":"RedisArray::eval","p":"RedisArray.html#method_eval","d":""},{"t":"M","n":"RedisArray::eval_ro","p":"RedisArray.html#method_eval_ro","d":""},{"t":"M","n":"RedisArray::evalsha","p":"RedisArray.html#method_evalsha","d":""},{"t":"M","n":"RedisArray::evalsha_ro","p":"RedisArray.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisArray::exists","p":"RedisArray.html#method_exists","d":""},{"t":"M","n":"RedisArray::expire","p":"RedisArray.html#method_expire","d":""},{"t":"M","n":"RedisArray::expireAt","p":"RedisArray.html#method_expireAt","d":""},{"t":"M","n":"RedisArray::failover","p":"RedisArray.html#method_failover","d":""},{"t":"M","n":"RedisArray::expiretime","p":"RedisArray.html#method_expiretime","d":""},{"t":"M","n":"RedisArray::pexpiretime","p":"RedisArray.html#method_pexpiretime","d":""},{"t":"M","n":"RedisArray::fcall","p":"RedisArray.html#method_fcall","d":""},{"t":"M","n":"RedisArray::fcall_ro","p":"RedisArray.html#method_fcall_ro","d":""},{"t":"M","n":"RedisArray::function","p":"RedisArray.html#method_function","d":""},{"t":"M","n":"RedisArray::geoadd","p":"RedisArray.html#method_geoadd","d":""},{"t":"M","n":"RedisArray::geodist","p":"RedisArray.html#method_geodist","d":""},{"t":"M","n":"RedisArray::geohash","p":"RedisArray.html#method_geohash","d":""},{"t":"M","n":"RedisArray::geopos","p":"RedisArray.html#method_geopos","d":""},{"t":"M","n":"RedisArray::georadius","p":"RedisArray.html#method_georadius","d":""},{"t":"M","n":"RedisArray::georadius_ro","p":"RedisArray.html#method_georadius_ro","d":""},{"t":"M","n":"RedisArray::georadiusbymember","p":"RedisArray.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisArray::georadiusbymember_ro","p":"RedisArray.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisArray::geosearch","p":"RedisArray.html#method_geosearch","d":""},{"t":"M","n":"RedisArray::geosearchstore","p":"RedisArray.html#method_geosearchstore","d":""},{"t":"M","n":"RedisArray::get","p":"RedisArray.html#method_get","d":""},{"t":"M","n":"RedisArray::getWithMeta","p":"RedisArray.html#method_getWithMeta","d":""},{"t":"M","n":"RedisArray::getAuth","p":"RedisArray.html#method_getAuth","d":""},{"t":"M","n":"RedisArray::getBit","p":"RedisArray.html#method_getBit","d":""},{"t":"M","n":"RedisArray::getEx","p":"RedisArray.html#method_getEx","d":""},{"t":"M","n":"RedisArray::getDBNum","p":"RedisArray.html#method_getDBNum","d":""},{"t":"M","n":"RedisArray::getDel","p":"RedisArray.html#method_getDel","d":""},{"t":"M","n":"RedisArray::getHost","p":"RedisArray.html#method_getHost","d":""},{"t":"M","n":"RedisArray::getLastError","p":"RedisArray.html#method_getLastError","d":""},{"t":"M","n":"RedisArray::getMode","p":"RedisArray.html#method_getMode","d":""},{"t":"M","n":"RedisArray::getPersistentID","p":"RedisArray.html#method_getPersistentID","d":""},{"t":"M","n":"RedisArray::getPort","p":"RedisArray.html#method_getPort","d":""},{"t":"M","n":"RedisArray::serverName","p":"RedisArray.html#method_serverName","d":""},{"t":"M","n":"RedisArray::serverVersion","p":"RedisArray.html#method_serverVersion","d":""},{"t":"M","n":"RedisArray::getRange","p":"RedisArray.html#method_getRange","d":""},{"t":"M","n":"RedisArray::lcs","p":"RedisArray.html#method_lcs","d":""},{"t":"M","n":"RedisArray::getReadTimeout","p":"RedisArray.html#method_getReadTimeout","d":""},{"t":"M","n":"RedisArray::getset","p":"RedisArray.html#method_getset","d":""},{"t":"M","n":"RedisArray::getTimeout","p":"RedisArray.html#method_getTimeout","d":""},{"t":"M","n":"RedisArray::getTransferredBytes","p":"RedisArray.html#method_getTransferredBytes","d":""},{"t":"M","n":"RedisArray::clearTransferredBytes","p":"RedisArray.html#method_clearTransferredBytes","d":""},{"t":"M","n":"RedisArray::hDel","p":"RedisArray.html#method_hDel","d":""},{"t":"M","n":"RedisArray::hExists","p":"RedisArray.html#method_hExists","d":""},{"t":"M","n":"RedisArray::hGet","p":"RedisArray.html#method_hGet","d":""},{"t":"M","n":"RedisArray::hGetAll","p":"RedisArray.html#method_hGetAll","d":""},{"t":"M","n":"RedisArray::hGetWithMeta","p":"RedisArray.html#method_hGetWithMeta","d":""},{"t":"M","n":"RedisArray::hIncrBy","p":"RedisArray.html#method_hIncrBy","d":""},{"t":"M","n":"RedisArray::hIncrByFloat","p":"RedisArray.html#method_hIncrByFloat","d":""},{"t":"M","n":"RedisArray::hKeys","p":"RedisArray.html#method_hKeys","d":""},{"t":"M","n":"RedisArray::hLen","p":"RedisArray.html#method_hLen","d":""},{"t":"M","n":"RedisArray::hMget","p":"RedisArray.html#method_hMget","d":""},{"t":"M","n":"RedisArray::hgetex","p":"RedisArray.html#method_hgetex","d":""},{"t":"M","n":"RedisArray::hsetex","p":"RedisArray.html#method_hsetex","d":""},{"t":"M","n":"RedisArray::hgetdel","p":"RedisArray.html#method_hgetdel","d":""},{"t":"M","n":"RedisArray::hMset","p":"RedisArray.html#method_hMset","d":""},{"t":"M","n":"RedisArray::hRandField","p":"RedisArray.html#method_hRandField","d":""},{"t":"M","n":"RedisArray::hSet","p":"RedisArray.html#method_hSet","d":""},{"t":"M","n":"RedisArray::hSetNx","p":"RedisArray.html#method_hSetNx","d":""},{"t":"M","n":"RedisArray::hStrLen","p":"RedisArray.html#method_hStrLen","d":""},{"t":"M","n":"RedisArray::hVals","p":"RedisArray.html#method_hVals","d":""},{"t":"M","n":"RedisArray::httl","p":"RedisArray.html#method_httl","d":""},{"t":"M","n":"RedisArray::hpttl","p":"RedisArray.html#method_hpttl","d":""},{"t":"M","n":"RedisArray::hexpiretime","p":"RedisArray.html#method_hexpiretime","d":""},{"t":"M","n":"RedisArray::hpexpiretime","p":"RedisArray.html#method_hpexpiretime","d":""},{"t":"M","n":"RedisArray::hpersist","p":"RedisArray.html#method_hpersist","d":""},{"t":"M","n":"RedisArray::expiremember","p":"RedisArray.html#method_expiremember","d":""},{"t":"M","n":"RedisArray::expirememberat","p":"RedisArray.html#method_expirememberat","d":""},{"t":"M","n":"RedisArray::incr","p":"RedisArray.html#method_incr","d":""},{"t":"M","n":"RedisArray::incrBy","p":"RedisArray.html#method_incrBy","d":""},{"t":"M","n":"RedisArray::incrByFloat","p":"RedisArray.html#method_incrByFloat","d":""},{"t":"M","n":"RedisArray::isConnected","p":"RedisArray.html#method_isConnected","d":""},{"t":"M","n":"RedisArray::lInsert","p":"RedisArray.html#method_lInsert","d":""},{"t":"M","n":"RedisArray::lLen","p":"RedisArray.html#method_lLen","d":""},{"t":"M","n":"RedisArray::lMove","p":"RedisArray.html#method_lMove","d":""},{"t":"M","n":"RedisArray::blmove","p":"RedisArray.html#method_blmove","d":""},{"t":"M","n":"RedisArray::lPop","p":"RedisArray.html#method_lPop","d":""},{"t":"M","n":"RedisArray::lPos","p":"RedisArray.html#method_lPos","d":""},{"t":"M","n":"RedisArray::lPush","p":"RedisArray.html#method_lPush","d":""},{"t":"M","n":"RedisArray::rPush","p":"RedisArray.html#method_rPush","d":""},{"t":"M","n":"RedisArray::lPushx","p":"RedisArray.html#method_lPushx","d":""},{"t":"M","n":"RedisArray::rPushx","p":"RedisArray.html#method_rPushx","d":""},{"t":"M","n":"RedisArray::lSet","p":"RedisArray.html#method_lSet","d":""},{"t":"M","n":"RedisArray::lastSave","p":"RedisArray.html#method_lastSave","d":""},{"t":"M","n":"RedisArray::lindex","p":"RedisArray.html#method_lindex","d":""},{"t":"M","n":"RedisArray::lrange","p":"RedisArray.html#method_lrange","d":""},{"t":"M","n":"RedisArray::lrem","p":"RedisArray.html#method_lrem","d":""},{"t":"M","n":"RedisArray::ltrim","p":"RedisArray.html#method_ltrim","d":""},{"t":"M","n":"RedisArray::move","p":"RedisArray.html#method_move","d":""},{"t":"M","n":"RedisArray::msetex","p":"RedisArray.html#method_msetex","d":""},{"t":"M","n":"RedisArray::msetnx","p":"RedisArray.html#method_msetnx","d":""},{"t":"M","n":"RedisArray::object","p":"RedisArray.html#method_object","d":""},{"t":"M","n":"RedisArray::open","p":"RedisArray.html#method_open","d":""},{"t":"M","n":"RedisArray::pconnect","p":"RedisArray.html#method_pconnect","d":""},{"t":"M","n":"RedisArray::persist","p":"RedisArray.html#method_persist","d":""},{"t":"M","n":"RedisArray::pexpire","p":"RedisArray.html#method_pexpire","d":""},{"t":"M","n":"RedisArray::pexpireAt","p":"RedisArray.html#method_pexpireAt","d":""},{"t":"M","n":"RedisArray::pfadd","p":"RedisArray.html#method_pfadd","d":""},{"t":"M","n":"RedisArray::pfcount","p":"RedisArray.html#method_pfcount","d":""},{"t":"M","n":"RedisArray::pfmerge","p":"RedisArray.html#method_pfmerge","d":""},{"t":"M","n":"RedisArray::pipeline","p":"RedisArray.html#method_pipeline","d":""},{"t":"M","n":"RedisArray::popen","p":"RedisArray.html#method_popen","d":""},{"t":"M","n":"RedisArray::psetex","p":"RedisArray.html#method_psetex","d":""},{"t":"M","n":"RedisArray::psubscribe","p":"RedisArray.html#method_psubscribe","d":""},{"t":"M","n":"RedisArray::pttl","p":"RedisArray.html#method_pttl","d":""},{"t":"M","n":"RedisArray::publish","p":"RedisArray.html#method_publish","d":""},{"t":"M","n":"RedisArray::pubsub","p":"RedisArray.html#method_pubsub","d":""},{"t":"M","n":"RedisArray::punsubscribe","p":"RedisArray.html#method_punsubscribe","d":""},{"t":"M","n":"RedisArray::rPop","p":"RedisArray.html#method_rPop","d":""},{"t":"M","n":"RedisArray::randomKey","p":"RedisArray.html#method_randomKey","d":""},{"t":"M","n":"RedisArray::rawcommand","p":"RedisArray.html#method_rawcommand","d":""},{"t":"M","n":"RedisArray::rename","p":"RedisArray.html#method_rename","d":""},{"t":"M","n":"RedisArray::renameNx","p":"RedisArray.html#method_renameNx","d":""},{"t":"M","n":"RedisArray::reset","p":"RedisArray.html#method_reset","d":""},{"t":"M","n":"RedisArray::restore","p":"RedisArray.html#method_restore","d":""},{"t":"M","n":"RedisArray::role","p":"RedisArray.html#method_role","d":""},{"t":"M","n":"RedisArray::rpoplpush","p":"RedisArray.html#method_rpoplpush","d":""},{"t":"M","n":"RedisArray::sAdd","p":"RedisArray.html#method_sAdd","d":""},{"t":"M","n":"RedisArray::sAddArray","p":"RedisArray.html#method_sAddArray","d":""},{"t":"M","n":"RedisArray::sDiff","p":"RedisArray.html#method_sDiff","d":""},{"t":"M","n":"RedisArray::sDiffStore","p":"RedisArray.html#method_sDiffStore","d":""},{"t":"M","n":"RedisArray::sInter","p":"RedisArray.html#method_sInter","d":""},{"t":"M","n":"RedisArray::sintercard","p":"RedisArray.html#method_sintercard","d":""},{"t":"M","n":"RedisArray::sInterStore","p":"RedisArray.html#method_sInterStore","d":""},{"t":"M","n":"RedisArray::sMembers","p":"RedisArray.html#method_sMembers","d":""},{"t":"M","n":"RedisArray::sMisMember","p":"RedisArray.html#method_sMisMember","d":""},{"t":"M","n":"RedisArray::sMove","p":"RedisArray.html#method_sMove","d":""},{"t":"M","n":"RedisArray::sPop","p":"RedisArray.html#method_sPop","d":""},{"t":"M","n":"RedisArray::sRandMember","p":"RedisArray.html#method_sRandMember","d":""},{"t":"M","n":"RedisArray::sUnion","p":"RedisArray.html#method_sUnion","d":""},{"t":"M","n":"RedisArray::sUnionStore","p":"RedisArray.html#method_sUnionStore","d":""},{"t":"M","n":"RedisArray::scard","p":"RedisArray.html#method_scard","d":""},{"t":"M","n":"RedisArray::script","p":"RedisArray.html#method_script","d":""},{"t":"M","n":"RedisArray::set","p":"RedisArray.html#method_set","d":""},{"t":"M","n":"RedisArray::setBit","p":"RedisArray.html#method_setBit","d":""},{"t":"M","n":"RedisArray::setRange","p":"RedisArray.html#method_setRange","d":""},{"t":"M","n":"RedisArray::setex","p":"RedisArray.html#method_setex","d":""},{"t":"M","n":"RedisArray::setnx","p":"RedisArray.html#method_setnx","d":""},{"t":"M","n":"RedisArray::sismember","p":"RedisArray.html#method_sismember","d":""},{"t":"M","n":"RedisArray::slaveof","p":"RedisArray.html#method_slaveof","d":""},{"t":"M","n":"RedisArray::replicaof","p":"RedisArray.html#method_replicaof","d":""},{"t":"M","n":"RedisArray::touch","p":"RedisArray.html#method_touch","d":""},{"t":"M","n":"RedisArray::slowlog","p":"RedisArray.html#method_slowlog","d":""},{"t":"M","n":"RedisArray::sort","p":"RedisArray.html#method_sort","d":""},{"t":"M","n":"RedisArray::sort_ro","p":"RedisArray.html#method_sort_ro","d":""},{"t":"M","n":"RedisArray::sortAsc","p":"RedisArray.html#method_sortAsc","d":""},{"t":"M","n":"RedisArray::sortAscAlpha","p":"RedisArray.html#method_sortAscAlpha","d":""},{"t":"M","n":"RedisArray::sortDesc","p":"RedisArray.html#method_sortDesc","d":""},{"t":"M","n":"RedisArray::sortDescAlpha","p":"RedisArray.html#method_sortDescAlpha","d":""},{"t":"M","n":"RedisArray::srem","p":"RedisArray.html#method_srem","d":""},{"t":"M","n":"RedisArray::ssubscribe","p":"RedisArray.html#method_ssubscribe","d":""},{"t":"M","n":"RedisArray::strlen","p":"RedisArray.html#method_strlen","d":""},{"t":"M","n":"RedisArray::subscribe","p":"RedisArray.html#method_subscribe","d":""},{"t":"M","n":"RedisArray::sunsubscribe","p":"RedisArray.html#method_sunsubscribe","d":""},{"t":"M","n":"RedisArray::swapdb","p":"RedisArray.html#method_swapdb","d":""},{"t":"M","n":"RedisArray::time","p":"RedisArray.html#method_time","d":""},{"t":"M","n":"RedisArray::ttl","p":"RedisArray.html#method_ttl","d":""},{"t":"M","n":"RedisArray::type","p":"RedisArray.html#method_type","d":""},{"t":"M","n":"RedisArray::unsubscribe","p":"RedisArray.html#method_unsubscribe","d":""},{"t":"M","n":"RedisArray::watch","p":"RedisArray.html#method_watch","d":""},{"t":"M","n":"RedisArray::wait","p":"RedisArray.html#method_wait","d":""},{"t":"M","n":"RedisArray::xack","p":"RedisArray.html#method_xack","d":""},{"t":"M","n":"RedisArray::xadd","p":"RedisArray.html#method_xadd","d":""},{"t":"M","n":"RedisArray::xautoclaim","p":"RedisArray.html#method_xautoclaim","d":""},{"t":"M","n":"RedisArray::xclaim","p":"RedisArray.html#method_xclaim","d":""},{"t":"M","n":"RedisArray::xdel","p":"RedisArray.html#method_xdel","d":""},{"t":"M","n":"RedisArray::xdelex","p":"RedisArray.html#method_xdelex","d":""},{"t":"M","n":"RedisArray::xinfo","p":"RedisArray.html#method_xinfo","d":""},{"t":"M","n":"RedisArray::xlen","p":"RedisArray.html#method_xlen","d":""},{"t":"M","n":"RedisArray::xpending","p":"RedisArray.html#method_xpending","d":""},{"t":"M","n":"RedisArray::xrange","p":"RedisArray.html#method_xrange","d":""},{"t":"M","n":"RedisArray::xread","p":"RedisArray.html#method_xread","d":""},{"t":"M","n":"RedisArray::xreadgroup","p":"RedisArray.html#method_xreadgroup","d":""},{"t":"M","n":"RedisArray::xrevrange","p":"RedisArray.html#method_xrevrange","d":""},{"t":"M","n":"RedisArray::vadd","p":"RedisArray.html#method_vadd","d":""},{"t":"M","n":"RedisArray::vsim","p":"RedisArray.html#method_vsim","d":""},{"t":"M","n":"RedisArray::vcard","p":"RedisArray.html#method_vcard","d":""},{"t":"M","n":"RedisArray::vdim","p":"RedisArray.html#method_vdim","d":""},{"t":"M","n":"RedisArray::vinfo","p":"RedisArray.html#method_vinfo","d":""},{"t":"M","n":"RedisArray::vismember","p":"RedisArray.html#method_vismember","d":""},{"t":"M","n":"RedisArray::vemb","p":"RedisArray.html#method_vemb","d":""},{"t":"M","n":"RedisArray::vrandmember","p":"RedisArray.html#method_vrandmember","d":""},{"t":"M","n":"RedisArray::vrange","p":"RedisArray.html#method_vrange","d":""},{"t":"M","n":"RedisArray::vrem","p":"RedisArray.html#method_vrem","d":""},{"t":"M","n":"RedisArray::vsetattr","p":"RedisArray.html#method_vsetattr","d":""},{"t":"M","n":"RedisArray::vgetattr","p":"RedisArray.html#method_vgetattr","d":""},{"t":"M","n":"RedisArray::vlinks","p":"RedisArray.html#method_vlinks","d":""},{"t":"M","n":"RedisArray::xtrim","p":"RedisArray.html#method_xtrim","d":""},{"t":"M","n":"RedisArray::zAdd","p":"RedisArray.html#method_zAdd","d":""},{"t":"M","n":"RedisArray::zCard","p":"RedisArray.html#method_zCard","d":""},{"t":"M","n":"RedisArray::zCount","p":"RedisArray.html#method_zCount","d":""},{"t":"M","n":"RedisArray::zIncrBy","p":"RedisArray.html#method_zIncrBy","d":""},{"t":"M","n":"RedisArray::zLexCount","p":"RedisArray.html#method_zLexCount","d":""},{"t":"M","n":"RedisArray::zMscore","p":"RedisArray.html#method_zMscore","d":""},{"t":"M","n":"RedisArray::zPopMax","p":"RedisArray.html#method_zPopMax","d":""},{"t":"M","n":"RedisArray::zPopMin","p":"RedisArray.html#method_zPopMin","d":""},{"t":"M","n":"RedisArray::zRange","p":"RedisArray.html#method_zRange","d":""},{"t":"M","n":"RedisArray::zRangeByLex","p":"RedisArray.html#method_zRangeByLex","d":""},{"t":"M","n":"RedisArray::zRangeByScore","p":"RedisArray.html#method_zRangeByScore","d":""},{"t":"M","n":"RedisArray::zRandMember","p":"RedisArray.html#method_zRandMember","d":""},{"t":"M","n":"RedisArray::zRank","p":"RedisArray.html#method_zRank","d":""},{"t":"M","n":"RedisArray::zRem","p":"RedisArray.html#method_zRem","d":""},{"t":"M","n":"RedisArray::zRemRangeByLex","p":"RedisArray.html#method_zRemRangeByLex","d":""},{"t":"M","n":"RedisArray::zRemRangeByRank","p":"RedisArray.html#method_zRemRangeByRank","d":""},{"t":"M","n":"RedisArray::zRemRangeByScore","p":"RedisArray.html#method_zRemRangeByScore","d":""},{"t":"M","n":"RedisArray::zRevRange","p":"RedisArray.html#method_zRevRange","d":""},{"t":"M","n":"RedisArray::zRevRangeByLex","p":"RedisArray.html#method_zRevRangeByLex","d":""},{"t":"M","n":"RedisArray::zRevRangeByScore","p":"RedisArray.html#method_zRevRangeByScore","d":""},{"t":"M","n":"RedisArray::zRevRank","p":"RedisArray.html#method_zRevRank","d":""},{"t":"M","n":"RedisArray::zScore","p":"RedisArray.html#method_zScore","d":""},{"t":"M","n":"RedisArray::zdiff","p":"RedisArray.html#method_zdiff","d":""},{"t":"M","n":"RedisArray::zdiffstore","p":"RedisArray.html#method_zdiffstore","d":""},{"t":"M","n":"RedisArray::zinter","p":"RedisArray.html#method_zinter","d":""},{"t":"M","n":"RedisArray::zintercard","p":"RedisArray.html#method_zintercard","d":""},{"t":"M","n":"RedisArray::zinterstore","p":"RedisArray.html#method_zinterstore","d":""},{"t":"M","n":"RedisArray::zunion","p":"RedisArray.html#method_zunion","d":""},{"t":"M","n":"RedisArray::zunionstore","p":"RedisArray.html#method_zunionstore","d":""},{"t":"M","n":"RedisArray::digest","p":"RedisArray.html#method_digest","d":""},{"t":"M","n":"RedisCluster::__construct","p":"RedisCluster.html#method___construct","d":null},{"t":"M","n":"RedisCluster::_compress","p":"RedisCluster.html#method__compress","d":"{\\Redis::_compress()}"},{"t":"M","n":"RedisCluster::_uncompress","p":"RedisCluster.html#method__uncompress","d":""},{"t":"M","n":"RedisCluster::_serialize","p":"RedisCluster.html#method__serialize","d":""},{"t":"M","n":"RedisCluster::_unserialize","p":"RedisCluster.html#method__unserialize","d":""},{"t":"M","n":"RedisCluster::_pack","p":"RedisCluster.html#method__pack","d":""},{"t":"M","n":"RedisCluster::_digest","p":"RedisCluster.html#method__digest","d":""},{"t":"M","n":"RedisCluster::_unpack","p":"RedisCluster.html#method__unpack","d":""},{"t":"M","n":"RedisCluster::_prefix","p":"RedisCluster.html#method__prefix","d":""},{"t":"M","n":"RedisCluster::_masters","p":"RedisCluster.html#method__masters","d":null},{"t":"M","n":"RedisCluster::_redir","p":"RedisCluster.html#method__redir","d":null},{"t":"M","n":"RedisCluster::acl","p":"RedisCluster.html#method_acl","d":""},{"t":"M","n":"RedisCluster::append","p":"RedisCluster.html#method_append","d":""},{"t":"M","n":"RedisCluster::bgrewriteaof","p":"RedisCluster.html#method_bgrewriteaof","d":""},{"t":"M","n":"RedisCluster::wait","p":"RedisCluster.html#method_wait","d":""},{"t":"M","n":"RedisCluster::waitaof","p":"RedisCluster.html#method_waitaof","d":""},{"t":"M","n":"RedisCluster::bgsave","p":"RedisCluster.html#method_bgsave","d":""},{"t":"M","n":"RedisCluster::bitcount","p":"RedisCluster.html#method_bitcount","d":""},{"t":"M","n":"RedisCluster::bitop","p":"RedisCluster.html#method_bitop","d":""},{"t":"M","n":"RedisCluster::bitpos","p":"RedisCluster.html#method_bitpos","d":"

    Return the position of the first bit set to 0 or 1 in a string.

    "},{"t":"M","n":"RedisCluster::blpop","p":"RedisCluster.html#method_blpop","d":""},{"t":"M","n":"RedisCluster::brpop","p":"RedisCluster.html#method_brpop","d":""},{"t":"M","n":"RedisCluster::brpoplpush","p":"RedisCluster.html#method_brpoplpush","d":""},{"t":"M","n":"RedisCluster::lmove","p":"RedisCluster.html#method_lmove","d":"

    Move an element from one list into another.

    "},{"t":"M","n":"RedisCluster::blmove","p":"RedisCluster.html#method_blmove","d":"

    Move an element from one list to another, blocking up to a timeout until an element is available.

    "},{"t":"M","n":"RedisCluster::bzpopmax","p":"RedisCluster.html#method_bzpopmax","d":""},{"t":"M","n":"RedisCluster::bzpopmin","p":"RedisCluster.html#method_bzpopmin","d":""},{"t":"M","n":"RedisCluster::bzmpop","p":"RedisCluster.html#method_bzmpop","d":""},{"t":"M","n":"RedisCluster::zmpop","p":"RedisCluster.html#method_zmpop","d":""},{"t":"M","n":"RedisCluster::blmpop","p":"RedisCluster.html#method_blmpop","d":""},{"t":"M","n":"RedisCluster::lmpop","p":"RedisCluster.html#method_lmpop","d":""},{"t":"M","n":"RedisCluster::clearlasterror","p":"RedisCluster.html#method_clearlasterror","d":""},{"t":"M","n":"RedisCluster::client","p":"RedisCluster.html#method_client","d":""},{"t":"M","n":"RedisCluster::close","p":"RedisCluster.html#method_close","d":""},{"t":"M","n":"RedisCluster::cluster","p":"RedisCluster.html#method_cluster","d":""},{"t":"M","n":"RedisCluster::command","p":"RedisCluster.html#method_command","d":""},{"t":"M","n":"RedisCluster::config","p":"RedisCluster.html#method_config","d":""},{"t":"M","n":"RedisCluster::dbsize","p":"RedisCluster.html#method_dbsize","d":""},{"t":"M","n":"RedisCluster::copy","p":"RedisCluster.html#method_copy","d":""},{"t":"M","n":"RedisCluster::decr","p":"RedisCluster.html#method_decr","d":""},{"t":"M","n":"RedisCluster::decrby","p":"RedisCluster.html#method_decrby","d":""},{"t":"M","n":"RedisCluster::decrbyfloat","p":"RedisCluster.html#method_decrbyfloat","d":""},{"t":"M","n":"RedisCluster::del","p":"RedisCluster.html#method_del","d":""},{"t":"M","n":"RedisCluster::delex","p":"RedisCluster.html#method_delex","d":"

    Delete a key conditionally based on its value or hash digest

    "},{"t":"M","n":"RedisCluster::delifeq","p":"RedisCluster.html#method_delifeq","d":"

    Delete a key if it's equal to the specified value. This command is\nspecific to Valkey >= 9.0

    "},{"t":"M","n":"RedisCluster::discard","p":"RedisCluster.html#method_discard","d":""},{"t":"M","n":"RedisCluster::dump","p":"RedisCluster.html#method_dump","d":""},{"t":"M","n":"RedisCluster::echo","p":"RedisCluster.html#method_echo","d":""},{"t":"M","n":"RedisCluster::eval","p":"RedisCluster.html#method_eval","d":""},{"t":"M","n":"RedisCluster::eval_ro","p":"RedisCluster.html#method_eval_ro","d":""},{"t":"M","n":"RedisCluster::evalsha","p":"RedisCluster.html#method_evalsha","d":""},{"t":"M","n":"RedisCluster::evalsha_ro","p":"RedisCluster.html#method_evalsha_ro","d":""},{"t":"M","n":"RedisCluster::exec","p":"RedisCluster.html#method_exec","d":""},{"t":"M","n":"RedisCluster::exists","p":"RedisCluster.html#method_exists","d":""},{"t":"M","n":"RedisCluster::touch","p":"RedisCluster.html#method_touch","d":""},{"t":"M","n":"RedisCluster::expire","p":"RedisCluster.html#method_expire","d":""},{"t":"M","n":"RedisCluster::expireat","p":"RedisCluster.html#method_expireat","d":""},{"t":"M","n":"RedisCluster::expiretime","p":"RedisCluster.html#method_expiretime","d":""},{"t":"M","n":"RedisCluster::pexpiretime","p":"RedisCluster.html#method_pexpiretime","d":""},{"t":"M","n":"RedisCluster::flushall","p":"RedisCluster.html#method_flushall","d":""},{"t":"M","n":"RedisCluster::flushdb","p":"RedisCluster.html#method_flushdb","d":""},{"t":"M","n":"RedisCluster::geoadd","p":"RedisCluster.html#method_geoadd","d":""},{"t":"M","n":"RedisCluster::geodist","p":"RedisCluster.html#method_geodist","d":""},{"t":"M","n":"RedisCluster::geohash","p":"RedisCluster.html#method_geohash","d":""},{"t":"M","n":"RedisCluster::geopos","p":"RedisCluster.html#method_geopos","d":""},{"t":"M","n":"RedisCluster::georadius","p":"RedisCluster.html#method_georadius","d":""},{"t":"M","n":"RedisCluster::georadius_ro","p":"RedisCluster.html#method_georadius_ro","d":""},{"t":"M","n":"RedisCluster::georadiusbymember","p":"RedisCluster.html#method_georadiusbymember","d":""},{"t":"M","n":"RedisCluster::georadiusbymember_ro","p":"RedisCluster.html#method_georadiusbymember_ro","d":""},{"t":"M","n":"RedisCluster::geosearch","p":"RedisCluster.html#method_geosearch","d":""},{"t":"M","n":"RedisCluster::geosearchstore","p":"RedisCluster.html#method_geosearchstore","d":""},{"t":"M","n":"RedisCluster::get","p":"RedisCluster.html#method_get","d":""},{"t":"M","n":"RedisCluster::getdel","p":"RedisCluster.html#method_getdel","d":""},{"t":"M","n":"RedisCluster::getWithMeta","p":"RedisCluster.html#method_getWithMeta","d":""},{"t":"M","n":"RedisCluster::getex","p":"RedisCluster.html#method_getex","d":""},{"t":"M","n":"RedisCluster::getbit","p":"RedisCluster.html#method_getbit","d":""},{"t":"M","n":"RedisCluster::getlasterror","p":"RedisCluster.html#method_getlasterror","d":""},{"t":"M","n":"RedisCluster::getmode","p":"RedisCluster.html#method_getmode","d":""},{"t":"M","n":"RedisCluster::getoption","p":"RedisCluster.html#method_getoption","d":""},{"t":"M","n":"RedisCluster::getrange","p":"RedisCluster.html#method_getrange","d":""},{"t":"M","n":"RedisCluster::lcs","p":"RedisCluster.html#method_lcs","d":""},{"t":"M","n":"RedisCluster::getset","p":"RedisCluster.html#method_getset","d":""},{"t":"M","n":"RedisCluster::gettransferredbytes","p":"RedisCluster.html#method_gettransferredbytes","d":""},{"t":"M","n":"RedisCluster::cleartransferredbytes","p":"RedisCluster.html#method_cleartransferredbytes","d":""},{"t":"M","n":"RedisCluster::hdel","p":"RedisCluster.html#method_hdel","d":""},{"t":"M","n":"RedisCluster::hexists","p":"RedisCluster.html#method_hexists","d":""},{"t":"M","n":"RedisCluster::hget","p":"RedisCluster.html#method_hget","d":""},{"t":"M","n":"RedisCluster::hgetall","p":"RedisCluster.html#method_hgetall","d":""},{"t":"M","n":"RedisCluster::hgetWithMeta","p":"RedisCluster.html#method_hgetWithMeta","d":""},{"t":"M","n":"RedisCluster::hincrby","p":"RedisCluster.html#method_hincrby","d":""},{"t":"M","n":"RedisCluster::hincrbyfloat","p":"RedisCluster.html#method_hincrbyfloat","d":""},{"t":"M","n":"RedisCluster::hkeys","p":"RedisCluster.html#method_hkeys","d":""},{"t":"M","n":"RedisCluster::hlen","p":"RedisCluster.html#method_hlen","d":""},{"t":"M","n":"RedisCluster::hmget","p":"RedisCluster.html#method_hmget","d":""},{"t":"M","n":"RedisCluster::hgetex","p":"RedisCluster.html#method_hgetex","d":""},{"t":"M","n":"RedisCluster::hsetex","p":"RedisCluster.html#method_hsetex","d":""},{"t":"M","n":"RedisCluster::hgetdel","p":"RedisCluster.html#method_hgetdel","d":""},{"t":"M","n":"RedisCluster::hmset","p":"RedisCluster.html#method_hmset","d":""},{"t":"M","n":"RedisCluster::hscan","p":"RedisCluster.html#method_hscan","d":""},{"t":"M","n":"RedisCluster::expiremember","p":"RedisCluster.html#method_expiremember","d":""},{"t":"M","n":"RedisCluster::expirememberat","p":"RedisCluster.html#method_expirememberat","d":""},{"t":"M","n":"RedisCluster::hrandfield","p":"RedisCluster.html#method_hrandfield","d":""},{"t":"M","n":"RedisCluster::hset","p":"RedisCluster.html#method_hset","d":""},{"t":"M","n":"RedisCluster::hsetnx","p":"RedisCluster.html#method_hsetnx","d":""},{"t":"M","n":"RedisCluster::hstrlen","p":"RedisCluster.html#method_hstrlen","d":""},{"t":"M","n":"RedisCluster::hexpire","p":"RedisCluster.html#method_hexpire","d":""},{"t":"M","n":"RedisCluster::hpexpire","p":"RedisCluster.html#method_hpexpire","d":""},{"t":"M","n":"RedisCluster::hexpireat","p":"RedisCluster.html#method_hexpireat","d":""},{"t":"M","n":"RedisCluster::hpexpireat","p":"RedisCluster.html#method_hpexpireat","d":""},{"t":"M","n":"RedisCluster::httl","p":"RedisCluster.html#method_httl","d":""},{"t":"M","n":"RedisCluster::hpttl","p":"RedisCluster.html#method_hpttl","d":""},{"t":"M","n":"RedisCluster::hexpiretime","p":"RedisCluster.html#method_hexpiretime","d":""},{"t":"M","n":"RedisCluster::hpexpiretime","p":"RedisCluster.html#method_hpexpiretime","d":""},{"t":"M","n":"RedisCluster::hpersist","p":"RedisCluster.html#method_hpersist","d":""},{"t":"M","n":"RedisCluster::hvals","p":"RedisCluster.html#method_hvals","d":""},{"t":"M","n":"RedisCluster::incr","p":"RedisCluster.html#method_incr","d":""},{"t":"M","n":"RedisCluster::incrby","p":"RedisCluster.html#method_incrby","d":""},{"t":"M","n":"RedisCluster::incrbyfloat","p":"RedisCluster.html#method_incrbyfloat","d":""},{"t":"M","n":"RedisCluster::info","p":"RedisCluster.html#method_info","d":"

    Retrieve information about the connected redis-server. If no arguments are passed to\nthis function, redis will return every info field. Alternatively you may pass a specific\nsection you want returned (e.g. 'server', or 'memory') to receive only information pertaining\nto that section.

    "},{"t":"M","n":"RedisCluster::keys","p":"RedisCluster.html#method_keys","d":""},{"t":"M","n":"RedisCluster::lastsave","p":"RedisCluster.html#method_lastsave","d":""},{"t":"M","n":"RedisCluster::lget","p":"RedisCluster.html#method_lget","d":""},{"t":"M","n":"RedisCluster::lindex","p":"RedisCluster.html#method_lindex","d":""},{"t":"M","n":"RedisCluster::linsert","p":"RedisCluster.html#method_linsert","d":""},{"t":"M","n":"RedisCluster::llen","p":"RedisCluster.html#method_llen","d":""},{"t":"M","n":"RedisCluster::lpop","p":"RedisCluster.html#method_lpop","d":""},{"t":"M","n":"RedisCluster::lpos","p":"RedisCluster.html#method_lpos","d":""},{"t":"M","n":"RedisCluster::lpush","p":"RedisCluster.html#method_lpush","d":""},{"t":"M","n":"RedisCluster::lpushx","p":"RedisCluster.html#method_lpushx","d":""},{"t":"M","n":"RedisCluster::lrange","p":"RedisCluster.html#method_lrange","d":""},{"t":"M","n":"RedisCluster::lrem","p":"RedisCluster.html#method_lrem","d":""},{"t":"M","n":"RedisCluster::lset","p":"RedisCluster.html#method_lset","d":""},{"t":"M","n":"RedisCluster::ltrim","p":"RedisCluster.html#method_ltrim","d":""},{"t":"M","n":"RedisCluster::mget","p":"RedisCluster.html#method_mget","d":""},{"t":"M","n":"RedisCluster::mset","p":"RedisCluster.html#method_mset","d":""},{"t":"M","n":"RedisCluster::msetnx","p":"RedisCluster.html#method_msetnx","d":""},{"t":"M","n":"RedisCluster::msetex","p":"RedisCluster.html#method_msetex","d":""},{"t":"M","n":"RedisCluster::multi","p":"RedisCluster.html#method_multi","d":null},{"t":"M","n":"RedisCluster::object","p":"RedisCluster.html#method_object","d":""},{"t":"M","n":"RedisCluster::persist","p":"RedisCluster.html#method_persist","d":""},{"t":"M","n":"RedisCluster::pexpire","p":"RedisCluster.html#method_pexpire","d":""},{"t":"M","n":"RedisCluster::pexpireat","p":"RedisCluster.html#method_pexpireat","d":""},{"t":"M","n":"RedisCluster::pfadd","p":"RedisCluster.html#method_pfadd","d":""},{"t":"M","n":"RedisCluster::pfcount","p":"RedisCluster.html#method_pfcount","d":""},{"t":"M","n":"RedisCluster::pfmerge","p":"RedisCluster.html#method_pfmerge","d":""},{"t":"M","n":"RedisCluster::ping","p":"RedisCluster.html#method_ping","d":"

    PING an instance in the redis cluster.

    "},{"t":"M","n":"RedisCluster::psetex","p":"RedisCluster.html#method_psetex","d":""},{"t":"M","n":"RedisCluster::psubscribe","p":"RedisCluster.html#method_psubscribe","d":""},{"t":"M","n":"RedisCluster::pttl","p":"RedisCluster.html#method_pttl","d":""},{"t":"M","n":"RedisCluster::publish","p":"RedisCluster.html#method_publish","d":""},{"t":"M","n":"RedisCluster::pubsub","p":"RedisCluster.html#method_pubsub","d":""},{"t":"M","n":"RedisCluster::punsubscribe","p":"RedisCluster.html#method_punsubscribe","d":""},{"t":"M","n":"RedisCluster::randomkey","p":"RedisCluster.html#method_randomkey","d":""},{"t":"M","n":"RedisCluster::rawcommand","p":"RedisCluster.html#method_rawcommand","d":""},{"t":"M","n":"RedisCluster::rename","p":"RedisCluster.html#method_rename","d":""},{"t":"M","n":"RedisCluster::renamenx","p":"RedisCluster.html#method_renamenx","d":""},{"t":"M","n":"RedisCluster::restore","p":"RedisCluster.html#method_restore","d":""},{"t":"M","n":"RedisCluster::role","p":"RedisCluster.html#method_role","d":""},{"t":"M","n":"RedisCluster::rpop","p":"RedisCluster.html#method_rpop","d":""},{"t":"M","n":"RedisCluster::rpoplpush","p":"RedisCluster.html#method_rpoplpush","d":""},{"t":"M","n":"RedisCluster::rpush","p":"RedisCluster.html#method_rpush","d":""},{"t":"M","n":"RedisCluster::rpushx","p":"RedisCluster.html#method_rpushx","d":""},{"t":"M","n":"RedisCluster::sadd","p":"RedisCluster.html#method_sadd","d":""},{"t":"M","n":"RedisCluster::saddarray","p":"RedisCluster.html#method_saddarray","d":""},{"t":"M","n":"RedisCluster::save","p":"RedisCluster.html#method_save","d":""},{"t":"M","n":"RedisCluster::scan","p":"RedisCluster.html#method_scan","d":""},{"t":"M","n":"RedisCluster::scard","p":"RedisCluster.html#method_scard","d":""},{"t":"M","n":"RedisCluster::script","p":"RedisCluster.html#method_script","d":""},{"t":"M","n":"RedisCluster::sdiff","p":"RedisCluster.html#method_sdiff","d":""},{"t":"M","n":"RedisCluster::sdiffstore","p":"RedisCluster.html#method_sdiffstore","d":""},{"t":"M","n":"RedisCluster::set","p":"RedisCluster.html#method_set","d":""},{"t":"M","n":"RedisCluster::setbit","p":"RedisCluster.html#method_setbit","d":""},{"t":"M","n":"RedisCluster::setex","p":"RedisCluster.html#method_setex","d":""},{"t":"M","n":"RedisCluster::setnx","p":"RedisCluster.html#method_setnx","d":""},{"t":"M","n":"RedisCluster::setoption","p":"RedisCluster.html#method_setoption","d":""},{"t":"M","n":"RedisCluster::setrange","p":"RedisCluster.html#method_setrange","d":""},{"t":"M","n":"RedisCluster::sinter","p":"RedisCluster.html#method_sinter","d":""},{"t":"M","n":"RedisCluster::sintercard","p":"RedisCluster.html#method_sintercard","d":""},{"t":"M","n":"RedisCluster::sinterstore","p":"RedisCluster.html#method_sinterstore","d":""},{"t":"M","n":"RedisCluster::sismember","p":"RedisCluster.html#method_sismember","d":""},{"t":"M","n":"RedisCluster::smismember","p":"RedisCluster.html#method_smismember","d":""},{"t":"M","n":"RedisCluster::slowlog","p":"RedisCluster.html#method_slowlog","d":""},{"t":"M","n":"RedisCluster::smembers","p":"RedisCluster.html#method_smembers","d":""},{"t":"M","n":"RedisCluster::smove","p":"RedisCluster.html#method_smove","d":""},{"t":"M","n":"RedisCluster::sort","p":"RedisCluster.html#method_sort","d":""},{"t":"M","n":"RedisCluster::sort_ro","p":"RedisCluster.html#method_sort_ro","d":""},{"t":"M","n":"RedisCluster::spop","p":"RedisCluster.html#method_spop","d":""},{"t":"M","n":"RedisCluster::srandmember","p":"RedisCluster.html#method_srandmember","d":""},{"t":"M","n":"RedisCluster::srem","p":"RedisCluster.html#method_srem","d":""},{"t":"M","n":"RedisCluster::sscan","p":"RedisCluster.html#method_sscan","d":""},{"t":"M","n":"RedisCluster::strlen","p":"RedisCluster.html#method_strlen","d":""},{"t":"M","n":"RedisCluster::subscribe","p":"RedisCluster.html#method_subscribe","d":""},{"t":"M","n":"RedisCluster::sunion","p":"RedisCluster.html#method_sunion","d":""},{"t":"M","n":"RedisCluster::sunionstore","p":"RedisCluster.html#method_sunionstore","d":""},{"t":"M","n":"RedisCluster::time","p":"RedisCluster.html#method_time","d":""},{"t":"M","n":"RedisCluster::ttl","p":"RedisCluster.html#method_ttl","d":""},{"t":"M","n":"RedisCluster::type","p":"RedisCluster.html#method_type","d":""},{"t":"M","n":"RedisCluster::unsubscribe","p":"RedisCluster.html#method_unsubscribe","d":""},{"t":"M","n":"RedisCluster::unlink","p":"RedisCluster.html#method_unlink","d":""},{"t":"M","n":"RedisCluster::unwatch","p":"RedisCluster.html#method_unwatch","d":""},{"t":"M","n":"RedisCluster::watch","p":"RedisCluster.html#method_watch","d":""},{"t":"M","n":"RedisCluster::vadd","p":"RedisCluster.html#method_vadd","d":""},{"t":"M","n":"RedisCluster::vsim","p":"RedisCluster.html#method_vsim","d":""},{"t":"M","n":"RedisCluster::vcard","p":"RedisCluster.html#method_vcard","d":""},{"t":"M","n":"RedisCluster::vdim","p":"RedisCluster.html#method_vdim","d":""},{"t":"M","n":"RedisCluster::vinfo","p":"RedisCluster.html#method_vinfo","d":""},{"t":"M","n":"RedisCluster::vismember","p":"RedisCluster.html#method_vismember","d":"

    Check if an element is a member of a vectorset

    "},{"t":"M","n":"RedisCluster::vemb","p":"RedisCluster.html#method_vemb","d":""},{"t":"M","n":"RedisCluster::vrandmember","p":"RedisCluster.html#method_vrandmember","d":""},{"t":"M","n":"RedisCluster::vrange","p":"RedisCluster.html#method_vrange","d":"

    Retreive a lexographical range of elements from a vector set

    "},{"t":"M","n":"RedisCluster::vrem","p":"RedisCluster.html#method_vrem","d":""},{"t":"M","n":"RedisCluster::vlinks","p":"RedisCluster.html#method_vlinks","d":""},{"t":"M","n":"RedisCluster::vgetattr","p":"RedisCluster.html#method_vgetattr","d":""},{"t":"M","n":"RedisCluster::vsetattr","p":"RedisCluster.html#method_vsetattr","d":""},{"t":"M","n":"RedisCluster::gcra","p":"RedisCluster.html#method_gcra","d":""},{"t":"M","n":"RedisCluster::xack","p":"RedisCluster.html#method_xack","d":""},{"t":"M","n":"RedisCluster::xadd","p":"RedisCluster.html#method_xadd","d":""},{"t":"M","n":"RedisCluster::xclaim","p":"RedisCluster.html#method_xclaim","d":""},{"t":"M","n":"RedisCluster::xdel","p":"RedisCluster.html#method_xdel","d":""},{"t":"M","n":"RedisCluster::xdelex","p":"RedisCluster.html#method_xdelex","d":""},{"t":"M","n":"RedisCluster::xgroup","p":"RedisCluster.html#method_xgroup","d":""},{"t":"M","n":"RedisCluster::xautoclaim","p":"RedisCluster.html#method_xautoclaim","d":""},{"t":"M","n":"RedisCluster::xinfo","p":"RedisCluster.html#method_xinfo","d":""},{"t":"M","n":"RedisCluster::xlen","p":"RedisCluster.html#method_xlen","d":""},{"t":"M","n":"RedisCluster::xpending","p":"RedisCluster.html#method_xpending","d":""},{"t":"M","n":"RedisCluster::xrange","p":"RedisCluster.html#method_xrange","d":""},{"t":"M","n":"RedisCluster::xread","p":"RedisCluster.html#method_xread","d":""},{"t":"M","n":"RedisCluster::xreadgroup","p":"RedisCluster.html#method_xreadgroup","d":""},{"t":"M","n":"RedisCluster::xrevrange","p":"RedisCluster.html#method_xrevrange","d":""},{"t":"M","n":"RedisCluster::xtrim","p":"RedisCluster.html#method_xtrim","d":""},{"t":"M","n":"RedisCluster::zadd","p":"RedisCluster.html#method_zadd","d":""},{"t":"M","n":"RedisCluster::zcard","p":"RedisCluster.html#method_zcard","d":""},{"t":"M","n":"RedisCluster::zcount","p":"RedisCluster.html#method_zcount","d":""},{"t":"M","n":"RedisCluster::zincrby","p":"RedisCluster.html#method_zincrby","d":""},{"t":"M","n":"RedisCluster::zinterstore","p":"RedisCluster.html#method_zinterstore","d":""},{"t":"M","n":"RedisCluster::zintercard","p":"RedisCluster.html#method_zintercard","d":""},{"t":"M","n":"RedisCluster::zlexcount","p":"RedisCluster.html#method_zlexcount","d":""},{"t":"M","n":"RedisCluster::zpopmax","p":"RedisCluster.html#method_zpopmax","d":""},{"t":"M","n":"RedisCluster::zpopmin","p":"RedisCluster.html#method_zpopmin","d":""},{"t":"M","n":"RedisCluster::zrange","p":"RedisCluster.html#method_zrange","d":""},{"t":"M","n":"RedisCluster::zrangestore","p":"RedisCluster.html#method_zrangestore","d":""},{"t":"M","n":"RedisCluster::zrandmember","p":"RedisCluster.html#method_zrandmember","d":""},{"t":"M","n":"RedisCluster::zrangebylex","p":"RedisCluster.html#method_zrangebylex","d":""},{"t":"M","n":"RedisCluster::zrangebyscore","p":"RedisCluster.html#method_zrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrank","p":"RedisCluster.html#method_zrank","d":""},{"t":"M","n":"RedisCluster::zrem","p":"RedisCluster.html#method_zrem","d":""},{"t":"M","n":"RedisCluster::zremrangebylex","p":"RedisCluster.html#method_zremrangebylex","d":""},{"t":"M","n":"RedisCluster::zremrangebyrank","p":"RedisCluster.html#method_zremrangebyrank","d":""},{"t":"M","n":"RedisCluster::zremrangebyscore","p":"RedisCluster.html#method_zremrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrange","p":"RedisCluster.html#method_zrevrange","d":""},{"t":"M","n":"RedisCluster::zrevrangebylex","p":"RedisCluster.html#method_zrevrangebylex","d":""},{"t":"M","n":"RedisCluster::zrevrangebyscore","p":"RedisCluster.html#method_zrevrangebyscore","d":""},{"t":"M","n":"RedisCluster::zrevrank","p":"RedisCluster.html#method_zrevrank","d":""},{"t":"M","n":"RedisCluster::zscan","p":"RedisCluster.html#method_zscan","d":""},{"t":"M","n":"RedisCluster::zscore","p":"RedisCluster.html#method_zscore","d":""},{"t":"M","n":"RedisCluster::zmscore","p":"RedisCluster.html#method_zmscore","d":""},{"t":"M","n":"RedisCluster::zunionstore","p":"RedisCluster.html#method_zunionstore","d":""},{"t":"M","n":"RedisCluster::zinter","p":"RedisCluster.html#method_zinter","d":""},{"t":"M","n":"RedisCluster::zdiffstore","p":"RedisCluster.html#method_zdiffstore","d":""},{"t":"M","n":"RedisCluster::zunion","p":"RedisCluster.html#method_zunion","d":""},{"t":"M","n":"RedisCluster::zdiff","p":"RedisCluster.html#method_zdiff","d":""},{"t":"M","n":"RedisCluster::digest","p":"RedisCluster.html#method_digest","d":""},{"t":"M","n":"RedisSentinel::__construct","p":"RedisSentinel.html#method___construct","d":null},{"t":"M","n":"RedisSentinel::ckquorum","p":"RedisSentinel.html#method_ckquorum","d":""},{"t":"M","n":"RedisSentinel::failover","p":"RedisSentinel.html#method_failover","d":""},{"t":"M","n":"RedisSentinel::flushconfig","p":"RedisSentinel.html#method_flushconfig","d":""},{"t":"M","n":"RedisSentinel::getMasterAddrByName","p":"RedisSentinel.html#method_getMasterAddrByName","d":""},{"t":"M","n":"RedisSentinel::master","p":"RedisSentinel.html#method_master","d":""},{"t":"M","n":"RedisSentinel::masters","p":"RedisSentinel.html#method_masters","d":""},{"t":"M","n":"RedisSentinel::myid","p":"RedisSentinel.html#method_myid","d":null},{"t":"M","n":"RedisSentinel::ping","p":"RedisSentinel.html#method_ping","d":""},{"t":"M","n":"RedisSentinel::reset","p":"RedisSentinel.html#method_reset","d":""},{"t":"M","n":"RedisSentinel::sentinels","p":"RedisSentinel.html#method_sentinels","d":""},{"t":"M","n":"RedisSentinel::slaves","p":"RedisSentinel.html#method_slaves","d":""},{"t":"N","n":"","p":"[Global_Namespace].html"}]} diff --git a/docs/doctum.js b/docs/doctum.js index d487386524..59bc208247 100644 --- a/docs/doctum.js +++ b/docs/doctum.js @@ -261,10 +261,13 @@ var Doctum = { /** * Clean the search query * - * @param string query + * @param string|null query * @return string */ cleanSearchQuery: function (query) { + if (typeof query !== 'string') { + return ''; + } // replace any chars that could lead to injecting code in our regex // remove start or end spaces // replace backslashes by an escaped version, use case in search: \myRootFunction diff --git a/docs/index.html b/docs/index.html index e0ee6367d8..80508d60a0 100644 --- a/docs/index.html +++ b/docs/index.html @@ -5,7 +5,7 @@ All Classes | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> diff --git a/docs/interfaces.html b/docs/interfaces.html index 5497f56840..a66c01db17 100644 --- a/docs/interfaces.html +++ b/docs/interfaces.html @@ -5,7 +5,7 @@ Interfaces | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> diff --git a/docs/js/highlight.min.js b/docs/js/highlight.min.js new file mode 100644 index 0000000000..5d699ae6a4 --- /dev/null +++ b/docs/js/highlight.min.js @@ -0,0 +1,1213 @@ +/*! + Highlight.js v11.9.0 (git: f47103d4f1) + (c) 2006-2023 undefined and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(n){ +return n instanceof Map?n.clear=n.delete=n.set=()=>{ +throw Error("map is read-only")}:n instanceof Set&&(n.add=n.clear=n.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(n),Object.getOwnPropertyNames(n).forEach((t=>{ +const a=n[t],i=typeof a;"object"!==i&&"function"!==i||Object.isFrozen(a)||e(a) +})),n}class n{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function t(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function a(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n] +;return n.forEach((e=>{for(const n in e)t[n]=e[n]})),t}const i=e=>!!e.scope +;class r{constructor(e,n){ +this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){ +this.buffer+=t(e)}openNode(e){if(!i(e))return;const n=((e,{prefix:n})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const t=e.split(".") +;return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ") +}return`${n}${e}`})(e.scope,{prefix:this.classPrefix});this.span(n)} +closeNode(e){i(e)&&(this.buffer+="")}value(){return this.buffer}span(e){ +this.buffer+=``}}const s=(e={})=>{const n={children:[]} +;return Object.assign(n,e),n};class o{constructor(){ +this.rootNode=s(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const n=s({scope:e}) +;this.add(n),this.stack.push(n)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){ +return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n), +n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +o._collapse(e)})))}}class l extends o{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,n){const t=e.root +;n&&(t.scope="language:"+n),this.add(t)}toHTML(){ +return new r(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function c(e){ +return e?"string"==typeof e?e:e.source:null}function d(e){return b("(?=",e,")")} +function g(e){return b("(?:",e,")*")}function u(e){return b("(?:",e,")?")} +function b(...e){return e.map((e=>c(e))).join("")}function m(...e){const n=(e=>{ +const n=e[e.length-1] +;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} +})(e);return"("+(n.capture?"":"?:")+e.map((e=>c(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const _=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function h(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t +;let a=c(e),i="";for(;a.length>0;){const e=_.exec(a);if(!e){i+=a;break} +i+=a.substring(0,e.index), +a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0], +"("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)} +const f="[a-zA-Z]\\w*",E="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",N="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",v={ +begin:"\\\\[\\s\\S]",relevance:0},O={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[v]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[v]},x=(e,n,t={})=>{const i=a({scope:"comment",begin:e,end:n, +contains:[]},t);i.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const r=m("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return i.contains.push({begin:b(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i +},M=x("//","$"),S=x("/\\*","\\*/"),A=x("#","$");var C=Object.freeze({ +__proto__:null,APOS_STRING_MODE:O,BACKSLASH_ESCAPE:v,BINARY_NUMBER_MODE:{ +scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:x, +C_BLOCK_COMMENT_MODE:S,C_LINE_COMMENT_MODE:M,C_NUMBER_MODE:{scope:"number", +begin:N,relevance:0},C_NUMBER_RE:N,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}}),HASH_COMMENT_MODE:A,IDENT_RE:f, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+E,relevance:0}, +NUMBER_MODE:{scope:"number",begin:y,relevance:0},NUMBER_RE:y, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[v,{begin:/\[/,end:/\]/,relevance:0,contains:[v]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const n=/^#![ ]*\// +;return e.binary&&(e.begin=b(n,/.*\b/,e.binary,/\b.*/)),a({scope:"meta",begin:n, +end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:f,relevance:0},UNDERSCORE_IDENT_RE:E, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:E,relevance:0}});function T(e,n){ +"."===e.input[e.index-1]&&n.ignoreMatch()}function R(e,n){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function D(e,n){ +n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=T,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function I(e,n){ +Array.isArray(e.illegal)&&(e.illegal=m(...e.illegal))}function L(e,n){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function B(e,n){ +void 0===e.relevance&&(e.relevance=1)}const $=(e,n)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n] +})),e.keywords=t.keywords,e.begin=b(t.beforeMatch,d(t.begin)),e.starts={ +relevance:0,contains:[Object.assign(t,{endsParent:!0})] +},e.relevance=0,delete t.beforeMatch +},z=["of","and","for","in","not","or","if","then","parent","list","value"],F="keyword" +;function U(e,n,t=F){const a=Object.create(null) +;return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((t=>{ +Object.assign(a,U(e[t],n,t))})),a;function i(e,t){ +n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((n=>{const t=n.split("|") +;a[t[0]]=[e,j(t[0],t[1])]}))}}function j(e,n){ +return n?Number(n):(e=>z.includes(e.toLowerCase()))(e)?0:1}const P={},K=e=>{ +console.error(e)},H=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{ +P[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),P[`${e}/${n}`]=!0) +},G=Error();function Z(e,n,{key:t}){let a=0;const i=e[t],r={},s={} +;for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=p(n[e-1]) +;e[t]=s,e[t]._emit=r,e[t]._multi=!0}function W(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +G +;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"), +G;Z(e,e.begin,{key:"beginScope"}),e.begin=h(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +G +;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"), +G;Z(e,e.end,{key:"endScope"}),e.end=h(e.end,{joinWith:""})}})(e)}function Q(e){ +function n(n,t){ +return RegExp(c(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":"")) +}class t{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,n){ +n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(h(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const n=this.matcherRe.exec(e);if(!n)return null +;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),a=this.matchIndexes[t] +;return n.splice(0,t),Object.assign(n,a)}}class i{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t +;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))), +n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){ +this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){ +const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex +;let t=n.exec(e) +;if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{ +const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)} +return t&&(this.regexIndex+=t.position+1, +this.regexIndex===this.count&&this.considerAll()),t}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=a(e.classNameAliases||{}),function t(r,s){const o=r +;if(r.isCompiled)return o +;[R,L,W,$].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))), +r.__beforeBegin=null,[D,I,B].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null +;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), +l=r.keywords.$pattern, +delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=U(r.keywords,e.case_insensitive)), +o.keywordPatternRe=n(l,!0), +s&&(r.begin||(r.begin=/\B|\b/),o.beginRe=n(o.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), +r.end&&(o.endRe=n(o.end)), +o.terminatorEnd=c(o.end)||"",r.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)), +r.illegal&&(o.illegalRe=n(r.illegal)), +r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((n=>a(e,{ +variants:null},n)))),e.cachedVariants?e.cachedVariants:X(e)?a(e,{ +starts:e.starts?a(e.starts):null +}):Object.isFrozen(e)?a(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{t(e,o) +})),r.starts&&t(r.starts,s),o.matcher=(e=>{const n=new i +;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function X(e){ +return!!e&&(e.endsWithParent||X(e.starts))}class V extends Error{ +constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}} +const J=t,Y=a,ee=Symbol("nomatch"),ne=t=>{ +const a=Object.create(null),i=Object.create(null),r=[];let s=!0 +;const o="Could not find the language '{}', did you forget to load/include a language module?",c={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:l};function _(e){ +return p.noHighlightRe.test(e)}function h(e,n,t){let a="",i="" +;"object"==typeof n?(a=e, +t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."), +q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};x("before:highlight",r) +;const s=r.result?r.result:f(r.language,r.code,t) +;return s.code=r.code,x("after:highlight",s),s}function f(e,t,i,r){ +const l=Object.create(null);function c(){if(!x.keywords)return void S.addText(A) +;let e=0;x.keywordPatternRe.lastIndex=0;let n=x.keywordPatternRe.exec(A),t="" +;for(;n;){t+=A.substring(e,n.index) +;const i=w.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,x.keywords[a]);if(r){ +const[e,a]=r +;if(S.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(C+=a),e.startsWith("_"))t+=n[0];else{ +const t=w.classNameAliases[e]||e;g(n[0],t)}}else t+=n[0] +;e=x.keywordPatternRe.lastIndex,n=x.keywordPatternRe.exec(A)}var a +;t+=A.substring(e),S.addText(t)}function d(){null!=x.subLanguage?(()=>{ +if(""===A)return;let e=null;if("string"==typeof x.subLanguage){ +if(!a[x.subLanguage])return void S.addText(A) +;e=f(x.subLanguage,A,!0,M[x.subLanguage]),M[x.subLanguage]=e._top +}else e=E(A,x.subLanguage.length?x.subLanguage:null) +;x.relevance>0&&(C+=e.relevance),S.__addSublanguage(e._emitter,e.language) +})():c(),A=""}function g(e,n){ +""!==e&&(S.startScope(n),S.addText(e),S.endScope())}function u(e,n){let t=1 +;const a=n.length-1;for(;t<=a;){if(!e._emit[t]){t++;continue} +const a=w.classNameAliases[e[t]]||e[t],i=n[t];a?g(i,a):(A=i,c(),A=""),t++}} +function b(e,n){ +return e.scope&&"string"==typeof e.scope&&S.openNode(w.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(g(A,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +A=""):e.beginScope._multi&&(u(e.beginScope,n),A="")),x=Object.create(e,{parent:{ +value:x}}),x}function m(e,t,a){let i=((e,n)=>{const t=e&&e.exec(n) +;return t&&0===t.index})(e.endRe,a);if(i){if(e["on:end"]){const a=new n(e) +;e["on:end"](t,a),a.isMatchIgnored&&(i=!1)}if(i){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return m(e.parent,t,a)}function _(e){ +return 0===x.matcher.regexIndex?(A+=e[0],1):(D=!0,0)}function h(e){ +const n=e[0],a=t.substring(e.index),i=m(x,e,a);if(!i)return ee;const r=x +;x.endScope&&x.endScope._wrap?(d(), +g(n,x.endScope._wrap)):x.endScope&&x.endScope._multi?(d(), +u(x.endScope,e)):r.skip?A+=n:(r.returnEnd||r.excludeEnd||(A+=n), +d(),r.excludeEnd&&(A=n));do{ +x.scope&&S.closeNode(),x.skip||x.subLanguage||(C+=x.relevance),x=x.parent +}while(x!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:n.length} +let y={};function N(a,r){const o=r&&r[0];if(A+=a,null==o)return d(),0 +;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===o){ +if(A+=t.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`) +;throw n.languageName=e,n.badRule=y.rule,n}return 1} +if(y=r,"begin"===r.type)return(e=>{ +const t=e[0],a=e.rule,i=new n(a),r=[a.__beforeBegin,a["on:begin"]] +;for(const n of r)if(n&&(n(e,i),i.isMatchIgnored))return _(t) +;return a.skip?A+=t:(a.excludeBegin&&(A+=t), +d(),a.returnBegin||a.excludeBegin||(A=t)),b(a,e),a.returnBegin?0:t.length})(r) +;if("illegal"===r.type&&!i){ +const e=Error('Illegal lexeme "'+o+'" for mode "'+(x.scope||"")+'"') +;throw e.mode=x,e}if("end"===r.type){const e=h(r);if(e!==ee)return e} +if("illegal"===r.type&&""===o)return 1 +;if(R>1e5&&R>3*r.index)throw Error("potential infinite loop, way more iterations than matches") +;return A+=o,o.length}const w=v(e) +;if(!w)throw K(o.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const O=Q(w);let k="",x=r||O;const M={},S=new p.__emitter(p);(()=>{const e=[] +;for(let n=x;n!==w;n=n.parent)n.scope&&e.unshift(n.scope) +;e.forEach((e=>S.openNode(e)))})();let A="",C=0,T=0,R=0,D=!1;try{ +if(w.__emitTokens)w.__emitTokens(t,S);else{for(x.matcher.considerAll();;){ +R++,D?D=!1:x.matcher.considerAll(),x.matcher.lastIndex=T +;const e=x.matcher.exec(t);if(!e)break;const n=N(t.substring(T,e.index),e) +;T=e.index+n}N(t.substring(T))}return S.finalize(),k=S.toHTML(),{language:e, +value:k,relevance:C,illegal:!1,_emitter:S,_top:x}}catch(n){ +if(n.message&&n.message.includes("Illegal"))return{language:e,value:J(t), +illegal:!0,relevance:0,_illegalBy:{message:n.message,index:T, +context:t.slice(T-100,T+100),mode:n.mode,resultSoFar:k},_emitter:S};if(s)return{ +language:e,value:J(t),illegal:!1,relevance:0,errorRaised:n,_emitter:S,_top:x} +;throw n}}function E(e,n){n=n||p.languages||Object.keys(a);const t=(e=>{ +const n={value:J(e),illegal:!1,relevance:0,_top:c,_emitter:new p.__emitter(p)} +;return n._emitter.addText(e),n})(e),i=n.filter(v).filter(k).map((n=>f(n,e,!1))) +;i.unshift(t);const r=i.sort(((e,n)=>{ +if(e.relevance!==n.relevance)return n.relevance-e.relevance +;if(e.language&&n.language){if(v(e.language).supersetOf===n.language)return 1 +;if(v(n.language).supersetOf===e.language)return-1}return 0})),[s,o]=r,l=s +;return l.secondBest=o,l}function y(e){let n=null;const t=(e=>{ +let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"" +;const t=p.languageDetectRe.exec(n);if(t){const n=v(t[1]) +;return n||(H(o.replace("{}",t[1])), +H("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"} +return n.split(/\s+/).find((e=>_(e)||v(e)))})(e);if(_(t))return +;if(x("before:highlightElement",{el:e,language:t +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new V("One of your code blocks includes unescaped HTML.",e.innerHTML) +;n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a) +;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,n,t)=>{const a=n&&i[n]||t +;e.classList.add("hljs"),e.classList.add("language-"+a) +})(e,t,r.language),e.result={language:r.language,re:r.relevance, +relevance:r.relevance},r.secondBest&&(e.secondBest={ +language:r.secondBest.language,relevance:r.secondBest.relevance +}),x("after:highlightElement",{el:e,result:r,text:a})}let N=!1;function w(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(y):N=!0 +}function v(e){return e=(e||"").toLowerCase(),a[e]||a[i[e]]} +function O(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +i[e.toLowerCase()]=n}))}function k(e){const n=v(e) +;return n&&!n.disableAutodetect}function x(e,n){const t=e;r.forEach((e=>{ +e[t]&&e[t](n)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +N&&w()}),!1),Object.assign(t,{highlight:h,highlightAuto:E,highlightAll:w, +highlightElement:y, +highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"), +q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{p=Y(p,e)}, +initHighlighting:()=>{ +w(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +w(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,n)=>{let i=null;try{i=n(t)}catch(n){ +if(K("Language definition for '{}' could not be registered.".replace("{}",e)), +!s)throw n;K(n),i=c} +i.name||(i.name=e),a[e]=i,i.rawDefinition=n.bind(null,t),i.aliases&&O(i.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete a[e] +;for(const n of Object.keys(i))i[n]===e&&delete i[n]}, +listLanguages:()=>Object.keys(a),getLanguage:v,registerAliases:O, +autoDetection:k,inherit:Y,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{ +e["before:highlightBlock"](Object.assign({block:n.el},n)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{ +e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)}, +removePlugin:e=>{const n=r.indexOf(e);-1!==n&&r.splice(n,1)}}),t.debugMode=()=>{ +s=!1},t.safeMode=()=>{s=!0},t.versionString="11.9.0",t.regex={concat:b, +lookahead:d,either:m,optional:u,anyNumberOfTimes:g} +;for(const n in C)"object"==typeof C[n]&&e(C[n]);return Object.assign(t,C),t +},te=ne({});te.newInstance=()=>ne({});var ae=te;const ie=e=>({IMPORTANT:{ +scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{ +scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/}, +FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/}, +ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}),re=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],se=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],oe=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],le=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],ce=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),de=oe.concat(le) +;var ge="[0-9](_*[0-9])*",ue=`\\.(${ge})`,be="[0-9a-fA-F](_*[0-9a-fA-F])*",me={ +className:"number",variants:[{ +begin:`(\\b(${ge})((${ue})|\\.)?|(${ue}))[eE][+-]?(${ge})[fFdD]?\\b`},{ +begin:`\\b(${ge})((${ue})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ +begin:`(${ue})[fFdD]?\\b`},{begin:`\\b(${ge})[fFdD]\\b`},{ +begin:`\\b0[xX]((${be})\\.?|(${be})?\\.(${be}))[pP][+-]?(${ge})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${be})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};function pe(e,n,t){return-1===t?"":e.replace(n,(a=>pe(e,n,t-1)))} +const _e="[A-Za-z$_][0-9A-Za-z$_]*",he=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],fe=["true","false","null","undefined","NaN","Infinity"],Ee=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],ye=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],Ne=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],we=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],ve=[].concat(Ne,Ee,ye) +;function Oe(e){const n=e.regex,t=_e,a={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const t=e[0].length+e.index,a=e.input[t] +;if("<"===a||","===a)return void n.ignoreMatch();let i +;">"===a&&(((e,{after:n})=>{const t="",M={ +match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(x)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[f]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{ +PARAMS_CONTAINS:h,CLASS_REFERENCE:y},illegal:/#(?![$_A-z])/, +contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,d,g,u,b,m,{match:/\$\d+/},l,y,{ +className:"attr",begin:t+n.lookahead(":"),relevance:0},M,{ +begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[m,e.REGEXP_MODE,{ +className:"function",begin:x,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:i,contains:h}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin, +"on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{ +begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},N,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[f,e.inherit(e.TITLE_MODE,{begin:t, +className:"title.function"})]},{match:/\.\.\./,relevance:0},O,{match:"\\$"+t, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[f]},w,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},E,k,{match:/\$[(.]/}]}} +const ke=e=>b(/\b/,e,/\w$/.test(e)?/\b/:/\B/),xe=["Protocol","Type"].map(ke),Me=["init","self"].map(ke),Se=["Any","Self"],Ae=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Ce=["false","nil","true"],Te=["assignment","associativity","higherThan","left","lowerThan","none","right"],Re=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],De=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Ie=m(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),Le=m(Ie,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),Be=b(Ie,Le,"*"),$e=m(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),ze=m($e,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Fe=b($e,ze,"*"),Ue=b(/[A-Z]/,ze,"*"),je=["attached","autoclosure",b(/convention\(/,m("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",b(/objc\(/,Fe,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],Pe=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] +;var Ke=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={ +begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]} +;Object.assign(t,{className:"variable",variants:[{ +begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},r={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(s);const o={begin:/\$?\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] +},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, +keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","function","select"], +literal:["true","false"], +built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] +},contains:[l,e.SHEBANG(),c,o,e.HASH_COMMENT_MODE,r,{match:/(\/[a-z._-]+)+/},s,{ +match:/\\"/},{className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}, +grmr_c:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" +},b=[c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],m={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:b.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:b.concat(["self"]),relevance:0}]),relevance:0},p={ +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], +relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, +keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s] +}]},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, +strings:o,keywords:u}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="(?!struct)("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",begin:"\\b[a-z\\d_]*_t\\b"},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"], +keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"], +literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"], +_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"] +},b={className:"function.dispatch",relevance:0,keywords:{ +_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"] +}, +begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/)) +},m=[b,c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],p={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:m.concat(["self"]),relevance:0}]),relevance:0},_={className:"function", +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[d],relevance:0},{begin:/::/,relevance:0},{ +begin:/:/,endsWithParent:!0,contains:[o,l]},{relevance:0,match:/,/},{ +className:"params",begin:/\(/,end:/\)/,keywords:u,relevance:0, +contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/,end:/\)/,keywords:u, +relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s]}] +},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C++", +aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"",keywords:u,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:u},{ +match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/], +className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={ +keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]), +built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], +literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{ +begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{ +begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] +},r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/, +keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/, +end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ +},e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{ +begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/, +contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]}) +;s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE], +o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{ +illegal:/\n/})];const g={variants:[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},u={begin:"<",end:">",contains:[{beginKeywords:"in out"},t] +},b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={ +begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], +keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, +contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ +begin:"\x3c!--|--\x3e"},{begin:""}]}] +}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", +end:"$",keywords:{ +keyword:"if else elif endif define undef warning error line region endregion pragma checksum" +}},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, +illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" +},t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", +relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", +begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ +className:"string",begin:/"/,end:/"/}]},{ +beginKeywords:"new return throw await else",relevance:0},{className:"function", +begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ +beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", +relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, +contains:[g,a,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{ +const n=e.regex,t=ie(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{ +name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{ +keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"}, +contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ +},t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 +},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+oe.join("|")+")"},{begin:":(:)?("+le.join("|")+")"}] +},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b"},{ +begin:/:/,end:/[;}{]/, +contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[...a,{className:"string",begin:/[^)]/,endsWithParent:!0, +excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]", +relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ +},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:se.join(" ")},contains:[{ +begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{ +className:"selector-tag",begin:"\\b("+re.join("|")+")\\b"}]}},grmr_diff:e=>{ +const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{ +className:"meta",relevance:10, +match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]}},grmr_go:e=>{const n={ +keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"], +type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], +literal:["true","false","iota","nil"], +built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] +};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex;return{name:"GraphQL",aliases:["gql"], +case_insensitive:!0,disableAutodetect:!1,keywords:{ +keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"], +literal:["true","false","null"]}, +contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ +scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation", +begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/, +end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{ +scope:"symbol",begin:n.concat(/[_A-Za-z][_0-9A-Za-z]*/,n.lookahead(/\s*:/)), +relevance:0}],illegal:[/[;<']/,/BEGIN/]}},grmr_ini:e=>{const n=e.regex,t={ +className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{ +begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/, +end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{ +begin:/\$\{(.*?)\}/}]},r={className:"literal", +begin:/\bon|off|true|false|yes|no\b/},s={className:"string", +contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{ +begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}] +},o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0 +},l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ +name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, +contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{ +begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)), +className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{ +const n=e.regex,t="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",a=t+pe("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={ +keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], +literal:["false","true","null"], +type:["char","boolean","long","float","int","byte","short","double"], +built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{ +begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} +;return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, +className:"string",contains:[e.BACKSLASH_ESCAPE] +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{ +1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ +begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=(?!=)/],className:{1:"type", +3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword", +3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{ +begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ +2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0, +contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,me,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},me,r]}},grmr_javascript:Oe, +grmr_json:e=>{const n=["true","false","null"],t={scope:"literal", +beginKeywords:n.join(" ")};return{name:"JSON",keywords:{literal:n},contains:[{ +className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{ +match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,t,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}},grmr_kotlin:e=>{const n={ +keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", +built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", +literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" +},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={ +className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", +variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'", +illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, +contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={ +className:"meta", +begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" +},o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, +end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}] +},l=me,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={ +variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, +contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g], +{name:"Kotlin",aliases:["kt","kts"],keywords:n, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", +begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword", +begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", +begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$", +returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, +keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, +endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, +endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0 +},e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{ +begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{ +3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, +illegal:"extends implements",contains:[{ +beginKeywords:"public protected internal private constructor" +},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, +excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/, +excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env", +end:"$",illegal:"\n"},l]}},grmr_less:e=>{ +const n=ie(e),t=de,a="[\\w-]+",i="("+a+"|@\\{"+a+"\\})",r=[],s=[],o=e=>({ +className:"string",begin:"~?"+e+".*?"+e}),l=(e,n,t)=>({className:e,begin:n, +relevance:t}),c={$pattern:/[a-z-]+/,keyword:"and or not only", +attribute:se.join(" ")},d={begin:"\\(",end:"\\)",contains:s,keywords:c, +relevance:0} +;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,o("'"),o('"'),n.CSS_NUMBER_MODE,{ +begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", +excludeEnd:!0} +},n.HEXCOLOR,d,l("variable","@@?"+a,10),l("variable","@\\{"+a+"\\}"),l("built_in","~?`[^`]*?`"),{ +className:"attribute",begin:a+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0 +},n.IMPORTANT,{beginKeywords:"and not"},n.FUNCTION_DISPATCH);const g=s.concat({ +begin:/\{/,end:/\}/,contains:r}),u={beginKeywords:"when",endsWithParent:!0, +contains:[{beginKeywords:"and not"}].concat(s)},b={begin:i+"\\s*:", +returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/ +},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b", +end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}] +},m={className:"keyword", +begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", +starts:{end:"[;{}]",keywords:c,returnEnd:!0,contains:s,relevance:0}},p={ +className:"variable",variants:[{begin:"@"+a+"\\s*:",relevance:15},{begin:"@"+a +}],starts:{end:"[;}]",returnEnd:!0,contains:g}},_={variants:[{ +begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:i,end:/\{/}],returnBegin:!0, +returnEnd:!0,illegal:"[<='$\"]",relevance:0, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,u,l("keyword","all\\b"),l("variable","@\\{"+a+"\\}"),{ +begin:"\\b("+re.join("|")+")\\b",className:"selector-tag" +},n.CSS_NUMBER_MODE,l("selector-tag",i,0),l("selector-id","#"+i),l("selector-class","\\."+i,0),l("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{ +className:"selector-pseudo",begin:":("+oe.join("|")+")"},{ +className:"selector-pseudo",begin:":(:)?("+le.join("|")+")"},{begin:/\(/, +end:/\)/,relevance:0,contains:g},{begin:"!important"},n.FUNCTION_DISPATCH]},h={ +begin:a+":(:)?"+`(${t.join("|")})`,returnBegin:!0,contains:[_]} +;return r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,m,p,h,b,_,u,n.FUNCTION_DISPATCH), +{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:r}}, +grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"] +},i=[e.COMMENT("--(?!"+n+")","$"),e.COMMENT("--"+n,t,{contains:[a],relevance:10 +})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:i}].concat(i) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={ +className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{ +const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},t={ +variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},i={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[] +}),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r) +;let o=[n,t];return[a,i,r,s].forEach((e=>{e.contains=e.contains.concat(o) +})),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}},grmr_objectivec:e=>{ +const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n, +keyword:["@interface","@class","@protocol","@implementation"]};return{ +name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], +keywords:{"variable.language":["this","super"],$pattern:n, +keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"], +literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"], +built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"], +type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"] +},illegal:"/,end:/$/,illegal:"\\n" +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class", +begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t, +contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE, +relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={ +$pattern:/[\w.]+/, +keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" +},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/, +end:/\}/},s={variants:[{begin:/\$\d/},{ +begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") +},{begin:/[$%@][^\s\w{]/,relevance:0}] +},o=[e.BACKSLASH_ESCAPE,i,s],l=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(e,a,i="\\1")=>{ +const r="\\1"===i?i:n.concat(i,a) +;return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t) +},d=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),g=[s,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ +endsWithParent:!0}),r,{className:"string",contains:o,variants:[{ +begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", +end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ +begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", +relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", +contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ +begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number", +begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", +relevance:0},{ +begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", +keywords:"split return print reverse grep",relevance:0, +contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ +begin:c("s|tr|y",n.either(...l,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{ +begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{ +className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ +begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...l,{capture:!0 +}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{ +begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub", +end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{ +begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$", +subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}] +}];return i.contains=g,r.contains=g,{name:"Perl",aliases:["pl","pm"],keywords:a, +contains:g}},grmr_php:e=>{ +const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r={ +scope:"variable",match:"\\$+"+a},s={scope:"subst",variants:[{begin:/\$\w+/},{ +begin:/\{\$/,end:/\}/}]},o=e.inherit(e.APOS_STRING_MODE,{illegal:null +}),l="[ \t\n]",c={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{ +illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(s)}),o,{ +begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/, +contains:e.QUOTE_STRING_MODE.contains.concat(s),"on:begin":(e,n)=>{ +n.data._beginMatch=e[1]||e[2]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}},e.END_SAME_AS_BEGIN({ +begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/})]},d={scope:"number",variants:[{ +begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{ +begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{ +begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?" +}],relevance:0 +},g=["false","null","true"],u=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],m={ +keyword:u,literal:(e=>{const n=[];return e.forEach((e=>{ +n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase()) +})),n})(g),built_in:b},p=e=>e.map((e=>e.replace(/\|\d+$/,""))),_={variants:[{ +match:[/new/,n.concat(l,"+"),n.concat("(?!",p(b).join("\\b|"),"\\b)"),i],scope:{ +1:"keyword",4:"title.class"}}]},h=n.concat(a,"\\b(?!\\()"),f={variants:[{ +match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant" +}},{match:[/::/,/class/],scope:{2:"variable.language"}},{ +match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{1:"title.class", +3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))], +scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class", +3:"variable.language"}}]},E={scope:"attr", +match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},y={relevance:0, +begin:/\(/,end:/\)/,keywords:m,contains:[E,r,f,e.C_BLOCK_COMMENT_MODE,c,d,_] +},N={relevance:0, +match:[/\b/,n.concat("(?!fn\\b|function\\b|",p(u).join("\\b|"),"|",p(b).join("\\b|"),"\\b)"),a,n.concat(l,"*"),n.lookahead(/(?=\()/)], +scope:{3:"title.function.invoke"},contains:[y]};y.contains.push(N) +;const w=[E,f,e.C_BLOCK_COMMENT_MODE,c,d,_];return{case_insensitive:!1, +keywords:m,contains:[{begin:n.concat(/#\[\s*/,i),beginScope:"meta",end:/]/, +endScope:"meta",keywords:{literal:g,keyword:["new","array"]},contains:[{ +begin:/\[/,end:/]/,keywords:{literal:g,keyword:["new","array"]}, +contains:["self",...w]},...w,{scope:"meta",match:i}] +},e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{ +scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/, +keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE, +contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{ +begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{ +begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},r,N,f,{ +match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},_,{ +scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/, +excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use" +},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params", +begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:m, +contains:["self",r,f,e.C_BLOCK_COMMENT_MODE,c,d]}]},{scope:"class",variants:[{ +beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", +illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ +beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ +beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, +contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{ +beginKeywords:"use",relevance:0,end:";",contains:[{ +match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},c,d]} +},grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{ +begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*", +end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0 +},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null, +skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null, +contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text", +aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{ +const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={ +$pattern:/[A-Za-z]\w+|__\w+__/,keyword:a, +built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], +literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], +type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] +},r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, +end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, +end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/, +relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ +begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, +end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, +contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={ +className:"number",relevance:0,variants:[{ +begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{ +begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{ +begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})` +},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})` +}]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i, +contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ +className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, +end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i, +contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{ +name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i, +illegal:/(<\/|\?)|=>/,contains:[r,u,{begin:/\bself\b/},{beginKeywords:"if", +relevance:0},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{ +1:"keyword",3:"title.function"},contains:[m]},{variants:[{ +match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}], +scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{ +className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}}, +grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt", +starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{ +begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{ +const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/) +;return{name:"R",keywords:{$pattern:t, +keyword:"function if in break next repeat else for while", +literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", +built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" +},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/, +starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)), +endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{ +scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0 +}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}] +}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE], +variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', +relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{ +1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"}, +match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{ +2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"}, +match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{ +match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`", +contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{ +const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={ +"variable.constant":["__FILE__","__LINE__","__ENCODING__"], +"variable.language":["self","super"], +keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield","include","extend","prepend","public","private","protected","raise","throw"], +built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"], +literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={ +begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s] +}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10 +}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/, +end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c], +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{ +begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{ +begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//, +end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{ +begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{ +begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ +begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ +begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ +begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), +contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, +contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number", +relevance:0,variants:[{ +begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ +begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" +},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ +begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{ +className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0, +keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{ +match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class", +4:"title.class.inherited"},keywords:r},{match:[/(include|extend)\s+/,i],scope:{ +2:"title.class"},keywords:r},{relevance:0,match:[i,/\.new[. (]/],scope:{ +1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},{relevance:0,match:a,scope:"title.class"},{ +match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{ +begin:e.IDENT_RE+"::"},{className:"symbol", +begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", +begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable", +begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ +className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0, +relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*", +keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c], +illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{ +begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[", +end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l) +;c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m} +},{className:"meta.prompt", +begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", +starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby", +aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/, +contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}}, +grmr_rust:e=>{const n=e.regex,t={className:"title.function.invoke",relevance:0, +begin:n.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,n.lookahead(/\s*\(/)) +},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],r=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:r, +keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], +literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},t]}}, +grmr_scss:e=>{const n=ie(e),t=le,a=oe,i="@[a-z-]+",r={className:"variable", +begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS", +case_insensitive:!0,illegal:"[=/|']", +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{ +className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ +className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 +},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", +begin:"\\b("+re.join("|")+")\\b",relevance:0},{className:"selector-pseudo", +begin:":("+a.join("|")+")"},{className:"selector-pseudo", +begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/, +contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute", +begin:"\\b("+ce.join("|")+")\\b"},{ +begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" +},{begin:/:/,end:/[;}{]/,relevance:0, +contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT,n.FUNCTION_DISPATCH] +},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{ +begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, +keyword:"and or not only",attribute:se.join(" ")},contains:[{begin:i, +className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" +},r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE] +},n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session", +aliases:["console","shellsession"],contains:[{className:"meta.prompt", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]}),grmr_sql:e=>{ +const n=e.regex,t=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],r=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=r,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!r.includes(e))),c={ +begin:n.concat(/\b/,n.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}} +;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ +$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t +;return n=n||[],e.map((e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)) +})(l,{when:e=>e.length<3}),literal:a,type:i, +built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] +},contains:[{begin:n.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/, +keyword:l.concat(s),literal:a,type:i}},{className:"type", +begin:n.either("double precision","large object","with timezone","without timezone") +},c,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string", +variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/, +contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{ +className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/, +relevance:0}]}},grmr_swift:e=>{const n={match:/\s+/,relevance:0 +},t=e.COMMENT("/\\*","\\*/",{contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={ +match:[/\./,m(...xe,...Me)],className:{2:"keyword"}},r={match:b(/\./,m(...Ae)), +relevance:0},s=Ae.filter((e=>"string"==typeof e)).concat(["_|0"]),o={variants:[{ +className:"keyword", +match:m(...Ae.filter((e=>"string"!=typeof e)).concat(Se).map(ke),...Me)}]},l={ +$pattern:m(/\b\w+/,/#\w+/),keyword:s.concat(Re),literal:Ce},c=[i,r,o],g=[{ +match:b(/\./,m(...De)),relevance:0},{className:"built_in", +match:b(/\b/,m(...De),/(?=\()/)}],u={match:/->/,relevance:0},p=[u,{ +className:"operator",relevance:0,variants:[{match:Be},{match:`\\.(\\.|${Le})+`}] +}],_="([0-9]_*)+",h="([0-9a-fA-F]_*)+",f={className:"number",relevance:0, +variants:[{match:`\\b(${_})(\\.(${_}))?([eE][+-]?(${_}))?\\b`},{ +match:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(${_}))?\\b`},{match:/\b0o([0-7]_*)+\b/ +},{match:/\b0b([01]_*)+\b/}]},E=(e="")=>({className:"subst",variants:[{ +match:b(/\\/,e,/[0\\tnr"']/)},{match:b(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}] +}),y=(e="")=>({className:"subst",match:b(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/) +}),N=(e="")=>({className:"subst",label:"interpol",begin:b(/\\/,e,/\(/),end:/\)/ +}),w=(e="")=>({begin:b(e,/"""/),end:b(/"""/,e),contains:[E(e),y(e),N(e)] +}),v=(e="")=>({begin:b(e,/"/),end:b(/"/,e),contains:[E(e),N(e)]}),O={ +className:"string", +variants:[w(),w("#"),w("##"),w("###"),v(),v("#"),v("##"),v("###")] +},k=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0, +contains:[e.BACKSLASH_ESCAPE]}],x={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//, +contains:k},M=e=>{const n=b(e,/\//),t=b(/\//,e);return{begin:n,end:t, +contains:[...k,{scope:"comment",begin:`#(?!.*${t})`,end:/$/}]}},S={ +scope:"regexp",variants:[M("###"),M("##"),M("#"),x]},A={match:b(/`/,Fe,/`/) +},C=[A,{className:"variable",match:/\$\d+/},{className:"variable", +match:`\\$${ze}+`}],T=[{match:/(@|#(un)?)available/,scope:"keyword",starts:{ +contains:[{begin:/\(/,end:/\)/,keywords:Pe,contains:[...p,f,O]}]}},{ +scope:"keyword",match:b(/@/,m(...je))},{scope:"meta",match:b(/@/,Fe)}],R={ +match:d(/\b[A-Z]/),relevance:0,contains:[{className:"type", +match:b(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,ze,"+") +},{className:"type",match:Ue,relevance:0},{match:/[?!]+/,relevance:0},{ +match:/\.\.\./,relevance:0},{match:b(/\s+&\s+/,d(Ue)),relevance:0}]},D={ +begin://,keywords:l,contains:[...a,...c,...T,u,R]};R.contains.push(D) +;const I={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{ +match:b(Fe,/\s*:/),keywords:"_|0",relevance:0 +},...a,S,...c,...g,...p,f,O,...C,...T,R]},L={begin://, +keywords:"repeat each",contains:[...a,R]},B={begin:/\(/,end:/\)/,keywords:l, +contains:[{begin:m(d(b(Fe,/\s*:/)),d(b(Fe,/\s+/,Fe,/\s*:/))),end:/:/, +relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params", +match:Fe}]},...a,...c,...p,f,O,...T,R,I],endsParent:!0,illegal:/["']/},$={ +match:[/(func|macro)/,/\s+/,m(A.match,Fe,Be)],className:{1:"keyword", +3:"title.function"},contains:[L,B,n],illegal:[/\[/,/%/]},z={ +match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"}, +contains:[L,B,n],illegal:/\[|%/},F={match:[/operator/,/\s+/,Be],className:{ +1:"keyword",3:"title"}},U={begin:[/precedencegroup/,/\s+/,Ue],className:{ +1:"keyword",3:"title"},contains:[R],keywords:[...Te,...Ce],end:/}/} +;for(const e of O.variants){const n=e.contains.find((e=>"interpol"===e.label)) +;n.keywords=l;const t=[...c,...g,...p,f,O,...C];n.contains=[...t,{begin:/\(/, +end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:l, +contains:[...a,$,z,{beginKeywords:"struct protocol class extension enum actor", +end:"\\{",excludeEnd:!0,keywords:l,contains:[e.inherit(e.TITLE_MODE,{ +className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c] +},F,U,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0 +},S,...c,...g,...p,f,O,...C,...T,R,I]}},grmr_typescript:e=>{ +const n=Oe(e),t=_e,a=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],i={ +beginKeywords:"namespace",end:/\{/,excludeEnd:!0, +contains:[n.exports.CLASS_REFERENCE]},r={beginKeywords:"interface",end:/\{/, +excludeEnd:!0,keywords:{keyword:"interface extends",built_in:a}, +contains:[n.exports.CLASS_REFERENCE]},s={$pattern:_e, +keyword:he.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]), +literal:fe,built_in:ve.concat(a),"variable.language":we},o={className:"meta", +begin:"@"+t},l=(e,n,t)=>{const a=e.contains.findIndex((e=>e.label===n)) +;if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)} +;return Object.assign(n.keywords,s), +n.exports.PARAMS_CONTAINS.push(o),n.contains=n.contains.concat([o,i,r]), +l(n,"shebang",e.SHEBANG()),l(n,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),n.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(n,{ +name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),n},grmr_vbnet:e=>{ +const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={ +className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{ +begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{ +begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}] +},o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}] +}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]}) +;return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0, +classNameAliases:{label:"symbol"},keywords:{ +keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", +built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", +type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", +literal:"true false nothing"}, +illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ +className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, +end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0, +variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ +},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ +begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ +className:"label",begin:/^\w+:/},o,l,{className:"meta", +begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, +end:/$/,keywords:{ +keyword:"const disable else elseif enable end externalsource if region then"}, +contains:[l]}]}},grmr_wasm:e=>{e.regex;const n=e.COMMENT(/\(;/,/;\)/) +;return n.contains.push("self"),{name:"WebAssembly",keywords:{$pattern:/[\w.]+/, +keyword:["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"] +},contains:[e.COMMENT(/;;/,/$/),n,{match:[/(?:offset|align)/,/\s*/,/=/], +className:{1:"keyword",3:"operator"}},{className:"variable",begin:/\$[\w_]+/},{ +match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},{ +begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword", +3:"title.function"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\.)/, +className:"type"},{className:"keyword", +match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ +},{className:"number",relevance:0, +match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ +}]}},grmr_xml:e=>{ +const n=e.regex,t=n.concat(/[\p{L}_]/u,n.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),a={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/, +contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{ +className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={ +endsWithParent:!0,illegal:/`]+/}]}]}]};return{ +name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{ +className:"meta",begin://,contains:[i,r,o,s]}]}] +},e.COMMENT(//,{relevance:10}),{begin://, +relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, +relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:n.concat(//,/>/,/\s/)))), +end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{ +className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{ +className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]} +},grmr_yaml:e=>{ +const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),r={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},s={begin:/\{/, +end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={begin:"\\[",end:"\\]", +contains:[r],illegal:"\\n",relevance:0},l=[{className:"attr",variants:[{ +begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ +begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type", +begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,a],c=[...l] +;return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:l}}});const He=ae;for(const e of Object.keys(Ke)){ +const n=e.replace("grmr_","").replace("_","-");He.registerLanguage(n,Ke[e])} +return He}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); \ No newline at end of file diff --git a/docs/namespaces.html b/docs/namespaces.html index 8863bd8627..6c6ec9ed60 100644 --- a/docs/namespaces.html +++ b/docs/namespaces.html @@ -5,7 +5,7 @@ Namespaces | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> diff --git a/docs/opensearch.xml b/docs/opensearch.xml index 73c0fe9792..d51e04039a 100644 --- a/docs/opensearch.xml +++ b/docs/opensearch.xml @@ -1,7 +1,7 @@ - PhpRedis API (main) - Searches PhpRedis API (main) + PhpRedis API (develop) + Searches PhpRedis API (develop) PhpRedis API UTF-8 diff --git a/docs/renderer.index b/docs/renderer.index index 616851d61a..addbc8c22a 100644 --- a/docs/renderer.index +++ b/docs/renderer.index @@ -1 +1 @@ -O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"39efb36886d0e29b476aa5ccb2e551c2a37fc7cb";s:10:"RedisArray";s:40:"fb17c785beccf1dbeedaa48afb4aa7d48fd8b655";s:12:"RedisCluster";s:40:"2f2132e45b1d60011f8ef9298cb35b7ba2b247d5";s:21:"RedisClusterException";s:40:"2f2132e45b1d60011f8ef9298cb35b7ba2b247d5";s:14:"RedisException";s:40:"39efb36886d0e29b476aa5ccb2e551c2a37fc7cb";s:13:"RedisSentinel";s:40:"4055ace9f1cf20bef89bdb5d3219470b4c8915e6";}i:1;a:1:{i:0;s:4:"main";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file +O:21:"Doctum\Renderer\Index":3:{i:0;a:6:{s:5:"Redis";s:40:"3ee3118802fef67d6bd7176f2a72f0568d962c8b";s:10:"RedisArray";s:40:"1293a0ccef1e700da87d9ace76c3a05d91c0ffc7";s:12:"RedisCluster";s:40:"6c7a87611b3bc9039650a3cf2e3c4d4f916611b0";s:21:"RedisClusterException";s:40:"6c7a87611b3bc9039650a3cf2e3c4d4f916611b0";s:14:"RedisException";s:40:"3ee3118802fef67d6bd7176f2a72f0568d962c8b";s:13:"RedisSentinel";s:40:"ca40579af888c5bb0661cd0201d840297474479a";}i:1;a:1:{i:0;s:7:"develop";}i:2;a:1:{i:0;s:0:"";}} \ No newline at end of file diff --git a/docs/search.html b/docs/search.html index c6ab501268..6dcda341d0 100644 --- a/docs/search.html +++ b/docs/search.html @@ -5,7 +5,7 @@ Search | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> diff --git a/docs/traits.html b/docs/traits.html index 4c73c7a8fa..b376801cb7 100644 --- a/docs/traits.html +++ b/docs/traits.html @@ -5,7 +5,7 @@ Traits | PhpRedis API - + @@ -16,11 +16,400 @@ + + + + + title="PhpRedis API (develop)" /> diff --git a/doctum-config.php b/doctum-config.php index 4dd445ef4a..03315073d3 100644 --- a/doctum-config.php +++ b/doctum-config.php @@ -1,28 +1,30 @@ files() ->name('*.stub.php') ->in($root); -//$versions = GitVersionCollection::create($root) -// ->add('develop', 'develop'); - return new Doctum($iterator, [ 'title' => 'PhpRedis API', 'language' => 'en', 'source_dir' => $root, - 'build_dir' => "{$root}/docs", - 'cache_dir' => "{$root}/docs/.cache", + 'build_dir' => $root . '/docs', + 'cache_dir' => $root . '/docs/.cache', + 'theme' => 'phpredis', + 'template_dirs' => [ + $root . '/doctum-theme', + ], 'base_url' => 'https://phpredis.github.io/', -// 'versions' => $versions, - 'remote_repository' => new GitHubRemoteRepository('phpredis/phpredis', $root), + 'remote_repository' => new GitHubRemoteRepository( + 'phpredis/phpredis', + $root + ), ]); diff --git a/doctum-theme/class.twig b/doctum-theme/class.twig new file mode 100644 index 0000000000..a6c0104606 --- /dev/null +++ b/doctum-theme/class.twig @@ -0,0 +1,70 @@ +{% extends 'default/class.twig' %} +{% from "macros.twig" import method_link, method_source_link, deprecations, internals, todos %} + +{% block method %} + {% set signature_plain = block('method_signature')|striptags %} + {% set signature_plain = signature_plain|replace({' ': ' ', "\n": ' ', "\t": ' '})|trim %} + {% for i in 1..5 %} + {% set signature_plain = signature_plain|replace({' ': ' '}) %} + {% endfor %} + +

    +
    {% if method.class is not same as(class) %}{{ 'in %s'|trans|format(method_link(method, false, true))|raw }} {% endif %}{{ method_source_link(method) }}
    + {{ signature_plain }} +

    +
    + {%- if method.hasSince() -%} + {{ 'Since:'|trans }} {{ method.getSince() }} +
    + {%- endif -%} + {{ deprecations(method) }} + {{ internals(method) }} + +
    + {% if not method.shortdesc and not method.longdesc %} +

    {% trans 'No description' %}

    + {% else %} + {% if method.shortdesc -%} +

    {{ method.shortdesc|desc(class)|md_to_html }}

    + {%- endif %} + {% if method.longdesc -%} +

    {{ method.longdesc|desc(class)|md_to_html }}

    + {%- endif %} + {%- endif %} + {{- todos(method) -}} +
    +
    + {% if method.parameters %} +

    {% trans 'Parameters' %}

    + + {{ block('parameters') }} + {% endif %} + + {% if method.hintDesc or method.hint %} +

    {% trans 'Return Value' %}

    + +
    + {{ block('return') }} +
    + {% endif %} + + {% if method.exceptions %} +

    {% trans 'Exceptions' %}

    + + {{ block('exceptions') }} + {% endif %} + + {% if method.tags('see') %} +

    {% trans 'See also' %}

    + + {{ block('see') }} + {% endif %} + + {% if method.hasExamples() %} +

    {% trans 'Examples' %}

    + + {{ block('examples') }} + {% endif %} +
    +
    +{% endblock %} diff --git a/doctum-theme/css/highlight-github.min.css b/doctum-theme/css/highlight-github.min.css new file mode 100644 index 0000000000..7a9730c0dc --- /dev/null +++ b/doctum-theme/css/highlight-github.min.css @@ -0,0 +1,10 @@ +pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*! + Theme: GitHub + Description: Light theme as seen on github.com + Author: github.com + Maintainer: @Hirse + Updated: 2021-05-15 + + Outdated base version: https://github.com/primer/github-syntax-light + Current colors taken from GitHub's CSS +*/.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#005cc5}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#22863a}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-code,.hljs-comment,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0} diff --git a/doctum-theme/js/highlight.min.js b/doctum-theme/js/highlight.min.js new file mode 100644 index 0000000000..5d699ae6a4 --- /dev/null +++ b/doctum-theme/js/highlight.min.js @@ -0,0 +1,1213 @@ +/*! + Highlight.js v11.9.0 (git: f47103d4f1) + (c) 2006-2023 undefined and other contributors + License: BSD-3-Clause + */ +var hljs=function(){"use strict";function e(n){ +return n instanceof Map?n.clear=n.delete=n.set=()=>{ +throw Error("map is read-only")}:n instanceof Set&&(n.add=n.clear=n.delete=()=>{ +throw Error("set is read-only") +}),Object.freeze(n),Object.getOwnPropertyNames(n).forEach((t=>{ +const a=n[t],i=typeof a;"object"!==i&&"function"!==i||Object.isFrozen(a)||e(a) +})),n}class n{constructor(e){ +void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1} +ignoreMatch(){this.isMatchIgnored=!0}}function t(e){ +return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'") +}function a(e,...n){const t=Object.create(null);for(const n in e)t[n]=e[n] +;return n.forEach((e=>{for(const n in e)t[n]=e[n]})),t}const i=e=>!!e.scope +;class r{constructor(e,n){ +this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){ +this.buffer+=t(e)}openNode(e){if(!i(e))return;const n=((e,{prefix:n})=>{ +if(e.startsWith("language:"))return e.replace("language:","language-") +;if(e.includes(".")){const t=e.split(".") +;return[`${n}${t.shift()}`,...t.map(((e,n)=>`${e}${"_".repeat(n+1)}`))].join(" ") +}return`${n}${e}`})(e.scope,{prefix:this.classPrefix});this.span(n)} +closeNode(e){i(e)&&(this.buffer+="
    ")}value(){return this.buffer}span(e){ +this.buffer+=``}}const s=(e={})=>{const n={children:[]} +;return Object.assign(n,e),n};class o{constructor(){ +this.rootNode=s(),this.stack=[this.rootNode]}get top(){ +return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){ +this.top.children.push(e)}openNode(e){const n=s({scope:e}) +;this.add(n),this.stack.push(n)}closeNode(){ +if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){ +for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)} +walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){ +return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n), +n.children.forEach((n=>this._walk(e,n))),e.closeNode(n)),e}static _collapse(e){ +"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{ +o._collapse(e)})))}}class l extends o{constructor(e){super(),this.options=e} +addText(e){""!==e&&this.add(e)}startScope(e){this.openNode(e)}endScope(){ +this.closeNode()}__addSublanguage(e,n){const t=e.root +;n&&(t.scope="language:"+n),this.add(t)}toHTML(){ +return new r(this,this.options).value()}finalize(){ +return this.closeAllNodes(),!0}}function c(e){ +return e?"string"==typeof e?e:e.source:null}function d(e){return b("(?=",e,")")} +function g(e){return b("(?:",e,")*")}function u(e){return b("(?:",e,")?")} +function b(...e){return e.map((e=>c(e))).join("")}function m(...e){const n=(e=>{ +const n=e[e.length-1] +;return"object"==typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{} +})(e);return"("+(n.capture?"":"?:")+e.map((e=>c(e))).join("|")+")"} +function p(e){return RegExp(e.toString()+"|").exec("").length-1} +const _=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./ +;function h(e,{joinWith:n}){let t=0;return e.map((e=>{t+=1;const n=t +;let a=c(e),i="";for(;a.length>0;){const e=_.exec(a);if(!e){i+=a;break} +i+=a.substring(0,e.index), +a=a.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+(Number(e[1])+n):(i+=e[0], +"("===e[0]&&t++)}return i})).map((e=>`(${e})`)).join(n)} +const f="[a-zA-Z]\\w*",E="[a-zA-Z_]\\w*",y="\\b\\d+(\\.\\d+)?",N="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",w="\\b(0b[01]+)",v={ +begin:"\\\\[\\s\\S]",relevance:0},O={scope:"string",begin:"'",end:"'", +illegal:"\\n",contains:[v]},k={scope:"string",begin:'"',end:'"',illegal:"\\n", +contains:[v]},x=(e,n,t={})=>{const i=a({scope:"comment",begin:e,end:n, +contains:[]},t);i.contains.push({scope:"doctag", +begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)", +end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0}) +;const r=m("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/) +;return i.contains.push({begin:b(/[ ]+/,"(",r,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),i +},M=x("//","$"),S=x("/\\*","\\*/"),A=x("#","$");var C=Object.freeze({ +__proto__:null,APOS_STRING_MODE:O,BACKSLASH_ESCAPE:v,BINARY_NUMBER_MODE:{ +scope:"number",begin:w,relevance:0},BINARY_NUMBER_RE:w,COMMENT:x, +C_BLOCK_COMMENT_MODE:S,C_LINE_COMMENT_MODE:M,C_NUMBER_MODE:{scope:"number", +begin:N,relevance:0},C_NUMBER_RE:N,END_SAME_AS_BEGIN:e=>Object.assign(e,{ +"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}}),HASH_COMMENT_MODE:A,IDENT_RE:f, +MATCH_NOTHING_RE:/\b\B/,METHOD_GUARD:{begin:"\\.\\s*"+E,relevance:0}, +NUMBER_MODE:{scope:"number",begin:y,relevance:0},NUMBER_RE:y, +PHRASAL_WORDS_MODE:{ +begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/ +},QUOTE_STRING_MODE:k,REGEXP_MODE:{scope:"regexp",begin:/\/(?=[^/\n]*\/)/, +end:/\/[gimuy]*/,contains:[v,{begin:/\[/,end:/\]/,relevance:0,contains:[v]}]}, +RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~", +SHEBANG:(e={})=>{const n=/^#![ ]*\// +;return e.binary&&(e.begin=b(n,/.*\b/,e.binary,/\b.*/)),a({scope:"meta",begin:n, +end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)}, +TITLE_MODE:{scope:"title",begin:f,relevance:0},UNDERSCORE_IDENT_RE:E, +UNDERSCORE_TITLE_MODE:{scope:"title",begin:E,relevance:0}});function T(e,n){ +"."===e.input[e.index-1]&&n.ignoreMatch()}function R(e,n){ +void 0!==e.className&&(e.scope=e.className,delete e.className)}function D(e,n){ +n&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)", +e.__beforeBegin=T,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords, +void 0===e.relevance&&(e.relevance=0))}function I(e,n){ +Array.isArray(e.illegal)&&(e.illegal=m(...e.illegal))}function L(e,n){ +if(e.match){ +if(e.begin||e.end)throw Error("begin & end are not supported with match") +;e.begin=e.match,delete e.match}}function B(e,n){ +void 0===e.relevance&&(e.relevance=1)}const $=(e,n)=>{if(!e.beforeMatch)return +;if(e.starts)throw Error("beforeMatch cannot be used with starts") +;const t=Object.assign({},e);Object.keys(e).forEach((n=>{delete e[n] +})),e.keywords=t.keywords,e.begin=b(t.beforeMatch,d(t.begin)),e.starts={ +relevance:0,contains:[Object.assign(t,{endsParent:!0})] +},e.relevance=0,delete t.beforeMatch +},z=["of","and","for","in","not","or","if","then","parent","list","value"],F="keyword" +;function U(e,n,t=F){const a=Object.create(null) +;return"string"==typeof e?i(t,e.split(" ")):Array.isArray(e)?i(t,e):Object.keys(e).forEach((t=>{ +Object.assign(a,U(e[t],n,t))})),a;function i(e,t){ +n&&(t=t.map((e=>e.toLowerCase()))),t.forEach((n=>{const t=n.split("|") +;a[t[0]]=[e,j(t[0],t[1])]}))}}function j(e,n){ +return n?Number(n):(e=>z.includes(e.toLowerCase()))(e)?0:1}const P={},K=e=>{ +console.error(e)},H=(e,...n)=>{console.log("WARN: "+e,...n)},q=(e,n)=>{ +P[`${e}/${n}`]||(console.log(`Deprecated as of ${e}. ${n}`),P[`${e}/${n}`]=!0) +},G=Error();function Z(e,n,{key:t}){let a=0;const i=e[t],r={},s={} +;for(let e=1;e<=n.length;e++)s[e+a]=i[e],r[e+a]=!0,a+=p(n[e-1]) +;e[t]=s,e[t]._emit=r,e[t]._multi=!0}function W(e){(e=>{ +e.scope&&"object"==typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope, +delete e.scope)})(e),"string"==typeof e.beginScope&&(e.beginScope={ +_wrap:e.beginScope}),"string"==typeof e.endScope&&(e.endScope={_wrap:e.endScope +}),(e=>{if(Array.isArray(e.begin)){ +if(e.skip||e.excludeBegin||e.returnBegin)throw K("skip, excludeBegin, returnBegin not compatible with beginScope: {}"), +G +;if("object"!=typeof e.beginScope||null===e.beginScope)throw K("beginScope must be object"), +G;Z(e,e.begin,{key:"beginScope"}),e.begin=h(e.begin,{joinWith:""})}})(e),(e=>{ +if(Array.isArray(e.end)){ +if(e.skip||e.excludeEnd||e.returnEnd)throw K("skip, excludeEnd, returnEnd not compatible with endScope: {}"), +G +;if("object"!=typeof e.endScope||null===e.endScope)throw K("endScope must be object"), +G;Z(e,e.end,{key:"endScope"}),e.end=h(e.end,{joinWith:""})}})(e)}function Q(e){ +function n(n,t){ +return RegExp(c(n),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(t?"g":"")) +}class t{constructor(){ +this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0} +addRule(e,n){ +n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]), +this.matchAt+=p(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null) +;const e=this.regexes.map((e=>e[1]));this.matcherRe=n(h(e,{joinWith:"|" +}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex +;const n=this.matcherRe.exec(e);if(!n)return null +;const t=n.findIndex(((e,n)=>n>0&&void 0!==e)),a=this.matchIndexes[t] +;return n.splice(0,t),Object.assign(n,a)}}class i{constructor(){ +this.rules=[],this.multiRegexes=[], +this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){ +if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t +;return this.rules.slice(e).forEach((([e,t])=>n.addRule(e,t))), +n.compile(),this.multiRegexes[e]=n,n}resumingScanAtSamePosition(){ +return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,n){ +this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){ +const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex +;let t=n.exec(e) +;if(this.resumingScanAtSamePosition())if(t&&t.index===this.lastIndex);else{ +const n=this.getMatcher(0);n.lastIndex=this.lastIndex+1,t=n.exec(e)} +return t&&(this.regexIndex+=t.position+1, +this.regexIndex===this.count&&this.considerAll()),t}} +if(e.compilerExtensions||(e.compilerExtensions=[]), +e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.") +;return e.classNameAliases=a(e.classNameAliases||{}),function t(r,s){const o=r +;if(r.isCompiled)return o +;[R,L,W,$].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))), +r.__beforeBegin=null,[D,I,B].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null +;return"object"==typeof r.keywords&&r.keywords.$pattern&&(r.keywords=Object.assign({},r.keywords), +l=r.keywords.$pattern, +delete r.keywords.$pattern),l=l||/\w+/,r.keywords&&(r.keywords=U(r.keywords,e.case_insensitive)), +o.keywordPatternRe=n(l,!0), +s&&(r.begin||(r.begin=/\B|\b/),o.beginRe=n(o.begin),r.end||r.endsWithParent||(r.end=/\B|\b/), +r.end&&(o.endRe=n(o.end)), +o.terminatorEnd=c(o.end)||"",r.endsWithParent&&s.terminatorEnd&&(o.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)), +r.illegal&&(o.illegalRe=n(r.illegal)), +r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((n=>a(e,{ +variants:null},n)))),e.cachedVariants?e.cachedVariants:X(e)?a(e,{ +starts:e.starts?a(e.starts):null +}):Object.isFrozen(e)?a(e):e))("self"===e?r:e)))),r.contains.forEach((e=>{t(e,o) +})),r.starts&&t(r.starts,s),o.matcher=(e=>{const n=new i +;return e.contains.forEach((e=>n.addRule(e.begin,{rule:e,type:"begin" +}))),e.terminatorEnd&&n.addRule(e.terminatorEnd,{type:"end" +}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n})(o),o}(e)}function X(e){ +return!!e&&(e.endsWithParent||X(e.starts))}class V extends Error{ +constructor(e,n){super(e),this.name="HTMLInjectionError",this.html=n}} +const J=t,Y=a,ee=Symbol("nomatch"),ne=t=>{ +const a=Object.create(null),i=Object.create(null),r=[];let s=!0 +;const o="Could not find the language '{}', did you forget to load/include a language module?",c={ +disableAutodetect:!0,name:"Plain text",contains:[]};let p={ +ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i, +languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-", +cssSelector:"pre code",languages:null,__emitter:l};function _(e){ +return p.noHighlightRe.test(e)}function h(e,n,t){let a="",i="" +;"object"==typeof n?(a=e, +t=n.ignoreIllegals,i=n.language):(q("10.7.0","highlight(lang, code, ...args) has been deprecated."), +q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"), +i=e,a=n),void 0===t&&(t=!0);const r={code:a,language:i};x("before:highlight",r) +;const s=r.result?r.result:f(r.language,r.code,t) +;return s.code=r.code,x("after:highlight",s),s}function f(e,t,i,r){ +const l=Object.create(null);function c(){if(!x.keywords)return void S.addText(A) +;let e=0;x.keywordPatternRe.lastIndex=0;let n=x.keywordPatternRe.exec(A),t="" +;for(;n;){t+=A.substring(e,n.index) +;const i=w.case_insensitive?n[0].toLowerCase():n[0],r=(a=i,x.keywords[a]);if(r){ +const[e,a]=r +;if(S.addText(t),t="",l[i]=(l[i]||0)+1,l[i]<=7&&(C+=a),e.startsWith("_"))t+=n[0];else{ +const t=w.classNameAliases[e]||e;g(n[0],t)}}else t+=n[0] +;e=x.keywordPatternRe.lastIndex,n=x.keywordPatternRe.exec(A)}var a +;t+=A.substring(e),S.addText(t)}function d(){null!=x.subLanguage?(()=>{ +if(""===A)return;let e=null;if("string"==typeof x.subLanguage){ +if(!a[x.subLanguage])return void S.addText(A) +;e=f(x.subLanguage,A,!0,M[x.subLanguage]),M[x.subLanguage]=e._top +}else e=E(A,x.subLanguage.length?x.subLanguage:null) +;x.relevance>0&&(C+=e.relevance),S.__addSublanguage(e._emitter,e.language) +})():c(),A=""}function g(e,n){ +""!==e&&(S.startScope(n),S.addText(e),S.endScope())}function u(e,n){let t=1 +;const a=n.length-1;for(;t<=a;){if(!e._emit[t]){t++;continue} +const a=w.classNameAliases[e[t]]||e[t],i=n[t];a?g(i,a):(A=i,c(),A=""),t++}} +function b(e,n){ +return e.scope&&"string"==typeof e.scope&&S.openNode(w.classNameAliases[e.scope]||e.scope), +e.beginScope&&(e.beginScope._wrap?(g(A,w.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap), +A=""):e.beginScope._multi&&(u(e.beginScope,n),A="")),x=Object.create(e,{parent:{ +value:x}}),x}function m(e,t,a){let i=((e,n)=>{const t=e&&e.exec(n) +;return t&&0===t.index})(e.endRe,a);if(i){if(e["on:end"]){const a=new n(e) +;e["on:end"](t,a),a.isMatchIgnored&&(i=!1)}if(i){ +for(;e.endsParent&&e.parent;)e=e.parent;return e}} +if(e.endsWithParent)return m(e.parent,t,a)}function _(e){ +return 0===x.matcher.regexIndex?(A+=e[0],1):(D=!0,0)}function h(e){ +const n=e[0],a=t.substring(e.index),i=m(x,e,a);if(!i)return ee;const r=x +;x.endScope&&x.endScope._wrap?(d(), +g(n,x.endScope._wrap)):x.endScope&&x.endScope._multi?(d(), +u(x.endScope,e)):r.skip?A+=n:(r.returnEnd||r.excludeEnd||(A+=n), +d(),r.excludeEnd&&(A=n));do{ +x.scope&&S.closeNode(),x.skip||x.subLanguage||(C+=x.relevance),x=x.parent +}while(x!==i.parent);return i.starts&&b(i.starts,e),r.returnEnd?0:n.length} +let y={};function N(a,r){const o=r&&r[0];if(A+=a,null==o)return d(),0 +;if("begin"===y.type&&"end"===r.type&&y.index===r.index&&""===o){ +if(A+=t.slice(r.index,r.index+1),!s){const n=Error(`0 width match regex (${e})`) +;throw n.languageName=e,n.badRule=y.rule,n}return 1} +if(y=r,"begin"===r.type)return(e=>{ +const t=e[0],a=e.rule,i=new n(a),r=[a.__beforeBegin,a["on:begin"]] +;for(const n of r)if(n&&(n(e,i),i.isMatchIgnored))return _(t) +;return a.skip?A+=t:(a.excludeBegin&&(A+=t), +d(),a.returnBegin||a.excludeBegin||(A=t)),b(a,e),a.returnBegin?0:t.length})(r) +;if("illegal"===r.type&&!i){ +const e=Error('Illegal lexeme "'+o+'" for mode "'+(x.scope||"")+'"') +;throw e.mode=x,e}if("end"===r.type){const e=h(r);if(e!==ee)return e} +if("illegal"===r.type&&""===o)return 1 +;if(R>1e5&&R>3*r.index)throw Error("potential infinite loop, way more iterations than matches") +;return A+=o,o.length}const w=v(e) +;if(!w)throw K(o.replace("{}",e)),Error('Unknown language: "'+e+'"') +;const O=Q(w);let k="",x=r||O;const M={},S=new p.__emitter(p);(()=>{const e=[] +;for(let n=x;n!==w;n=n.parent)n.scope&&e.unshift(n.scope) +;e.forEach((e=>S.openNode(e)))})();let A="",C=0,T=0,R=0,D=!1;try{ +if(w.__emitTokens)w.__emitTokens(t,S);else{for(x.matcher.considerAll();;){ +R++,D?D=!1:x.matcher.considerAll(),x.matcher.lastIndex=T +;const e=x.matcher.exec(t);if(!e)break;const n=N(t.substring(T,e.index),e) +;T=e.index+n}N(t.substring(T))}return S.finalize(),k=S.toHTML(),{language:e, +value:k,relevance:C,illegal:!1,_emitter:S,_top:x}}catch(n){ +if(n.message&&n.message.includes("Illegal"))return{language:e,value:J(t), +illegal:!0,relevance:0,_illegalBy:{message:n.message,index:T, +context:t.slice(T-100,T+100),mode:n.mode,resultSoFar:k},_emitter:S};if(s)return{ +language:e,value:J(t),illegal:!1,relevance:0,errorRaised:n,_emitter:S,_top:x} +;throw n}}function E(e,n){n=n||p.languages||Object.keys(a);const t=(e=>{ +const n={value:J(e),illegal:!1,relevance:0,_top:c,_emitter:new p.__emitter(p)} +;return n._emitter.addText(e),n})(e),i=n.filter(v).filter(k).map((n=>f(n,e,!1))) +;i.unshift(t);const r=i.sort(((e,n)=>{ +if(e.relevance!==n.relevance)return n.relevance-e.relevance +;if(e.language&&n.language){if(v(e.language).supersetOf===n.language)return 1 +;if(v(n.language).supersetOf===e.language)return-1}return 0})),[s,o]=r,l=s +;return l.secondBest=o,l}function y(e){let n=null;const t=(e=>{ +let n=e.className+" ";n+=e.parentNode?e.parentNode.className:"" +;const t=p.languageDetectRe.exec(n);if(t){const n=v(t[1]) +;return n||(H(o.replace("{}",t[1])), +H("Falling back to no-highlight mode for this block.",e)),n?t[1]:"no-highlight"} +return n.split(/\s+/).find((e=>_(e)||v(e)))})(e);if(_(t))return +;if(x("before:highlightElement",{el:e,language:t +}),e.dataset.highlighted)return void console.log("Element previously highlighted. To highlight again, first unset `dataset.highlighted`.",e) +;if(e.children.length>0&&(p.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."), +console.warn("https://github.com/highlightjs/highlight.js/wiki/security"), +console.warn("The element with unescaped HTML:"), +console.warn(e)),p.throwUnescapedHTML))throw new V("One of your code blocks includes unescaped HTML.",e.innerHTML) +;n=e;const a=n.textContent,r=t?h(a,{language:t,ignoreIllegals:!0}):E(a) +;e.innerHTML=r.value,e.dataset.highlighted="yes",((e,n,t)=>{const a=n&&i[n]||t +;e.classList.add("hljs"),e.classList.add("language-"+a) +})(e,t,r.language),e.result={language:r.language,re:r.relevance, +relevance:r.relevance},r.secondBest&&(e.secondBest={ +language:r.secondBest.language,relevance:r.secondBest.relevance +}),x("after:highlightElement",{el:e,result:r,text:a})}let N=!1;function w(){ +"loading"!==document.readyState?document.querySelectorAll(p.cssSelector).forEach(y):N=!0 +}function v(e){return e=(e||"").toLowerCase(),a[e]||a[i[e]]} +function O(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach((e=>{ +i[e.toLowerCase()]=n}))}function k(e){const n=v(e) +;return n&&!n.disableAutodetect}function x(e,n){const t=e;r.forEach((e=>{ +e[t]&&e[t](n)}))} +"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{ +N&&w()}),!1),Object.assign(t,{highlight:h,highlightAuto:E,highlightAll:w, +highlightElement:y, +highlightBlock:e=>(q("10.7.0","highlightBlock will be removed entirely in v12.0"), +q("10.7.0","Please use highlightElement now."),y(e)),configure:e=>{p=Y(p,e)}, +initHighlighting:()=>{ +w(),q("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")}, +initHighlightingOnLoad:()=>{ +w(),q("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.") +},registerLanguage:(e,n)=>{let i=null;try{i=n(t)}catch(n){ +if(K("Language definition for '{}' could not be registered.".replace("{}",e)), +!s)throw n;K(n),i=c} +i.name||(i.name=e),a[e]=i,i.rawDefinition=n.bind(null,t),i.aliases&&O(i.aliases,{ +languageName:e})},unregisterLanguage:e=>{delete a[e] +;for(const n of Object.keys(i))i[n]===e&&delete i[n]}, +listLanguages:()=>Object.keys(a),getLanguage:v,registerAliases:O, +autoDetection:k,inherit:Y,addPlugin:e=>{(e=>{ +e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=n=>{ +e["before:highlightBlock"](Object.assign({block:n.el},n)) +}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=n=>{ +e["after:highlightBlock"](Object.assign({block:n.el},n))})})(e),r.push(e)}, +removePlugin:e=>{const n=r.indexOf(e);-1!==n&&r.splice(n,1)}}),t.debugMode=()=>{ +s=!1},t.safeMode=()=>{s=!0},t.versionString="11.9.0",t.regex={concat:b, +lookahead:d,either:m,optional:u,anyNumberOfTimes:g} +;for(const n in C)"object"==typeof C[n]&&e(C[n]);return Object.assign(t,C),t +},te=ne({});te.newInstance=()=>ne({});var ae=te;const ie=e=>({IMPORTANT:{ +scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{ +scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/}, +FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/}, +ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$", +contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{ +scope:"number", +begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?", +relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z_][A-Za-z0-9_-]*/} +}),re=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],se=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],oe=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],le=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],ce=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","block-size","border","border-block","border-block-color","border-block-end","border-block-end-color","border-block-end-style","border-block-end-width","border-block-start","border-block-start-color","border-block-start-style","border-block-start-width","border-block-style","border-block-width","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-inline","border-inline-color","border-inline-end","border-inline-end-color","border-inline-end-style","border-inline-end-width","border-inline-start","border-inline-start-color","border-inline-start-style","border-inline-start-width","border-inline-style","border-inline-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inline-size","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-block","margin-block-end","margin-block-start","margin-bottom","margin-inline","margin-inline-end","margin-inline-start","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-block-size","max-height","max-inline-size","max-width","min-block-size","min-height","min-inline-size","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-block","padding-block-end","padding-block-start","padding-bottom","padding-inline","padding-inline-end","padding-inline-start","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","scrollbar-color","scrollbar-gutter","scrollbar-width","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse(),de=oe.concat(le) +;var ge="[0-9](_*[0-9])*",ue=`\\.(${ge})`,be="[0-9a-fA-F](_*[0-9a-fA-F])*",me={ +className:"number",variants:[{ +begin:`(\\b(${ge})((${ue})|\\.)?|(${ue}))[eE][+-]?(${ge})[fFdD]?\\b`},{ +begin:`\\b(${ge})((${ue})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{ +begin:`(${ue})[fFdD]?\\b`},{begin:`\\b(${ge})[fFdD]\\b`},{ +begin:`\\b0[xX]((${be})\\.?|(${be})?\\.(${be}))[pP][+-]?(${ge})[fFdD]?\\b`},{ +begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${be})[lL]?\\b`},{ +begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}], +relevance:0};function pe(e,n,t){return-1===t?"":e.replace(n,(a=>pe(e,n,t-1)))} +const _e="[A-Za-z$_][0-9A-Za-z$_]*",he=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],fe=["true","false","null","undefined","NaN","Infinity"],Ee=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],ye=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],Ne=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],we=["arguments","this","super","console","window","document","localStorage","sessionStorage","module","global"],ve=[].concat(Ne,Ee,ye) +;function Oe(e){const n=e.regex,t=_e,a={begin:/<[A-Za-z0-9\\._:-]+/, +end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{ +const t=e[0].length+e.index,a=e.input[t] +;if("<"===a||","===a)return void n.ignoreMatch();let i +;">"===a&&(((e,{after:n})=>{const t="",M={ +match:[/const|var|let/,/\s+/,t,/\s*/,/=\s*/,/(async\s*)?/,n.lookahead(x)], +keywords:"async",className:{1:"keyword",3:"title.function"},contains:[f]} +;return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:i,exports:{ +PARAMS_CONTAINS:h,CLASS_REFERENCE:y},illegal:/#(?![$_A-z])/, +contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),{ +label:"use_strict",className:"meta",relevance:10, +begin:/^\s*['"]use (strict|asm)['"]/ +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,d,g,u,b,m,{match:/\$\d+/},l,y,{ +className:"attr",begin:t+n.lookahead(":"),relevance:0},M,{ +begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*", +keywords:"return throw case",relevance:0,contains:[m,e.REGEXP_MODE,{ +className:"function",begin:x,returnBegin:!0,end:"\\s*=>",contains:[{ +className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{ +className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0, +excludeEnd:!0,keywords:i,contains:h}]}]},{begin:/,/,relevance:0},{match:/\s+/, +relevance:0},{variants:[{begin:"<>",end:""},{ +match:/<[A-Za-z0-9\\._:-]+\s*\/>/},{begin:a.begin, +"on:begin":a.isTrulyOpeningTag,end:a.end}],subLanguage:"xml",contains:[{ +begin:a.begin,end:a.end,skip:!0,contains:["self"]}]}]},N,{ +beginKeywords:"while if switch catch for"},{ +begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{", +returnBegin:!0,label:"func.def",contains:[f,e.inherit(e.TITLE_MODE,{begin:t, +className:"title.function"})]},{match:/\.\.\./,relevance:0},O,{match:"\\$"+t, +relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"}, +contains:[f]},w,{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},E,k,{match:/\$[(.]/}]}} +const ke=e=>b(/\b/,e,/\w$/.test(e)?/\b/:/\B/),xe=["Protocol","Type"].map(ke),Me=["init","self"].map(ke),Se=["Any","Self"],Ae=["actor","any","associatedtype","async","await",/as\?/,/as!/,"as","borrowing","break","case","catch","class","consume","consuming","continue","convenience","copy","default","defer","deinit","didSet","distributed","do","dynamic","each","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","macro","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],Ce=["false","nil","true"],Te=["assignment","associativity","higherThan","left","lowerThan","none","right"],Re=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warning"],De=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],Ie=m(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),Le=m(Ie,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),Be=b(Ie,Le,"*"),$e=m(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),ze=m($e,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),Fe=b($e,ze,"*"),Ue=b(/[A-Z]/,ze,"*"),je=["attached","autoclosure",b(/convention\(/,m("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","freestanding","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",b(/objc\(/,Fe,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","Sendable","testable","UIApplicationMain","unchecked","unknown","usableFromInline","warn_unqualified_access"],Pe=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"] +;var Ke=Object.freeze({__proto__:null,grmr_bash:e=>{const n=e.regex,t={},a={ +begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[t]}]} +;Object.assign(t,{className:"variable",variants:[{ +begin:n.concat(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},a]});const i={ +className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},r={ +begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/, +end:/(\w+)/,className:"string"})]}},s={className:"string",begin:/"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,t,i]};i.contains.push(s);const o={begin:/\$?\(\(/, +end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,t] +},l=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10 +}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0, +contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{ +name:"Bash",aliases:["sh"],keywords:{$pattern:/\b[a-z][a-z0-9._-]+\b/, +keyword:["if","then","else","elif","fi","for","while","until","in","do","done","case","esac","function","select"], +literal:["true","false"], +built_in:["break","cd","continue","eval","exec","exit","export","getopts","hash","pwd","readonly","return","shift","test","times","trap","umask","unset","alias","bind","builtin","caller","command","declare","echo","enable","help","let","local","logout","mapfile","printf","read","readarray","source","type","typeset","ulimit","unalias","set","shopt","autoload","bg","bindkey","bye","cap","chdir","clone","comparguments","compcall","compctl","compdescribe","compfiles","compgroups","compquote","comptags","comptry","compvalues","dirs","disable","disown","echotc","echoti","emulate","fc","fg","float","functions","getcap","getln","history","integer","jobs","kill","limit","log","noglob","popd","print","pushd","pushln","rehash","sched","setcap","setopt","stat","suspend","ttyctl","unfunction","unhash","unlimit","unsetopt","vared","wait","whence","where","which","zcompile","zformat","zftp","zle","zmodload","zparseopts","zprof","zpty","zregexparse","zsocket","zstyle","ztcp","chcon","chgrp","chown","chmod","cp","dd","df","dir","dircolors","ln","ls","mkdir","mkfifo","mknod","mktemp","mv","realpath","rm","rmdir","shred","sync","touch","truncate","vdir","b2sum","base32","base64","cat","cksum","comm","csplit","cut","expand","fmt","fold","head","join","md5sum","nl","numfmt","od","paste","ptx","pr","sha1sum","sha224sum","sha256sum","sha384sum","sha512sum","shuf","sort","split","sum","tac","tail","tr","tsort","unexpand","uniq","wc","arch","basename","chroot","date","dirname","du","echo","env","expr","factor","groups","hostid","id","link","logname","nice","nohup","nproc","pathchk","pinky","printenv","printf","pwd","readlink","runcon","seq","sleep","stat","stdbuf","stty","tee","test","timeout","tty","uname","unlink","uptime","users","who","whoami","yes"] +},contains:[l,e.SHEBANG(),c,o,e.HASH_COMMENT_MODE,r,{match:/(\/[a-z._-]+)+/},s,{ +match:/\\"/},{className:"string",begin:/'/,end:/'/},{match:/\\'/},t]}}, +grmr_c:e=>{const n=e.regex,t=e.COMMENT("//","$",{contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{ +match:/\batomic_[a-z]{3,6}\b/}]},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +keyword:["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"], +type:["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"], +literal:"true false NULL", +built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr" +},b=[c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],m={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:b.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:b.concat(["self"]),relevance:0}]),relevance:0},p={ +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[e.inherit(d,{className:"title.function"})], +relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/, +keywords:u,relevance:0,contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/, +end:/\)/,keywords:u,relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s] +}]},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["h"],keywords:u, +disableAutodetect:!0,illegal:"=]/,contains:[{ +beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:c, +strings:o,keywords:u}}},grmr_cpp:e=>{const n=e.regex,t=e.COMMENT("//","$",{ +contains:[{begin:/\\\n/}] +}),a="decltype\\(auto\\)",i="[a-zA-Z_]\\w*::",r="(?!struct)("+a+"|"+n.optional(i)+"[a-zA-Z_]\\w*"+n.optional("<[^<>]+>")+")",s={ +className:"type",begin:"\\b[a-z\\d_]*_t\\b"},o={className:"string",variants:[{ +begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{ +begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)", +end:"'",illegal:"."},e.END_SAME_AS_BEGIN({ +begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},l={ +className:"number",variants:[{begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)" +},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{ +keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include" +},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{ +className:"string",begin:/<.*?>/},t,e.C_BLOCK_COMMENT_MODE]},d={ +className:"title",begin:n.optional(i)+e.IDENT_RE,relevance:0 +},g=n.optional(i)+e.IDENT_RE+"\\s*\\(",u={ +type:["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"], +keyword:["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"], +literal:["NULL","false","nullopt","nullptr","true"],built_in:["_Pragma"], +_type_hints:["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"] +},b={className:"function.dispatch",relevance:0,keywords:{ +_hint:["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"] +}, +begin:n.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,n.lookahead(/(<[^<>]+>|)\s*\(/)) +},m=[b,c,s,t,e.C_BLOCK_COMMENT_MODE,l,o],p={variants:[{begin:/=/,end:/;/},{ +begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}], +keywords:u,contains:m.concat([{begin:/\(/,end:/\)/,keywords:u, +contains:m.concat(["self"]),relevance:0}]),relevance:0},_={className:"function", +begin:"("+r+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0, +keywords:u,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:u,relevance:0},{ +begin:g,returnBegin:!0,contains:[d],relevance:0},{begin:/::/,relevance:0},{ +begin:/:/,endsWithParent:!0,contains:[o,l]},{relevance:0,match:/,/},{ +className:"params",begin:/\(/,end:/\)/,keywords:u,relevance:0, +contains:[t,e.C_BLOCK_COMMENT_MODE,o,l,s,{begin:/\(/,end:/\)/,keywords:u, +relevance:0,contains:["self",t,e.C_BLOCK_COMMENT_MODE,o,l,s]}] +},s,t,e.C_BLOCK_COMMENT_MODE,c]};return{name:"C++", +aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"",keywords:u,contains:["self",s]},{begin:e.IDENT_RE+"::",keywords:u},{ +match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/], +className:{1:"keyword",3:"title.class"}}])}},grmr_csharp:e=>{const n={ +keyword:["abstract","as","base","break","case","catch","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","scoped","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]), +built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","uint","ushort"], +literal:["default","false","null","true"]},t=e.inherit(e.TITLE_MODE,{ +begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{ +begin:"\\b(0b[01']+)"},{ +begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{ +begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)" +}],relevance:0},i={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}] +},r=e.inherit(i,{illegal:/\n/}),s={className:"subst",begin:/\{/,end:/\}/, +keywords:n},o=e.inherit(s,{illegal:/\n/}),l={className:"string",begin:/\$"/, +end:'"',illegal:/\n/,contains:[{begin:/\{\{/},{begin:/\}\}/ +},e.BACKSLASH_ESCAPE,o]},c={className:"string",begin:/\$@"/,end:'"',contains:[{ +begin:/\{\{/},{begin:/\}\}/},{begin:'""'},s]},d=e.inherit(c,{illegal:/\n/, +contains:[{begin:/\{\{/},{begin:/\}\}/},{begin:'""'},o]}) +;s.contains=[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE], +o.contains=[d,l,r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{ +illegal:/\n/})];const g={variants:[c,l,i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},u={begin:"<",end:">",contains:[{beginKeywords:"in out"},t] +},b=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",m={ +begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"], +keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0, +contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{ +begin:"\x3c!--|--\x3e"},{begin:""}]}] +}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#", +end:"$",keywords:{ +keyword:"if else elif endif define undef warning error line region endregion pragma checksum" +}},g,a,{beginKeywords:"class interface",relevance:0,end:/[{;=]/, +illegal:/[^\s:,]/,contains:[{beginKeywords:"where class" +},t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace", +relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"record",relevance:0,end:/[{;=]/,illegal:/[^\s:]/, +contains:[t,u,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta", +begin:"^\\s*\\[(?=[\\w])",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{ +className:"string",begin:/"/,end:/"/}]},{ +beginKeywords:"new return throw await else",relevance:0},{className:"function", +begin:"("+b+"\\s+)+"+e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{ +beginKeywords:"public private protected static internal protected abstract async extern override unsafe virtual new sealed partial", +relevance:0},{begin:e.IDENT_RE+"\\s*(<[^=]+>\\s*)?\\(",returnBegin:!0, +contains:[e.TITLE_MODE,u],relevance:0},{match:/\(\)/},{className:"params", +begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0, +contains:[g,a,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},m]}},grmr_css:e=>{ +const n=e.regex,t=ie(e),a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{ +name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{ +keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"}, +contains:[t.BLOCK_COMMENT,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/ +},t.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0 +},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0 +},t.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{ +begin:":("+oe.join("|")+")"},{begin:":(:)?("+le.join("|")+")"}] +},t.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b"},{ +begin:/:/,end:/[;}{]/, +contains:[t.BLOCK_COMMENT,t.HEXCOLOR,t.IMPORTANT,t.CSS_NUMBER_MODE,...a,{ +begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri" +},contains:[...a,{className:"string",begin:/[^)]/,endsWithParent:!0, +excludeEnd:!0}]},t.FUNCTION_DISPATCH]},{begin:n.lookahead(/@/),end:"[{;]", +relevance:0,illegal:/:/,contains:[{className:"keyword",begin:/@-?\w[\w]*(-\w+)*/ +},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{ +$pattern:/[a-z-]+/,keyword:"and or not only",attribute:se.join(" ")},contains:[{ +begin:/[a-z-]+(?=:)/,className:"attribute"},...a,t.CSS_NUMBER_MODE]}]},{ +className:"selector-tag",begin:"\\b("+re.join("|")+")\\b"}]}},grmr_diff:e=>{ +const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{ +className:"meta",relevance:10, +match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/) +},{className:"comment",variants:[{ +begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/), +end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{ +className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/, +end:/$/}]}},grmr_go:e=>{const n={ +keyword:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var"], +type:["bool","byte","complex64","complex128","error","float32","float64","int8","int16","int32","int64","string","uint8","uint16","uint32","uint64","int","uint","uintptr","rune"], +literal:["true","false","iota","nil"], +built_in:["append","cap","close","complex","copy","imag","len","make","new","panic","print","println","real","recover","delete"] +};return{name:"Go",aliases:["golang"],keywords:n,illegal:"{const n=e.regex;return{name:"GraphQL",aliases:["gql"], +case_insensitive:!0,disableAutodetect:!1,keywords:{ +keyword:["query","mutation","subscription","type","input","schema","directive","interface","union","scalar","fragment","enum","on"], +literal:["true","false","null"]}, +contains:[e.HASH_COMMENT_MODE,e.QUOTE_STRING_MODE,e.NUMBER_MODE,{ +scope:"punctuation",match:/[.]{3}/,relevance:0},{scope:"punctuation", +begin:/[\!\(\)\:\=\[\]\{\|\}]{1}/,relevance:0},{scope:"variable",begin:/\$/, +end:/\W/,excludeEnd:!0,relevance:0},{scope:"meta",match:/@\w+/,excludeEnd:!0},{ +scope:"symbol",begin:n.concat(/[_A-Za-z][_0-9A-Za-z]*/,n.lookahead(/\s*:/)), +relevance:0}],illegal:[/[;<']/,/BEGIN/]}},grmr_ini:e=>{const n=e.regex,t={ +className:"number",relevance:0,variants:[{begin:/([+-]+)?[\d]+_[\d_]+/},{ +begin:e.NUMBER_RE}]},a=e.COMMENT();a.variants=[{begin:/;/,end:/$/},{begin:/#/, +end:/$/}];const i={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{ +begin:/\$\{(.*?)\}/}]},r={className:"literal", +begin:/\bon|off|true|false|yes|no\b/},s={className:"string", +contains:[e.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{ +begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}] +},o={begin:/\[/,end:/\]/,contains:[a,r,i,s,t,"self"],relevance:0 +},l=n.either(/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/);return{ +name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/, +contains:[a,{className:"section",begin:/\[+/,end:/\]+/},{ +begin:n.concat(l,"(\\s*\\.\\s*",l,")*",n.lookahead(/\s*=\s*[^#\s]/)), +className:"attr",starts:{end:/$/,contains:[a,o,r,i,s,t]}}]}},grmr_java:e=>{ +const n=e.regex,t="[\xc0-\u02b8a-zA-Z_$][\xc0-\u02b8a-zA-Z_$0-9]*",a=t+pe("(?:<"+t+"~~~(?:\\s*,\\s*"+t+"~~~)*>)?",/~~~/g,2),i={ +keyword:["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do","sealed","yield","permits"], +literal:["false","true","null"], +type:["char","boolean","long","float","int","byte","short","double"], +built_in:["super","this"]},r={className:"meta",begin:"@"+t,contains:[{ +begin:/\(/,end:/\)/,contains:["self"]}]},s={className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0} +;return{name:"Java",aliases:["jsp"],keywords:i,illegal:/<\/|#/, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/, +relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{ +begin:/import java\.[a-z]+\./,keywords:"import",relevance:2 +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/, +className:"string",contains:[e.BACKSLASH_ESCAPE] +},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{ +match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,t],className:{ +1:"keyword",3:"title.class"}},{match:/non-sealed/,scope:"keyword"},{ +begin:[n.concat(/(?!else)/,t),/\s+/,t,/\s+/,/=(?!=)/],className:{1:"type", +3:"variable",5:"operator"}},{begin:[/record/,/\s+/,t],className:{1:"keyword", +3:"title.class"},contains:[s,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{ +beginKeywords:"new throw return else",relevance:0},{ +begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{ +2:"title.function"},keywords:i,contains:[{className:"params",begin:/\(/, +end:/\)/,keywords:i,relevance:0, +contains:[r,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,me,e.C_BLOCK_COMMENT_MODE] +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},me,r]}},grmr_javascript:Oe, +grmr_json:e=>{const n=["true","false","null"],t={scope:"literal", +beginKeywords:n.join(" ")};return{name:"JSON",keywords:{literal:n},contains:[{ +className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},{ +match:/[{}[\],:]/,className:"punctuation",relevance:0 +},e.QUOTE_STRING_MODE,t,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE], +illegal:"\\S"}},grmr_kotlin:e=>{const n={ +keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual", +built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing", +literal:"true false null"},t={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@" +},a={className:"subst",begin:/\$\{/,end:/\}/,contains:[e.C_NUMBER_MODE]},i={ +className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},r={className:"string", +variants:[{begin:'"""',end:'"""(?=[^"])',contains:[i,a]},{begin:"'",end:"'", +illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/, +contains:[e.BACKSLASH_ESCAPE,i,a]}]};a.contains.push(r);const s={ +className:"meta", +begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?" +},o={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/, +end:/\)/,contains:[e.inherit(r,{className:"string"}),"self"]}] +},l=me,c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),d={ +variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/, +contains:[]}]},g=d;return g.variants[1].contains=[d],d.variants[1].contains=[g], +{name:"Kotlin",aliases:["kt","kts"],keywords:n, +contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag", +begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword", +begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol", +begin:/@\w+/}]}},t,s,o,{className:"function",beginKeywords:"fun",end:"[(]|$", +returnBegin:!0,excludeEnd:!0,keywords:n,relevance:5,contains:[{ +begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0, +contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://, +keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/, +endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/, +endsWithParent:!0,contains:[d,e.C_LINE_COMMENT_MODE,c],relevance:0 +},e.C_LINE_COMMENT_MODE,c,s,o,r,e.C_NUMBER_MODE]},c]},{ +begin:[/class|interface|trait/,/\s+/,e.UNDERSCORE_IDENT_RE],beginScope:{ +3:"title.class"},keywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0, +illegal:"extends implements",contains:[{ +beginKeywords:"public protected internal private constructor" +},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0, +excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,){\s]|$/, +excludeBegin:!0,returnEnd:!0},s,o]},r,{className:"meta",begin:"^#!/usr/bin/env", +end:"$",illegal:"\n"},l]}},grmr_less:e=>{ +const n=ie(e),t=de,a="[\\w-]+",i="("+a+"|@\\{"+a+"\\})",r=[],s=[],o=e=>({ +className:"string",begin:"~?"+e+".*?"+e}),l=(e,n,t)=>({className:e,begin:n, +relevance:t}),c={$pattern:/[a-z-]+/,keyword:"and or not only", +attribute:se.join(" ")},d={begin:"\\(",end:"\\)",contains:s,keywords:c, +relevance:0} +;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,o("'"),o('"'),n.CSS_NUMBER_MODE,{ +begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]", +excludeEnd:!0} +},n.HEXCOLOR,d,l("variable","@@?"+a,10),l("variable","@\\{"+a+"\\}"),l("built_in","~?`[^`]*?`"),{ +className:"attribute",begin:a+"\\s*:",end:":",returnBegin:!0,excludeEnd:!0 +},n.IMPORTANT,{beginKeywords:"and not"},n.FUNCTION_DISPATCH);const g=s.concat({ +begin:/\{/,end:/\}/,contains:r}),u={beginKeywords:"when",endsWithParent:!0, +contains:[{beginKeywords:"and not"}].concat(s)},b={begin:i+"\\s*:", +returnBegin:!0,end:/[;}]/,relevance:0,contains:[{begin:/-(webkit|moz|ms|o)-/ +},n.CSS_VARIABLE,{className:"attribute",begin:"\\b("+ce.join("|")+")\\b", +end:/(?=:)/,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}] +},m={className:"keyword", +begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b", +starts:{end:"[;{}]",keywords:c,returnEnd:!0,contains:s,relevance:0}},p={ +className:"variable",variants:[{begin:"@"+a+"\\s*:",relevance:15},{begin:"@"+a +}],starts:{end:"[;}]",returnEnd:!0,contains:g}},_={variants:[{ +begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:i,end:/\{/}],returnBegin:!0, +returnEnd:!0,illegal:"[<='$\"]",relevance:0, +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,u,l("keyword","all\\b"),l("variable","@\\{"+a+"\\}"),{ +begin:"\\b("+re.join("|")+")\\b",className:"selector-tag" +},n.CSS_NUMBER_MODE,l("selector-tag",i,0),l("selector-id","#"+i),l("selector-class","\\."+i,0),l("selector-tag","&",0),n.ATTRIBUTE_SELECTOR_MODE,{ +className:"selector-pseudo",begin:":("+oe.join("|")+")"},{ +className:"selector-pseudo",begin:":(:)?("+le.join("|")+")"},{begin:/\(/, +end:/\)/,relevance:0,contains:g},{begin:"!important"},n.FUNCTION_DISPATCH]},h={ +begin:a+":(:)?"+`(${t.join("|")})`,returnBegin:!0,contains:[_]} +;return r.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,m,p,h,b,_,u,n.FUNCTION_DISPATCH), +{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:r}}, +grmr_lua:e=>{const n="\\[=*\\[",t="\\]=*\\]",a={begin:n,end:t,contains:["self"] +},i=[e.COMMENT("--(?!"+n+")","$"),e.COMMENT("--"+n,t,{contains:[a],relevance:10 +})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE, +literal:"true false nil", +keyword:"and break do else elseif end for goto if in local not or repeat return then until while", +built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove" +},contains:i.concat([{className:"function",beginKeywords:"function",end:"\\)", +contains:[e.inherit(e.TITLE_MODE,{ +begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params", +begin:"\\(",endsWithParent:!0,contains:i}].concat(i) +},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string", +begin:n,end:t,contains:[a],relevance:5}])}},grmr_makefile:e=>{const n={ +className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%{ +const n={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},t={ +variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{ +begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/, +relevance:2},{ +begin:e.regex.concat(/\[.+?\]\(/,/[A-Za-z][A-Za-z0-9+.-]*/,/:\/\/.*?\)/), +relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{ +begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/ +},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0, +returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)", +excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[", +end:"\\]",excludeBegin:!0,excludeEnd:!0}]},a={className:"strong",contains:[], +variants:[{begin:/_{2}(?!\s)/,end:/_{2}/},{begin:/\*{2}(?!\s)/,end:/\*{2}/}] +},i={className:"emphasis",contains:[],variants:[{begin:/\*(?![*\s])/,end:/\*/},{ +begin:/_(?![_\s])/,end:/_/,relevance:0}]},r=e.inherit(a,{contains:[] +}),s=e.inherit(i,{contains:[]});a.contains.push(s),i.contains.push(r) +;let o=[n,t];return[a,i,r,s].forEach((e=>{e.contains=e.contains.concat(o) +})),o=o.concat(a,i),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{ +className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{ +begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n", +contains:o}]}]},n,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)", +end:"\\s+",excludeEnd:!0},a,i,{className:"quote",begin:"^>\\s+",contains:o, +end:"$"},{className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{ +begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{ +begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))", +contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{ +begin:"^[-\\*]{3,}",end:"$"},t,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{ +className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{ +className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}},grmr_objectivec:e=>{ +const n=/[a-zA-Z@][a-zA-Z0-9_]*/,t={$pattern:n, +keyword:["@interface","@class","@protocol","@implementation"]};return{ +name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"], +keywords:{"variable.language":["this","super"],$pattern:n, +keyword:["while","export","sizeof","typedef","const","struct","for","union","volatile","static","mutable","if","do","return","goto","enum","else","break","extern","asm","case","default","register","explicit","typename","switch","continue","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"], +literal:["false","true","FALSE","TRUE","nil","YES","NO","NULL"], +built_in:["dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"], +type:["int","float","char","unsigned","signed","short","long","double","wchar_t","unichar","void","bool","BOOL","id|0","_Bool"] +},illegal:"/,end:/$/,illegal:"\\n" +},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class", +begin:"("+t.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:t, +contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE, +relevance:0}]}},grmr_perl:e=>{const n=e.regex,t=/[dualxmsipngr]{0,12}/,a={ +$pattern:/[\w.]+/, +keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0" +},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:a},r={begin:/->\{/, +end:/\}/},s={variants:[{begin:/\$\d/},{ +begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])") +},{begin:/[$%@][^\s\w{]/,relevance:0}] +},o=[e.BACKSLASH_ESCAPE,i,s],l=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],c=(e,a,i="\\1")=>{ +const r="\\1"===i?i:n.concat(i,a) +;return n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,r,/(?:\\.|[^\\\/])*?/,i,t) +},d=(e,a,i)=>n.concat(n.concat("(?:",e,")"),a,/(?:\\.|[^\\\/])*?/,i,t),g=[s,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{ +endsWithParent:!0}),r,{className:"string",contains:o,variants:[{ +begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[", +end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{ +begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">", +relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'", +contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`", +contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{ +begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number", +begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b", +relevance:0},{ +begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*", +keywords:"split return print reverse grep",relevance:0, +contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{ +begin:c("s|tr|y",n.either(...l,{capture:!0}))},{begin:c("s|tr|y","\\(","\\)")},{ +begin:c("s|tr|y","\\[","\\]")},{begin:c("s|tr|y","\\{","\\}")}],relevance:2},{ +className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{ +begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...l,{capture:!0 +}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{ +begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub", +end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{ +begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$", +subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}] +}];return i.contains=g,r.contains=g,{name:"Perl",aliases:["pl","pm"],keywords:a, +contains:g}},grmr_php:e=>{ +const n=e.regex,t=/(?![A-Za-z0-9])(?![$])/,a=n.concat(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/,t),i=n.concat(/(\\?[A-Z][a-z0-9_\x7f-\xff]+|\\?[A-Z]+(?=[A-Z][a-z0-9_\x7f-\xff])){1,}/,t),r={ +scope:"variable",match:"\\$+"+a},s={scope:"subst",variants:[{begin:/\$\w+/},{ +begin:/\{\$/,end:/\}/}]},o=e.inherit(e.APOS_STRING_MODE,{illegal:null +}),l="[ \t\n]",c={scope:"string",variants:[e.inherit(e.QUOTE_STRING_MODE,{ +illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(s)}),o,{ +begin:/<<<[ \t]*(?:(\w+)|"(\w+)")\n/,end:/[ \t]*(\w+)\b/, +contains:e.QUOTE_STRING_MODE.contains.concat(s),"on:begin":(e,n)=>{ +n.data._beginMatch=e[1]||e[2]},"on:end":(e,n)=>{ +n.data._beginMatch!==e[1]&&n.ignoreMatch()}},e.END_SAME_AS_BEGIN({ +begin:/<<<[ \t]*'(\w+)'\n/,end:/[ \t]*(\w+)\b/})]},d={scope:"number",variants:[{ +begin:"\\b0[bB][01]+(?:_[01]+)*\\b"},{begin:"\\b0[oO][0-7]+(?:_[0-7]+)*\\b"},{ +begin:"\\b0[xX][\\da-fA-F]+(?:_[\\da-fA-F]+)*\\b"},{ +begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:[eE][+-]?\\d+)?" +}],relevance:0 +},g=["false","null","true"],u=["__CLASS__","__DIR__","__FILE__","__FUNCTION__","__COMPILER_HALT_OFFSET__","__LINE__","__METHOD__","__NAMESPACE__","__TRAIT__","die","echo","exit","include","include_once","print","require","require_once","array","abstract","and","as","binary","bool","boolean","break","callable","case","catch","class","clone","const","continue","declare","default","do","double","else","elseif","empty","enddeclare","endfor","endforeach","endif","endswitch","endwhile","enum","eval","extends","final","finally","float","for","foreach","from","global","goto","if","implements","instanceof","insteadof","int","integer","interface","isset","iterable","list","match|0","mixed","new","never","object","or","private","protected","public","readonly","real","return","string","switch","throw","trait","try","unset","use","var","void","while","xor","yield"],b=["Error|0","AppendIterator","ArgumentCountError","ArithmeticError","ArrayIterator","ArrayObject","AssertionError","BadFunctionCallException","BadMethodCallException","CachingIterator","CallbackFilterIterator","CompileError","Countable","DirectoryIterator","DivisionByZeroError","DomainException","EmptyIterator","ErrorException","Exception","FilesystemIterator","FilterIterator","GlobIterator","InfiniteIterator","InvalidArgumentException","IteratorIterator","LengthException","LimitIterator","LogicException","MultipleIterator","NoRewindIterator","OutOfBoundsException","OutOfRangeException","OuterIterator","OverflowException","ParentIterator","ParseError","RangeException","RecursiveArrayIterator","RecursiveCachingIterator","RecursiveCallbackFilterIterator","RecursiveDirectoryIterator","RecursiveFilterIterator","RecursiveIterator","RecursiveIteratorIterator","RecursiveRegexIterator","RecursiveTreeIterator","RegexIterator","RuntimeException","SeekableIterator","SplDoublyLinkedList","SplFileInfo","SplFileObject","SplFixedArray","SplHeap","SplMaxHeap","SplMinHeap","SplObjectStorage","SplObserver","SplPriorityQueue","SplQueue","SplStack","SplSubject","SplTempFileObject","TypeError","UnderflowException","UnexpectedValueException","UnhandledMatchError","ArrayAccess","BackedEnum","Closure","Fiber","Generator","Iterator","IteratorAggregate","Serializable","Stringable","Throwable","Traversable","UnitEnum","WeakReference","WeakMap","Directory","__PHP_Incomplete_Class","parent","php_user_filter","self","static","stdClass"],m={ +keyword:u,literal:(e=>{const n=[];return e.forEach((e=>{ +n.push(e),e.toLowerCase()===e?n.push(e.toUpperCase()):n.push(e.toLowerCase()) +})),n})(g),built_in:b},p=e=>e.map((e=>e.replace(/\|\d+$/,""))),_={variants:[{ +match:[/new/,n.concat(l,"+"),n.concat("(?!",p(b).join("\\b|"),"\\b)"),i],scope:{ +1:"keyword",4:"title.class"}}]},h=n.concat(a,"\\b(?!\\()"),f={variants:[{ +match:[n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{2:"variable.constant" +}},{match:[/::/,/class/],scope:{2:"variable.language"}},{ +match:[i,n.concat(/::/,n.lookahead(/(?!class\b)/)),h],scope:{1:"title.class", +3:"variable.constant"}},{match:[i,n.concat("::",n.lookahead(/(?!class\b)/))], +scope:{1:"title.class"}},{match:[i,/::/,/class/],scope:{1:"title.class", +3:"variable.language"}}]},E={scope:"attr", +match:n.concat(a,n.lookahead(":"),n.lookahead(/(?!::)/))},y={relevance:0, +begin:/\(/,end:/\)/,keywords:m,contains:[E,r,f,e.C_BLOCK_COMMENT_MODE,c,d,_] +},N={relevance:0, +match:[/\b/,n.concat("(?!fn\\b|function\\b|",p(u).join("\\b|"),"|",p(b).join("\\b|"),"\\b)"),a,n.concat(l,"*"),n.lookahead(/(?=\()/)], +scope:{3:"title.function.invoke"},contains:[y]};y.contains.push(N) +;const w=[E,f,e.C_BLOCK_COMMENT_MODE,c,d,_];return{case_insensitive:!1, +keywords:m,contains:[{begin:n.concat(/#\[\s*/,i),beginScope:"meta",end:/]/, +endScope:"meta",keywords:{literal:g,keyword:["new","array"]},contains:[{ +begin:/\[/,end:/]/,keywords:{literal:g,keyword:["new","array"]}, +contains:["self",...w]},...w,{scope:"meta",match:i}] +},e.HASH_COMMENT_MODE,e.COMMENT("//","$"),e.COMMENT("/\\*","\\*/",{contains:[{ +scope:"doctag",match:"@[A-Za-z]+"}]}),{match:/__halt_compiler\(\);/, +keywords:"__halt_compiler",starts:{scope:"comment",end:e.MATCH_NOTHING_RE, +contains:[{match:/\?>/,scope:"meta",endsParent:!0}]}},{scope:"meta",variants:[{ +begin:/<\?php/,relevance:10},{begin:/<\?=/},{begin:/<\?/,relevance:.1},{ +begin:/\?>/}]},{scope:"variable.language",match:/\$this\b/},r,N,f,{ +match:[/const/,/\s/,a],scope:{1:"keyword",3:"variable.constant"}},_,{ +scope:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/, +excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use" +},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{scope:"params", +begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:m, +contains:["self",r,f,e.C_BLOCK_COMMENT_MODE,c,d]}]},{scope:"class",variants:[{ +beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait", +illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{ +beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{ +beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/, +contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{scope:"title.class"})]},{ +beginKeywords:"use",relevance:0,end:";",contains:[{ +match:/\b(as|const|function)\b/,scope:"keyword"},e.UNDERSCORE_TITLE_MODE]},c,d]} +},grmr_php_template:e=>({name:"PHP template",subLanguage:"xml",contains:[{ +begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*", +end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0 +},e.inherit(e.APOS_STRING_MODE,{illegal:null,className:null,contains:null, +skip:!0}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null,className:null, +contains:null,skip:!0})]}]}),grmr_plaintext:e=>({name:"Plain text", +aliases:["text","txt"],disableAutodetect:!0}),grmr_python:e=>{ +const n=e.regex,t=/[\p{XID_Start}_]\p{XID_Continue}*/u,a=["and","as","assert","async","await","break","case","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","match","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],i={ +$pattern:/[A-Za-z]\w+|__\w+__/,keyword:a, +built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"], +literal:["__debug__","Ellipsis","False","None","NotImplemented","True"], +type:["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"] +},r={className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/, +end:/\}/,keywords:i,illegal:/#/},o={begin:/\{\{/,relevance:0},l={ +className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/, +contains:[e.BACKSLASH_ESCAPE,r],relevance:10},{ +begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/, +contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/, +end:/"""/,contains:[e.BACKSLASH_ESCAPE,r,o,s]},{begin:/([uU]|[rR])'/,end:/'/, +relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{ +begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/, +end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/, +contains:[e.BACKSLASH_ESCAPE,o,s]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/, +contains:[e.BACKSLASH_ESCAPE,o,s]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE] +},c="[0-9](_?[0-9])*",d=`(\\b(${c}))?\\.(${c})|\\b(${c})\\.`,g="\\b|"+a.join("|"),u={ +className:"number",relevance:0,variants:[{ +begin:`(\\b(${c})|(${d}))[eE][+-]?(${c})[jJ]?(?=${g})`},{begin:`(${d})[jJ]?`},{ +begin:`\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${g})`},{ +begin:`\\b0[bB](_?[01])+[lL]?(?=${g})`},{begin:`\\b0[oO](_?[0-7])+[lL]?(?=${g})` +},{begin:`\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${g})`},{begin:`\\b(${c})[jJ](?=${g})` +}]},b={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:i, +contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},m={ +className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/, +end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:i, +contains:["self",r,u,l,e.HASH_COMMENT_MODE]}]};return s.contains=[l,u,r],{ +name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:i, +illegal:/(<\/|\?)|=>/,contains:[r,u,{begin:/\bself\b/},{beginKeywords:"if", +relevance:0},l,b,e.HASH_COMMENT_MODE,{match:[/\bdef/,/\s+/,t],scope:{ +1:"keyword",3:"title.function"},contains:[m]},{variants:[{ +match:[/\bclass/,/\s+/,t,/\s*/,/\(\s*/,t,/\s*\)/]},{match:[/\bclass/,/\s+/,t]}], +scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{ +className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[u,m,l]}]}}, +grmr_python_repl:e=>({aliases:["pycon"],contains:[{className:"meta.prompt", +starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{ +begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}),grmr_r:e=>{ +const n=e.regex,t=/(?:(?:[a-zA-Z]|\.[._a-zA-Z])[._a-zA-Z0-9]*)|\.(?!\d)/,a=n.either(/0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*[pP][+-]?\d+i?/,/0[xX][0-9a-fA-F]+(?:[pP][+-]?\d+)?[Li]?/,/(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?[Li]?/),i=/[=!<>:]=|\|\||&&|:::?|<-|<<-|->>|->|\|>|[-+*\/?!$&|:<=>@^~]|\*\*/,r=n.either(/[()]/,/[{}]/,/\[\[/,/[[\]]/,/\\/,/,/) +;return{name:"R",keywords:{$pattern:t, +keyword:"function if in break next repeat else for while", +literal:"NULL NA TRUE FALSE Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10", +built_in:"LETTERS letters month.abb month.name pi T F abs acos acosh all any anyNA Arg as.call as.character as.complex as.double as.environment as.integer as.logical as.null.default as.numeric as.raw asin asinh atan atanh attr attributes baseenv browser c call ceiling class Conj cos cosh cospi cummax cummin cumprod cumsum digamma dim dimnames emptyenv exp expression floor forceAndCall gamma gc.time globalenv Im interactive invisible is.array is.atomic is.call is.character is.complex is.double is.environment is.expression is.finite is.function is.infinite is.integer is.language is.list is.logical is.matrix is.na is.name is.nan is.null is.numeric is.object is.pairlist is.raw is.recursive is.single is.symbol lazyLoadDBfetch length lgamma list log max min missing Mod names nargs nzchar oldClass on.exit pos.to.env proc.time prod quote range Re rep retracemem return round seq_along seq_len seq.int sign signif sin sinh sinpi sqrt standardGeneric substitute sum switch tan tanh tanpi tracemem trigamma trunc unclass untracemem UseMethod xtfrm" +},contains:[e.COMMENT(/#'/,/$/,{contains:[{scope:"doctag",match:/@examples/, +starts:{end:n.lookahead(n.either(/\n^#'\s*(?=@[a-zA-Z]+)/,/\n^(?!#')/)), +endsParent:!0}},{scope:"doctag",begin:"@param",end:/$/,contains:[{ +scope:"variable",variants:[{match:t},{match:/`(?:\\.|[^`\\])+`/}],endsParent:!0 +}]},{scope:"doctag",match:/@[a-zA-Z]+/},{scope:"keyword",match:/\\[a-zA-Z]+/}] +}),e.HASH_COMMENT_MODE,{scope:"string",contains:[e.BACKSLASH_ESCAPE], +variants:[e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\(/,end:/\)(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\{/,end:/\}(-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]"(-*)\[/,end:/\](-*)"/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\(/,end:/\)(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\{/,end:/\}(-*)'/ +}),e.END_SAME_AS_BEGIN({begin:/[rR]'(-*)\[/,end:/\](-*)'/}),{begin:'"',end:'"', +relevance:0},{begin:"'",end:"'",relevance:0}]},{relevance:0,variants:[{scope:{ +1:"operator",2:"number"},match:[i,a]},{scope:{1:"operator",2:"number"}, +match:[/%[^%]*%/,a]},{scope:{1:"punctuation",2:"number"},match:[r,a]},{scope:{ +2:"number"},match:[/[^a-zA-Z0-9._]|^/,a]}]},{scope:{3:"operator"}, +match:[t,/\s+/,/<-/,/\s+/]},{scope:"operator",relevance:0,variants:[{match:i},{ +match:/%[^%]*%/}]},{scope:"punctuation",relevance:0,match:r},{begin:"`",end:"`", +contains:[{begin:/\\./}]}]}},grmr_ruby:e=>{ +const n=e.regex,t="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",a=n.either(/\b([A-Z]+[a-z0-9]+)+/,/\b([A-Z]+[a-z0-9]+)+[A-Z]+/),i=n.concat(a,/(::\w+)*/),r={ +"variable.constant":["__FILE__","__LINE__","__ENCODING__"], +"variable.language":["self","super"], +keyword:["alias","and","begin","BEGIN","break","case","class","defined","do","else","elsif","end","END","ensure","for","if","in","module","next","not","or","redo","require","rescue","retry","return","then","undef","unless","until","when","while","yield","include","extend","prepend","public","private","protected","raise","throw"], +built_in:["proc","lambda","attr_accessor","attr_reader","attr_writer","define_method","private_constant","module_function"], +literal:["true","false","nil"]},s={className:"doctag",begin:"@[A-Za-z]+"},o={ +begin:"#<",end:">"},l=[e.COMMENT("#","$",{contains:[s] +}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10 +}),e.COMMENT("^__END__",e.MATCH_NOTHING_RE)],c={className:"subst",begin:/#\{/, +end:/\}/,keywords:r},d={className:"string",contains:[e.BACKSLASH_ESCAPE,c], +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{ +begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{ +begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//, +end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{ +begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{ +begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{ +begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{ +begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{ +begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)), +contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/, +contains:[e.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",u={className:"number", +relevance:0,variants:[{ +begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{ +begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b" +},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{ +begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{ +begin:"\\b0(_?[0-7])+r?i?\\b"}]},b={variants:[{match:/\(\)/},{ +className:"params",begin:/\(/,end:/(?=\))/,excludeBegin:!0,endsParent:!0, +keywords:r}]},m=[d,{variants:[{match:[/class\s+/,i,/\s+<\s+/,i]},{ +match:[/\b(class|module)\s+/,i]}],scope:{2:"title.class", +4:"title.class.inherited"},keywords:r},{match:[/(include|extend)\s+/,i],scope:{ +2:"title.class"},keywords:r},{relevance:0,match:[i,/\.new[. (]/],scope:{ +1:"title.class"}},{relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/, +className:"variable.constant"},{relevance:0,match:a,scope:"title.class"},{ +match:[/def/,/\s+/,t],scope:{1:"keyword",3:"title.function"},contains:[b]},{ +begin:e.IDENT_RE+"::"},{className:"symbol", +begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol", +begin:":(?!\\s)",contains:[d,{begin:t}],relevance:0},u,{className:"variable", +begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{ +className:"params",begin:/\|/,end:/\|/,excludeBegin:!0,excludeEnd:!0, +relevance:0,keywords:r},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*", +keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c], +illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{ +begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[", +end:"\\][a-z]*"}]}].concat(o,l),relevance:0}].concat(o,l) +;c.contains=m,b.contains=m;const p=[{begin:/^\s*=>/,starts:{end:"$",contains:m} +},{className:"meta.prompt", +begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+[>*]|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])", +starts:{end:"$",keywords:r,contains:m}}];return l.unshift(o),{name:"Ruby", +aliases:["rb","gemspec","podspec","thor","irb"],keywords:r,illegal:/\/\*/, +contains:[e.SHEBANG({binary:"ruby"})].concat(p).concat(l).concat(m)}}, +grmr_rust:e=>{const n=e.regex,t={className:"title.function.invoke",relevance:0, +begin:n.concat(/\b/,/(?!let|for|while|if|else|match\b)/,e.IDENT_RE,n.lookahead(/\s*\(/)) +},a="([ui](8|16|32|64|128|size)|f(32|64))?",i=["drop ","Copy","Send","Sized","Sync","Drop","Fn","FnMut","FnOnce","ToOwned","Clone","Debug","PartialEq","PartialOrd","Eq","Ord","AsRef","AsMut","Into","From","Default","Iterator","Extend","IntoIterator","DoubleEndedIterator","ExactSizeIterator","SliceConcatExt","ToString","assert!","assert_eq!","bitflags!","bytes!","cfg!","col!","concat!","concat_idents!","debug_assert!","debug_assert_eq!","env!","eprintln!","panic!","file!","format!","format_args!","include_bytes!","include_str!","line!","local_data_key!","module_path!","option_env!","print!","println!","select!","stringify!","try!","unimplemented!","unreachable!","vec!","write!","writeln!","macro_rules!","assert_ne!","debug_assert_ne!"],r=["i8","i16","i32","i64","i128","isize","u8","u16","u32","u64","u128","usize","f32","f64","str","char","bool","Box","Option","Result","String","Vec"] +;return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",type:r, +keyword:["abstract","as","async","await","become","box","break","const","continue","crate","do","dyn","else","enum","extern","false","final","fn","for","if","impl","in","let","loop","macro","match","mod","move","mut","override","priv","pub","ref","return","self","Self","static","struct","super","trait","true","try","type","typeof","unsafe","unsized","use","virtual","where","while","yield"], +literal:["true","false","Some","None","Ok","Err"],built_in:i},illegal:""},t]}}, +grmr_scss:e=>{const n=ie(e),t=le,a=oe,i="@[a-z-]+",r={className:"variable", +begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b",relevance:0};return{name:"SCSS", +case_insensitive:!0,illegal:"[=/|']", +contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,n.CSS_NUMBER_MODE,{ +className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{ +className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0 +},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag", +begin:"\\b("+re.join("|")+")\\b",relevance:0},{className:"selector-pseudo", +begin:":("+a.join("|")+")"},{className:"selector-pseudo", +begin:":(:)?("+t.join("|")+")"},r,{begin:/\(/,end:/\)/, +contains:[n.CSS_NUMBER_MODE]},n.CSS_VARIABLE,{className:"attribute", +begin:"\\b("+ce.join("|")+")\\b"},{ +begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b" +},{begin:/:/,end:/[;}{]/,relevance:0, +contains:[n.BLOCK_COMMENT,r,n.HEXCOLOR,n.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.IMPORTANT,n.FUNCTION_DISPATCH] +},{begin:"@(page|font-face)",keywords:{$pattern:i,keyword:"@page @font-face"}},{ +begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/, +keyword:"and or not only",attribute:se.join(" ")},contains:[{begin:i, +className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute" +},r,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,n.HEXCOLOR,n.CSS_NUMBER_MODE] +},n.FUNCTION_DISPATCH]}},grmr_shell:e=>({name:"Shell Session", +aliases:["console","shellsession"],contains:[{className:"meta.prompt", +begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/, +subLanguage:"bash"}}]}),grmr_sql:e=>{ +const n=e.regex,t=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],r=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],s=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],o=r,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!r.includes(e))),c={ +begin:n.concat(/\b/,n.either(...o),/\s*\(/),relevance:0,keywords:{built_in:o}} +;return{name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{ +$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:n,when:t}={})=>{const a=t +;return n=n||[],e.map((e=>e.match(/\|\d+$/)||n.includes(e)?e:a(e)?e+"|0":e)) +})(l,{when:e=>e.length<3}),literal:a,type:i, +built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"] +},contains:[{begin:n.either(...s),relevance:0,keywords:{$pattern:/[\w\.]+/, +keyword:l.concat(s),literal:a,type:i}},{className:"type", +begin:n.either("double precision","large object","with timezone","without timezone") +},c,{className:"variable",begin:/@[a-z0-9][a-z0-9_]*/},{className:"string", +variants:[{begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/, +contains:[{begin:/""/}]},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,{ +className:"operator",begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/, +relevance:0}]}},grmr_swift:e=>{const n={match:/\s+/,relevance:0 +},t=e.COMMENT("/\\*","\\*/",{contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],i={ +match:[/\./,m(...xe,...Me)],className:{2:"keyword"}},r={match:b(/\./,m(...Ae)), +relevance:0},s=Ae.filter((e=>"string"==typeof e)).concat(["_|0"]),o={variants:[{ +className:"keyword", +match:m(...Ae.filter((e=>"string"!=typeof e)).concat(Se).map(ke),...Me)}]},l={ +$pattern:m(/\b\w+/,/#\w+/),keyword:s.concat(Re),literal:Ce},c=[i,r,o],g=[{ +match:b(/\./,m(...De)),relevance:0},{className:"built_in", +match:b(/\b/,m(...De),/(?=\()/)}],u={match:/->/,relevance:0},p=[u,{ +className:"operator",relevance:0,variants:[{match:Be},{match:`\\.(\\.|${Le})+`}] +}],_="([0-9]_*)+",h="([0-9a-fA-F]_*)+",f={className:"number",relevance:0, +variants:[{match:`\\b(${_})(\\.(${_}))?([eE][+-]?(${_}))?\\b`},{ +match:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(${_}))?\\b`},{match:/\b0o([0-7]_*)+\b/ +},{match:/\b0b([01]_*)+\b/}]},E=(e="")=>({className:"subst",variants:[{ +match:b(/\\/,e,/[0\\tnr"']/)},{match:b(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}] +}),y=(e="")=>({className:"subst",match:b(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/) +}),N=(e="")=>({className:"subst",label:"interpol",begin:b(/\\/,e,/\(/),end:/\)/ +}),w=(e="")=>({begin:b(e,/"""/),end:b(/"""/,e),contains:[E(e),y(e),N(e)] +}),v=(e="")=>({begin:b(e,/"/),end:b(/"/,e),contains:[E(e),N(e)]}),O={ +className:"string", +variants:[w(),w("#"),w("##"),w("###"),v(),v("#"),v("##"),v("###")] +},k=[e.BACKSLASH_ESCAPE,{begin:/\[/,end:/\]/,relevance:0, +contains:[e.BACKSLASH_ESCAPE]}],x={begin:/\/[^\s](?=[^/\n]*\/)/,end:/\//, +contains:k},M=e=>{const n=b(e,/\//),t=b(/\//,e);return{begin:n,end:t, +contains:[...k,{scope:"comment",begin:`#(?!.*${t})`,end:/$/}]}},S={ +scope:"regexp",variants:[M("###"),M("##"),M("#"),x]},A={match:b(/`/,Fe,/`/) +},C=[A,{className:"variable",match:/\$\d+/},{className:"variable", +match:`\\$${ze}+`}],T=[{match:/(@|#(un)?)available/,scope:"keyword",starts:{ +contains:[{begin:/\(/,end:/\)/,keywords:Pe,contains:[...p,f,O]}]}},{ +scope:"keyword",match:b(/@/,m(...je))},{scope:"meta",match:b(/@/,Fe)}],R={ +match:d(/\b[A-Z]/),relevance:0,contains:[{className:"type", +match:b(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,ze,"+") +},{className:"type",match:Ue,relevance:0},{match:/[?!]+/,relevance:0},{ +match:/\.\.\./,relevance:0},{match:b(/\s+&\s+/,d(Ue)),relevance:0}]},D={ +begin://,keywords:l,contains:[...a,...c,...T,u,R]};R.contains.push(D) +;const I={begin:/\(/,end:/\)/,relevance:0,keywords:l,contains:["self",{ +match:b(Fe,/\s*:/),keywords:"_|0",relevance:0 +},...a,S,...c,...g,...p,f,O,...C,...T,R]},L={begin://, +keywords:"repeat each",contains:[...a,R]},B={begin:/\(/,end:/\)/,keywords:l, +contains:[{begin:m(d(b(Fe,/\s*:/)),d(b(Fe,/\s+/,Fe,/\s*:/))),end:/:/, +relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params", +match:Fe}]},...a,...c,...p,f,O,...T,R,I],endsParent:!0,illegal:/["']/},$={ +match:[/(func|macro)/,/\s+/,m(A.match,Fe,Be)],className:{1:"keyword", +3:"title.function"},contains:[L,B,n],illegal:[/\[/,/%/]},z={ +match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"}, +contains:[L,B,n],illegal:/\[|%/},F={match:[/operator/,/\s+/,Be],className:{ +1:"keyword",3:"title"}},U={begin:[/precedencegroup/,/\s+/,Ue],className:{ +1:"keyword",3:"title"},contains:[R],keywords:[...Te,...Ce],end:/}/} +;for(const e of O.variants){const n=e.contains.find((e=>"interpol"===e.label)) +;n.keywords=l;const t=[...c,...g,...p,f,O,...C];n.contains=[...t,{begin:/\(/, +end:/\)/,contains:["self",...t]}]}return{name:"Swift",keywords:l, +contains:[...a,$,z,{beginKeywords:"struct protocol class extension enum actor", +end:"\\{",excludeEnd:!0,keywords:l,contains:[e.inherit(e.TITLE_MODE,{ +className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...c] +},F,U,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0 +},S,...c,...g,...p,f,O,...C,...T,R,I]}},grmr_typescript:e=>{ +const n=Oe(e),t=_e,a=["any","void","number","boolean","string","object","never","symbol","bigint","unknown"],i={ +beginKeywords:"namespace",end:/\{/,excludeEnd:!0, +contains:[n.exports.CLASS_REFERENCE]},r={beginKeywords:"interface",end:/\{/, +excludeEnd:!0,keywords:{keyword:"interface extends",built_in:a}, +contains:[n.exports.CLASS_REFERENCE]},s={$pattern:_e, +keyword:he.concat(["type","namespace","interface","public","private","protected","implements","declare","abstract","readonly","enum","override"]), +literal:fe,built_in:ve.concat(a),"variable.language":we},o={className:"meta", +begin:"@"+t},l=(e,n,t)=>{const a=e.contains.findIndex((e=>e.label===n)) +;if(-1===a)throw Error("can not find mode to replace");e.contains.splice(a,1,t)} +;return Object.assign(n.keywords,s), +n.exports.PARAMS_CONTAINS.push(o),n.contains=n.contains.concat([o,i,r]), +l(n,"shebang",e.SHEBANG()),l(n,"use_strict",{className:"meta",relevance:10, +begin:/^\s*['"]use strict['"]/ +}),n.contains.find((e=>"func.def"===e.label)).relevance=0,Object.assign(n,{ +name:"TypeScript",aliases:["ts","tsx","mts","cts"]}),n},grmr_vbnet:e=>{ +const n=e.regex,t=/\d{1,2}\/\d{1,2}\/\d{4}/,a=/\d{4}-\d{1,2}-\d{1,2}/,i=/(\d|1[012])(:\d+){0,2} *(AM|PM)/,r=/\d{1,2}(:\d{1,2}){1,2}/,s={ +className:"literal",variants:[{begin:n.concat(/# */,n.either(a,t),/ *#/)},{ +begin:n.concat(/# */,r,/ *#/)},{begin:n.concat(/# */,i,/ *#/)},{ +begin:n.concat(/# */,n.either(a,t),/ +/,n.either(i,r),/ *#/)}] +},o=e.COMMENT(/'''/,/$/,{contains:[{className:"doctag",begin:/<\/?/,end:/>/}] +}),l=e.COMMENT(null,/$/,{variants:[{begin:/'/},{begin:/([\t ]|^)REM(?=\s)/}]}) +;return{name:"Visual Basic .NET",aliases:["vb"],case_insensitive:!0, +classNameAliases:{label:"symbol"},keywords:{ +keyword:"addhandler alias aggregate ansi as async assembly auto binary by byref byval call case catch class compare const continue custom declare default delegate dim distinct do each equals else elseif end enum erase error event exit explicit finally for friend from function get global goto group handles if implements imports in inherits interface into iterator join key let lib loop me mid module mustinherit mustoverride mybase myclass namespace narrowing new next notinheritable notoverridable of off on operator option optional order overloads overridable overrides paramarray partial preserve private property protected public raiseevent readonly redim removehandler resume return select set shadows shared skip static step stop structure strict sub synclock take text then throw to try unicode until using when where while widening with withevents writeonly yield", +built_in:"addressof and andalso await directcast gettype getxmlnamespace is isfalse isnot istrue like mod nameof new not or orelse trycast typeof xor cbool cbyte cchar cdate cdbl cdec cint clng cobj csbyte cshort csng cstr cuint culng cushort", +type:"boolean byte char date decimal double integer long object sbyte short single string uinteger ulong ushort", +literal:"true false nothing"}, +illegal:"//|\\{|\\}|endif|gosub|variant|wend|^\\$ ",contains:[{ +className:"string",begin:/"(""|[^/n])"C\b/},{className:"string",begin:/"/, +end:/"/,illegal:/\n/,contains:[{begin:/""/}]},s,{className:"number",relevance:0, +variants:[{begin:/\b\d[\d_]*((\.[\d_]+(E[+-]?[\d_]+)?)|(E[+-]?[\d_]+))[RFD@!#]?/ +},{begin:/\b\d[\d_]*((U?[SIL])|[%&])?/},{begin:/&H[\dA-F_]+((U?[SIL])|[%&])?/},{ +begin:/&O[0-7_]+((U?[SIL])|[%&])?/},{begin:/&B[01_]+((U?[SIL])|[%&])?/}]},{ +className:"label",begin:/^\w+:/},o,l,{className:"meta", +begin:/[\t ]*#(const|disable|else|elseif|enable|end|externalsource|if|region)\b/, +end:/$/,keywords:{ +keyword:"const disable else elseif enable end externalsource if region then"}, +contains:[l]}]}},grmr_wasm:e=>{e.regex;const n=e.COMMENT(/\(;/,/;\)/) +;return n.contains.push("self"),{name:"WebAssembly",keywords:{$pattern:/[\w.]+/, +keyword:["anyfunc","block","br","br_if","br_table","call","call_indirect","data","drop","elem","else","end","export","func","global.get","global.set","local.get","local.set","local.tee","get_global","get_local","global","if","import","local","loop","memory","memory.grow","memory.size","module","mut","nop","offset","param","result","return","select","set_global","set_local","start","table","tee_local","then","type","unreachable"] +},contains:[e.COMMENT(/;;/,/$/),n,{match:[/(?:offset|align)/,/\s*/,/=/], +className:{1:"keyword",3:"operator"}},{className:"variable",begin:/\$[\w_]+/},{ +match:/(\((?!;)|\))+/,className:"punctuation",relevance:0},{ +begin:[/(?:func|call|call_indirect)/,/\s+/,/\$[^\s)]+/],className:{1:"keyword", +3:"title.function"}},e.QUOTE_STRING_MODE,{match:/(i32|i64|f32|f64)(?!\.)/, +className:"type"},{className:"keyword", +match:/\b(f32|f64|i32|i64)(?:\.(?:abs|add|and|ceil|clz|const|convert_[su]\/i(?:32|64)|copysign|ctz|demote\/f64|div(?:_[su])?|eqz?|extend_[su]\/i32|floor|ge(?:_[su])?|gt(?:_[su])?|le(?:_[su])?|load(?:(?:8|16|32)_[su])?|lt(?:_[su])?|max|min|mul|nearest|neg?|or|popcnt|promote\/f32|reinterpret\/[fi](?:32|64)|rem_[su]|rot[lr]|shl|shr_[su]|store(?:8|16|32)?|sqrt|sub|trunc(?:_[su]\/f(?:32|64))?|wrap\/i64|xor))\b/ +},{className:"number",relevance:0, +match:/[+-]?\b(?:\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?|0x[\da-fA-F](?:_?[\da-fA-F])*(?:\.[\da-fA-F](?:_?[\da-fA-D])*)?(?:[pP][+-]?\d(?:_?\d)*)?)\b|\binf\b|\bnan(?::0x[\da-fA-F](?:_?[\da-fA-D])*)?\b/ +}]}},grmr_xml:e=>{ +const n=e.regex,t=n.concat(/[\p{L}_]/u,n.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),a={ +className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/, +contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}] +},r=e.inherit(i,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{ +className:"string"}),o=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),l={ +endsWithParent:!0,illegal:/`]+/}]}]}]};return{ +name:"HTML, XML", +aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"], +case_insensitive:!0,unicodeRegex:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,o,s,r,{begin:/\[/,end:/\]/,contains:[{ +className:"meta",begin://,contains:[i,r,o,s]}]}] +},e.COMMENT(//,{relevance:10}),{begin://, +relevance:10},a,{className:"meta",end:/\?>/,variants:[{begin:/<\?xml/, +relevance:10,contains:[o]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"style"},contains:[l],starts:{ +end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag", +begin:/)/,end:/>/,keywords:{name:"script"},contains:[l],starts:{ +end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{ +className:"tag",begin:/<>|<\/>/},{className:"tag", +begin:n.concat(//,/>/,/\s/)))), +end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:l}]},{ +className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(t,/>/))),contains:[{ +className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]} +},grmr_yaml:e=>{ +const n="true false yes no null",t="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={ +className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/ +},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable", +variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(a,{ +variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),r={ +end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},s={begin:/\{/, +end:/\}/,contains:[r],illegal:"\\n",relevance:0},o={begin:"\\[",end:"\\]", +contains:[r],illegal:"\\n",relevance:0},l=[{className:"attr",variants:[{ +begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{ +begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$", +relevance:10},{className:"string", +begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{ +begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0, +relevance:0},{className:"type",begin:"!\\w+!"+t},{className:"type", +begin:"!<"+t+">"},{className:"type",begin:"!"+t},{className:"type",begin:"!!"+t +},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta", +begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)", +relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{ +className:"number", +begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b" +},{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},s,o,a],c=[...l] +;return c.pop(),c.push(i),r.contains=c,{name:"YAML",case_insensitive:!0, +aliases:["yml"],contains:l}}});const He=ae;for(const e of Object.keys(Ke)){ +const n=e.replace("grmr_","").replace("_","-");He.registerLanguage(n,Ke[e])} +return He}() +;"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); \ No newline at end of file diff --git a/doctum-theme/layout/base.twig b/doctum-theme/layout/base.twig new file mode 100644 index 0000000000..637c84e83e --- /dev/null +++ b/doctum-theme/layout/base.twig @@ -0,0 +1,393 @@ +{% extends 'default/layout/base.twig' %} + +{% block head %} + {{ parent() }} + + + +{% endblock %} diff --git a/doctum-theme/macros.twig b/doctum-theme/macros.twig new file mode 100644 index 0000000000..fb899e15cf --- /dev/null +++ b/doctum-theme/macros.twig @@ -0,0 +1,194 @@ + +{% macro class_category_name(categoryId) -%} +{% if categoryId == 1 %}{% trans 'class' %}{% endif %} +{% if categoryId == 2 %}{% trans 'interface' %}{% endif %} +{% if categoryId == 3 %}{% trans 'trait' %}{% endif %} +{%- endmacro %} + +{% macro namespace_link(namespace) -%} +
    {{ namespace == '' ? global_namespace_name() : namespace|raw }} +{%- endmacro %} + +{% macro class_link(class, absolute) -%} + {%- if class.isProjectClass() -%} + + {%- elseif class.isPhpClass() -%} + + {%- endif %} + {{- abbr_class(class, absolute|default(false)) }} + {%- if class.isProjectClass() or class.isPhpClass() %}{% endif %} +{%- endmacro %} + +{% macro method_link(method, absolute, classonly) -%} +{# #} +{# #}{{- abbr_class(method.class) }}{% if not classonly|default(false) %}::{{ method.name|raw }}{% endif -%} +{# #} +{%- endmacro %} + +{% macro property_link(property, absolute, classonly) -%} +{# #} +{# #}{{- abbr_class(property.class) }}{% if not classonly|default(false) %}#{{ property.name|raw }}{% endif -%} +{# #} +{%- endmacro %} + +{% macro hint_link(hints, isIntersectionType = false) -%} + {%- from _self import class_link %} + + {%- if hints %} + {%- for hint in hints %} + {%- if hint.class %} + {{- class_link(hint.name) }} + {%- elseif hint.name %} + {{- abbr_class(hint.name) }} + {%- endif %} + {%- if hint.array %}[]{% endif %} + {%- if not loop.last %}{%- if isIntersectionType %}&{% else %}|{% endif %}{% endif %} + {%- endfor %} + {%- endif %} +{%- endmacro %} + +{% macro source_link(project, class) -%} + {% if class.sourcepath %} + ({% trans 'View source'%}) + {%- endif %} +{%- endmacro %} + +{% macro method_source_link(method) -%} + {% if method.sourcepath %} + {#- l10n: Method at line %s -#} + {{'at line %s'|trans|format( + method.line + )|raw }} + {%- else %} + {#- l10n: Method at line %s -#} + {{- 'at line %s'|trans|format( + method.line + )|raw -}} + {%- endif %} +{%- endmacro %} + +{% macro method_parameters_signature(method) -%} + {%- from "macros.twig" import hint_link -%} + ( + {%- for parameter in method.parameters %} + {%- if parameter.hashint %}{{ hint_link(parameter.hint, parameter.isIntersectionType()) }} {% endif -%} + {%- if parameter.variadic %}...{% endif %}${{ parameter.name|raw }} + {%- if parameter.default is not null %} = {{ parameter.default }}{% endif %} + {%- if not loop.last %}, {% endif %} + {%- endfor -%} + ) +{%- endmacro %} + +{% macro function_parameters_signature(method) -%} + {%- from "macros.twig" import hint_link -%} + ( + {%- for parameter in method.parameters %} + {%- if parameter.hashint %}{{ hint_link(parameter.hint, parameter.isIntersectionType()) }} {% endif -%} + {%- if parameter.variadic %}...{% endif %}${{ parameter.name|raw }} + {%- if parameter.default is not null %} = {{ parameter.default }}{% endif %} + {%- if not loop.last %}, {% endif %} + {%- endfor -%} + ) +{%- endmacro %} + +{% macro render_classes(classes) -%} + {% from _self import class_link, deprecated %} + +
    + {% for class in classes %} +
    +
    + {% if class.isInterface %} + {{- class_link(class, true) -}} + {% else %} + {{- class_link(class, true) -}} + {% endif %} + {{- deprecated(class) -}} +
    +
    + {{- class.shortdesc|desc(class)|md_to_html -}} +
    +
    + {% endfor %} +
    +{%- endmacro %} + +{% macro breadcrumbs(namespace) %} + {% set current_ns = '' %} + {% for ns in namespace|split('\\') %} + {%- if current_ns -%} + {% set current_ns = current_ns ~ '\\' ~ ns %} + {%- else -%} + {% set current_ns = ns %} + {%- endif -%} +
  • {{ ns|raw }}
  • \
  • + {%- endfor %} +{% endmacro %} + +{% macro deprecated(reflection) %} + {% if reflection.deprecated %}{% trans 'deprecated' %}{% endif %} +{% endmacro %} + +{% macro deprecations(reflection) %} + {% from _self import deprecated %} + + {% if reflection.deprecated %} +

    + {{ deprecated(reflection )}} + {% for tag in reflection.deprecated %} + + {{ tag[0]|raw }} + {{ tag[1:]|join(' ')|raw }} + + {% endfor %} +

    + {% endif %} +{% endmacro %} + +{% macro internals(reflection) %} + {% if reflection.isInternal() %} + {% for internalTag in reflection.getInternal() %} + + + + + +
    {% trans 'internal' %} {{ internalTag[0]|raw }} {{ internalTag[1:]|join(' ')|raw }}
    + {% endfor %} +   + {% endif %} +{% endmacro %} + +{% macro categories(reflection) %} + {% if reflection.hasCategories() %} +

    + {% for categoryTag in reflection.getCategories() %} + {% for category in categoryTag %} + {{ category }} + {% endfor %} + {% endfor %} +

    + {% endif %} +{% endmacro %} + +{% macro todo(reflection) %} + {% if project.config('insert_todos') == true %} + {% if reflection.todo %}{% trans 'todo' %}{% endif %} + {% endif %} +{% endmacro %} + +{% macro todos(reflection) %} + {% from _self import todo %} + + {% if reflection.todo %} +

    + {{ todo(reflection )}} + {% for tag in reflection.todo %} + + {{ tag[0]|raw }} + {{ tag[1:]|join(' ')|raw }} + + {% endfor %} +

    + {% endif %} +{% endmacro %} diff --git a/doctum-theme/manifest.yml b/doctum-theme/manifest.yml new file mode 100644 index 0000000000..dd1a77902b --- /dev/null +++ b/doctum-theme/manifest.yml @@ -0,0 +1,6 @@ +name: phpredis +parent: default + +static: + 'css/highlight-github.min.css': 'css/highlight-github.min.css' + 'js/highlight.min.js': 'js/highlight.min.js' diff --git a/doctum.md b/doctum.md index 374e27ccb9..e30407b28d 100644 --- a/doctum.md +++ b/doctum.md @@ -5,4 +5,8 @@ curl -O https://doctum.long-term.support/releases/latest/doctum.phar chmod +x doctum.phar ./doctum.phar update doctum-config.php -``` \ No newline at end of file +``` + +The build uses a custom Doctum theme stored in `doctum-theme/` so that extra +assets such as highlight.js are bundled with the generated HTML. Keep that +directory in sync if you ever update Doctum upstream assets. diff --git a/library.c b/library.c index eb9df5a1a6..f00c287d61 100644 --- a/library.c +++ b/library.c @@ -56,9 +56,14 @@ #include #endif -#include #include +#if PHP_VERSION_ID < 80400 +#include +#else +#include +#endif + #define UNSERIALIZE_NONE 0 #define UNSERIALIZE_KEYS 1 #define UNSERIALIZE_VALS 2 @@ -68,6 +73,10 @@ #define SCORE_DECODE_INT 1 #define SCORE_DECODE_DOUBLE 2 +#define REDIS_CALLBACKS_INIT_SIZE 8 +#define REDIS_CALLBACKS_MAX_DOUBLE 32768 +#define REDIS_CALLBACKS_ADD_SIZE 4096 + /* PhpRedis often returns either FALSE or NULL depending on whether we have * an option set, so this macro just wraps that often repeated logic */ #define REDIS_ZVAL_NULL(sock_, zv_) \ @@ -79,6 +88,27 @@ } \ } while (0) +/** Set return value to false in case of we are in atomic mode or add FALSE to output array in pipeline mode */ +#define REDIS_RESPONSE_ERROR(redis_sock, z_tab) \ + do { \ + if (IS_ATOMIC(redis_sock)) { \ + RETVAL_FALSE; \ + } else { \ + add_next_index_bool(z_tab, 0); \ + } \ + } while (0) + +/** Set return value to `zval` in case of we are in atomic mode or add `zval` to output array in pipeline mode */ +#define REDIS_RETURN_ZVAL(redis_sock, z_tab, zval) \ + do { \ + if (IS_ATOMIC(redis_sock)) { \ + /* Move value of `zval` to `return_value` */ \ + ZVAL_COPY_VALUE(return_value, &zval); \ + } else { \ + zend_hash_next_index_insert_new(Z_ARRVAL_P(z_tab), &zval); \ + } \ + } while (0) + #ifndef PHP_WIN32 #include /* TCP_NODELAY */ #include /* SO_KEEPALIVE */ @@ -93,18 +123,8 @@ extern int le_redis_pconnect; static int redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int count); -/* Register a persistent resource in a a way that works for every PHP 7 version. */ -void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id) { -#if PHP_VERSION_ID < 70300 - zend_resource res; - res.type = le_id; - res.ptr = ptr; - - zend_hash_str_update_mem(&EG(persistent_list), ZSTR_VAL(id), ZSTR_LEN(id), &res, sizeof(res)); -#else - zend_register_persistent_resource(ZSTR_VAL(id), ZSTR_LEN(id), ptr, le_id); -#endif -} +static int redis_bulk_resp_to_zval(RedisSock *redis_sock, zval *zdst, + int *dstlen) ; static ConnectionPool * redis_sock_get_connection_pool(RedisSock *redis_sock) @@ -125,41 +145,52 @@ redis_sock_get_connection_pool(RedisSock *redis_sock) /* Create the pool and store it in our persistent list */ pool = pecalloc(1, sizeof(*pool), 1); zend_llist_init(&pool->list, sizeof(php_stream *), NULL, 1); - redis_register_persistent_resource(persistent_id, pool, le_redis_pconnect); + zend_register_persistent_resource(ZSTR_VAL(persistent_id), + ZSTR_LEN(persistent_id), + pool, le_redis_pconnect); zend_string_release(persistent_id); + return pool; } -/* Helper to reselect the proper DB number when we reconnect */ -static int reselect_db(RedisSock *redis_sock) { - char *cmd, *response; - int cmd_len, response_len; - - cmd_len = redis_spprintf(redis_sock, NULL, &cmd, "SELECT", "d", - redis_sock->dbNumber); - - if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { - efree(cmd); - return -1; +static int redis_sock_response_ok(RedisSock *redis_sock, char *buf, int buf_size) { + size_t len; + if (UNEXPECTED(redis_sock_gets(redis_sock, buf, buf_size - 1, &len) < 0)) { + return 0; + } + if (UNEXPECTED(redis_strncmp(buf, ZEND_STRL("+OK")))) { + if (buf[0] == '-') { + redis_sock_set_err(redis_sock, buf + 1, len - 1); + } + return 0; } + return 1; +} - efree(cmd); +/* Helper to select the proper DB number */ +static int redis_select_db(RedisSock *redis_sock) { + char response[4096]; + smart_string cmd = {0}; - if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { + REDIS_CMD_INIT_SSTR_STATIC(&cmd, 1, "SELECT"); + redis_cmd_append_sstr_long(&cmd, redis_sock->dbNumber); + + if (redis_sock_write(redis_sock, cmd.c, cmd.len) < 0) { + efree(cmd.c); return -1; } - if (strncmp(response, "+OK", 3)) { - efree(response); + efree(cmd.c); + + if (!redis_sock_response_ok(redis_sock, response, sizeof(response))) { return -1; } - efree(response); return 0; } -/* Append an AUTH command to a smart string if neccessary. This will either +/* Append an AUTH command to a smart string if necessary. This will either * append the new style AUTH , old style AUTH , or * append no command at all. Function returns 1 if we appended a command * and 0 otherwise. */ @@ -190,7 +221,6 @@ redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass) redis_sock->pass = pass ? zend_string_copy(pass) : NULL; } - PHP_REDIS_API void redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv) { zend_string *user, *pass; @@ -217,28 +247,47 @@ redis_sock_free_auth(RedisSock *redis_sock) { } } +#if PHP_VERSION_ID < 80000 +static void redis_array_release(HashTable *ht) { + if (!ht || (GC_FLAGS(ht) & GC_IMMUTABLE)) { + return; + } + + if (GC_DELREF(ht) == 0) { + zend_array_destroy(ht); + } +} +#else +static inline void redis_array_release(HashTable *ht) { + zend_array_release(ht); +} +#endif + + +void redis_sock_free_context(RedisSock *redis_sock) { + if (redis_sock->context == NULL) + return; + + redis_array_release(redis_sock->context); + redis_sock->context = NULL; +} + PHP_REDIS_API char * redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen) { - char *cmd; + smart_string cmd = {0}; - /* AUTH requires at least a password */ - if (redis_sock->pass == NULL) + if (redis_sock_append_auth(redis_sock, &cmd) == 0) { return NULL; - - if (redis_sock->user) { - *cmdlen = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "SS", redis_sock->user, redis_sock->pass); - } else { - *cmdlen = redis_spprintf(redis_sock, NULL, &cmd, "AUTH", "S", redis_sock->pass); } - return cmd; + *cmdlen = cmd.len; + return cmd.c; } /* Send Redis AUTH and process response */ PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) { char *cmd, inbuf[4096]; int cmdlen; - size_t len; if ((cmd = redis_sock_auth_cmd(redis_sock, &cmdlen)) == NULL) return SUCCESS; @@ -249,7 +298,7 @@ PHP_REDIS_API int redis_sock_auth(RedisSock *redis_sock) { } efree(cmd); - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "+OK", 3)) { + if (!redis_sock_response_ok(redis_sock, inbuf, sizeof(inbuf))) { return FAILURE; } return SUCCESS; @@ -359,7 +408,8 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw) for (retry_index = 0; !no_retry && retry_index < redis_sock->max_retries; ++retry_index) { /* close existing stream before reconnecting */ if (redis_sock->stream) { - redis_sock_disconnect(redis_sock, 1); + /* reconnect no need to reset mode, it will cause pipeline mode socket exception */ + redis_sock_disconnect(redis_sock, 1, 0); } /* Sleep based on our backoff algorithm */ zend_ulong delay = redis_backoff_compute(&redis_sock->backoff, retry_index); @@ -378,7 +428,7 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw) redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED; /* If we're using a non-zero db, reselect it */ - if (redis_sock->dbNumber && reselect_db(redis_sock) != 0) { + if (redis_sock->dbNumber && redis_select_db(redis_sock) != 0) { errmsg = "SELECT failed while reconnecting"; break; } @@ -390,7 +440,7 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw) } } /* close stream and mark socket as failed */ - redis_sock_disconnect(redis_sock, 1); + redis_sock_disconnect(redis_sock, 1, 1); redis_sock->status = REDIS_SOCK_STATUS_FAILED; if (!no_throw) { REDIS_THROW_EXCEPTION( errmsg, 0); @@ -400,7 +450,7 @@ redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zend_bool no_throw) PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - REDIS_SCAN_TYPE type, zend_long *iter) + REDIS_SCAN_TYPE type, uint64_t *cursor) { REDIS_REPLY_TYPE reply_type; long reply_info; @@ -433,7 +483,7 @@ redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* Push the iterator out to the caller */ - *iter = atol(p_iter); + *cursor = strtoull(p_iter, NULL, 10); efree(p_iter); /* Read our actual keys/members/etc differently depending on what kind of @@ -484,7 +534,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, { HashTable *subs; subscribeCallback *cb; - subscribeContext *sctx = (subscribeContext*)ctx; + subscribeContext *sctx = ctx; zval *z_tmp, z_resp; int i; @@ -514,7 +564,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, zend_hash_str_update_mem(subs, Z_STRVAL_P(z_tmp), Z_STRLEN_P(z_tmp), &sctx->cb, sizeof(sctx->cb)); - zval_dtor(&z_resp); + zval_ptr_dtor_nogc(&z_resp); } if (strcasecmp(sctx->kw, "ssubscribe") == 0) { @@ -544,8 +594,9 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, /* Multibulk response, {[pattern], type, channel, payload } */ while (redis_sock->subs[i]) { zval z_ret, z_args[4], *z_type, *z_chan, *z_pat = NULL, *z_data; - HashTable *ht_tab; int tab_idx = 1, is_pmsg = 0; + HashTable *ht_tab; + zend_string *zs; ZVAL_NULL(&z_resp); if (!redis_sock_read_multibulk_reply_zval(redis_sock, &z_resp)) { @@ -567,27 +618,31 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, ) { is_pmsg = *Z_STRVAL_P(z_type)=='p'; } else { - zval_dtor(&z_resp); + zval_ptr_dtor_nogc(&z_resp); continue; } // Extract pattern if it's a pmessage - if(is_pmsg) { - if ((z_pat = zend_hash_index_find(ht_tab, tab_idx++)) == NULL) { + if (is_pmsg) { + z_pat = zend_hash_index_find(ht_tab, tab_idx++); + if (z_pat == NULL || Z_TYPE_P(z_pat) != IS_STRING) goto failure; - } } - // Extract channel and data - if ((z_chan = zend_hash_index_find(ht_tab, tab_idx++)) == NULL || - (z_data = zend_hash_index_find(ht_tab, tab_idx++)) == NULL - ) { + /* Extract channel */ + z_chan = zend_hash_index_find(ht_tab, tab_idx++); + if (z_chan == NULL || Z_TYPE_P(z_chan) != IS_STRING) goto failure; - } - if ((cb = zend_hash_str_find_ptr(redis_sock->subs[i], Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan))) == NULL) { + /* Finally, extract data */ + z_data = zend_hash_index_find(ht_tab, tab_idx++); + if (z_data == NULL) + goto failure; + + /* Find our callback, either by channel or pattern string */ + zs = z_pat != NULL ? Z_STR_P(z_pat) : Z_STR_P(z_chan); + if ((cb = zend_hash_find_ptr(redis_sock->subs[i], zs)) == NULL) goto failure; - } // Different args for SUBSCRIBE and PSUBSCRIBE z_args[0] = *getThis(); @@ -612,7 +667,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, // If we have a return value free it zval_ptr_dtor(&z_ret); - zval_dtor(&z_resp); + zval_ptr_dtor_nogc(&z_resp); } RETVAL_TRUE; @@ -624,7 +679,7 @@ PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, zend_hash_destroy(subs); efree(subs); failure: - zval_dtor(&z_resp); + zval_ptr_dtor_nogc(&z_resp); RETVAL_FALSE; return FAILURE; } @@ -633,7 +688,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { - subscribeContext *sctx = (subscribeContext*)ctx; + subscribeContext *sctx = ctx; zval *z_chan, z_ret, z_resp; int i; @@ -656,8 +711,8 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, (z_chan = zend_hash_index_find(Z_ARRVAL(z_resp), 1)) == NULL ) { efree(sctx); - zval_dtor(&z_resp); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_resp); + zval_ptr_dtor_nogc(&z_ret); RETVAL_FALSE; return FAILURE; } @@ -671,7 +726,7 @@ PHP_REDIS_API int redis_unsubscribe_response(INTERNAL_FUNCTION_PARAMETERS, add_assoc_bool_ex(&z_ret, Z_STRVAL_P(z_chan), Z_STRLEN_P(z_chan), 1); } - zval_dtor(&z_resp); + zval_ptr_dtor_nogc(&z_resp); } efree(sctx); @@ -754,13 +809,13 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len) size_t len; *buf_len = 0; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || len < 1) { return NULL; } switch(inbuf[0]) { case '-': - redis_sock_set_err(redis_sock, inbuf+1, len); + redis_sock_set_err(redis_sock, inbuf + 1, len - 1); /* Filter our ERROR through the few that should actually throw */ redis_error_throw(redis_sock); @@ -772,7 +827,7 @@ redis_sock_read(RedisSock *redis_sock, int *buf_len) case '*': /* For null multi-bulk replies (like timeouts from brpoplpush): */ - if(memcmp(inbuf + 1, "-1", 2) == 0) { + if(len > 2 && memcmp(inbuf + 1, "-1", 2) == 0) { return NULL; } REDIS_FALLTHROUGH; @@ -817,7 +872,7 @@ static zend_string *redis_hash_auth(zend_string *user, zend_string *pass) { if (user == NULL && pass == NULL) return NULL; - /* Theoretically inpossible but check anyway */ + /* Theoretically impossible but check anyway */ algo = zend_string_init("sha256", sizeof("sha256") - 1, 0); if ((ops = redis_hash_fetch_ops(algo)) == NULL) { zend_string_release(algo); @@ -921,6 +976,7 @@ redis_pool_spprintf(RedisSock *redis_sock, char *fmt, ...) { * S - Pointer to a zend_string * k - Same as 's' but the value will be prefixed if phpredis is set up do do * that and the working slot will be set if it has been passed. + * K - Same as 'S' but the value will be prefixed if phpredis is set up to do * v - A z_val which will be serialized if phpredis is configured to serialize. * f - A double value * F - Alias to 'f' @@ -962,6 +1018,10 @@ redis_spprintf(RedisSock *redis_sock, short *slot, char **ret, char *kw, char *f if (slot) *slot = cluster_hash_key(arg.str, arglen); if (argfree) efree(arg.str); break; + case 'K': + arg.zstr = va_arg(ap, zend_string*); + redis_cmd_append_sstr_key_zstr(&cmd, arg.zstr, redis_sock, slot); + break; case 'v': arg.zv = va_arg(ap, zval*); argfree = redis_pack(redis_sock, arg.zv, &dup, &arglen); @@ -1032,29 +1092,44 @@ int redis_cmd_append_sstr(smart_string *str, char *append, int append_len) { * Append an integer to a smart string command */ int redis_cmd_append_sstr_int(smart_string *str, int append) { - char int_buf[32]; - int int_len = snprintf(int_buf, sizeof(int_buf), "%d", append); - return redis_cmd_append_sstr(str, int_buf, int_len); + return redis_cmd_append_sstr_long(str, (long) append); } /* * Append a long to a smart string command */ int redis_cmd_append_sstr_long(smart_string *str, long append) { + return redis_cmd_append_sstr_zend_long(str, (zend_long) append); +} + +/* + * Append a zend_long to a smart string command + */ +int redis_cmd_append_sstr_zend_long(smart_string *str, zend_long lval) { char long_buf[32]; - int long_len = snprintf(long_buf, sizeof(long_buf), "%ld", append); - return redis_cmd_append_sstr(str, long_buf, long_len); + char *result = zend_print_long_to_buf(long_buf + sizeof(long_buf) - 1, lval); + int int_len = long_buf + sizeof(long_buf) - 1 - result; + return redis_cmd_append_sstr(str, result, int_len); } /* * Append a 64-bit integer to our command */ int redis_cmd_append_sstr_i64(smart_string *str, int64_t append) { - char nbuf[64]; + char nbuf[21]; int len = snprintf(nbuf, sizeof(nbuf), "%" PRId64, append); return redis_cmd_append_sstr(str, nbuf, len); } +/* + * Append a 64-bit unsigned integer to our command + */ +int redis_cmd_append_sstr_u64(smart_string *str, uint64_t append) { + char nbuf[21]; + int len = snprintf(nbuf, sizeof(nbuf), "%" PRIu64, append); + return redis_cmd_append_sstr(str, nbuf, len); +} + /* * Append a double to a smart string command */ @@ -1078,7 +1153,7 @@ redis_cmd_append_sstr_dbl(smart_string *str, double value) * the value may be serialized, if we're configured to do that. */ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock) { int valfree, retval; - zend_string *zstr; + zend_string *zstr, *tmp; size_t vallen; char *val; @@ -1087,9 +1162,9 @@ int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock retval = redis_cmd_append_sstr(str, val, vallen); if (valfree) efree(val); } else { - zstr = zval_get_string(z); - retval = redis_cmd_append_sstr_zstr(str, zstr); - zend_string_release(zstr); + zstr = zval_get_tmp_string(z, &tmp); + retval = redis_cmd_append_sstr(str, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + zend_tmp_string_release(tmp); } return retval; @@ -1117,12 +1192,12 @@ int redis_cmd_append_sstr_key_zstr(smart_string *dst, zend_string *key, RedisSoc } int redis_cmd_append_sstr_key_zval(smart_string *dst, zval *zv, RedisSock *redis_sock, short *slot) { - zend_string *key; + zend_string *key, *tmp; int res; - key = zval_get_string(zv); - res = redis_cmd_append_sstr_key_zstr(dst, key, redis_sock, slot); - zend_string_release(key); + key = zval_get_tmp_string(zv, &tmp); + res = redis_cmd_append_sstr_key(dst, ZSTR_VAL(key), ZSTR_LEN(key), redis_sock, slot); + zend_tmp_string_release(tmp); return res; } @@ -1164,11 +1239,7 @@ redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, double ret; if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } @@ -1189,26 +1260,24 @@ PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r long l; if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } - if (strncmp(response, "+string", 7) == 0) { + if (redis_strncmp(response, ZEND_STRL("+string")) == 0) { l = REDIS_STRING; - } else if (strncmp(response, "+set", 4) == 0){ + } else if (redis_strncmp(response, ZEND_STRL("+set")) == 0){ l = REDIS_SET; - } else if (strncmp(response, "+list", 5) == 0){ + } else if (redis_strncmp(response, ZEND_STRL("+list")) == 0){ l = REDIS_LIST; - } else if (strncmp(response, "+zset", 5) == 0){ + } else if (redis_strncmp(response, ZEND_STRL("+zset")) == 0){ l = REDIS_ZSET; - } else if (strncmp(response, "+hash", 5) == 0){ + } else if (redis_strncmp(response, ZEND_STRL("+hash")) == 0){ l = REDIS_HASH; - } else if (strncmp(response, "+stream", 7) == 0) { + } else if (redis_strncmp(response, ZEND_STRL("+stream")) == 0) { l = REDIS_STREAM; + } else if (redis_strncmp(response, ZEND_STRL("+vectorset")) == 0) { + l = REDIS_VECTORSET; } else { l = REDIS_NOT_FOUND; } @@ -1245,7 +1314,9 @@ redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval } PHP_REDIS_API int -redis_srandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +redis_randmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ FailableResultCallback cb; /* Whether or not we have a COUNT argument */ @@ -1274,11 +1345,7 @@ PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *r /* Free source response */ efree(response); - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); return SUCCESS; } @@ -1304,8 +1371,11 @@ redis_parse_info_response(char *response, zval *z_ret) add_assoc_long_ex(z_ret, p1, p - p1, lval); break; case IS_DOUBLE: - add_assoc_double_ex(z_ret, p1, p - p1, dval); - break; + if (dval < HUGE_VAL) { + add_assoc_double_ex(z_ret, p1, p - p1, dval); + break; + } + // fall through default: add_assoc_string_ex(z_ret, p1, p - p1, p + 1); } @@ -1368,11 +1438,7 @@ redis_client_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zva efree(resp); /* Return or append depending if we're atomic */ - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); return SUCCESS; } @@ -1402,11 +1468,7 @@ redis_client_list_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zva efree(resp); /* Return or append depending if we're atomic */ - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); return SUCCESS; } @@ -1513,9 +1575,9 @@ redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval ZEND_ASSERT(ctx == PHPREDIS_CTX_PTR || ctx == PHPREDIS_CTX_PTR + 1); if (ctx == PHPREDIS_CTX_PTR) { - return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_long_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } else { - return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); + return redis_string_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, NULL); } } @@ -1544,7 +1606,7 @@ redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, for (i = 0; i < elements; ++i) { if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf), &len) < 0) { - zval_dtor(zdst); + zval_ptr_dtor_nogc(zdst); return FAILURE; } add_next_index_long(zdst, atol(inbuf + 1)); @@ -1574,11 +1636,7 @@ redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z res = FAILURE; } - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&zdst, 0, 0); - } else { - add_next_index_zval(z_tab, &zdst); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, zdst); return res; } @@ -1638,11 +1696,7 @@ PHP_REDIS_API int redis_long_response(INTERNAL_FUNCTION_PARAMETERS, int response_len; if ((response = redis_sock_read(redis_sock, &response_len)) == NULL || *response != TYPE_INT) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); if (response) efree(response); return FAILURE; } @@ -1674,11 +1728,11 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, int decode) { + HashTable *keytable = Z_ARRVAL_P(z_tab); + zend_string *hkey, *tmp; zval z_ret, z_sub; - HashTable *keytable; - array_init(&z_ret); - keytable = Z_ARRVAL_P(z_tab); + array_init_size(&z_ret, zend_hash_num_elements(keytable) / 2); for(zend_hash_internal_pointer_reset(keytable); zend_hash_has_more_elements(keytable) == SUCCESS; @@ -1691,14 +1745,14 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, } /* get current value, a key */ - zend_string *hkey = zval_get_string(z_key_p); + hkey = zval_get_tmp_string(z_key_p, &tmp); /* move forward */ zend_hash_move_forward(keytable); /* fetch again */ if ((z_value_p = zend_hash_get_current_data(keytable)) == NULL) { - zend_string_release(hkey); + zend_tmp_string_release(tmp); continue; /* this should never happen, according to the PHP people. */ } @@ -1707,18 +1761,20 @@ static void array_zip_values_and_scores(RedisSock *redis_sock, zval *z_tab, /* Decode the score depending on flag */ if (decode == SCORE_DECODE_INT && Z_STRLEN_P(z_value_p) > 0) { - add_assoc_long_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atoi(hval+1)); + ZVAL_LONG(&z_sub, atoi(hval+1)); } else if (decode == SCORE_DECODE_DOUBLE) { - add_assoc_double_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), atof(hval)); + ZVAL_DOUBLE(&z_sub, atof(hval)); } else { ZVAL_ZVAL(&z_sub, z_value_p, 1, 0); - add_assoc_zval_ex(&z_ret, ZSTR_VAL(hkey), ZSTR_LEN(hkey), &z_sub); } - zend_string_release(hkey); + + zend_symtable_update(Z_ARRVAL_P(&z_ret), hkey, &z_sub); + + zend_tmp_string_release(tmp); } /* replace */ - zval_dtor(z_tab); + zval_ptr_dtor_nogc(z_tab); ZVAL_ZVAL(z_tab, &z_ret, 0, 0); } @@ -1734,7 +1790,7 @@ array_zip_values_recursive(zval *z_tab) zend_hash_move_forward(Z_ARRVAL_P(z_tab)) ) { if ((zv = zend_hash_get_current_data(Z_ARRVAL_P(z_tab))) == NULL) { - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); return FAILURE; } if (Z_TYPE_P(zv) == IS_STRING) { @@ -1742,12 +1798,12 @@ array_zip_values_recursive(zval *z_tab) zend_hash_move_forward(Z_ARRVAL_P(z_tab)); if ((zv = zend_hash_get_current_data(Z_ARRVAL_P(z_tab))) == NULL) { zend_string_release(zkey); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); return FAILURE; } if (Z_TYPE_P(zv) == IS_ARRAY && array_zip_values_recursive(zv) != SUCCESS) { zend_string_release(zkey); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); return FAILURE; } ZVAL_ZVAL(&z_sub, zv, 1, 0); @@ -1755,14 +1811,14 @@ array_zip_values_recursive(zval *z_tab) zend_string_release(zkey); } else { if (Z_TYPE_P(zv) == IS_ARRAY && array_zip_values_recursive(zv) != SUCCESS) { - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); return FAILURE; } ZVAL_ZVAL(&z_sub, zv, 1, 0); add_next_index_zval(&z_ret, &z_sub); } } - zval_dtor(z_tab); + zval_ptr_dtor_nogc(z_tab); ZVAL_ZVAL(z_tab, &z_ret, 0, 0); return SUCCESS; } @@ -1774,28 +1830,25 @@ redis_mbulk_reply_zipped(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int numElems; if (read_mbulk_header(redis_sock, &numElems) < 0) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } zval z_multi_result; - array_init(&z_multi_result); /* pre-allocate array for multi's results. */ - /* Grab our key, value, key, value array */ - redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, unserialize); + if (numElems < 1) { + ZVAL_EMPTY_ARRAY(&z_multi_result); + } else { + array_init_size(&z_multi_result, numElems); /* pre-allocate array for multi's results. */ - /* Zip keys and values */ - array_zip_values_and_scores(redis_sock, &z_multi_result, decode); + /* Grab our key, value, key, value array */ + redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, unserialize); - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_multi_result, 0, 1); - } else { - add_next_index_zval(z_tab, &z_multi_result); + /* Zip keys and values */ + array_zip_values_and_scores(redis_sock, &z_multi_result, decode); } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result); + return 0; } @@ -1844,9 +1897,10 @@ redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, array_init_size(&zele, elements); if (ctx == PHPREDIS_CTX_PTR) { - for (int i = 0; i < elements; i++) { + int i; + for (i = 0; i < elements; i++) { if (read_mbulk_header(redis_sock, &subele) < 0 || subele != 2) { - zval_dtor(&zele); + zval_ptr_dtor_nogc(&zele); goto fail; } redis_mbulk_reply_loop(redis_sock, &zele, subele, UNSERIALIZE_KEYS); @@ -1862,7 +1916,7 @@ redis_read_mpop_response(RedisSock *redis_sock, zval *zdst, int elements, return SUCCESS; fail: - zval_dtor(zdst); + zval_ptr_dtor_nogc(zdst); ZVAL_FALSE(zdst); return FAILURE; @@ -1882,11 +1936,7 @@ redis_mpop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZVAL_FALSE(&zret); } - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&zret, 0, 0); - } else { - add_next_index_zval(z_tab, &zret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, zret); return res; } @@ -1947,7 +1997,7 @@ redis_read_geosearch_response(zval *zdst, RedisSock *redis_sock, } ZEND_HASH_FOREACH_END(); // Cleanup - zval_dtor(&z_multi_result); + zval_ptr_dtor_nogc(&z_multi_result); } return SUCCESS; @@ -1966,11 +2016,7 @@ redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZVAL_FALSE(&zret); } - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&zret, 0, 1); - } else { - add_next_index_zval(z_tab, &zret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, zret); return SUCCESS; } @@ -1982,11 +2028,7 @@ redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s zval z_ret; if (read_mbulk_header(redis_sock, &numElems) < 0) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } @@ -1994,11 +2036,7 @@ redis_client_trackinginfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_s redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret); array_zip_values_and_scores(redis_sock, &z_ret, 0); - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); return SUCCESS; } @@ -2024,6 +2062,87 @@ redis_client_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval } } +static int +redis_hello_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + zval z_ret, *zv; + int numElems; + + if (read_mbulk_header(redis_sock, &numElems) < 0) + goto fail; + + array_init(&z_ret); + + if (redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret) != SUCCESS || + array_zip_values_recursive(&z_ret) != SUCCESS) + { + zval_ptr_dtor_nogc(&z_ret); + goto fail; + } + + if (redis_sock->hello.server) { + zend_string_release(redis_sock->hello.server); + } + + if ((zv = zend_hash_str_find(Z_ARRVAL(z_ret), ZEND_STRL("dragonfly_version")))) { + redis_sock->hello.server = zend_string_init(ZEND_STRL("dragonfly"), 0); + } else { + zv = zend_hash_str_find(Z_ARRVAL(z_ret), ZEND_STRL("server")); + redis_sock->hello.server = zv ? zval_get_string(zv) : ZSTR_EMPTY_ALLOC(); + } + + if (redis_sock->hello.version) { + zend_string_release(redis_sock->hello.version); + } + zv = zend_hash_str_find(Z_ARRVAL(z_ret), ZEND_STRL("version")); + redis_sock->hello.version = zv ? zval_get_string(zv) : ZSTR_EMPTY_ALLOC(); + + zval_ptr_dtor_nogc(&z_ret); + + if (ctx == PHPREDIS_CTX_PTR) { + ZVAL_STR_COPY(&z_ret, redis_sock->hello.server); + } else if (ctx == PHPREDIS_CTX_PTR + 1) { + ZVAL_STR_COPY(&z_ret, redis_sock->hello.version); + } else { + ZEND_ASSERT(!"memory corruption?"); + return FAILURE; + } + + if (IS_ATOMIC(redis_sock)) { + RETVAL_ZVAL(&z_ret, 0, 1); + } else { + add_next_index_zval(z_tab, &z_ret); + } + + return SUCCESS; + +fail: + if (IS_ATOMIC(redis_sock)) { + RETVAL_FALSE; + } else { + add_next_index_bool(z_tab, 0); + } + return FAILURE; +} + + +PHP_REDIS_API int +redis_hello_server_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + return redis_hello_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, PHPREDIS_CTX_PTR); +} + +PHP_REDIS_API int +redis_hello_version_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx) +{ + return redis_hello_response(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + z_tab, PHPREDIS_CTX_PTR + 1); +} + static int redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { @@ -2031,11 +2150,7 @@ redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * zval z_ret; if (read_mbulk_header(redis_sock, &numElems) < 0) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } @@ -2043,11 +2158,7 @@ redis_function_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval * redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret); array_zip_values_recursive(&z_ret); - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); return SUCCESS; } @@ -2075,21 +2186,13 @@ redis_command_info_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv zval z_ret; if (read_mbulk_header(redis_sock, &numElems) < 0) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } array_init(&z_ret); redis_read_multibulk_recursive(redis_sock, numElems, 0, &z_ret); - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); return SUCCESS; } @@ -2159,20 +2262,12 @@ redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (read_mbulk_header(redis_sock, &messages) < 0 || redis_read_stream_messages(redis_sock, messages, &z_messages) < 0) { - zval_dtor(&z_messages); - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + zval_ptr_dtor_nogc(&z_messages); + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return -1; } - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_messages, 0, 1); - } else { - add_next_index_zval(z_tab, &z_messages); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_messages); return 0; } @@ -2207,7 +2302,7 @@ redis_read_stream_messages_multi(RedisSock *redis_sock, int count, zval *z_strea return 0; failure: efree(id); - zval_dtor(&z_messages); + zval_ptr_dtor_nogc(&z_messages); return -1; } @@ -2229,21 +2324,13 @@ redis_xread_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, goto cleanup; } - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_rv, 0, 1); - } else { - add_next_index_zval(z_tab, &z_rv); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_rv); return 0; cleanup: - zval_dtor(&z_rv); + zval_ptr_dtor_nogc(&z_rv); failure: - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return -1; } @@ -2298,9 +2385,9 @@ redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, zva zval z_msgs = {0}; char *id = NULL; long id_len = 0; - int messages; + int messages = 0; - ZEND_ASSERT(!is_xautoclaim || count == 3); + ZEND_ASSERT(!is_xautoclaim || (count == 2 || count == 3)); ZVAL_UNDEF(rv); @@ -2326,15 +2413,18 @@ redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, zva if (is_xautoclaim) { zval z_deleted = {0}; - if (redis_sock_read_multibulk_reply_zval(redis_sock, &z_deleted) == NULL) + if (count == 3 && redis_sock_read_multibulk_reply_zval(redis_sock, &z_deleted) == NULL) goto failure; array_init(rv); - // Package up ID, message, and deleted messages in our reply + // Package up ID and message add_next_index_stringl(rv, id, id_len); add_next_index_zval(rv, &z_msgs); - add_next_index_zval(rv, &z_deleted); + + // Add deleted messages if they exist + if (count == 3) + add_next_index_zval(rv, &z_deleted); efree(id); } else { @@ -2345,8 +2435,8 @@ redis_read_xclaim_reply(RedisSock *redis_sock, int count, int is_xautoclaim, zva return 0; failure: - zval_dtor(&z_msgs); - zval_dtor(rv); + zval_ptr_dtor_nogc(&z_msgs); + zval_ptr_dtor_nogc(rv); if (id) efree(id); return -1; @@ -2367,20 +2457,12 @@ redis_xclaim_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (redis_read_xclaim_reply(redis_sock, count, ctx == PHPREDIS_CTX_PTR, &z_ret) < 0) goto failure; - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); return 0; failure: - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return -1; } @@ -2426,7 +2508,7 @@ redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements) case TYPE_MULTIBULK: array_init(&zv); if (redis_read_xinfo_response(redis_sock, &zv, li) != SUCCESS) { - zval_dtor(&zv); + zval_ptr_dtor_nogc(&zv); goto failure; } if (key) { @@ -2450,28 +2532,273 @@ redis_read_xinfo_response(RedisSock *redis_sock, zval *z_ret, int elements) } PHP_REDIS_API int -redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) -{ - zval z_ret; - int elements; +redis_read_vinfo_response(RedisSock *redis_sock, zval *z_ret, long long count) { + char kbuf[256], vbuf[256]; + REDIS_REPLY_TYPE type; + size_t klen, vlen; + long lval; - if (read_mbulk_header(redis_sock, &elements) == SUCCESS) { - array_init(&z_ret); - if (redis_read_xinfo_response(redis_sock, &z_ret, elements) == SUCCESS) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); + if (count < 0 || count % 2 != 0 || Z_TYPE_P(z_ret) != IS_ARRAY) { + zend_error_noreturn(E_ERROR, "Internal vinfo handler error"); + } + + for (long long i = 0; i < count; i += 2) { + if (redis_read_reply_type(redis_sock, &type, &lval) < 0 || + type != TYPE_LINE || + redis_sock_gets(redis_sock, kbuf, sizeof(kbuf), &klen) < 0) + { + return FAILURE; + } + + if (redis_read_reply_type(redis_sock, &type, &lval) < 0) { + return FAILURE; + } + + switch (type) { + case TYPE_LINE: + if (redis_sock_gets(redis_sock, vbuf, sizeof(vbuf), &vlen) < 0) { + return FAILURE; + } + add_assoc_stringl_ex(z_ret, kbuf, klen, vbuf, vlen); + break; + case TYPE_INT: + add_assoc_long_ex(z_ret, kbuf, klen, lval); + break; + default: + return FAILURE; + } + } + + return SUCCESS; +} + + +PHP_REDIS_API int +redis_vinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + zval z_ret; + int count; + + if (read_mbulk_header(redis_sock, &count) < 0 || + count < 0 || count % 2 != 0) + { + REDIS_RESPONSE_ERROR(redis_sock, z_tab); + return FAILURE; + } + + array_init_size(&z_ret, count / 2); + + if (redis_read_vinfo_response(redis_sock, &z_ret, count) != SUCCESS) { + zval_ptr_dtor_nogc(&z_ret); + REDIS_RESPONSE_ERROR(redis_sock, z_tab); + return FAILURE; + } + + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); + + return SUCCESS; +} + +/* Unfortunately VEMB decided to use +string\r\n for the encoding when RAW is + * sent which PhpRedis will parse as `(true)` so we need a specific handler for + * it */ +PHP_REDIS_API int +redis_read_vemb_response(RedisSock *redis_sock, zval *z_ret, long long count) { + REDIS_REPLY_TYPE type; + char kbuf[256], *str; + size_t klen; + double dval; + long tlen; + + if (count < 0 || Z_TYPE_P(z_ret) != IS_ARRAY) { + zend_error_noreturn(E_ERROR, "Internal vemb handler error"); + } + + for (long long i = 0; i < count; i++) { + if (redis_read_reply_type(redis_sock, &type, &tlen) < 0) { + return FAILURE; + } + + if (type == TYPE_LINE) { + if (redis_sock_gets(redis_sock, kbuf, sizeof(kbuf), &klen) < 0) + return FAILURE; + add_next_index_stringl(z_ret, kbuf, klen); + } else if (type == TYPE_BULK) { + if ((str = redis_sock_read_bulk_reply(redis_sock, tlen)) == NULL) + return FAILURE; + + if (is_numeric_string(str, tlen, NULL, &dval, 0) == IS_DOUBLE) { + add_next_index_double(z_ret, dval); + } else { + add_next_index_stringl(z_ret, str, tlen); } - return SUCCESS; + + efree(str); + } else { + return FAILURE; } - zval_dtor(&z_ret); } - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; + + return SUCCESS; +} + +PHP_REDIS_API int +redis_vemb_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + zval z_ret; + int count; + + if (read_mbulk_header(redis_sock, &count) < 0 || count < 1) { + REDIS_RESPONSE_ERROR(redis_sock, z_tab); + return FAILURE; + } + + array_init_size(&z_ret, count); + + if (redis_read_vemb_response(redis_sock, &z_ret, count) != SUCCESS) { + zval_ptr_dtor_nogc(&z_ret); + REDIS_RESPONSE_ERROR(redis_sock, z_tab); + return FAILURE; + } + + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); + + return SUCCESS; +} + +PHP_REDIS_API int +redis_read_vlinks_response(RedisSock *redis_sock, zval *z_ret, + long long elements, void *ctx) +{ + long long i; + zval z_ele; + int links; + + array_init_size(z_ret, elements); + + for (i = 0; i < elements; i++) { + if (read_mbulk_header(redis_sock, &links) < 0) + return FAILURE; + + array_init(&z_ele); + redis_mbulk_reply_loop(redis_sock, &z_ele, links, UNSERIALIZE_KEYS); + + if (ctx == PHPREDIS_CTX_PTR) { + array_zip_values_and_scores(redis_sock, &z_ele, SCORE_DECODE_DOUBLE); + } + + add_next_index_zval(z_ret, &z_ele); + } + + return SUCCESS; +} + +PHP_REDIS_API int +redis_vlinks_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + int elements; + zval z_ret; + + ZVAL_FALSE(&z_ret); + + if (read_mbulk_header(redis_sock, &elements) < 0) + goto fail; + + /* TODO: Figure out if we want `false` or `[]` here */ + if (elements < 0) { + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); + return SUCCESS; + } + + if (redis_read_vlinks_response(redis_sock, &z_ret, elements, ctx) != SUCCESS) + goto fail; + + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); + return SUCCESS; + +fail: + zval_ptr_dtor_nogc(&z_ret); + REDIS_RESPONSE_ERROR(redis_sock, z_tab); + return FAILURE; +} + +PHP_REDIS_API int +redis_deserialize_vgetattr_reply(zval *zret, const char *str, size_t len) { +#ifdef HAVE_REDIS_JSON + #if PHP_VERSION_ID < 80000 + #define PHP_JSON_IN(s) ((char*)s) + #else + #define PHP_JSON_IN(s) (s) + #endif + + if (php_json_decode(zret, PHP_JSON_IN(str), len, 1, + PHP_JSON_PARSER_DEFAULT_DEPTH) == FAILURE) + { + php_error_docref(NULL, E_WARNING, "Failed to deserialize attributes"); + return FAILURE; + } + + return SUCCESS; + + #undef PHP_JSON_IN +#else + php_error_docref(NULL, E_WARNING, + "PhpRedis is not compiled with JSON support"); + return FAILURE; +#endif +} + +PHP_REDIS_API int +redis_vgetattr_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + zval z_ret; + char *attr; + int len; + + attr = redis_sock_read(redis_sock, &len); + if (attr == NULL) { + REDIS_RESPONSE_ERROR(redis_sock, z_tab); + return FAILURE; + } + + if (len == 0) { + ZVAL_FALSE(&z_ret); + } else if (ctx == PHPREDIS_CTX_PTR) { + if (redis_deserialize_vgetattr_reply(&z_ret, attr, len) != SUCCESS) { + ZVAL_STRINGL(&z_ret, attr, len); + } } else { - add_next_index_bool(z_tab, 0); + ZVAL_STRINGL(&z_ret, attr, len); + } + + efree(attr); + + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); + + return SUCCESS; +} + +PHP_REDIS_API int +redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + zval z_ret; + int elements; + + if (read_mbulk_header(redis_sock, &elements) == SUCCESS) { + array_init(&z_ret); + if (redis_read_xinfo_response(redis_sock, &z_ret, elements) == SUCCESS) { + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); + return SUCCESS; + } + zval_ptr_dtor_nogc(&z_ret); } + + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } @@ -2562,18 +2889,14 @@ int redis_acl_custom_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, res = cb(redis_sock, &zret, len); if (res == FAILURE) { - zval_dtor(&zret); + zval_ptr_dtor_nogc(&zret); ZVAL_FALSE(&zret); } } else { ZVAL_FALSE(&zret); } - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&zret, 0, 0); - } else { - add_next_index_zval(z_tab, &zret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, zret); return res; } @@ -2640,36 +2963,56 @@ redis_1_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_ta return ret ? SUCCESS : FAILURE; } -PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { +static int redis_bulk_resp_to_zval(RedisSock *redis_sock, zval *zdst, int *dstlen) { + char *resp; + int len; - char *response; - int response_len; + resp = redis_sock_read(redis_sock, &len); + if (dstlen) *dstlen = len; - if ((response = redis_sock_read(redis_sock, &response_len)) - == NULL) - { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + if (resp == NULL) { + ZVAL_FALSE(zdst); return FAILURE; } + + redis_unpack(redis_sock, resp, len, zdst); + + efree(resp); + return SUCCESS; +} + +PHP_REDIS_API int +redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + zval zret; + int ret; + + ret = redis_bulk_resp_to_zval(redis_sock, &zret, NULL); + + REDIS_RETURN_ZVAL(redis_sock, z_tab, zret); + + return ret; +} + +PHP_REDIS_API int +redis_bulk_withmeta_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) +{ + zval zret, zbulk; + int len, ret; + + ret = redis_bulk_resp_to_zval(redis_sock, &zbulk, &len); + + redis_with_metadata(&zret, &zbulk, len); + if (IS_ATOMIC(redis_sock)) { - if (!redis_unpack(redis_sock, response, response_len, return_value)) { - RETVAL_STRINGL(response, response_len); - } + RETVAL_ZVAL(&zret, 0, 1); } else { - zval z_unpacked; - if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) { - add_next_index_zval(z_tab, &z_unpacked); - } else { - add_next_index_stringl(z_tab, response, response_len); - } + add_next_index_zval(z_tab, &zret); } - efree(response); - return SUCCESS; + return ret; } /* like string response, but never unserialized. */ @@ -2684,11 +3027,7 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if ((response = redis_sock_read(redis_sock, &response_len)) == NULL) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } if (IS_ATOMIC(redis_sock)) { @@ -2701,67 +3040,6 @@ redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -/* Response for DEBUG object which is a formatted single line reply */ -PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - zval *z_tab, void *ctx) -{ - char *resp, *p, *p2, *p3, *p4; - int is_numeric, resp_len; - - /* Add or return false if we can't read from the socket */ - if((resp = redis_sock_read(redis_sock, &resp_len))==NULL) { - if (IS_ATOMIC(redis_sock)) { - RETURN_FALSE; - } - add_next_index_bool(z_tab, 0); - return; - } - - zval z_result; - array_init(&z_result); - - /* Skip the '+' */ - p = resp + 1; - - /* : ... */ - while((p2 = strchr(p, ':'))!=NULL) { - /* Null terminate at the ':' */ - *p2++ = '\0'; - - /* Null terminate at the space if we have one */ - if((p3 = strchr(p2, ' '))!=NULL) { - *p3++ = '\0'; - } else { - p3 = resp + resp_len; - } - - is_numeric = 1; - for(p4=p2; *p4; ++p4) { - if(*p4 < '0' || *p4 > '9') { - is_numeric = 0; - break; - } - } - - /* Add our value */ - if(is_numeric) { - add_assoc_long(&z_result, p, atol(p2)); - } else { - add_assoc_string(&z_result, p, p2); - } - - p = p3; - } - - efree(resp); - - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_result, 0, 1); - } else { - add_next_index_zval(z_tab, &z_result); - } -} - PHP_REDIS_API int redis_sock_configure(RedisSock *redis_sock, HashTable *opts) { @@ -2804,8 +3082,14 @@ redis_sock_configure(RedisSock *redis_sock, HashTable *opts) redis_sock->persistent_id = zval_get_string(val); redis_sock->persistent = 1; } else { - redis_sock->persistent = zval_is_true(val); + redis_sock->persistent = zend_is_true(val); + } + } else if (zend_string_equals_literal_ci(zkey, "maxRetries")) { + if (Z_TYPE_P(val) != IS_LONG || Z_LVAL_P(val) < 0) { + REDIS_VALUE_EXCEPTION("Max retries must be a non-negative integer"); + return FAILURE; } + redis_sock->max_retries = zval_get_long(val); } else if (zend_string_equals_literal_ci(zkey, "retryInterval")) { if (Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_DOUBLE) { REDIS_VALUE_EXCEPTION("Invalid retry interval"); @@ -2813,7 +3097,7 @@ redis_sock_configure(RedisSock *redis_sock, HashTable *opts) } redis_sock->retry_interval = zval_get_long(val); } else if (zend_string_equals_literal_ci(zkey, "ssl")) { - if (redis_sock_set_stream_context(redis_sock, val) != SUCCESS) { + if (redis_sock_set_context_zval(redis_sock, val) != SUCCESS) { REDIS_VALUE_EXCEPTION("Invalid SSL context options"); return FAILURE; } @@ -2823,6 +3107,12 @@ redis_sock_configure(RedisSock *redis_sock, HashTable *opts) return FAILURE; } redis_sock_set_auth_zval(redis_sock, val); + } else if (zend_string_equals_literal_ci(zkey, "database")) { + if (Z_TYPE_P(val) != IS_LONG || Z_LVAL_P(val) < 0 || Z_LVAL_P(val) > INT_MAX) { + REDIS_VALUE_EXCEPTION("Invalid database number"); + return FAILURE; + } + redis_sock->dbNumber = Z_LVAL_P(val); } else if (zend_string_equals_literal_ci(zkey, "backoff")) { if (redis_sock_set_backoff(redis_sock, val) != SUCCESS) { REDIS_VALUE_EXCEPTION("Invalid backoff options"); @@ -2852,7 +3142,7 @@ redis_sock_create(char *host, int host_len, int port, redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->retry_interval = retry_interval * 1000; redis_sock->max_retries = 10; - redis_initialize_backoff(&redis_sock->backoff, retry_interval); + redis_initialize_backoff(&redis_sock->backoff, redis_sock->retry_interval); redis_sock->persistent = persistent; if (persistent && persistent_id != NULL) { @@ -2871,11 +3161,13 @@ redis_sock_create(char *host, int host_len, int port, } static int redis_uniqid(char *buf, size_t buflen) { + static unsigned long counter = 0; struct timeval tv; + gettimeofday(&tv, NULL); return snprintf(buf, buflen, "phpredis:%08lx%05lx:%08lx", - (long)tv.tv_sec, (long)tv.tv_usec, (long)php_rand()); + (long)tv.tv_sec, (long)tv.tv_usec, counter++); } static int redis_stream_liveness_check(php_stream *stream) { @@ -2949,7 +3241,7 @@ static int redis_stream_detect_dirty(php_stream *stream) { if ((fd = redis_stream_fd_for_select(stream)) == -1) return FAILURE; - /* We want to detect a readable socket (it shouln't be) */ + /* We want to detect a readable socket (it shouldn't be) */ REDIS_POLL_FD_SET(pfd, fd, PHP_POLLREADABLE); rv = php_poll2(&pfd, 1, redis_pool_poll_timeout()); @@ -2960,10 +3252,30 @@ static int redis_stream_detect_dirty(php_stream *stream) { return rv == 0 ? SUCCESS : FAILURE; } +static inline zend_bool +redis_check_echo_response(RedisSock *redis_sock, char *hdr, const char *id, + size_t idlen) +{ + char buf[256] = {0}; + size_t len; + + /* Sentinel doesn't have ECHO but it will contain the ID in the error + * message so just check that */ + if (redis_sock->sentinel) { + return redis_strncmp(hdr, ZEND_STRL("-ERR unknown command")) == 0 && + strstr(hdr, id) != NULL; + } + + /* Non-sentinel: Read and verify the ID */ + return *hdr == TYPE_BULK && atoi(hdr + 1) == idlen && + redis_sock_gets(redis_sock, buf, sizeof(buf) - 1, &len) == 0 && + redis_strncmp(buf, id, idlen) == 0; +} + static int redis_sock_check_liveness(RedisSock *redis_sock) { - char id[64], inbuf[4096]; + char id[64], inbuf[256] = {0}; int idlen, auth; smart_string cmd = {0}; size_t len; @@ -3002,14 +3314,19 @@ redis_sock_check_liveness(RedisSock *redis_sock) } if (auth) { - if (strncmp(inbuf, "+OK", 3) == 0 || strncmp(inbuf, "-ERR Client sent AUTH", 21) == 0) { + if (redis_strncmp(inbuf, ZEND_STRL("+OK")) == 0 || + redis_strncmp(inbuf, ZEND_STRL("-ERR Client sent AUTH")) == 0) + { /* successfully authenticated or authentication isn't required */ if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0) { goto failure; } - } else if (strncmp(inbuf, "-NOAUTH", 7) == 0) { - /* connection is fine but authentication failed, next command must fails too */ - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "-NOAUTH", 7) != 0) { + } else if (redis_strncmp(inbuf, ZEND_STRL("-NOAUTH")) == 0) { + /* connection is fine but authentication failed, next command must + * fail too */ + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 + || redis_strncmp(inbuf, ZEND_STRL("-NOAUTH")) != 0) + { goto failure; } return SUCCESS; @@ -3018,24 +3335,16 @@ redis_sock_check_liveness(RedisSock *redis_sock) } redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED; } else { - if (strncmp(inbuf, "-NOAUTH", 7) == 0) { + if (redis_strncmp(inbuf, ZEND_STRL("-NOAUTH")) == 0) { /* connection is fine but authentication required */ return SUCCESS; } } - /* check echo response */ - if ((redis_sock->sentinel && ( - strncmp(inbuf, "-ERR unknown command", 20) != 0 || - strstr(inbuf, id) == NULL - )) || *inbuf != TYPE_BULK || atoi(inbuf + 1) != idlen || - redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || - strncmp(inbuf, id, idlen) != 0 - ) { - goto failure; + if (redis_check_echo_response(redis_sock, inbuf, id, idlen)) { + return SUCCESS; } - return SUCCESS; failure: redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; if (redis_sock->stream) { @@ -3045,6 +3354,26 @@ redis_sock_check_liveness(RedisSock *redis_sock) return FAILURE; } +static php_stream_context * +alloc_stream_context(RedisSock *redis_sock) { + php_stream_context *ctx; + zend_string *zkey; + zval *z_ele; + + if (redis_sock->context == NULL) + return NULL; + + ctx = php_stream_context_alloc(); + + ZEND_HASH_FOREACH_STR_KEY_VAL(redis_sock->context, zkey, z_ele) { + if (zkey != NULL) { + php_stream_context_set_option(ctx, "ssl", ZSTR_VAL(zkey), z_ele); + } + } ZEND_HASH_FOREACH_END(); + + return ctx; +} + /** * redis_sock_connect */ @@ -3056,14 +3385,18 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) const char *fmtstr = "%s://%s:%d"; int host_len, usocket = 0, err = 0, tcp_flag = 1; ConnectionPool *p = NULL; + php_stream_context *ctx; + + // Monotonically incrementing persistent id counter + static unsigned long long counter = 0; if (redis_sock->stream != NULL) { - redis_sock_disconnect(redis_sock, 0); + redis_sock_disconnect(redis_sock, 0, 1); } address = ZSTR_VAL(redis_sock->host); if ((pos = strstr(address, "://")) == NULL) { - strcpy(scheme, redis_sock->stream_ctx ? "ssl" : "tcp"); + strcpy(scheme, redis_sock->context ? "ssl" : "tcp"); } else { snprintf(scheme, sizeof(scheme), "%.*s", (int)(pos - address), address); address = pos + sizeof("://") - 1; @@ -3101,12 +3434,13 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) int limit = INI_INT("redis.pconnect.connection_limit"); if (limit > 0 && p->nb_active >= limit) { - redis_sock_set_err(redis_sock, "Connection limit reached", sizeof("Connection limit reached") - 1); + redis_sock_set_err(redis_sock, ZEND_STRL("Connection limit reached")); return FAILURE; } gettimeofday(&tv, NULL); - persistent_id = strpprintf(0, "phpredis_%ld%ld", tv.tv_sec, tv.tv_usec); + persistent_id = strpprintf(0, "phpredis_%ld%ld:%llu", tv.tv_sec, + (long)tv.tv_usec, ++counter); } else { if (redis_sock->persistent_id) { persistent_id = strpprintf(0, "phpredis:%s:%s", host, ZSTR_VAL(redis_sock->persistent_id)); @@ -3122,10 +3456,22 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock) tv_ptr = &tv; } + ctx = alloc_stream_context(redis_sock); + redis_sock->stream = php_stream_xport_create(host, host_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, persistent_id ? ZSTR_VAL(persistent_id) : NULL, - tv_ptr, redis_sock->stream_ctx, &estr, &err); + tv_ptr, ctx, &estr, &err); + + /* On successful connection, the context is moved into the stream so we + * can disown it. If the connection failed, clean it up ourselves. */ + if (ctx && redis_sock->stream) { + ZEND_ASSERT(GC_REFCOUNT(ctx->res) == 2); + GC_DELREF(ctx->res); + } else if (ctx) { + ZEND_ASSERT(GC_REFCOUNT(ctx->res) == 1); + zend_list_delete(ctx->res); + } if (persistent_id) { zend_string_release(persistent_id); @@ -3188,7 +3534,7 @@ redis_sock_server_open(RedisSock *redis_sock) redis_sock->status = REDIS_SOCK_STATUS_AUTHENTICATED; // fall through case REDIS_SOCK_STATUS_AUTHENTICATED: - if (redis_sock->dbNumber && reselect_db(redis_sock) != SUCCESS) { + if (redis_sock->dbNumber && redis_select_db(redis_sock) != SUCCESS) { break; } redis_sock->status = REDIS_SOCK_STATUS_READY; @@ -3206,7 +3552,7 @@ redis_sock_server_open(RedisSock *redis_sock) * redis_sock_disconnect */ PHP_REDIS_API int -redis_sock_disconnect(RedisSock *redis_sock, int force) +redis_sock_disconnect(RedisSock *redis_sock, int force, int is_reset_mode) { if (redis_sock == NULL) { return FAILURE; @@ -3218,7 +3564,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force) } if (force || !IS_ATOMIC(redis_sock)) { php_stream_pclose(redis_sock->stream); - free_reply_callbacks(redis_sock); + redis_free_reply_callbacks(redis_sock); if (p) p->nb_active--; } else if (p) { zend_llist_prepend_element(&p->list, &redis_sock->stream); @@ -3228,7 +3574,9 @@ redis_sock_disconnect(RedisSock *redis_sock, int force) } redis_sock->stream = NULL; } - redis_sock->mode = ATOMIC; + if (is_reset_mode) { + redis_sock->mode = ATOMIC; + } redis_sock->status = REDIS_SOCK_STATUS_DISCONNECTED; redis_sock->watching = 0; @@ -3241,11 +3589,7 @@ redis_sock_disconnect(RedisSock *redis_sock, int force) PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) { - // Free our last error - if (redis_sock->err != NULL) { - zend_string_release(redis_sock->err); - redis_sock->err = NULL; - } + redis_sock_clear_err(redis_sock); if (msg != NULL && msg_len > 0) { // Copy in our new error message @@ -3253,25 +3597,41 @@ redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len) } } -PHP_REDIS_API int -redis_sock_set_stream_context(RedisSock *redis_sock, zval *options) -{ - zend_string *zkey; - zval *z_ele; +PHP_REDIS_API void redis_sock_clear_err(RedisSock *redis_sock) { + if (redis_sock->err == NULL) + return; - if (!redis_sock || Z_TYPE_P(options) != IS_ARRAY) - return FAILURE; + zend_string_release(redis_sock->err); + redis_sock->err = NULL; +} - if (!redis_sock->stream_ctx) - redis_sock->stream_ctx = php_stream_context_alloc(); +#if PHP_VERSION_ID < 80000 +#define GC_TRY_ADDREF(ht) do { \ + if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) { \ + GC_ADDREF(ht); \ + } \ +} while(0) +#endif - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), zkey, z_ele) { - if (zkey != NULL) { - php_stream_context_set_option(redis_sock->stream_ctx, "ssl", ZSTR_VAL(zkey), z_ele); - } - } ZEND_HASH_FOREACH_END(); - return SUCCESS; +void redis_sock_set_context(RedisSock *redis_sock, HashTable *ht) { + redis_sock_free_context(redis_sock); + + if (ht == NULL) + return; + + GC_TRY_ADDREF(ht); + redis_sock->context = ht; +} + +int redis_sock_set_context_zval(RedisSock *redis_sock, zval *zv) { + HashTable *ht; + + ht = zv && Z_TYPE_P(zv) == IS_ARRAY ? Z_ARRVAL_P(zv) : NULL; + + redis_sock_set_context(redis_sock, ht); + + return ht ? SUCCESS : FAILURE; } PHP_REDIS_API int @@ -3323,25 +3683,19 @@ PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, int numElems; if (read_mbulk_header(redis_sock, &numElems) < 0) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } if (numElems == -1 && redis_sock->null_mbulk_as_null) { ZVAL_NULL(&z_multi_result); + } else if (numElems < 1) { + ZVAL_EMPTY_ARRAY(&z_multi_result); } else { - array_init(&z_multi_result); + array_init_size(&z_multi_result, numElems); redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_ALL); } - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_multi_result, 0, 1); - } else { - add_next_index_zval(z_tab, &z_multi_result); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result); return 0; } @@ -3354,24 +3708,20 @@ redis_mbulk_reply_raw(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval int numElems; if (read_mbulk_header(redis_sock, &numElems) < 0) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } zval z_multi_result; - array_init(&z_multi_result); /* pre-allocate array for multi's results. */ - redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE); - - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_multi_result, 0, 1); + if (numElems < 1) { + ZVAL_EMPTY_ARRAY(&z_multi_result); } else { - add_next_index_zval(z_tab, &z_multi_result); + array_init_size(&z_multi_result, numElems); /* pre-allocate array for multi's results. */ + redis_mbulk_reply_loop(redis_sock, &z_multi_result, numElems, UNSERIALIZE_NONE); } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result); + return SUCCESS; } @@ -3383,29 +3733,25 @@ redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zv zval z_multi_result; if (read_mbulk_header(redis_sock, &numElems) < 0) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } + REDIS_RESPONSE_ERROR(redis_sock, z_tab); return FAILURE; } - array_init(&z_multi_result); - for (i = 0; i < numElems; ++i) { - if ((line = redis_sock_read(redis_sock, &len)) == NULL) { - add_next_index_bool(&z_multi_result, 0); - continue; + if (numElems < 1) { + ZVAL_EMPTY_ARRAY(&z_multi_result); + } else { + array_init_size(&z_multi_result, numElems); + for (i = 0; i < numElems; ++i) { + if ((line = redis_sock_read(redis_sock, &len)) == NULL) { + add_next_index_bool(&z_multi_result, 0); + continue; + } + add_next_index_double(&z_multi_result, atof(line)); + efree(line); } - add_next_index_double(&z_multi_result, atof(line)); - efree(line); } - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_multi_result, 0, 1); - } else { - add_next_index_zval(z_tab, &z_multi_result); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result); return SUCCESS; } @@ -3414,7 +3760,7 @@ PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize) { - zval z_unpacked; + zval z_value; char *line; int i, len; @@ -3433,11 +3779,13 @@ redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, (unserialize == UNSERIALIZE_VALS && i % 2 != 0) ); - if (unwrap && redis_unpack(redis_sock, line, len, &z_unpacked)) { - add_next_index_zval(z_tab, &z_unpacked); + if (unwrap) { + redis_unpack(redis_sock, line, len, &z_value); } else { - add_next_index_stringl(z_tab, line, len); + ZVAL_STRINGL_FAST(&z_value, line, len); } + zend_hash_next_index_insert_new(Z_ARRVAL_P(z_tab), &z_value); + efree(line); } } @@ -3495,58 +3843,46 @@ redis_mbulk_reply_zipped_raw_variant(RedisSock *redis_sock, zval *zret, int coun /* Specialized multibulk processing for HMGET where we need to pair requested * keys with their returned values */ -PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) +PHP_REDIS_API int +redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx) { - char *response; - int response_len; - int i, numElems; + HashTable *htctx; + int numElems; + zval *zfield; + char *rresp; + int len; - zval *z_keys = ctx; + htctx = ctx; - if (read_mbulk_header(redis_sock, &numElems) < 0) { - if (IS_ATOMIC(redis_sock)) { - RETVAL_FALSE; - } else { - add_next_index_bool(z_tab, 0); - } - goto failure; + if (read_mbulk_header(redis_sock, &numElems) < 0 || + zend_hash_num_elements(htctx) != numElems) + { + zend_hash_destroy(htctx); + FREE_HASHTABLE(htctx); + REDIS_RESPONSE_ERROR(redis_sock, z_tab); + return FAILURE; } - zval z_multi_result; - array_init(&z_multi_result); /* pre-allocate array for multi's results. */ - - for(i = 0; i < numElems; ++i) { - zend_string *zstr = zval_get_string(&z_keys[i]); - response = redis_sock_read(redis_sock, &response_len); - if(response != NULL) { - zval z_unpacked; - if (redis_unpack(redis_sock, response, response_len, &z_unpacked)) { - add_assoc_zval_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), &z_unpacked); - } else { - add_assoc_stringl_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), response, response_len); - } - efree(response); + + zval z_multi_result, zunpacked; + + ZEND_HASH_FOREACH_VAL(htctx, zfield) { + rresp = redis_sock_read(redis_sock, &len); + + if (rresp != NULL) { + redis_unpack(redis_sock, rresp, len, &zunpacked); + efree(rresp); } else { - add_assoc_bool_ex(&z_multi_result, ZSTR_VAL(zstr), ZSTR_LEN(zstr), 0); + ZVAL_FALSE(&zunpacked); } - zend_string_release(zstr); - zval_dtor(&z_keys[i]); - } - efree(z_keys); - if (IS_ATOMIC(redis_sock)) { - RETVAL_ZVAL(&z_multi_result, 0, 1); - } else { - add_next_index_zval(z_tab, &z_multi_result); - } + ZVAL_COPY_VALUE(zfield, &zunpacked); + } ZEND_HASH_FOREACH_END(); + + ZVAL_ARR(&z_multi_result, htctx); + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_multi_result); + return SUCCESS; -failure: - if (z_keys != NULL) { - for (i = 0; Z_TYPE(z_keys[i]) != IS_NULL; ++i) { - zval_dtor(&z_keys[i]); - } - efree(z_keys); - } - return FAILURE; } /** @@ -3564,17 +3900,44 @@ redis_sock_write(RedisSock *redis_sock, char *cmd, size_t sz) return -1; } +/* Grow array to double size if we need more space */ +fold_item* +redis_add_reply_callback(RedisSock *redis_sock) { + if (UNEXPECTED(redis_sock->reply_callback_count == redis_sock->reply_callback_capacity)) { + if (redis_sock->reply_callback_capacity == 0) { + redis_sock->reply_callback_capacity = REDIS_CALLBACKS_INIT_SIZE; + } else if (redis_sock->reply_callback_capacity < REDIS_CALLBACKS_MAX_DOUBLE) { + redis_sock->reply_callback_capacity *= 2; + } else { + redis_sock->reply_callback_capacity += REDIS_CALLBACKS_ADD_SIZE; + } + redis_sock->reply_callback = erealloc(redis_sock->reply_callback, redis_sock->reply_callback_capacity * sizeof(fold_item)); + } + return &redis_sock->reply_callback[redis_sock->reply_callback_count++]; +} + void -free_reply_callbacks(RedisSock *redis_sock) +redis_free_reply_callbacks(RedisSock *redis_sock) { - fold_item *fi; + if (redis_sock->reply_callback != NULL) { + efree(redis_sock->reply_callback); + redis_sock->reply_callback = NULL; + redis_sock->reply_callback_count = 0; + redis_sock->reply_callback_capacity = 0; + } +} - while (redis_sock->head != NULL) { - fi = redis_sock->head->next; - free(redis_sock->head); - redis_sock->head = fi; +static void +redis_sock_release_hello(struct RedisHello *hello) { + if (hello->server) { + zend_string_release(hello->server); + hello->server = NULL; + } + + if (hello->version) { + zend_string_release(hello->version); + hello->version = NULL; } - redis_sock->current = NULL; } /** @@ -3587,9 +3950,7 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) if (redis_sock->prefix) { zend_string_release(redis_sock->prefix); } - if (redis_sock->pipeline_cmd) { - zend_string_release(redis_sock->pipeline_cmd); - } + smart_string_free(&redis_sock->pipeline_cmd); if (redis_sock->err) { zend_string_release(redis_sock->err); } @@ -3607,7 +3968,10 @@ PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock) } } redis_sock_free_auth(redis_sock); - free_reply_callbacks(redis_sock); + redis_free_reply_callbacks(redis_sock); + redis_sock_release_hello(&redis_sock->hello); + redis_sock_free_context(redis_sock); + efree(redis_sock); } @@ -3834,12 +4198,38 @@ redis_uncompress(RedisSock *redis_sock, char **dst, size_t *dstlen, const char * return 0; } +static int serialize_generic_zval(char **dst, size_t *len, zval *zsrc) { + zend_string *zstr; + + zstr = zval_get_string_func(zsrc); + if (ZSTR_IS_INTERNED(zstr)) { + *dst = ZSTR_VAL(zstr); + *len = ZSTR_LEN(zstr); + return 0; + } + + *dst = estrndup(ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + *len = ZSTR_LEN(zstr); + + zend_string_release(zstr); + + return 1; +} + + PHP_REDIS_API int redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) { size_t tmplen; int tmpfree; char *tmp; + /* Don't pack actual numbers if the user asked us not to */ + if (UNEXPECTED(redis_sock->pack_ignore_numbers && + (Z_TYPE_P(z) == IS_LONG || Z_TYPE_P(z) == IS_DOUBLE))) + { + return serialize_generic_zval(val, val_len, z); + } + /* First serialize */ tmpfree = redis_serialize(redis_sock, z, &tmp, &tmplen); @@ -3854,19 +4244,49 @@ redis_pack(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) { PHP_REDIS_API int redis_unpack(RedisSock *redis_sock, const char *src, int srclen, zval *zdst) { + zend_long lval; + double dval; size_t len; char *buf; + if (UNEXPECTED((redis_sock->serializer != REDIS_SERIALIZER_NONE || + redis_sock->compression != REDIS_COMPRESSION_NONE) && + redis_sock->pack_ignore_numbers) && + srclen > 0 && srclen < 512) + { + switch (is_numeric_string(src, srclen, &lval, &dval, 0)) { + case IS_LONG: + ZVAL_LONG(zdst, lval); + return 1; + case IS_DOUBLE: + ZVAL_DOUBLE(zdst, dval); + return 1; + default: + /* Fallthrough */ + break; + } + } + + /* Input string is empty */ + if (srclen == 0) { + ZVAL_STR(zdst, ZSTR_EMPTY_ALLOC()); + return 1; + } + /* Uncompress, then unserialize */ if (redis_uncompress(redis_sock, &buf, &len, src, srclen)) { if (!redis_unserialize(redis_sock, buf, len, zdst)) { - ZVAL_STRINGL(zdst, buf, len); + ZVAL_STRINGL_FAST(zdst, buf, len); } efree(buf); return 1; } - return redis_unserialize(redis_sock, buf, len, zdst); + if (!redis_unserialize(redis_sock, src, srclen, zdst)) { + ZVAL_STRINGL_FAST(zdst, src, srclen); + } + + return 1; } PHP_REDIS_API int @@ -3901,24 +4321,22 @@ redis_serialize(RedisSock *redis_sock, zval *z, char **val, size_t *val_len) *val_len = 5; break; - default: { /* copy */ - zend_string *zstr = zval_get_string(z); - *val = estrndup(ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - *val_len = ZSTR_LEN(zstr); - zend_string_release(zstr); - return 1; - } + default: + return serialize_generic_zval(val, val_len, z); } break; case REDIS_SERIALIZER_PHP: PHP_VAR_SERIALIZE_INIT(ht); php_var_serialize(&sstr, z, &ht); + PHP_VAR_SERIALIZE_DESTROY(ht); + if (!sstr.s) { + break; + } *val = estrndup(ZSTR_VAL(sstr.s), ZSTR_LEN(sstr.s)); *val_len = ZSTR_LEN(sstr.s); smart_str_free(&sstr); - PHP_VAR_SERIALIZE_DESTROY(ht); return 1; @@ -4014,13 +4432,7 @@ redis_unserialize(RedisSock* redis_sock, const char *val, int val_len, break; case REDIS_SERIALIZER_JSON: #ifdef HAVE_REDIS_JSON - #if (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION < 1) - JSON_G(error_code) = PHP_JSON_ERROR_NONE; - php_json_decode(z_ret, (char*)val, val_len, 1, PHP_JSON_PARSER_DEFAULT_DEPTH); - ret = JSON_G(error_code) == PHP_JSON_ERROR_NONE; - #else ret = !php_json_decode(z_ret, (char *)val, val_len, 1, PHP_JSON_PARSER_DEFAULT_DEPTH); - #endif #endif break; EMPTY_SWITCH_DEFAULT_CASE() @@ -4107,7 +4519,7 @@ redis_sock_gets(RedisSock *redis_sock, char *buf, int buf_size, size_t *line_siz snprintf(buf, buf_size, "read error on connection to %s:%d", ZSTR_VAL(redis_sock->host), redis_sock->port); } // Close our socket - redis_sock_disconnect(redis_sock, 1); + redis_sock_disconnect(redis_sock, 1, 1); // Throw a read error exception REDIS_THROW_EXCEPTION(buf, 0); @@ -4156,6 +4568,9 @@ redis_read_reply_type(RedisSock *redis_sock, REDIS_REPLY_TYPE *reply_type, /* Set our size response */ *reply_info = atol(inbuf); + } else { + /* Always initialize to prevent UB */ + *reply_info = 0; } /* Success! */ @@ -4265,7 +4680,7 @@ redis_read_multibulk_recursive(RedisSock *redis_sock, long long elements, int st elements--; } - return 0; + return SUCCESS; } static int @@ -4313,12 +4728,7 @@ variant_reply_generic(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - if (IS_ATOMIC(redis_sock)) { - /* Set our return value */ - RETVAL_ZVAL(&z_ret, 0, 1); - } else { - add_next_index_zval(z_tab, &z_ret); - } + REDIS_RETURN_ZVAL(redis_sock, z_tab, z_ret); /* Success */ return 0; @@ -4349,21 +4759,23 @@ redis_read_variant_reply_strings(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_ return variant_reply_generic(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, 0, z_tab, ctx); } +/* The user may wish to send us something like [NULL, 'password'] or + * [false, 'password'] so don't convert NULL or FALSE into "". */ +static int redisTrySetAuthArg(zend_string **dst, zval *zsrc) { + if (Z_TYPE_P(zsrc) == IS_NULL || Z_TYPE_P(zsrc) == IS_FALSE) + return FAILURE; + + *dst = zval_get_string(zsrc); + + return SUCCESS; +} + PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass) { zval *zv; HashTable *ht; int num; - /* The user may wish to send us something like [NULL, 'password'] or - * [false, 'password'] so don't convert NULL or FALSE into "". */ - #define TRY_SET_AUTH_ARG(zv, ppzstr) \ - do { \ - if (Z_TYPE_P(zv) != IS_NULL && Z_TYPE_P(zv) != IS_FALSE) { \ - *(ppzstr) = zval_get_string(zv); \ - } \ - } while (0) - /* Null out user and password */ *user = *pass = NULL; @@ -4373,8 +4785,7 @@ int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass) /* Handle a non-array first */ if (Z_TYPE_P(ztest) != IS_ARRAY) { - TRY_SET_AUTH_ARG(ztest, pass); - return SUCCESS; + return redisTrySetAuthArg(pass, ztest); } /* Handle the array case */ @@ -4391,18 +4802,18 @@ int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass) if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "user")) || (zv = zend_hash_index_find(ht, 0))) { - TRY_SET_AUTH_ARG(zv, user); + redisTrySetAuthArg(user, zv); } if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "pass")) || (zv = zend_hash_index_find(ht, 1))) { - TRY_SET_AUTH_ARG(zv, pass); + redisTrySetAuthArg(pass, zv); } } else if ((zv = REDIS_HASH_STR_FIND_STATIC(ht, "pass")) || (zv = zend_hash_index_find(ht, 0))) { - TRY_SET_AUTH_ARG(zv, pass); + redisTrySetAuthArg(pass, zv); } /* If we at least have a password, we're good */ @@ -4416,6 +4827,17 @@ int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass) return FAILURE; } +PHP_REDIS_API void redis_with_metadata(zval *zdst, zval *zsrc, zend_long length) { + zval z_sub; + + array_init(zdst); + add_next_index_zval(zdst, zsrc); + + array_init(&z_sub); + add_assoc_long_ex(&z_sub, ZEND_STRL("length"), length); + add_next_index_zval(zdst, &z_sub); +} + /* Helper methods to extract configuration settings from a hash table */ zval *redis_hash_str_find_type(HashTable *ht, const char *key, int keylen, int type) { @@ -4502,4 +4924,45 @@ void redis_conf_auth(HashTable *ht, const char *key, size_t keylen, redis_extract_auth_info(zv, user, pass); } +/* Update a zval with the current 64 bit scan cursor. This presents a problem + * because we can only represent up to 63 bits in a PHP integer. So depending + * on the cursor value, we may need to represent it as a string. */ +void redisSetScanCursor(zval *zv, uint64_t cursor) { + char tmp[21]; + size_t len; + + ZEND_ASSERT(zv != NULL && (Z_TYPE_P(zv) == IS_LONG || + Z_TYPE_P(zv) == IS_STRING)); + + if (Z_TYPE_P(zv) == IS_STRING) + zend_string_release(Z_STR_P(zv)); + + if (cursor > ZEND_LONG_MAX) { + len = snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)cursor); + ZVAL_STRINGL(zv, tmp, len); + } else { + ZVAL_LONG(zv, cursor); + } +} + +/* Get a Redis SCAN cursor value out of a zval. These are always taken as a + * reference argument that that must be `null`, `int`, or `string`. */ +uint64_t redisGetScanCursor(zval *zv, zend_bool *was_zero) { + ZEND_ASSERT(zv != NULL && (Z_TYPE_P(zv) == IS_LONG || + Z_TYPE_P(zv) == IS_STRING || + Z_TYPE_P(zv) == IS_NULL)); + + if (Z_TYPE_P(zv) == IS_NULL) { + convert_to_long(zv); + *was_zero = 0; + return 0; + } else if (Z_TYPE_P(zv) == IS_STRING) { + *was_zero = Z_STRLEN_P(zv) == 1 && Z_STRVAL_P(zv)[0] == '0'; + return strtoull(Z_STRVAL_P(zv), NULL, 10); + } else { + *was_zero = Z_LVAL_P(zv) == 0; + return Z_LVAL_P(zv); + } +} + /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/library.h b/library.h index 8b669a4357..e1e2cf2788 100644 --- a/library.h +++ b/library.h @@ -1,6 +1,8 @@ #ifndef REDIS_LIBRARY_H #define REDIS_LIBRARY_H +#include "php_redis.h" + /* Non cluster command helper */ #define REDIS_SPPRINTF(ret, kw, fmt, ...) \ redis_spprintf(redis_sock, NULL, ret, kw, fmt, ##__VA_ARGS__) @@ -29,23 +31,54 @@ /* use RedisException when ValueError not available */ #define REDIS_VALUE_EXCEPTION(m) REDIS_THROW_EXCEPTION(m, 0) #define RETURN_THROWS() RETURN_FALSE + /* ZVAL_STRINGL_FAST and RETVAL_STRINGL_FAST macros are supported since PHP 8 */ + #define ZVAL_STRINGL_FAST(z, s, l) ZVAL_STRINGL(z, s, l) + #define RETVAL_STRINGL_FAST(s, l) RETVAL_STRINGL(s, l) #else #define redis_hash_fetch_ops(zstr) php_hash_fetch_ops(zstr) #define REDIS_VALUE_EXCEPTION(m) zend_value_error(m) #endif +#if PHP_VERSION_ID < 80200 +static zend_always_inline +zend_bool zend_string_starts_with_cstr(const zend_string *str, const char *prefix, + size_t prefix_length) +{ + return ZSTR_LEN(str) >= prefix_length && + !memcmp(ZSTR_VAL(str), prefix, prefix_length); +} +#endif + +#if PHP_VERSION_ID < 80000 +static zend_string *zend_string_concat2(const char *str1, size_t len1, + const char *str2, size_t len2) +{ + size_t len = len1 + len2; + zend_string *res = zend_string_alloc(len, 0); + + memcpy(ZSTR_VAL(res), str1, len1); + memcpy(ZSTR_VAL(res) + len1, str2, len2); + ZSTR_VAL(res)[len] = '\0'; + + return res; +} +#endif + -void redis_register_persistent_resource(zend_string *id, void *ptr, int le_id); -void free_reply_callbacks(RedisSock *redis_sock); +fold_item* redis_add_reply_callback(RedisSock *redis_sock); +void redis_free_reply_callbacks(RedisSock *redis_sock); PHP_REDIS_API int redis_extract_auth_info(zval *ztest, zend_string **user, zend_string **pass); +PHP_REDIS_API void redis_with_metadata(zval *zdst, zval *zsrc, zend_long length); int redis_cmd_init_sstr(smart_string *str, int num_args, char *keyword, int keyword_len); int redis_cmd_append_sstr(smart_string *str, char *append, int append_len); int redis_cmd_append_sstr_int(smart_string *str, int append); int redis_cmd_append_sstr_long(smart_string *str, long append); +int redis_cmd_append_sstr_zend_long(smart_string *str, zend_long append); int redis_cmd_append_sstr_i64(smart_string *str, int64_t append); +int redis_cmd_append_sstr_u64(smart_string *str, uint64_t append); int redis_cmd_append_sstr_dbl(smart_string *str, double value); int redis_cmd_append_sstr_zstr(smart_string *str, zend_string *zstr); int redis_cmd_append_sstr_zval(smart_string *str, zval *z, RedisSock *redis_sock); @@ -67,6 +100,7 @@ PHP_REDIS_API int redis_boolean_response_impl(INTERNAL_FUNCTION_PARAMETERS, Redi PHP_REDIS_API int redis_boolean_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_bulk_double_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_bulk_withmeta_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); @@ -83,10 +117,8 @@ PHP_REDIS_API char *redis_sock_auth_cmd(RedisSock *redis_sock, int *cmdlen); PHP_REDIS_API void redis_sock_set_auth(RedisSock *redis_sock, zend_string *user, zend_string *pass); PHP_REDIS_API void redis_sock_set_auth_zval(RedisSock *redis_sock, zval *zv); PHP_REDIS_API void redis_sock_free_auth(RedisSock *redis_sock); -PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force); +PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock, int force, int is_reset_mode); PHP_REDIS_API zval *redis_sock_read_multibulk_reply_zval(RedisSock *redis_sock, zval *z_tab); -PHP_REDIS_API int redis_sock_read_single_line(RedisSock *redis_sock, char *buffer, - size_t buflen, size_t *linelen, int set_err); PHP_REDIS_API char *redis_sock_read_bulk_reply(RedisSock *redis_sock, int bytes); PHP_REDIS_API int redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *_z_tab, void *ctx); PHP_REDIS_API void redis_mbulk_reply_loop(RedisSock *redis_sock, zval *z_tab, int count, int unserialize); @@ -100,8 +132,10 @@ PHP_REDIS_API int redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAMETERS PHP_REDIS_API int redis_mbulk_reply_assoc(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_mbulk_reply_double(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, zend_long *iter); +void redisSetScanCursor(zval *zv, uint64_t cursor); +uint64_t redisGetScanCursor(zval *zv, zend_bool *was_zero); +PHP_REDIS_API int redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_SCAN_TYPE type, uint64_t *cursor); PHP_REDIS_API int redis_xrange_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); @@ -116,6 +150,28 @@ PHP_REDIS_API int redis_read_xclaim_reply( PHP_REDIS_API int redis_xinfo_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_vinfo_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_read_vinfo_response(RedisSock *redis_sock, + zval *z_ret, long long count); + +PHP_REDIS_API int redis_vemb_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_read_vemb_response(RedisSock *redis_sock, zval *z_ret, + long long count); + +PHP_REDIS_API int redis_read_vlinks_response(RedisSock *redis_sock, zval *z_ret, + long long elements, void *ctx); +PHP_REDIS_API int redis_vlinks_reply(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); + +PHP_REDIS_API int +redis_deserialize_vgetattr_reply(zval *z_ret, const char *reply, size_t len); + +PHP_REDIS_API int +redis_vgetattr_reply(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zval *z_tab, void *ctx); + PHP_REDIS_API int redis_pubsub_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_subscribe_response(INTERNAL_FUNCTION_PARAMETERS, @@ -128,7 +184,12 @@ PHP_REDIS_API int redis_check_eof(RedisSock *redis_sock, zend_bool no_retry, zen PHP_REDIS_API RedisSock *redis_sock_get(zval *id, int nothrow); PHP_REDIS_API void redis_free_socket(RedisSock *redis_sock); PHP_REDIS_API void redis_sock_set_err(RedisSock *redis_sock, const char *msg, int msg_len); -PHP_REDIS_API int redis_sock_set_stream_context(RedisSock *redis_sock, zval *options); +PHP_REDIS_API void redis_sock_clear_err(RedisSock *redis_sock); + +void redis_sock_set_context(RedisSock *redis_sock, HashTable *ht); +void redis_sock_free_context(RedisSock *redis_sock); +int redis_sock_set_context_zval(RedisSock *redis_sock, zval *zv); + PHP_REDIS_API int redis_sock_set_backoff(RedisSock *redis_sock, zval *options); PHP_REDIS_API int @@ -193,7 +254,8 @@ PHP_REDIS_API int redis_geosearch_response(INTERNAL_FUNCTION_PARAMETERS, RedisSo PHP_REDIS_API int redis_hrandfield_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_pop_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API int redis_srandmember_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_randmember_response(INTERNAL_FUNCTION_PARAMETERS, + RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_lpos_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_object_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_read_lpos_response(zval *zdst, RedisSock *redis_sock, char reply_type, long long elements, void *ctx); @@ -203,6 +265,9 @@ PHP_REDIS_API int redis_function_response(INTERNAL_FUNCTION_PARAMETERS, RedisSoc PHP_REDIS_API int redis_command_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_select_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_hello_server_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_hello_version_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); + /* Helper methods to get configuration values from a HashTable. */ #define REDIS_HASH_STR_FIND_STATIC(ht, sstr) \ diff --git a/package.xml b/package.xml index cdc815a526..af4b93f3ae 100644 --- a/package.xml +++ b/package.xml @@ -3,48 +3,919 @@ http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> - redis - pecl.php.net - PHP extension for interfacing with Redis - - This extension provides an API for communicating with Redis servers. - - - Michael Grunder - mgrunder - michael.grunder@gmail.com - yes - - - Pavlo Yatsukhnenko - yatsukhnenko - p.yatsukhnenko@gmail.com - yes - - - Nicolas Favre-Felix - nff - n.favrefelix@gmail.com - no - - 2021-11-16 - - 5.3.5RC1 - 5.3.5RC1 - - - beta - beta - - PHP - - phpredis 5.3.5RC1 + redis + pecl.php.net + PHP extension for interfacing with key-value stores + + This extension provides an API for communicating with RESP-based key-value + stores, such as Redis, Valkey, and KeyDB. + + + Michael Grunder + mgrunder + michael.grunder@gmail.com + yes + + + Pavlo Yatsukhnenko + yatsukhnenko + p.yatsukhnenko@gmail.com + yes + + 2025-10-15 + + 6.3.0RC1 + 6.2.0 + + + beta + stable + + PHP + + + --- Sponsors --- + + A-VISION Advertising - https://github.com/A-VISION-BV + Avtandil Kikabidze - https://github.com/akalongman + Geoffrey Hoffman - https://github.com/phpguru + Object Cache Pro for WordPress - https://objectcache.pro/ + Open LMS - https://openlms.net/ + Relay - https://relay.so) + Salvatore Sanfilippo - https://github.com/antirez + Ty Karok - https://github.com/karock + + --- 6.3.0RC1 --- + + This release introduces support for dozens of new commands, including hash + field expiration, Valkey’s DELIFEQ, and Redis vector set operations. It also + delivers numerous bug fixes and performance improvements. + + Fixed: + + Fix possible hash field name truncation [834d2b37] (Michael Grunder) + Fix a possible segfault during failover [5ebb853e] (rlerdorf) + Bump version and fix XGROUP test [f5db01b7, 4517d5f9] remicollet + Fix an overflow bug in ZADD on Windows [35df8ad7] (Michael Grunder) + Fix errors and a warning [b8de91c9] (Michael Grunder) + Fix `RedisCluster` segfault [f61e8cd7] (Michael Grunder) + Fix passing NULL for hash expiry argument [ca80ee0e] (Michael Grunder) + Fix an off-by-one length calculation error [340f23b0] (Michael Grunder) + Fix hset fields handling [6b2f088d] (Pavlo Yatsukhnenko) + Fix HSetEx expiry argument handling [7805da75] (Pavlo Yatsukhnenko) + Fix the echo liveness check when in sentinel mode [2acab399] (Michael Grunder) + Fix double -> int truncation warning [152fdda9] (Michael Grunder) + Fix `SIGABRT` in PHP 8.4 with RedisArray [3c64b33f] (Novynn) + Fixing segfault in cluster_update_slot [b0ba827b] (Jacob Brown Austin) + Fix typo [d0b0c5cf] (Michael Grunder) + Fix compiling with PHP 8.5.0 alpha3 and newer [1e6f5477] (wyattoday) + Fix error length calculation + UB sanity check [e73130fe] (Michael Grunder) + Fix arguments order for `SET` command [f73f5fcc] (Pavlo Yatsukhnenko) + + Added: + + Add `hgetwithmeta` method [7d3b2e4d] (Pavlo Yatsukhnenko) + Add `maxRetries` to `redis_sock_configure` [e9e9e495] (Michael Grunder) + Implement vectorset commands [92dd256f, d80b7258, 7f9b1f41, 92716ed0, + dc91631b, 1deca628, 96378b70, 0fda9f29, 0ed0fc05, d1d69005, 286fa630, + 38115dec, c4b2ea6c, 6ce3bd53] (Michael Grunder) + Implement `GEOSEARCH[STORE]` `BYPOLYGON` [8d369f4d, f24814a4] (Michael Grunder) + Implement Valkey's `DELIFEQ` command [b1b0c191] (Michael Grunder) + Add an INI setting returning 5.x legacy behavior -- readonly session on lock + failure [8dada174] (arokettu) + Implement several hash expiration commands [7350768c] (Michael Grunder) + Introduce `Redis::serverName` and `Redis::serverVersion` methods [056c2dbe, + cbaf095f, fa3eb006] (Pavlo Yatsukhnenko, Michael Grunder) + New option 'database' for Redis class constructor [4f6a3ed1] (JakubOnderka) + Implement `HGETEX`, `HSETEX`, `HGETDEL`, and refactor `HMGET` [ce5b0fac] + (Michael Grunder) + Implement `GETDEL` for `RedisCluster` [d342e4ac] (Michael Grunder) + + ## Changed + + Check for `dragonfly_version` in `HELLO` response [593ba012] (Michael Grunder) + Simplify redis_unpack method calling [0a85bd82] (JakubOnderka) + Refactor `getWithMeta` logic [0445e683] (Michael Grunder) + cleanup session temp file [3828c929] (remicollet) + Make execHello protected [300c5fb2] (Michael Grunder) + + Documentation: + + Update Relay sponsorship [7a69d73] (Till Kruss) + Fix markdown headlines [1d662f56] (Till Kruss) + document PECL configure options [2066cfad] (Till Kruss) + Improve wording in README about project support [0ac01476] (Till Kruss) + Improve install instructions [c0076036] (Remi Collet) + Clean up README.md: fix typos, spelling, and grammar errors [09cd4c54] (Copilot) + Refine parameter descriptions and examples in README [f9f609e1] (joshtrichards) + Link to the correct header [d18db84c] (wyattoday) + Update README.md about supported PHP versions. [b7a97e5e] (AkameOuO) + Broaden return type for Redis::hGetAll [bfbab892] (mgiuffrida) + + Tests/CI: + + Add a regression test for `EVAL[SHA]` [22a2914b] (Michael Grunder) + Rework `HEXPIRE` test inclusion + bump Valkey [b83981aa] (Michael Grunder) + Add PHP 8.5 to CI [334937cb] (Pavlo Yatsukhnenko) + Attempt to fix flaky GitHub CI tests [80140003] (Michael Grunder) + Reorganize tests [807f806f] (Pavlo Yatsukhnenko) + + Internal/Performance: + + Introduce `[[nodiscard]]` type attribute where supported. [2d963e79] (Michael Grunder) + Fix typo (s/sees/seeds/) [25e6d5fc] (xabbuh) + Fix an unused variable warning [b48aa0d4] (Michael Grunder) + Fix several issues surfaced by `gcc -fanalyze` [8be2306e] (Michael Grunder) + Fix dead assignment [d564e8cf] (Michael Grunder) + Refactor `redis_replicaof_cmd` [659dc763] (Michael Grunder) + Refactor several command handling macros into typesafe static functions + [9802fc0e, 8f0931bb, 7c953d45, 950d2bc7, 601ebbff, c3a71631, b0040514, + fae89fa9, f880e1f7, b90e27f2, 1db39089] (Michael Grunder) + Refactor `EVAL[SHA]` command and add a regression test [6e5faf42] + (Michael Grunder) + New macros REDIS_RESPONSE_ERROR and REDIS_RETURN_ZVAL [614b86e4] + (JakubOnderka) + Remove wrapper macro which hides branching logic [58e1a04f] (Michael Grunder) + Use continue not break if we get a NULL node [8685c49c] (Michael Grunder) + Remove pointless casts [03837f02] (Michael Grunder) + Change `int` flags to `zend_bool` since we only use them as booleans + [77691947] (Michael Grunder) + Remove unused macros + simplify some logic [75acbb09] (Michael Grunder) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 7.4.0 + + + 1.4.0b1 + + + + redis + + + + + + + + + + + + beta + stable + + + 6.3.0RC1 + 6.2.0 + + 2025-10-15 + + + --- Sponsors --- + + A-VISION Advertising - https://github.com/A-VISION-BV + Avtandil Kikabidze - https://github.com/akalongman + Geoffrey Hoffman - https://github.com/phpguru + Object Cache Pro for WordPress - https://objectcache.pro/ + Open LMS - https://openlms.net/ + Relay - https://relay.so) + Salvatore Sanfilippo - https://github.com/antirez + Ty Karok - https://github.com/karock + + --- 6.3.0RC1 --- + + Fixed: + + Fix an overflow bug in ZADD on Windows [35df8ad7] (Michael Grunder) + Fix errors and a warning [b8de91c9] (Michael Grunder) + Fix `RedisCluster` segfault [f61e8cd7] (Michael Grunder) + Fix passing NULL for hash expiry argument [ca80ee0e] (Michael Grunder) + Fix an off-by-one length calculation error [340f23b0] (Michael Grunder) + Fix hset fields handling [6b2f088d] (Pavlo Yatsukhnenko) + Fix HSetEx expiry argument handling [7805da75] (Pavlo Yatsukhnenko) + Fix the echo liveness check when in sentinel mode [2acab399] (Michael Grunder) + Fix double -> int truncation warning [152fdda9] (Michael Grunder) + Fix `SIGABRT` in PHP 8.4 with RedisArray [3c64b33f] (Novynn) + Fixing segfault in cluster_update_slot [b0ba827b] (Jacob Brown Austin) + Fix typo [d0b0c5cf] (Michael Grunder) + Fix compiling with PHP 8.5.0 alpha3 and newer [1e6f5477] (wyattoday) + Fix error length calculation + UB sanity check [e73130fe] (Michael Grunder) + Fix arguments order for `SET` command [f73f5fcc] (Pavlo Yatsukhnenko) + + Added: + + Add `maxRetries` to `redis_sock_configure` [e9e9e495] (Michael Grunder) + Implement vectorset commands [92dd256f, d80b7258, 7f9b1f41, 92716ed0, + dc91631b, 1deca628, 96378b70, 0fda9f29, 0ed0fc05, d1d69005, 286fa630, + 38115dec, c4b2ea6c, 6ce3bd53] (Michael Grunder) + Implement `GEOSEARCH[STORE]` `BYPOLYGON` [8d369f4d, f24814a4] (Michael Grunder) + Implement Valkey's `DELIFEQ` command [b1b0c191] (Michael Grunder) + Add an INI setting returning 5.x legacy behavior -- readonly session on lock + failure [8dada174] (arokettu) + Implement several hash expiration commands [7350768c] (Michael Grunder) + Introduce `Redis::serverName` and `Redis::serverVersion` methods [056c2dbe, + cbaf095f, fa3eb006] (Pavlo Yatsukhnenko, Michael Grunder) + New option 'database' for Redis class constructor [4f6a3ed1] (JakubOnderka) + Implement `HGETEX`, `HSETEX`, `HGETDEL`, and refactor `HMGET` [ce5b0fac] + (Michael Grunder) + Implement `GETDEL` for `RedisCluster` [d342e4ac] (Michael Grunder) + + ## Changed + + Check for `dragonfly_version` in `HELLO` response [593ba012] (Michael Grunder) + Simplify redis_unpack method calling [0a85bd82] (JakubOnderka) + Refactor `getWithMeta` logic [0445e683] (Michael Grunder) + cleanup session temp file [3828c929] (remicollet) + Make execHello protected [300c5fb2] (Michael Grunder) + + Documentation: + + Update Relay sponsorship [7a69d73] (Till Kruss) + Fix markdown headlines [1d662f56] (Till Kruss) + document PECL configure options [2066cfad] (Till Kruss) + Improve wording in README about project support [0ac01476] (Till Kruss) + Improve install instructions [c0076036] (Remi Collet) + Clean up README.md: fix typos, spelling, and grammar errors [09cd4c54] (Copilot) + Refine parameter descriptions and examples in README [f9f609e1] (joshtrichards) + Link to the correct header [d18db84c] (wyattoday) + Update README.md about supported PHP versions. [b7a97e5e] (AkameOuO) + Broaden return type for Redis::hGetAll [bfbab892] (mgiuffrida) + + Tests/CI: + + Add a regression test for `EVAL[SHA]` [22a2914b] (Michael Grunder) + Rework `HEXPIRE` test inclusion + bump Valkey [b83981aa] (Michael Grunder) + Add PHP 8.5 to CI [334937cb] (Pavlo Yatsukhnenko) + Attempt to fix flaky GitHub CI tests [80140003] (Michael Grunder) + Reorganize tests [807f806f] (Pavlo Yatsukhnenko) + + Internal/Performance: + + Fix typo (s/sees/seeds/) [25e6d5fc] (xabbuh) + Fix an unused variable warning [b48aa0d4] (Michael Grunder) + Fix several issues surfaced by `gcc -fanalyze` [8be2306e] (Michael Grunder) + Fix dead assignment [d564e8cf] (Michael Grunder) + Refactor `redis_replicaof_cmd` [659dc763] (Michael Grunder) + Refactor several command handling macros into typesafe static functions + [9802fc0e, 8f0931bb, 7c953d45, 950d2bc7, 601ebbff, c3a71631, b0040514, + fae89fa9, f880e1f7, b90e27f2, 1db39089] (Michael Grunder) + Refactor `EVAL[SHA]` command and add a regression test [6e5faf42] + (Michael Grunder) + New macros REDIS_RESPONSE_ERROR and REDIS_RETURN_ZVAL [614b86e4] + (JakubOnderka) + Remove wrapper macro which hides branching logic [58e1a04f] (Michael Grunder) + Use continue not break if we get a NULL node [8685c49c] (Michael Grunder) + Remove pointless casts [03837f02] (Michael Grunder) + Change `int` flags to `zend_bool` since we only use them as booleans + [77691947] (Michael Grunder) + Remove unused macros + simplify some logic [75acbb09] (Michael Grunder) + + + + + stable + stable + + + 6.2.0 + 6.2.0 + + 2025-03-24 + + --- Sponsors --- + + A-VISION Advisering - https://a-vision.nu/ + Audiomack - https://audiomack.com + Avtandil Kikabidze - https://github.com/akalongman + Geoffrey Hoffman - https://github.com/phpguru + Object Cache Pro for WordPress - https://objectcache.pro + Open LMS - https://openlms.net + Salvatore Sanfilippo - https://github.com/antirez + Ty Karok - https://github.com/karock + Vanessa Santana - https://github.com/vanessa-dev + + * Special thanks to Jakub Onderka for nearly two dozen performance improvements in this release! + + --- 6.2.0 --- + + Fixed: + * Fix arguments order for SET command [f73f5fc] (Pavlo Yatsukhnenko) + * Fix error length calculation and UB sanity check [e73130fe] (michael-grunder) + * Invalidate slot cache on failed cluster connections [c7b87843] (James Kennedy) + * Don't cast a uint64_t to a long [faa4bc20] (michael-grunder) + * Fix potential NULL dereference [43e6cab8] (peter15914) + * Print cursor as unsigned 64 bit integer [138d07b6] (Bentley O'Kane-Chase) + * Fix XAUTOCLAIM argc when sending COUNT [0fe45d24] (michael-grunder) + + Added: + * Added `serverName()` and `serverVersion()` [fa3eb006, cbaf095f, 056c2dbe] + (Pavlo Yatsukhnenko, Michael Grunder) + * Added getWithMeta method [9036ffca, 36ab5850] (Pavlo Yatsukhnenko) + * Implement GETDEL command for RedisCluster [d342e4ac] (michael-grunder) + * Introduce Redis::OPT_PACK_IGNORE_NUMBERS option [f9ce9429, 29e5cf0d] (michael-grunder) + * Implement Valkey >= 8.1 IFEQ SET option [a2eef77f] (michael-grunder) + * Implement KeyDB's EXPIREMEMBER[AT] commands [4cd3f593] (michael-grunder) + * Set priority to 60 (for PIE installations) [9e504ede] (Pavlo Yatsukhnenko) + + Documentation: + * Fix phpdoc type of $pattern [5cad2076] (OHZEKI Naoki) + * Better documentation for the $tlsOptions parameter of RedisCluster [8144db37] (Jacob Brown) + + Tests/CI: + * Add details to the option doc block [abb0f6cc] (michael-grunder) + * Update CodeQL to v3 [41e11417, a10bca35] (Pavlo Yatsukhnenko) + * Add PHP 8.4 to CI [6097e7ba] (Pavlo Yatsukhnenko) + * Pin ubuntu version for KeyDB [eb66fc9e, 985b0313] (michael-grunder) + * Windows CI: update setup-php-sdk to v0.10 and enable caching [f89d4d8f] (Christoph M. Becker) + + Internal/Performance: + * Reduce buffer size for signed integer [044b3038, 35c59880] (Bentley O'Kane-Chase) + * Create a strncmp wrapper [085d61ec] (michael-grunder) + * Refactor and avoid allocation in rawcommand method [f68544f7] (Jakub Onderka) + * Use defines for callback growth + sanity check [42a42769] (michael-grunder) + * Switch from linked list to growing array for reply callbacks [a551fdc9] (Jakub Onderka) + * Reuse redis_sock_append_auth method [be388562] (Jakub Onderka) + * Switch pipeline_cmd from smart_str to smart_string [571ffbc8] (Jakub Onderka) + * Remove unused redis_debug_response method from library.c [7895636a] (Jakub Onderka) + * Optimise HMGET method [2434ba29] (Jakub Onderka) + * Avoid unnecessary allocation in redis_hset_cmd [aba09933] (Jakub Onderka) + * Avoid unnecessary allocation in redis_hdel_cmd [4082dd07] (Jakub Onderka) + * Avoid unnecessary allocation in redis_key_varval_cmd [99650e15] (Jakub Onderka) + * Use zval_get_tmp_string method that is faster when provided zval is string [f6906470] (Jakub Onderka) + * Optimise constructing Redis command string [2a2f908f] (Jakub Onderka) + * If no command is issued in multi mode, return immutable empty array [5156e032] (Jakub Onderka) + * Test for empty pipeline and multi [426de2bb] (Jakub Onderka) + * Optimise method array_zip_values_and_scores [400503b8] (Jakub Onderka) + * Faster parameter parsing in redis_key_cmd and redis_key_long_val_cmd [83a19656] (Jakub Onderka) + * Use immutable empty array in Redis::hKeys [3a2f3f45] (Jakub Onderka) + * Use immutable empty array in Redis::exec [60b5a886] (Jakub Onderka) + * Do not allocate empty string or string with one character [64da891e] (Jakub Onderka) + * Initialize arrays with known size [99beb922] (Jakub Onderka) + * Use smart str for constructing pipeline cmd [b665925e] (Jakub Onderka) + + + + + stable + stable + + + 6.1.0 + 6.0.0 + + 2024-10-04 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + Avtandil Kikabidze - https://github.com/akalongman + Ty Karok - https://github.com/karock + Object Cache Pro for WordPress - https://objectcache.pro + + --- 6.1.0 --- + + NOTE: There were no changes to C code between 6.1.0RC2 and 6.1.0 + + Documentation: + + * Update package.xml to make it clearer that we support many key-value stores + [52e69ede] (Remi Collet) + * Fix redis.io urls [0bae4bb0] (Vincent Langlet) + + Tests/CI: + + * Fix 2 tests with redis 6.2 [cc1be322] (Remi Collet) + + + --- 6.1.0RC2 --- + + Fixed: + + * Fixed a `SIGABRT` error in PHP 8.4 [a75a7e5a] (Michael Grunder) + * Clean up code for unsupported versions of PHP [37cebdd7] (Remi Collet) + * Add `SessionHelpers.php` to `package.xml`[e9474b80] (Remi Collet) + + Changed: + + * Raised minimum supported PHP version to 7.4 [8b519423] (Michael Grunder) + + Removed: + + * Removed erroneously duplicated changelog entries [40c89736] (Michael Grunder) + + Tests/CI: + + * Move to upload artifacts v4 [9d380500] (Michael Grunder) + + Added: + + * Added `composer.json` to support PIE (PHP Installer for Extensions) [b59e35a6] + (James Titcumb) + + --- 6.1.0RC1 --- + + Fixed: + + * Fix random connection timeouts with Redis Cluster. [eb7f31e7] (Jozsef Koszo) + * Fix argument count issue in HSET with associative array [6ea5b3e0] + (Viktor Djupsjobacka) + * SRANDMEMBER can return any type because of serialization. [6673b5b2] + (Michael Grunder) + * Fix HRANDFIELD command when WITHVALUES is used. [99f9fd83] (Michael Grunder) + * Allow context array to be nullable [50529f56] (Michael Grunder) + * Fix a macOS (M1) compiler warning. [7de29d57] (Michael Grunder) + * `GETEX` documentation/updates and implentation in `RedisCluster` [981c6931] + (Michael Grunder) + * Refactor redis_script_cmd and fix to `flush` subcommand. [7c551424] + (Pavlo Yatsukhnenko) + * Update liveness check and fix PHP 8.4 compilation error. [c139de3a] + (Michael Grunder) + * Rework how we declare ZSTD min/max constants. [34b5bd81] (Michael Grunder) + * Fix memory leak if we fail in ps_open_redis. [0e926165] (Michael Grunder) + * Fix segfault and remove redundant macros [a9e53fd1] (Pavlo Yatsukhnenko) + * Fix PHP 8.4 includes [a51215ce] (Michael Grunder) + * Handle arbitrarily large `SCAN` cursors properly. [2612d444, e52f0afa] + (Michael Grunder) + * Improve warning when we encounter an invalid EXPIRY in SET [732e466a] + (Michael Grunder) + * Fix Arginfo / zpp mismatch for DUMP command [50e5405c] (Pavlo Yatsukhnenko) + * RedisCluster::publish returns a cluster_long_resp [14f93339] (Alexandre Choura) + * Fix segfault when passing just false to auth. [6dc0a0be] (Michael Grunder) + * the VALUE argument type for hSetNx must be the same as for hSet [df074dbe] + (Uladzimir Tsykun) + * Other fixes [e18f6c6d, 3d7be358, 2b555c89, fa1a283a, 37c5f8d4] (Michael Grunder, Viktor Szepe) + + Added: + + * Compression support for PHP sessions. [da4ab0a7] (bitactive) + * Support for early_refresh in Redis sessions to match cluster behavior + [b6989018] (Bitactive) + * Implement WAITAOF command. [ed7c9f6f] (Michael Grunder) + + Removed: + + * PHP 7.1, 7.2, and 7.3 CI jobs [d68c30f8, dc39bd55] (Michael Grunder) + + Changed: + + * Fix the time unit of retry_interval [3fdd52b4] (woodong) + + Documentation: + + * Many documentation fixes. [eeb51099] (Michael Dwyer) + * fix missing code tags [f865d5b9] (divinity76) + * Mention Valkey support [5f1eecfb] (PlavorSeol) + * Mention KeyDB support in README.md [37fa3592] (Tim Starling) + * Remove mention of pickle [c7a73abb] (David Baker) + * Add session.save_path examples [8a39caeb] (Martin Vancl) + * Tighter return types for Redis::(keys|hKeys|hVals|hGetAll) [77ab62bc] + (Benjamin Morel) + * Update stubs [4d233977, ff305349, 12966a74, a4a283ab, 8f8ff72a, 5d293245] + (Michael Grunder, Takayasu Oyama, Pavlo Yatsukhnenko) + * Fix config.m4 when using custom dep paths [ece3f7be] (Michael Grunder) + * Fix retry_internal documentation [142c1f4a] (SplotyCode) + * Fix anchor link [9b5cad31] (Git'Fellow) + * Fix typo in link [bfd379f0] (deiga) + * Fix Fedora package url [60b1ba14, 717713e1] (Dmitrii Kotov) + * Update Redis Sentinel documentation to reflect changes to constructor in 6.0 + release [dc05d65c] (Pavlo Yatsukhnenko) + + Tests/CI: + + * Avoid fatal error in test execution. [57304970] (Michael Grunder) + * Refactor unit test framework. [b1771def] (Michael Grunder) + * Get unit tests working in `php-cgi`. [b808cc60] (Michael Grunder) + * Switch to `ZEND_STRL` in more places. [7050c989, f8c762e7] (Michael Grunder) + * Workaround weird PHP compiler crash. [d3b2d87b] (Michael Grunder) + * Refactor tests (formatting, modernization, etc). [dab6a62d, c6cd665b, 78b70ca8, + 3c125b09, 18b0da72, b88e72b1, 0f94d9c1, 59965971, 3dbc2bd8, 9b90c03b, c0d6f042] + (Michael Grunder) + * Spelling fixes [0d89e928] (Michael Grunder) + * Added Valkey support. [f350dc34] (Michael Grunder) + * Add a test for session compression. [9f3ca98c] (Michael Grunder) + * Test against valkey [a819a44b] (Michael Grunder) + * sessionSaveHandler injection. [9f8f80ca] (Pavlo Yatsukhnenko) + * KeyDB addiions [54d62c72, d9c48b78] (Michael Grunder) + * Add PHP 8.3 to CI [78d15140, e051a5db] (Robert Kelcak, Pavlo Yatsukhnenko) + * Use newInstance in RedisClusterTest [954fbab8] (Pavlo Yatsukhnenko) + * Use actions/checkout@v4 [f4c2ac26] (Pavlo Yatsukhnenko) + * Cluster nodes from ENV [eda39958, 0672703b] (Pavlo Yatsukhnenko) + * Ensure we're talking to redis-server in our high ports test. [7825efbc] + (Michael Grunder) + * Add missing option to installation example [2bddd84f] (Pavlo Yatsukhnenko) + * Fix typo in link [8f6bc98f] (Timo Sand) + * Update tests to allow users to use a custom class. [5f6ce414] (Michael Grunder) + + + + + stable + stable + + + 6.0.2 + 6.0.0 + + 2023-10-22 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Stackhero - https://github.com/stackhero-io + Florian Levis - https://github.com/Gounlaf + Luis Zarate - https://github.com/jlzaratec + + --- + + phpredis 6.0.2 + + This release contains fixes for OBJECT, PSUBSCRIBE and SCAN commands. + You can find a detailed list of changes in CHANGELOG.md and package.xml + or by inspecting the git commit logs. + + * Fix deprecation error when passing null to match_type parameter.[b835aaa3] (Pavlo Yatsukhnenko) + * Fix flaky test and OBJECT in a pipeline. [a7f51f70] (Michael Grunder) + * Find our callback by pattern with PSUBSCRIBE [2f276dcd] (Michael Grunder) + + + + + stable + stable + + + 6.0.1 + 6.0.0 + + 2023-09-23 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net/ + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro/ + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Stackhero - https://github.com/stackhero-io + Florian Levis - https://github.com/Gounlaf + Luis Zarate - https://github.com/jlzaratec + + --- + + phpredis 6.0.1 + + This release contains fix for unknown expiration modifier issue + as well as memory leak and segfault in exec function + and small documentation improvements. + + You can find a detailed list of changes in CHANGELOG.md and package.xml + or by inspecting the git commit logs. + + * Fix memory leak and segfault in Redis::exec [362e1141] (Pavlo Yatsukhnenko), (Markus Podar) + * Fix unknown expiration modifier [264c0c7e, 95bd184b] (Pavlo Yatsukhnenko) + * Update documentation [3674d663, 849bedb6, 1ad95b63] (Till Kruss), (Joost OrangeJuiced) + + + + + stable + stable + + + 6.0.0 + 6.0.0 + + 2023-09-09 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Luis Zarate - https://github.com/jlzaratec + + phpredis 6.0.0 + + - There were no changes between 6.0.0 and 6.0.0RC2. + + --- + + phpredis 6.0.0RC2 + + * Fix arginfo for arguments that default to null [8d99b7d1] (Nicolas Grekas) + * Fix C99 usages [54d9ca45] (Remi Collet) + * Raise minimal supported version to 7.2 [e10b9a85] (Remi Collet) + + --- + + phpredis 6.0.0RC1 + + * Fix restoring keys when using compression [82e08723] (Till Kruss) + * Fix missing auth in RedisSentinel stub [5db85561] (Lu Fei) + * Fix RedisSentinel pconnect check [42cbd88a] (Pavlo Yatsukhnenko) + * Fix NULL-pointer dereferences and handle possible UB [36457555] (Pavlo Yatsukhnenko) + * Fix security alerts [ee210f86, fb6a297c] (Pavlo Yatsukhnenko), (Michael Grunder) + * Fix segfault [55bf0202] (Pavlo Yatsukhnenko) + * Fix default host length [c40f9d6c] (Pavlo Yatsukhnenko) + * Fix redis session standalone stream ssl context [ed10f365, d1bc6727, 2ff11df5] (patricio.dorantes) + * Fix segfault with session+tls [a471c87a] (Pavlo Yatsukhnenko) + * Fix non standards conforming prototypes. [b3ce0486] (Michael Grunder) + * Avoid registering the same replicas multiple times [f2bfd723] (Marius Adam) + * Better unix:// or file:// detection. [d05d301b] (Michael Grunder) + * Future proof our igbinary header check [69355faa] (Michael Grunder) + * Fix BITOP cross-slot bug [af13f951] (Michael Grunder) + * SENTINEL RESET returns a long. [0243dd9d] (Michael Grunder) + * Fix redis_sock_read_multibulk_multi_reply_loop logic [d9cb5946, 5a643b62] (Pavlo Yatsukhnenko) + * Fix RPOP to unserialize/decompress data. [02c91d59] (Michael Grunder) + * Fix testObject for redis 7.2 [fea19b52, dcb95a3f] (Remi Collet) + * Fix bug: the pipeline mode socket return an unexpected result after reconnecting [a3327d9d] (thomaston) + * Fix stub files [9aa5f387, 74cf49f5, 8b1eafe8, e392dd88, b5ea5fd7, 71758b09, 2a6dee5d] (Nicolas Grekas), (Michael Grunder) + * Update documentation [b64d93e1, 703d71b5, eba1c6d2, 0f502c9e, 130b5d0b, 21c3ef94, b7bf22d4, 50151e7a, b9950727, ab4ce4ab, 8d80ca5b, c4de8667, 6982941b, 375d093d, 43da8dd9, 71344612, b9de0b97, 2d8a8a44, a2b0c86f, e0b24be1, e609fbe8, c4aef956, df50b2ad, cc2383f0, 0dd2836f, 7d5db510, 99340889, 70a55f3e, b04684d4, 980ea6b1, bb06ffa3, b8679d7a, 854f3aa4, a5c47901, cf63e96e, f05ba819, 17db2328, 450904f7, 114f4d60, 142bddf0, 87fa36d6, 531177d4, ecf65144, 53d142d9, c14a9e3a, 72f8eb25, 872b6931] (Karina Kwiatek), (Nicolas Grekas), (Muhammad Dyas Yaskur), (sergkash7), (Dawid Polak), (Michael Grunder), (Yurun), (twosee), (Juha), (Till Kruss) + * Allow to pass null as iterator [14d121bb] (Pavlo Yatsukhnenko) + * Add NOMKSTREAM option to XADD command. [f9436e25] (Pavlo Yatsukhnenko) + * Don't allow reconnect on read response [5a269ab6] (Pavlo Yatsukhnenko) + * Reset multi/pipline transaction on pconnect close [0879770a] (Pavlo Yatsukhnenko) + * Use read_mbulk_header helper where possible [ca8b4c93] (Pavlo Yatsukhnenko) + * Allow to pass null as auth argument [41517753] (Pavlo Yatsukhnenko) + * Refactor redis_parse_client_list_response [68136a29, aaa4c91a, 1fb2935b, cf2c052c] (Pavlo Yatsukhnenko) + * Refactor subscribe/unsubscribe [3c9e159c] (Pavlo Yatsukhnenko) + * Change PHPREDIS_CTX_PTR type [de3635da] (Pavlo Yatsukhnenko) + * Refactor redis_parse_info_response [982bd13b] (Pavlo Yatsukhnenko) + * Allow IPv6 address within square brackets [c28ad7bb] (Pavlo Yatsukhnenko) + * Allow multiple field-value pairs for hmset command. [e858e8e3] (Pavlo Yatsukhnenko) + * Refactor MINIT and use @generate-class-entries in stub files [3675f442] (Remi Collet) + * Use spl_ce_RuntimeException [3cd5ac1e, a7e5ea64] (Remi Collet) + * Regenerate arginfo using 8.2.0 [a38e08da] (Remi Collet) + * Refactor client command [a8d10291] (Pavlo Yatsukhnenko) + * Pull COUNT/ANY parsing into a helper function [d67b2020] (Michael Grunder) + * Return false or NULL on empty lpos response [39a01ac7] (Michael Grunder) + * BLPOP with a float timeout [a98605f2, dc9af529] (Michael Grunder) + * Make sure we set an error for key based scans [98fda1b8] (Michael Grunder) + * Add back a default switch case for setoption handler [87464932] (Michael Grunder) + * Update stubs so the tests pass in strict mode [bebd398c] (Michael Grunder) + * Move where we generate our salt [d2044c9f] (Michael Grunder) + * Refactor XINFO handler [3b0d8b77] (Michael Grunder) + * Refactor and fix XPENDING handler [457953f4] (Michael Grunder) + * Refactor FLUSHDB and update docs. [54a084e5] (Michael Grunder) + * Add missing directed node command to docs and refactor stubs. [5ac92d25] (Michael Grunder) + * Refactor BITPOS and implement BIT/BYTE option. [4d8afd38] (Michael Grunder) + * INFO with multiple sections [44d03ca0] (Michael Grunder) + * Refactor SLOWLOG command [d87f1428] (Michael Grunder) + * Refactor SORT and add SORT_RO command [8c7c5a3a] (Michael Grunder) + * Use ZEND_STRL in redis_commands.c [78de25a3] (Pavlo Yatsukhnenko) + * Refactor PubSub command [2a0d1c1e] (Pavlo Yatsukhnenko) + * Refactor SLAVEOF handler [f2cef8be] (Michael Grunder) + * Refactor ACL command [504810a5] (Pavlo Yatsukhnenko) + * Use fast_zpp API [376d4d27] (Pavlo Yatsukhnenko) + * Fix XAUTOCLAIM response handler [0b7bd83f] (Michael Grunder) + * Refactor command command [ff863f3f] (Pavlo Yatsukhnenko) + * Refactor rawCommand and WAIT [79c9d224] (Michael Grunder) + * Refactor SELECT command [86f15cca] (Michael Grunder) + * Refactor SRANDMEMBER command. [f62363c2] (Michael Grunder) + * Refactor OBJECT command. [acb5db76] (Michael Grunder) + * Refactor gen_varkey_cmd [3efa59cb] (Michael Grunder) + * Refactor MGET command. [8cb6dd17] (Michael Grunder) + * Refactor INFO and SCRIPT commands. [3574ef08] (Michael Grunder) + * Refactor MSET and MSETNX commands. [6d104481] (Michael Grunder) + * Refactor HMSET command. [90eb0470] (Michael Grunder) + * Refactor PFCOUNT command. [19fd7e0c] (Michael Grunder) + * Refactor SMOVE command. [204a02c5] (Michael Grunder) + * Rework ZRANGE argument handling. [aa0938a4] (Michael Grunder) + * Refactor a couple more command methods. [5b560ccf, c8224b93, 40e1b1bf, ccd419a4] (Michael Grunder) + * Refactor HMGET command [bb66a547] (Michael Grunder) + * Refactor CLIENT command [77c4f7a3] (Pavlo Yatsukhnenko) + * Refactor redis_long_response [f14a80db] (Pavlo Yatsukhnenko) + * Synchronize Redis and RedisSentinel constructors [ebb2386e] (Pavlo Yatsukhnenko) + * Use redis_sock_connect on connect [f6c8b9c6] (Pavlo Yatsukhnenko) + * Auto-select db in redis_sock_server_open [6930a81c] (Pavlo Yatsukhnenko) + * Use on-stack allocated valiables [7a055cad] (Pavlo Yatsukhnenko) + * Add XAUTOCLAIM command [01f3342c] (Pavlo Yatsukhnenko) + * Add SYNC arg to FLUSHALL and FLUSHDB, and ASYNC/SYNC arg to SCRIPT FLUSH [750b6cf3] (Pavlo Yatsukhnenko) + * Add reset command [947a2d38] (Pavlo Yatsukhnenko) + * Add hRandField command [fe397371] (Pavlo Yatsukhnenko) + * Add PXAT/EXAT arguments to SET command. [0a160685] (Pavlo Yatsukhnenko) + * Add GETEX, GETDEL commands. [11861d95] (Pavlo Yatsukhnenko) + * Add FAILOVER command. [4b767be7] (Pavlo Yatsukhnenko) + * Backoff settings in constructor [e6b3fe54] (Pavlo Yatsukhnenko) + * Add the COUNT argument to LPOP and RPOP [df97cc35] (Pavlo Yatsukhnenko) + * Unsubscribe from all channels [0f1ca0cc] (Pavlo Yatsukhnenko) + * Add lPos command. [687a5c78] (Pavlo Yatsukhnenko) + * Add the ANY argument to GEOSEARCH and GEORADIUS [bf6f31e3] (Pavlo Yatsukhnenko) + * Add 'BIT'/'BYTE' modifier to BITCOUNT + tests [a3d2f131] (Michael Grunder) + * Add missing configureoption entries in package.xml [59053f10] (Michele Locati) + * Implement CONFIG RESETSTAT [239678a0] (Michael Grunder) + * SINTERCARD and ZINTERCARD commands [64300508] (Michael Grunder) + * LCS command [c0e839f6] (Michael Grunder) + * EXPIRETIME and PEXPIRETIME [f5b2a09b] (Michael Grunder) + * [B]LMPOP and [B]ZMPOP commands [6ea978eb] (Michael Grunder) + * Implement new RESTORE options [9a3fe401] (Michael Grunder) + * Add new Redis 6.2.0 XTRIM options [6b34d17f] (Michael Grunder) + * Implement AUTH/AUTH2 arguments for MIGRATE [114d79d1] (Michael Grunder) + * Implement CONFIG REWRITE [525958ea] (Michael Grunder) + * Implement Redis 7.0.0 [P]EXPIRE[AT] [options 872ae107] (Michael Grunder) + * Variadic CONFIG GET/SET [36ef4bd8, a176f586] (Michael Grunder) + * EVAL_RO and EVALSHA_RO [f3a40830] (Michael Grunder) + * Implement ZRANGESTORE and add ZRANGE options [71bcbcb9] (Michael Grunder) + * XGROUP DELCONSUMER and ENTRIESREAD [1343f500] (Michael Grunder) + * Expose the transferred number of bytes [e0a88b7b, 90828019, 7a4cee2d] (Pavlo Yatsukhnenko), (Michael Grunder) + * TOUCH command [dc1f2398] (Michael Grunder) + * Redis Sentinel TLS support [f2bb2cdb] (Pavlo Yatsukhnenko) + * Add the CH, NX, XX arguments to GEOADD [2bb64038, e8f5b517] (Pavlo Yatsukhnenko) + * Implement SMISMEMBER for RedisCluster [abfac47b] (Michael Grunder) + * Implement ssubscribe/sunsubscribe [7644736e] (Pavlo Yatsukhnenko) + * Implement BLMOVE and add LMOVE/BLMOVE to cluster. [121e9d9c] (Michael Grunder) + * Implement LPOS for RedisCluster [7121aaae] (Michael Grunder) + * Implement GEOSEARCH and GEOSEARCHSTORE for RedisCluster. [fa5d1af9] (Michael Grunder) + * Implement HRANDFIELD for RedisCluster [e222b85e] (Michael Grunder) + * Implement COPY for RedisCluster [40a2c254] (Michael Grunder) + * Implement new ZSET commands for cluster [27900f39] (Michael Grunder) + * Add cluster support for strict sessions and lazy write [b6cf6361] (Michael Grunder) + * Add function command [90a0e9cc] (Pavlo Yatsukhnenko) + * Add FCALL/FCALL_RO commands [7c46ad2c] (Pavlo Yatsukhnenko) + * Remove unused macros [831d6118] (Pavlo Yatsukhnenko) + + + + + + stable + stable + + + 5.3.7 + 5.3.7 + + 2022-02-15 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Luis Zarate - https://github.com/jlzaratec + + phpredis 5.3.7 + + - There were no changes between 5.3.7 and 5.3.7RC2. + + --- + + phpredis 5.3.7RC2 + + - There were no changes between 5.3.7RC2 and 5.3.7RC1. + + --- + + phpredis 5.3.7RC1 + + - Fix RedisArray::[hsz]scan and tests [08a9d5db, 0264de18] (Pavlo Yatsukhnenko, Michael Grunder) + - Fix RedisArray::scan [8689ab1c] (Pavlo Yatsukhnenko) + - Fix LZF decompression logic [0719c1ec] (Michael Grunder) + + + + + + stable + stable + + + 5.3.6 + 5.3.6 + + 2022-01-17 + + --- Sponsors --- + + Audiomack - https://audiomack.com + Open LMS - https://openlms.net + BlueHost - https://bluehost.com + Object Cache Pro for WordPress - https://objectcache.pro + Avtandil Kikabidze - https://github.com/akalongman + Zaher Ghaibeh - https://github.com/zaherg + BatchLabs - https://batch.com + Luis Zarate - https://github.com/jlzaratec + + --- + + phpredis 5.3.6 + + - Fix a segfault in RedisArray::del [d2f2a7d9] (Pavlo Yatsukhnenko) + + + + + + stable + stable + + + 5.3.5 + 5.3.5 + + 2021-12-18 + + phpredis 5.3.5 This release adds support for exponential backoff w/jitter, experimental support for detecting a dirty connection, as well as many other fixes and improvements. - You can find a detailed list of changes in CHANGELOG.md and package.xml + You can find a detailed list of changes in Changelog.md and package.xml or by inspecting the git commit logs. --- Sponsors --- @@ -60,6 +931,14 @@ http://pear.php.net/dtd/package-2.0.xsd"> --- + phpredis 5.3.5 + + * Fix typo in cluster_scan_resp [44affad2] (Michael Grunder) + + --- + + phpredis 5.3.5RC1 + * Fixed segfault in redis_setoption_handler [692e4e84] (Pavlo Yatsukhnenko) * Fix masters array in the event of a cluster failover [bce692962] (Bar Shaul) * Fix 32 bit type error [672dec87f] (Remi Collet) @@ -112,102 +991,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> * SMISMEMBER Command [#1894, ae2382472, ed283e1ab] (Pavlo Yatsukhnenko) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 7.0.0 - - - 1.4.0b1 - - - - redis - - - - - - - - - - stablestable - 5.3.45.3.4 - 2021-03-24 - + + + + + stable + stable + + + 5.3.4 + 5.3.4 + + 2021-03-24 + phpredis 5.3.4 This release fixes a multi/pipeline segfault on apple silicon as well as @@ -228,13 +1024,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> ~ Avtandil Kikabidze - https://github.com/akalongman ~ Zaher Ghaibeh - https://github.com/zaherg ~ BatchLabs - https://batch.com - - - - stablestable - 5.3.35.3.3 - 2021-02-01 - + + + + + stable + stable + + + 5.3.3 + 5.3.3 + + 2021-02-01 + phpredis 5.3.3 This release mostly includes just small PHP 8 Windows compatibility fixes @@ -254,14 +1056,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> ~ Avtandil Kikabidze - https://github.com/akalongman ~ Zaher Ghaibeh - https://github.com/zaherg ~ BatchLabs - https://batch.com - - - - - stablestable - 5.3.25.3.2 - 2020-10-22 - + + + + + stable + stable + + + 5.3.2 + 5.3.2 + + 2020-10-22 + This release containse some bugfixes and small improvements. You can find a detailed list of changes in CHANGELOG.md and package.xml @@ -302,14 +1109,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Allow to specify stream context for rediscluster session handler [a8daaff8, 4fbe7df7] (Pavlo Yatsukhnenko) * Add new parameter to RedisCluster to specify stream ssl/tls context. [f771ea16] (Pavlo Yatsukhnenko) * Add new parameter to RedisSentinel to specify auth information [81c502ae] (Pavlo Yatsukhnenko) - - - - - stablestable - 5.3.15.3.1 - 2020-07-07 - + + + + + stable + stable + + + 5.3.1 + 5.3.1 + + 2020-07-07 + phpredis 5.3.1 This is a small bugfix release that fixes a couple of issues in 5.3.0. @@ -336,13 +1148,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> 3c56289c, 08f202e7] (Remi Collet) * Add openSUSE installation instructions [13a168f4] (Pavlo Yatsukhnenko) * Remove EOL Fedora installation instructions [b4779e6a] (Remi Collet) - - - - stablestable - 5.3.05.3.0 - 2020-06-30 - + + + + + stable + stable + + + 5.3.0 + 5.3.0 + + 2020-06-30 + phpredis 5.3.0 This release contains initial support for Redis 6 ACLs, LZ4 compression, @@ -420,14 +1238,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> (Pavlo Yatsukhnenko) * Fix documentation showing lPush and rPush are variadic [6808cd6a] (Michael Grunder) - - - - - stablestable - 5.2.25.2.2 - 2020-05-05 - + + + + + stable + stable + + + 5.2.2 + 5.2.2 + + 2020-05-05 + phpredis 5.2.2 This is a bugfix release that contains a fix for authentication @@ -440,17 +1263,22 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Sponsors ~ Audiomack.com - https://audiomack.com ~ Till Kruss - https://github.com/tillkruss - - - - - stablestable - 5.2.15.2.1 - 2020-03-19 - + + + + + stable + stable + + + 5.2.1 + 5.2.1 + + 2020-03-19 + phpredis 5.2.1 - This is a bugfix release that fixes `redis->zAdd` arginfo as well as a + This is a bugfix release that fixes `redis->zAdd` arginfo as well as a segfault when closing persistent connections. * Fix arginfo for Redis::zadd [a8e2b021] (Pavlo Yatsukhnenko) @@ -459,16 +1287,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Sponsors ~ Audiomack.com - https://audiomack.com ~ Till Kruss - https://github.com/tillkruss - - - - - - - stablestable - 5.2.05.2.0 - 2020-03-02 - + + + + + stable + stable + + + 5.2.0 + 5.2.0 + + 2020-03-02 + phpredis 5.2.0 - There were no changes between 5.2.0RC2 and 5.2.0. @@ -487,7 +1318,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> This release contains initial support for Redis Sentinel as well as many smaller bug fixes and improvements. It is especially of interest if you use persistent connections, as we've added logic to make sure they are in - a good state when retreving them from the pool. + a good state when retrieving them from the pool. IMPORTANT: Sentinel support is considered experimental and the API will likely change based on user feedback. @@ -501,7 +1332,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4, 383779ed] (Pavlo Yatsukhnenko) - * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, + * Housekeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, 0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre, Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre) @@ -531,7 +1362,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> This release contains initial support for Redis Sentinel as well as many smaller bug fixes and improvements. It is especially of interest if you use persistent connections, as we've added logic to make sure they are in - a good state when retreving them from the pool. + a good state when retrieving them from the pool. IMPORTANT: Sentinel support is considered experimental and the API will likely change based on user feedback. @@ -545,7 +1376,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Initial support for RedisSentinel [90cb69f3, c94e28f1, 46da22b0, 5a609fa4, 383779ed] (Pavlo Yatsukhnenko) - * Houskeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, + * Housekeeping (spelling, doc changes, etc) [23f9de30, d07a8df6, 2d39b48d, 0ef488fc, 2c35e435, f52bd8a8, 2ddc5f21, 1ff7dfb7, db446138] (Tyson Andre, Pavlo Yatsukhnenko, Michael Grunder, Tyson Andre) @@ -561,27 +1392,38 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Added challenge/response mechanism for persistent connections [a5f95925, 25cdaee6, 7b6072e0, 99ebd0cc, 3243f426] (Pavlo Yatsukhnenko, Michael Grunder) - - - - stablestable - 5.1.15.1.0 - 2019-11-11 - + + + + + stable + stable + + + 5.1.1 + 5.1.0 + + 2019-11-11 + phpredis 5.1.1 This release contains only bugfix for unix-socket connection. * Fix fail to connect to redis through unix socket [2bae8010, 9f4ededa] (Pavlo Yatsukhnenko, Michael Grunder) * Documentation improvements (@fitztrev) - - - - - stablestable - 5.1.05.1.0 - 2019-10-31 - + + + + + stable + stable + + + 5.1.0 + 5.1.0 + + 2019-10-31 + This release contains important bugfixes and improvements. phpredis 5.1.0 @@ -602,20 +1444,25 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fix unix-socket detection logic broken in 418428fa [a080b73f] (Pavlo Yatsukhnenko) * Fix memory leak and bug with getLastError for redis_mbulk_reply_assoc and redis_mbulk_reply_zipped. [7f42d628, 3a622a07] (Pavlo Yatsukhnenko), (Michael Grunder) * Fix bug with password contain "#" for redis_session [2bb08680] (Pavlo Yatsukhnenko) - * Add optional support for Zstd compression, using --enable-redis-ztsd. This requires libzstd version >= 1.3.0 [2abc61da] (Remi Collet) + * Add optional support for Zstd compression, using --enable-redis-ztsd. This requires libzstd version >= 1.3.0 [2abc61da] (Remi Collet) * Fix overallocation in RedisCluster directed node commands [cf93649] (Michael Grunder) * Also attach slaves when caching cluster slots [0d6d3fdd, b114fc26] (Michael Grunder) * Use zend_register_persistent_resource_ex for connection pooling [fdada7ae, 7c6c43a6] (Pavlo Yatsukhnenko) * Refactor redis_session [91a8e734, 978c3074] (Pavlo Yatsukhnenko) * Documentation improvements (@Steveb-p, @tangix, @ljack-adista, @jdreesen, Michael Grunder) - - - - - stablestable - 5.0.05.0.0 - 2019-07-02 - + + + + + stable + stable + + + 5.0.0 + 5.0.0 + + 2019-07-02 + This release contains important improvements and breaking changes. The most interesting are: drop PHP5 support, RedisCluster slots caching, JSON and msgpack serializers, soft deprecation of non-Redis commands. @@ -629,7 +1476,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Allow compilation without JSON serialization enabled and fixes for deprecated helper methods. [235a27] (Pavlo Yatsukhnenko) - * Fix php msgpack >= 2.0.3 version requirement. [6973478..a537df8] (Michael Grunder) + * Fix php msgpack >= 2.0.3 version requirement. [6973478..a537df8] (Michael Grunder) phpredis 5.0.0RC1 @@ -639,7 +1486,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * xInfo response format [4852a510, ac9dca0a] (Pavlo Yatsukhnenko) * Make the XREADGROUP optional COUNT and BLOCK arguments nullable [0c17bd27] (Michael Grunder) * Allow PING to take an optional argument [6e494170] (Michael Grunder) - * Allow ZRANGE to be called either with `true` or `['withscores' => true]` [19f3efcf] (Michael Grunder) + * Allow ZRANGE to be called either with `true` or `['withscores' => true]` [19f3efcf] (Michael Grunder) * Allow to specify server address as schema://host [418428fa] (Pavlo Yatsukhnenko) * Allow persistent_id to be passed as NULL with strict_types enabled [60223762] (Michael Grunder) * Add server address to exception message [e8fb49be, 34d6403d] (Pavlo Yatsukhnenko) @@ -652,17 +1499,22 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Add RedisCluster slots caching [9f0d7bc0, ea081e05] (Michael Grunder) * Drop PHP5 support [f9928642, 46a50c12, 4601887d, 6ebb36ce, fdbe9d29] (Michael Grunder) * Documentation improvements (@alexander-schranz, @cookieguru, Pavlo Yatsukhnenko, Michael Grunder) - - - - - stablestable - 4.3.04.3.0 - 2019-03-13 - + + + + + stable + stable + + + 4.3.0 + 4.3.0 + + 2019-03-13 + phpredis 4.3.0 - This is probably the last release with PHP 5 suport!!! + This is probably the last release with PHP 5 support!!! * Proper persistent connections pooling implementation [a3703820, c76e00fb, 0433dc03, c75b3b93] (Pavlo Yatsukhnenko) * RedisArray auth [b5549cff, 339cfa2b, 6b411aa8] (Pavlo Yatsukhnenko) @@ -688,14 +1540,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Remove unused parameter lazy_connect from redis_sock_create [c0793e8b] (Pavlo Yatsukhnenko) * Remove useless ZEND_ACC_[C|D]TOR. [bc9b5597] (@twosee) * Documentation improvements (@fanjiapeng, @alexander-schranz, @hmc, Pavlo Yatsukhnenko, Michael Grunder) - - - - - betabeta - 4.2.0RC34.2.0RC3 - 2018-11-08 - + + + + + beta + beta + + + 4.2.0RC3 + 4.2.0RC3 + + 2018-11-08 + phpredis 4.2.0RC3 The main feature of this release is new Streams API implemented by Michael Grunder. @@ -712,9 +1569,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> 4.2.0RC2: * Fix incorrect arginfo for `Redis::sRem` and `Redis::multi` [25b043ce] (Pavlo Yatsukhnenko) - * Update STREAM API to handle STATUS -> BULK reply change [0b97ec37] (Michael Grunder) + * Update STREAM API to handle STATUS -> BULK reply change [0b97ec37] (Michael Grunder) * Treat a -1 response from cluster_check_response as a timeout. [27df9220, 07ef7f4e, d1172426] (Michael Grunder) - * Use a ZSET insted of SET for EVAL tests [2e412373] (Michael Grunder) + * Use a ZSET instead of SET for EVAL tests [2e412373] (Michael Grunder) * Missing space between command and args [0af2a7fe] (@remicollet) 4.2.0RC1: @@ -729,14 +1586,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Display ini entries in output of phpinfo [908ac4b3] (Pavlo Yatsukhnenko) * Persistent connections can be closed via close method + change reconnection logic [1d997873] (Pavlo Yatsukhnenko) * Documentation improvements (@mg, @elcheco, @lucascourot, @nolimitdev, Michael Grunder) - - - - - stablestable - 4.1.14.1.1 - 2018-08-01 - + + + + + stable + stable + + + 4.1.1 + 4.1.1 + + 2018-08-01 + phpredis 4.1.1 This release contains only bugfixes and documentation improvements @@ -745,14 +1607,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fix compression in RedisCluster [a53e1a34] (Pavlo Yatsukhnenko) * Fix TravisCI builds [9bf32d30] (@jrchamp) * Highlight php codes in documentation [c3b023b0] (@ackintosh) - - - - - stablestable - 4.1.04.1.0 - 2018-01-10 - + + + + + stable + stable + + + 4.1.0 + 4.1.0 + + 2018-01-10 + phpredis 4.1.0 The primary new feature of this release is session locking functionality. Thanks to @SkydiveMarius! @@ -766,26 +1633,36 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Add session locking functionality [300c7251] (@SkydiveMarius, Michael Grunder, Pavlo Yatsukhnenko) * Fix compression in RedisCluster [1aed74b4] (Pavlo Yatsukhnenko) * Refactor geo* commands + documentation improvements (Michael Grunder) - - - - - stablestable - 4.0.24.0.2 - 2018-04-25 - + + + + + stable + stable + + + 4.0.2 + 4.0.2 + + 2018-04-25 + phpredis 4.0.2 This release contains only fix of exists method to take multiple keys and return integer value (was broken in 4.0.1) Thanks @RanjanRohit! - - - - - stablestable - 4.0.14.0.1 - 2018-04-18 - + + + + + stable + stable + + + 4.0.1 + 4.0.1 + + 2018-04-18 + phpredis 4.0.1 * Fix arginfo for connect/pconnect issue #1337 [c3b228] (@mathroc) @@ -793,14 +1670,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fix config.m4 for lzf issue #1325 [20e173] (Pavlo Yatsukhnenko) * Updates EXISTS documentation and notes change in 4.0.0 [bed186] (Michael Grunder) * Fix typo in notes [0bed36] (@szepeviktor) - - - - - stablestable - 4.0.04.0.0 - 2018-03-17 - + + + + + stable + stable + + + 4.0.0 + 4.0.0 + + 2018-03-17 + phpredis 4.0.0 *** WARNING! THIS RELEASE CONTAINS BREAKING API CHANGES! *** @@ -820,28 +1702,38 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Allow to use empty string as persistent_id [ec4fd1bd] (Pavlo Yatsukhnenko) * Don't use convert_to_string in redis_hmget_cmd [99335d6] (Pavlo Yatsukhnenko) * Allow mixing MULTI and PIPELINE modes (experimental) [5874b0] (Pavlo Yatsukhnenko) - * PHP >=7.3.0 uses zend_string to store `php_url` elements [b566fb44] (@fmk) + * PHP >=7.3.0 uses zend_string to store `php_url` elements [b566fb44] (@fmk) * Documentation improvements (Michael Grunder, @TomA-R) - - - - - stablestable - 3.1.63.1.6 - 2018-01-03 - + + + + + stable + stable + + + 3.1.6 + 3.1.6 + + 2018-01-03 + phpredis 3.1.6 - This release conains only fix of RedisArray distributor hashing function + This release contains only fix of RedisArray distributor hashing function which was broken in 3.1.4. Huge thanks to @rexchen123 - - - - - stablestable - 3.1.53.1.5 - 2017-12-20 - + + + + + stable + stable + + + 3.1.5 + 3.1.5 + + 2017-12-20 + phpredis 3.1.5 This is interim release which contains only bug fixes. @@ -851,15 +1743,20 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Allow to use empty string as persistent_id [344de5] (Pavlo Yatsukhnenko) * Fix cluster_init_seeds. [db1347] (@adlagares) * Fix z_seeds may be a reference [42581a] (@janic716) - * PHP >=7.3 uses zend_string for php_url elements [b566fb] (@fmk) - - - - - stablestable - 3.1.43.1.4 - 2017-09-27 - + * PHP >=7.3 uses zend_string for php_url elements [b566fb] (@fmk) + + + + + stable + stable + + + 3.1.4 + 3.1.4 + + 2017-09-27 + phpredis 3.1.4 The primary new feature phpredis 3.1.4 is the ability to send MULTI .. EXEC blocks in pipeline mode. There are @@ -889,25 +1786,30 @@ http://pear.php.net/dtd/package-2.0.xsd"> calls to pipeline [d11798, 77aeba] (Michael Grunder) * Use zend_string rather than char* for various context fields (err, prefix, etc) [2bf7b2] (Pavlo Yatsukhnenko) * Various other library fixes [142b51, 4452f6, e672f4, 658ee3, c9df77, 4a0a46] (Pavlo Yatsukhnenko) - - - - - stablestable - 3.1.33.1.3 - 2017-07-15 - + + + + + stable + stable + + + 3.1.3 + 3.1.3 + + 2017-07-15 + phpredis 3.1.3 This release contains two big improvements: - 1. Adding a new printf like command construction function with additionaly format specifiers specific to phpredis. - 2. Implementation of custom objects for Redis and RedisArray wich eliminates double hash lookup. + 1. Adding a new printf like command construction function with additionally format specifiers specific to phpredis. + 2. Implementation of custom objects for Redis and RedisArray which eliminates double hash lookup. Also many small improvements and bug fixes were made. * A printf like method to construct a Redis RESP command [a4a0ed, d75081, bdd287, 0eaeae, b3d00d] (Michael Grunder) * Use custom objects instead of zend_list for storing Redis/RedisArray [a765f8, 8fa85a] (Pavlo Yatsukhnenko) * Make sure redisCluster members are all initialized on (re)creation [162d88] (Michael Grunder) - * Fix Null Bulk String response parsing in cluster library [058753] (Alberto Fernández) + * Fix Null Bulk String response parsing in cluster library [058753] (Alberto Fernandez) * Add hStrLen command [c52077, fb88e1] (Pavlo Yatsukhnenko) * Add optional COUNT argument to sPop [d2e203] (Michael Grunder) * Allow sInterStore to take one arg [26aec4, 4cd06b] (Michael Grunder) @@ -926,30 +1828,40 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Better documentation [f0c25a, c5991f, 9ec9ae] (Michael Grunder) * Better TravisCI integration [e37c08] (Pavlo Yatsukhnenko) * Refactoring (Pavlo Yatsukhnenko, Michael Grunder) - - - - - stablestable - 3.1.23.1.2 - 2017-03-16 - + + + + + stable + stable + + + 3.1.2 + 3.1.2 + + 2017-03-16 + phpredis 3.1.2 * RedisArray segfault fix [564ce3] (Pavlo Yatsukhnenko) * Small memory leak fix [645888b] (Mike Grunder) * Segfault fix when recreating RedisCluster objects [abf7d4] (Michael Grunder) - * Fix for RedisCluster bulk response parsing [4121c4] (Alberto Fernández) + * Fix for RedisCluster bulk response parsing [4121c4] (Alberto Fernandez) * Re allow single array for sInterStore [6ef0c2, d01966] (Michael Grunder) * Better TravisCI integration [4fd2f6] (Pavlo Yatsukhnenko) - - - - - betabeta - 3.1.1RC23.1.1RC2 - 2017-01-16 - + + + + + beta + beta + + + 3.1.1RC2 + 3.1.1RC2 + + 2017-01-16 + phpredis 3.1.1RC2 @@ -973,16 +1885,21 @@ http://pear.php.net/dtd/package-2.0.xsd"> wrong size. (@remicollet) * Added php session unit test (@yatsukhnenko) - * Added explicit module dependancy for igbinary (@remicollet) + * Added explicit module dependency for igbinary (@remicollet) * Added phpinfo serialization information (@remicollet) - - - - - stablestable - 3.1.03.1.0 - 2016-12-14 - + + + + + stable + stable + + + 3.1.0 + 3.1.0 + + 2016-12-14 + phpredis 3.1.0 In this version of phpredis codebase was unified to work with all versions of php \o/ @@ -1000,17 +1917,22 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Use static declarations for spl_ce_RuntimeException decl (Jeremy Mikola) [a9857d69] * Fixed method call problem causes session handler to display two times (ZiHang Gao) [24f86c49] * psetex method returns '+OK' on success, not true (sitri@ndxbn) [afcd8445] - * Fix integer overflow for long (>32bit) increments in hIncrBy (iyesin) [58e1d799] + * Fix integer overflow for long (>32bit) increments in hIncrBy (iyesin) [58e1d799] * Move zend_object handler to the end (Michael Grunder) [34107966] * Using setOption on redis array causes immediate connection (Pavlo Yatsukhnenko) [f1a85b38] - - - - - stablestable - 2.2.82.2.8 - 2016-06-02 - + + + + + stable + stable + + + 2.2.8 + 2.2.8 + + 2016-06-02 + phpredis 2.2.8 The main improvement in this version of phpredis is support for Redis @@ -1020,7 +1942,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> In addition there have been many bug fixes and improvements to non cluster related commands, which are listed below. - I've attempted to include everyone who contribued to the project in each fix + I've attempted to include everyone who contributed to the project in each fix description and have included names or github user ids. Thanks to everyone for submitting bug reports and pull requests. A special @@ -1038,7 +1960,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * PHP liveness checking workaround (Shafreeck Sea) [c18d58b9] * Various documentation and code formatting and style fixes (ares333, - sanpili, Bryan Nelson, linfangrong, Romero Malaquias, Viktor Szépe) + sanpili, Bryan Nelson, linfangrong, Romero Malaquias, Viktor Szepe) * Fix scan reply processing to use long instead of int to avoid overflow (mixiaojiong). * Fix potential segfault in Redis Cluster session storage (Sergei Lomakov) @@ -1046,18 +1968,23 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Fixed memory leak in discard function [17b1f427] * Sanity check for igbinary unserialization (Maurus Cuelenaere) [3266b222, 5528297a] - * Fix segfault occuring from unclosed socket connection for Redis Cluster + * Fix segfault occurring from unclosed socket connection for Redis Cluster (CatKang) [04196aee] * Case insensitive zRangeByScore options * Fixed dreaded size_t vs long long compiler warning - - - - - stablestable - 2.2.72.2.7 - 2015-03-03 - + + + + + stable + stable + + + 2.2.7 + 2.2.7 + + 2015-03-03 + phpredis 2.2.7 -- Improvements --- @@ -1084,13 +2011,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Add a couple of cases where we throw on an error (LOADING/NOAUTH/MASTERDOWN) * Fix several issues with serialization NARY * @itcom -- Fix missing TSRMLS_CC and a TSRMLS_DC/TSRMLS_CC typo - - - - stablestable - 2.2.52.2.5 - 2014-03-15 - + + + + + stable + stable + + + 2.2.5 + 2.2.5 + + 2014-03-15 + phpredis 2.2.5 This is a minor release with several bug fixes as well as additions to support @@ -1111,13 +2044,19 @@ http://pear.php.net/dtd/package-2.0.xsd"> [DOC] Fix syntax error in documentation for the SET command (@mithunsatheesh) [DOC] Homebrew documentation instructions (@mathias) - - - - stablestable - 2.2.42.2.4 - 2013-09-01 - + + + + + stable + stable + + + 2.2.4 + 2.2.4 + + 2013-09-01 + ** ** Features / Improvements ** @@ -1125,7 +2064,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Randomized reconnect delay for RedisArray @mobli This feature adds an optional parameter when constructing a RedisArray object such that a random delay will be introduced if reconnections are made, - mitigating any 'thundering herd' type problems. + mitigating any 'thundering herd' type problems. * Lazy connections to RedisArray servers @mobli By default, RedisArray will attempt to connect to each server you pass in @@ -1149,15 +2088,21 @@ http://pear.php.net/dtd/package-2.0.xsd"> * Added ZEND_ACC_CTOR and ZEND_ACC_DTOR @euskadi31 * Stop throwing and clearing an exception on connect failure @matmoi * Fix a false positive unit test failure having to do with TTL returns - - - - stablestable - 2.2.32.2.3 - 2013-04-29 - + + + + + stable + stable + + + 2.2.3 + 2.2.3 + + 2013-04-29 + First release to PECL - - - + + + diff --git a/php_redis.h b/php_redis.h index c79367b4a6..83472647b2 100644 --- a/php_redis.h +++ b/php_redis.h @@ -23,7 +23,7 @@ #define PHP_REDIS_H /* phpredis version */ -#define PHP_REDIS_VERSION "6.0.0-dev" +#define PHP_REDIS_VERSION "6.3.0RC1" /* For convenience we store the salt as a printable hex string which requires 2 * characters per byte + 1 for the NULL terminator */ diff --git a/redis.c b/redis.c index 79b609ccc6..8bfb1e3ac5 100644 --- a/redis.c +++ b/redis.c @@ -27,12 +27,16 @@ #include "redis_cluster.h" #include "redis_commands.h" #include "redis_sentinel.h" -#include #include #include #include #include +#if PHP_VERSION_ID < 80400 +#include +#else +#include +#endif #ifdef PHP_SESSION #include @@ -107,10 +111,14 @@ PHP_INI_BEGIN() /* redis session */ PHP_INI_ENTRY("redis.session.locking_enabled", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_release_cmd", "eval", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_expire", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_retries", "100", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.lock_wait_time", "20000", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.lock_failure_readonly", "0", PHP_INI_ALL, NULL) PHP_INI_ENTRY("redis.session.early_refresh", "0", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.compression", "none", PHP_INI_ALL, NULL) + PHP_INI_ENTRY("redis.session.compression_level", "3", PHP_INI_ALL, NULL) PHP_INI_END() static const zend_module_dep redis_deps[] = { @@ -168,7 +176,7 @@ redis_send_discard(RedisSock *redis_sock) (resp = redis_sock_read(redis_sock,&resp_len)) != NULL) { /* success if we get OK */ - result = (resp_len == 3 && strncmp(resp,"+OK", 3) == 0) ? SUCCESS:FAILURE; + result = (resp_len == 3 && redis_strncmp(resp, ZEND_STRL("+OK")) == 0) ? SUCCESS:FAILURE; /* free our response */ efree(resp); @@ -192,7 +200,7 @@ free_redis_object(zend_object *object) zend_object_std_dtor(&redis->std); if (redis->sock) { - redis_sock_disconnect(redis->sock, 0); + redis_sock_disconnect(redis->sock, 0, 1); redis_free_socket(redis->sock); } } @@ -210,6 +218,7 @@ create_redis_object(zend_class_entry *ce) memcpy(&redis_object_handlers, zend_get_std_object_handlers(), sizeof(redis_object_handlers)); redis_object_handlers.offset = XtOffsetOf(redis_object, std); redis_object_handlers.free_obj = free_redis_object; + redis_object_handlers.clone_obj = NULL; redis->std.handlers = &redis_object_handlers; return &redis->std; @@ -233,6 +242,31 @@ redis_sock_get_instance(zval *id, int no_throw) return NULL; } +static zend_never_inline ZEND_COLD void redis_sock_throw_exception(RedisSock *redis_sock) { + char *errmsg = NULL; + if (redis_sock->status == REDIS_SOCK_STATUS_AUTHENTICATED) { + if (redis_sock->err != NULL) { + spprintf(&errmsg, 0, "Could not select database %ld '%s'", redis_sock->dbNumber, ZSTR_VAL(redis_sock->err)); + } else { + spprintf(&errmsg, 0, "Could not select database %ld", redis_sock->dbNumber); + } + } else if (redis_sock->status == REDIS_SOCK_STATUS_CONNECTED) { + if (redis_sock->err != NULL) { + spprintf(&errmsg, 0, "Could not authenticate '%s'", ZSTR_VAL(redis_sock->err)); + } else { + spprintf(&errmsg, 0, "Could not authenticate"); + } + } else { + if (redis_sock->port < 0) { + spprintf(&errmsg, 0, "Redis server %s went away", ZSTR_VAL(redis_sock->host)); + } else { + spprintf(&errmsg, 0, "Redis server %s:%d went away", ZSTR_VAL(redis_sock->host), redis_sock->port); + } + } + REDIS_THROW_EXCEPTION(errmsg, 0); + efree(errmsg); +} + /** * redis_sock_get */ @@ -245,16 +279,9 @@ redis_sock_get(zval *id, int no_throw) return NULL; } - if (redis_sock_server_open(redis_sock) < 0) { + if (UNEXPECTED(redis_sock_server_open(redis_sock) < 0)) { if (!no_throw) { - char *errmsg = NULL; - if (redis_sock->port < 0) { - spprintf(&errmsg, 0, "Redis server %s went away", ZSTR_VAL(redis_sock->host)); - } else { - spprintf(&errmsg, 0, "Redis server %s:%d went away", ZSTR_VAL(redis_sock->host), redis_sock->port); - } - REDIS_THROW_EXCEPTION(errmsg, 0); - efree(errmsg); + redis_sock_throw_exception(redis_sock); } return NULL; } @@ -485,7 +512,7 @@ PHP_METHOD(Redis,__destruct) { // queued redis_send_discard(redis_sock); } - free_reply_callbacks(redis_sock); + redis_free_reply_callbacks(redis_sock); } } @@ -531,7 +558,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) #endif if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Os|lds!lda", &object, redis_ce, &host, + "Os|lds!lda!", &object, redis_ce, &host, &host_len, &port, &timeout, &persistent_id, &persistent_id_len, &retry_interval, &read_timeout, &context) == FAILURE) @@ -561,8 +588,8 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) /* Does the host look like a unix socket */ af_unix = (host_len > 0 && host[0] == '/') || - (host_len > 6 && !strncasecmp(host, "unix://", sizeof("unix://") - 1)) || - (host_len > 6 && !strncasecmp(host, "file://", sizeof("file://") - 1)); + (host_len > 6 && (!strncasecmp(host, "unix://", sizeof("unix://") - 1) || + !strncasecmp(host, "file://", sizeof("file://") - 1))); /* If it's not a unix socket, set to default */ if (port == -1 && !af_unix) { @@ -573,17 +600,17 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) /* if there is a redis sock already we have to remove it */ if (redis->sock) { - redis_sock_disconnect(redis->sock, 0); + redis_sock_disconnect(redis->sock, 0, 1); redis_free_socket(redis->sock); } - redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, persistent, - persistent_id, retry_interval); + redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, + persistent, persistent_id, retry_interval); if (context) { /* Stream context (e.g. TLS) */ if ((ele = REDIS_HASH_STR_FIND_STATIC(Z_ARRVAL_P(context), "stream"))) { - redis_sock_set_stream_context(redis->sock, ele); + redis_sock_set_context_zval(redis->sock, ele); } /* AUTH */ @@ -604,6 +631,111 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) return SUCCESS; } +static inline void +pipeline_enqueue_command(RedisSock *redis_sock, const char *cmd, int cmd_len) { + smart_string_appendl(&redis_sock->pipeline_cmd, cmd, cmd_len); +} + +static void +redis_save_callback(RedisSock *redis_sock, FailableResultCallback cb, void *ctx) +{ + fold_item *fi; + + fi = redis_add_reply_callback(redis_sock); + fi->fun = cb; + fi->flags = redis_sock->flags; + fi->ctx = ctx; +} + +#define REDIS_PROCESS_RESPONSE_CLOSURE(function, closure_context) \ + if (!IS_PIPELINE(redis_sock)) { \ + if (redis_response_enqueued(redis_sock) != SUCCESS) { \ + RETURN_FALSE; \ + } \ + } \ + redis_save_callback(redis_sock, function, closure_context); \ + RETURN_ZVAL(getThis(), 1, 0); \ + + +static int redis_process_request(RedisSock *redis_sock, char *cmd, int cmdlen) { + int res = SUCCESS; + + if (IS_PIPELINE(redis_sock)) { + pipeline_enqueue_command(redis_sock, cmd, cmdlen); + } else if (UNEXPECTED(redis_sock_write(redis_sock, cmd, cmdlen) < 0)) { + res = FAILURE; + } + + efree(cmd); + return res; +} + +static void +redis_process_cmd(INTERNAL_FUNCTION_PARAMETERS, redis_cmd_cb cmd_cb, + FailableResultCallback resp_cb) +{ + RedisSock *redis_sock; + void *ctx = NULL; + int cmd_len; + char *cmd; + + redis_sock = redis_sock_get(getThis(), 0); + if (UNEXPECTED(redis_sock == NULL)) { + RETURN_FALSE; + } + + if (UNEXPECTED(cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &cmd, + &cmd_len, NULL, &ctx) == FAILURE)) + { + RETURN_FALSE; + } + + if (redis_process_request(redis_sock, cmd, cmd_len) != SUCCESS) { + RETURN_FALSE; + } + + if (IS_ATOMIC(redis_sock)) { + resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); + } else { + REDIS_PROCESS_RESPONSE_CLOSURE(resp_cb, ctx); + } +} + +#define REDIS_PROCESS_CMD(cmdname, resp_func) \ + redis_process_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, \ + redis_##cmdname##_cmd, resp_func) + +void +redis_process_kw_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, + redis_kw_cmd_cb cmd_cb, FailableResultCallback resp_cb, + void *ctx) +{ + RedisSock *redis_sock; + int cmd_len; + char *cmd; + + redis_sock = redis_sock_get(getThis(), 0); + if (UNEXPECTED(redis_sock == NULL)) { + RETURN_FALSE; + } + + if (UNEXPECTED(cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, (char*)kw, &cmd, + &cmd_len, NULL, &ctx) == FAILURE)) + { + RETURN_FALSE; + } + + if (redis_process_request(redis_sock, cmd, cmd_len) != SUCCESS) { + RETURN_FALSE; + } + + if (IS_ATOMIC(redis_sock)) { + resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, ctx); + } else { + REDIS_PROCESS_RESPONSE_CLOSURE(resp_cb, ctx); + } +} + /* {{{ proto long Redis::bitop(string op, string key, ...) */ PHP_METHOD(Redis, bitop) { REDIS_PROCESS_CMD(bitop, redis_long_response); @@ -632,7 +764,7 @@ PHP_METHOD(Redis, close) { RedisSock *redis_sock = redis_sock_get_connected(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (redis_sock_disconnect(redis_sock, 1) == SUCCESS) { + if (redis_sock_disconnect(redis_sock, 1, 1) == SUCCESS) { RETURN_TRUE; } RETURN_FALSE; @@ -723,13 +855,15 @@ PHP_METHOD(Redis, reset) } if (IS_PIPELINE(redis_sock)) { - php_error_docref(NULL, E_ERROR, "Reset ins't allowed in pipeline mode!"); + php_error_docref(NULL, E_ERROR, "Reset isn't allowed in pipeline mode!"); RETURN_FALSE; } - redis_cmd_init_sstr(&cmd, 0, "RESET", 5); + redis_cmd_init_sstr(&cmd, 0, ZEND_STRL("RESET")); - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); + if (redis_process_request(redis_sock, cmd.c, cmd.len) != SUCCESS) { + RETURN_FALSE; + } if ((response = redis_sock_read(redis_sock, &response_len)) != NULL) { ret = REDIS_STRCMP_STATIC(response, response_len, "+RESET"); @@ -744,7 +878,7 @@ PHP_METHOD(Redis, reset) RETURN_ZVAL(getThis(), 1, 0); } - free_reply_callbacks(redis_sock); + redis_free_reply_callbacks(redis_sock); redis_sock->status = REDIS_SOCK_STATUS_CONNECTED; redis_sock->mode = ATOMIC; redis_sock->dbNumber = 0; @@ -762,6 +896,14 @@ PHP_METHOD(Redis, get) } /* }}} */ +/* {{{ proto Redis|array|false Redis::getWithMeta(string key) + */ +PHP_METHOD(Redis, getWithMeta) +{ + REDIS_PROCESS_KW_CMD("GET", redis_key_cmd, redis_bulk_withmeta_response); +} +/* }}} */ + /* {{{ proto string Redis::getDel(string key) */ PHP_METHOD(Redis, getDel) @@ -848,6 +990,14 @@ PHP_METHOD(Redis, del) { } /* }}} */ +PHP_METHOD(Redis, delex) { + REDIS_PROCESS_CMD(delex, redis_long_response); +} + +PHP_METHOD(Redis, delifeq) { + REDIS_PROCESS_KW_CMD("DELIFEQ", redis_kv_cmd, redis_long_response); +} + /* {{{ proto long Redis::unlink(string $key1, string $key2 [, string $key3...]) }}} * {{{ proto long Redis::unlink(array $keys) */ PHP_METHOD(Redis, unlink) @@ -1125,7 +1275,7 @@ PHP_METHOD(Redis, sPop) } else if (ZEND_NUM_ARGS() == 2) { REDIS_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, redis_sock_read_multibulk_reply); } else { - ZEND_WRONG_PARAM_COUNT(); + zend_wrong_param_count(); } } @@ -1134,7 +1284,8 @@ PHP_METHOD(Redis, sPop) /* {{{ proto string Redis::sRandMember(string key [int count]) */ PHP_METHOD(Redis, sRandMember) { - REDIS_PROCESS_CMD(srandmember, redis_srandmember_response); + REDIS_PROCESS_KW_CMD("SRANDMEMBER", redis_randmember_cmd, + redis_randmember_response); } /* }}} */ @@ -1252,18 +1403,18 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) } /* Start constructing final command and append key */ - redis_cmd_init_sstr(&cmd, argc, "SORT", 4); + redis_cmd_init_sstr(&cmd, argc, ZEND_STRL("SORT")); redis_cmd_append_sstr_key(&cmd, key, keylen, redis_sock, NULL); /* BY pattern */ if (pattern && patternlen) { - redis_cmd_append_sstr(&cmd, "BY", sizeof("BY") - 1); + redis_cmd_append_sstr(&cmd, ZEND_STRL("BY")); redis_cmd_append_sstr(&cmd, pattern, patternlen); } /* LIMIT offset count */ if (offset >= 0 && count >= 0) { - redis_cmd_append_sstr(&cmd, "LIMIT", sizeof("LIMIT") - 1); + redis_cmd_append_sstr(&cmd, ZEND_STRL("LIMIT")); redis_cmd_append_sstr_long(&cmd, offset); redis_cmd_append_sstr_long(&cmd, count); } @@ -1273,37 +1424,41 @@ generic_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, int desc, int alpha) if (Z_TYPE_P(zget) == IS_ARRAY) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zget), zele) { zpattern = zval_get_string(zele); - redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1); + redis_cmd_append_sstr(&cmd, ZEND_STRL("GET")); redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern)); zend_string_release(zpattern); } ZEND_HASH_FOREACH_END(); } else { zpattern = zval_get_string(zget); - redis_cmd_append_sstr(&cmd, "GET", sizeof("GET") - 1); + redis_cmd_append_sstr(&cmd, ZEND_STRL("GET")); redis_cmd_append_sstr(&cmd, ZSTR_VAL(zpattern), ZSTR_LEN(zpattern)); zend_string_release(zpattern); } } /* Append optional DESC and ALPHA modifiers */ - if (desc) redis_cmd_append_sstr(&cmd, "DESC", sizeof("DESC") - 1); - if (alpha) redis_cmd_append_sstr(&cmd, "ALPHA", sizeof("ALPHA") - 1); + if (desc) redis_cmd_append_sstr(&cmd, ZEND_STRL("DESC")); + if (alpha) redis_cmd_append_sstr(&cmd, ZEND_STRL("ALPHA")); /* Finally append STORE if we've got it */ if (store && storelen) { - redis_cmd_append_sstr(&cmd, "STORE", sizeof("STORE") - 1); + redis_cmd_append_sstr(&cmd, ZEND_STRL("STORE")); redis_cmd_append_sstr_key(&cmd, store, storelen, redis_sock, NULL); } - REDIS_PROCESS_REQUEST(redis_sock, cmd.c, cmd.len); + if (redis_process_request(redis_sock, cmd.c, cmd.len) != SUCCESS) { + RETURN_FALSE; + } + if (IS_ATOMIC(redis_sock)) { if (redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL) < 0) { RETURN_FALSE; } + } else { + REDIS_PROCESS_RESPONSE_CLOSURE(redis_read_variant_reply, NULL); } - REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } /* {{{ proto array Redis::sortAsc(string key, string pattern, string get, @@ -1373,6 +1528,14 @@ PHP_METHOD(Redis, pexpiretime) { REDIS_PROCESS_KW_CMD("PEXPIRETIME", redis_key_cmd, redis_long_response); } +PHP_METHOD(Redis, expiremember) { + REDIS_PROCESS_CMD(expiremember, redis_long_response); +} + +PHP_METHOD(Redis, expirememberat) { + REDIS_PROCESS_CMD(expirememberat, redis_long_response); +} + /* }}} */ /* {{{ proto array Redis::lSet(string key, int index, string value) */ PHP_METHOD(Redis, lSet) { @@ -1426,7 +1589,7 @@ PHP_METHOD(Redis, flushAll) /* {{{ proto mixed Redis::function(string op, mixed ...args) */ PHP_METHOD(Redis, function) { - REDIS_PROCESS_CMD(function, redis_function_response) + REDIS_PROCESS_CMD(function, redis_function_response); } /* {{{ proto int Redis::dbSize() */ @@ -1497,6 +1660,10 @@ PHP_METHOD(Redis, msetnx) { } /* }}} */ +PHP_METHOD(Redis, msetex) { + REDIS_PROCESS_CMD(msetex, redis_long_response); +} + /* {{{ proto string Redis::rpoplpush(string srckey, string dstkey) */ PHP_METHOD(Redis, rpoplpush) { @@ -1695,7 +1862,7 @@ PHP_METHOD(Redis, zPopMax) } else if (ZEND_NUM_ARGS() == 2) { REDIS_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl); } else { - ZEND_WRONG_PARAM_COUNT(); + zend_wrong_param_count(); } } /* }}} */ @@ -1708,18 +1875,18 @@ PHP_METHOD(Redis, zPopMin) } else if (ZEND_NUM_ARGS() == 2) { REDIS_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, redis_mbulk_reply_zipped_keys_dbl); } else { - ZEND_WRONG_PARAM_COUNT(); + zend_wrong_param_count(); } } /* }}} */ -/* {{{ proto Redis::bzPopMax(Array(keys) [, timeout]): Array */ +/* {{{ proto Redis::bzPopMax(Array[keys] [, timeout]): Array */ PHP_METHOD(Redis, bzPopMax) { REDIS_PROCESS_KW_CMD("BZPOPMAX", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } /* }}} */ -/* {{{ proto Redis::bzPopMin(Array(keys) [, timeout]): Array */ +/* {{{ proto Redis::bzPopMin([keys] [, timeout]): Array */ PHP_METHOD(Redis, bzPopMin) { REDIS_PROCESS_KW_CMD("BZPOPMIN", redis_blocking_pop_cmd, redis_sock_read_multibulk_reply); } @@ -1771,6 +1938,13 @@ PHP_METHOD(Redis, hGet) } /* }}} */ +/* {{{ proto string Redis::hgetWithMeta(string key, string mem) */ +PHP_METHOD(Redis, hGetWithMeta) +{ + REDIS_PROCESS_KW_CMD("HGET", redis_key_str_cmd, redis_bulk_withmeta_response); +} +/* }}} */ + /* {{{ proto long Redis::hLen(string key) */ PHP_METHOD(Redis, hLen) { @@ -1831,6 +2005,18 @@ PHP_METHOD(Redis, hMget) { } /* }}} */ +PHP_METHOD(Redis, hgetex) { + REDIS_PROCESS_CMD(hgetex, redis_mbulk_reply_assoc); +} + +PHP_METHOD(Redis, hsetex) { + REDIS_PROCESS_CMD(hsetex, redis_long_response); +} + +PHP_METHOD(Redis, hgetdel) { + REDIS_PROCESS_CMD(hgetdel, redis_mbulk_reply_assoc); +} + /* {{{ proto bool Redis::hmset(string key, array keyvals) */ PHP_METHOD(Redis, hMset) { @@ -1838,6 +2024,51 @@ PHP_METHOD(Redis, hMset) } /* }}} */ +PHP_METHOD(Redis, hexpire) { + REDIS_PROCESS_KW_CMD("HEXPIRE", redis_hexpire_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpexpire) { + REDIS_PROCESS_KW_CMD("HPEXPIRE", redis_hexpire_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hexpireat) { + REDIS_PROCESS_KW_CMD("HEXPIREAT", redis_hexpire_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpexpireat) { + REDIS_PROCESS_KW_CMD("HPEXPIREAT", redis_hexpire_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, httl) { + REDIS_PROCESS_KW_CMD("HTTL", redis_httl_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpttl) { + REDIS_PROCESS_KW_CMD("HPTTL", redis_httl_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hexpiretime) { + REDIS_PROCESS_KW_CMD("HEXPIRETIME", redis_httl_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpexpiretime) { + REDIS_PROCESS_KW_CMD("HPEXPIRETIME", redis_httl_cmd, + redis_read_variant_reply); +} + +PHP_METHOD(Redis, hpersist) { + REDIS_PROCESS_KW_CMD("HPERSIST", redis_httl_cmd, + redis_read_variant_reply); +} + /* {{{ proto bool Redis::hRandField(string key, [array $options]) */ PHP_METHOD(Redis, hRandField) { @@ -1885,25 +2116,27 @@ PHP_METHOD(Redis, multi) /* Enable PIPELINE if we're not already in one */ if (IS_ATOMIC(redis_sock)) { - REDIS_ENABLE_MODE(redis_sock, PIPELINE); + redis_sock->mode |= PIPELINE; } } else if (multi_value == MULTI) { /* Don't want to do anything if we're already in MULTI mode */ if (!IS_MULTI(redis_sock)) { if (IS_PIPELINE(redis_sock)) { - PIPELINE_ENQUEUE_COMMAND(RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD) - 1); - REDIS_SAVE_CALLBACK(NULL, NULL); - REDIS_ENABLE_MODE(redis_sock, MULTI); + pipeline_enqueue_command(redis_sock, ZEND_STRL(RESP_MULTI_CMD)); + redis_save_callback(redis_sock, NULL, NULL); + redis_sock->mode |= MULTI; } else { - SOCKET_WRITE_COMMAND(redis_sock, RESP_MULTI_CMD, sizeof(RESP_MULTI_CMD) - 1) + if (redis_sock_write(redis_sock, ZEND_STRL(RESP_MULTI_CMD)) < 0) { + RETURN_FALSE; + } if ((resp = redis_sock_read(redis_sock, &resp_len)) == NULL) { RETURN_FALSE; - } else if (strncmp(resp, "+OK", 3) != 0) { + } else if (redis_strncmp(resp, ZEND_STRL("+OK")) != 0) { efree(resp); RETURN_FALSE; } efree(resp); - REDIS_ENABLE_MODE(redis_sock, MULTI); + redis_sock->mode |= MULTI; } } } else { @@ -1932,15 +2165,12 @@ PHP_METHOD(Redis, discard) if (IS_PIPELINE(redis_sock)) { ret = SUCCESS; - if (redis_sock->pipeline_cmd) { - zend_string_release(redis_sock->pipeline_cmd); - redis_sock->pipeline_cmd = NULL; - } + smart_string_free(&redis_sock->pipeline_cmd); } else if (IS_MULTI(redis_sock)) { ret = redis_send_discard(redis_sock); } if (ret == SUCCESS) { - free_reply_callbacks(redis_sock); + redis_free_reply_callbacks(redis_sock); redis_sock->mode = ATOMIC; RETURN_TRUE; } @@ -1961,6 +2191,12 @@ redis_sock_read_multibulk_multi_reply(INTERNAL_FUNCTION_PARAMETERS, return FAILURE; } + // No command issued, return empty immutable array + if (redis_sock->reply_callback == NULL) { + ZVAL_EMPTY_ARRAY(z_tab); + return SUCCESS; + } + array_init(z_tab); return redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAM_PASSTHRU, @@ -1982,50 +2218,51 @@ PHP_METHOD(Redis, exec) RETURN_FALSE; } + ZVAL_FALSE(&z_ret); + if (IS_MULTI(redis_sock)) { if (IS_PIPELINE(redis_sock)) { - PIPELINE_ENQUEUE_COMMAND(RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1); - REDIS_SAVE_CALLBACK(NULL, NULL); - REDIS_DISABLE_MODE(redis_sock, MULTI); + pipeline_enqueue_command(redis_sock, ZEND_STRL(RESP_EXEC_CMD)); + redis_save_callback(redis_sock, NULL, NULL); + redis_sock->mode &= ~MULTI; RETURN_ZVAL(getThis(), 1, 0); } - SOCKET_WRITE_COMMAND(redis_sock, RESP_EXEC_CMD, sizeof(RESP_EXEC_CMD) - 1) - - ZVAL_NULL(&z_ret); + if (redis_sock_write(redis_sock, ZEND_STRL(RESP_EXEC_CMD)) < 0) { + RETURN_FALSE; + } ret = redis_sock_read_multibulk_multi_reply( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret); - free_reply_callbacks(redis_sock); - REDIS_DISABLE_MODE(redis_sock, MULTI); + redis_free_reply_callbacks(redis_sock); + redis_sock->mode &= ~MULTI; redis_sock->watching = 0; if (ret < 0) { - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); ZVAL_FALSE(&z_ret); } } if (IS_PIPELINE(redis_sock)) { - if (redis_sock->pipeline_cmd == NULL) { + if (redis_sock->pipeline_cmd.len == 0) { /* Empty array when no command was run. */ - array_init(&z_ret); + ZVAL_EMPTY_ARRAY(&z_ret); } else { - if (redis_sock_write(redis_sock, ZSTR_VAL(redis_sock->pipeline_cmd), - ZSTR_LEN(redis_sock->pipeline_cmd)) < 0) { + if (redis_sock_write(redis_sock, redis_sock->pipeline_cmd.c, + redis_sock->pipeline_cmd.len) < 0) { ZVAL_FALSE(&z_ret); } else { array_init(&z_ret); if (redis_sock_read_multibulk_multi_reply_loop( INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_ret) != SUCCESS) { - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); ZVAL_FALSE(&z_ret); } } - zend_string_release(redis_sock->pipeline_cmd); - redis_sock->pipeline_cmd = NULL; + smart_string_free(&redis_sock->pipeline_cmd); } - free_reply_callbacks(redis_sock); - REDIS_DISABLE_MODE(redis_sock, PIPELINE); + redis_free_reply_callbacks(redis_sock); + redis_sock->mode &= ~PIPELINE; } - RETURN_ZVAL(&z_ret, 1, 0); + RETURN_ZVAL(&z_ret, 0, 1); } PHP_REDIS_API int @@ -2035,7 +2272,7 @@ redis_response_enqueued(RedisSock *redis_sock) int resp_len, ret = FAILURE; if ((resp = redis_sock_read(redis_sock, &resp_len)) != NULL) { - if (strncmp(resp, "+QUEUED", 7) == 0) { + if (redis_strncmp(resp, ZEND_STRL("+QUEUED")) == 0) { ret = SUCCESS; } efree(resp); @@ -2048,21 +2285,28 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab) { fold_item *fi; + uint8_t flags; + size_t i; - for (fi = redis_sock->head; fi; /* void */) { + flags = redis_sock->flags; + for (i = 0; i < redis_sock->reply_callback_count; i++) { + fi = &redis_sock->reply_callback[i]; if (fi->fun) { + redis_sock->flags = fi->flags; fi->fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, fi->ctx); - fi = fi->next; + redis_sock->flags = flags; continue; } size_t len; char inbuf[255]; - if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || strncmp(inbuf, "+OK", 3) != 0) { + if (redis_sock_gets(redis_sock, inbuf, sizeof(inbuf) - 1, &len) < 0 || + redis_strncmp(inbuf, ZEND_STRL("+OK")) != 0) + { return FAILURE; } - while ((fi = fi->next) && fi->fun) { + while (redis_sock->reply_callback[++i].fun) { if (redis_response_enqueued(redis_sock) != SUCCESS) { return FAILURE; } @@ -2078,13 +2322,10 @@ redis_sock_read_multibulk_multi_reply_loop(INTERNAL_FUNCTION_PARAMETERS, int num = atol(inbuf + 1); - if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) < 0) { + if (num > 0 && redis_read_multibulk_recursive(redis_sock, num, 0, &z_ret) != SUCCESS) { return FAILURE; } - - if (fi) fi = fi->next; } - redis_sock->current = fi; return SUCCESS; } @@ -2109,10 +2350,7 @@ PHP_METHOD(Redis, pipeline) /* Enable pipeline mode unless we're already in that mode in which case this * is just a NO OP */ if (IS_ATOMIC(redis_sock)) { - /* NB : we keep the function fold, to detect the last function. - * We need the response format of the n - 1 command. So, we can delete - * when n > 2, the { 1 .. n - 2} commands */ - REDIS_ENABLE_MODE(redis_sock, PIPELINE); + redis_sock->mode |= PIPELINE; } RETURN_ZVAL(getThis(), 1, 0); @@ -2125,7 +2363,7 @@ PHP_METHOD(Redis, publish) } /* }}} */ -/* {{{ proto void Redis::psubscribe(Array(pattern1, pattern2, ... patternN)) */ +/* {{{ proto void Redis::psubscribe([pattern1, pattern2, ... patternN]) */ PHP_METHOD(Redis, psubscribe) { REDIS_PROCESS_KW_CMD("PSUBSCRIBE", redis_subscribe_cmd, @@ -2133,7 +2371,7 @@ PHP_METHOD(Redis, psubscribe) } /* }}} */ -/* {{{ proto void Redis::ssubscribe(Array(shardchannel1, shardchannel2, ... shardchannelN)) */ +/* {{{ proto void Redis::ssubscribe([shardchannel1, shardchannel2, ... shardchannelN]) */ PHP_METHOD(Redis, ssubscribe) { REDIS_PROCESS_KW_CMD("SSUBSCRIBE", redis_subscribe_cmd, @@ -2141,7 +2379,7 @@ PHP_METHOD(Redis, ssubscribe) } /* }}} */ -/* {{{ proto void Redis::subscribe(Array(channel1, channel2, ... channelN)) */ +/* {{{ proto void Redis::subscribe([channel1, channel2, ... channelN]) */ PHP_METHOD(Redis, subscribe) { REDIS_PROCESS_KW_CMD("SUBSCRIBE", redis_subscribe_cmd, redis_subscribe_response); @@ -2149,7 +2387,7 @@ PHP_METHOD(Redis, subscribe) { /** * [ps]unsubscribe channel_0 channel_1 ... channel_n - * [ps]unsubscribe(array(channel_0, channel_1, ..., channel_n)) + * [ps]unsubscribe([channel_0, channel_1, ..., channel_n]) * response format : * array( * channel_0 => TRUE|FALSE, @@ -2177,6 +2415,10 @@ PHP_METHOD(Redis, sunsubscribe) redis_unsubscribe_response); } +PHP_METHOD(Redis, waitaof) { + REDIS_PROCESS_CMD(waitaof, redis_read_variant_reply); +} + /* {{{ proto string Redis::bgrewriteaof() */ PHP_METHOD(Redis, bgrewriteaof) { @@ -2387,6 +2629,18 @@ PHP_METHOD(Redis, _pack) { redis_pack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock); } +PHP_METHOD(Redis, _digest) { + RedisSock *redis_sock; + + redis_sock = redis_sock_get_instance(getThis(), 0); + if (redis_sock == NULL) { + RETURN_FALSE; + } + + redis_digest_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, + redis_exception_ce); +} + PHP_METHOD(Redis, _unpack) { RedisSock *redis_sock; @@ -2438,11 +2692,7 @@ PHP_METHOD(Redis, clearLastError) { RETURN_FALSE; } - // Clear error message - if (redis_sock->err) { - zend_string_release(redis_sock->err); - redis_sock->err = NULL; - } + redis_sock_clear_err(redis_sock); RETURN_TRUE; } @@ -2528,6 +2778,38 @@ PHP_METHOD(Redis, getPort) { } } +PHP_METHOD(Redis, serverName) { + RedisSock *rs; + + if ((rs = redis_sock_get_instance(getThis(), 1)) == NULL) { + RETURN_FALSE; + } else if (!IS_ATOMIC(rs)) { + php_error_docref(NULL, E_ERROR, + "Can't call serverName in multi or pipeline mode!"); + RETURN_FALSE; + } else if (rs->hello.server != NULL) { + RETURN_STR_COPY(rs->hello.server); + } + + REDIS_PROCESS_KW_CMD("HELLO", redis_empty_cmd, redis_hello_server_response); +} + +PHP_METHOD(Redis, serverVersion) { + RedisSock *rs; + + if ((rs = redis_sock_get_instance(getThis(), 1)) == NULL) { + RETURN_FALSE; + } else if (!IS_ATOMIC(rs)) { + php_error_docref(NULL, E_ERROR, + "Can't call serverVersion in multi or pipeline mode!"); + RETURN_FALSE; + } else if (rs->hello.version != NULL) { + RETURN_STR_COPY(rs->hello.version); + } + + REDIS_PROCESS_KW_CMD("HELLO", redis_empty_cmd, redis_hello_version_response); +} + /* {{{ proto Redis::getDBNum */ PHP_METHOD(Redis, getDBNum) { RedisSock *redis_sock; @@ -2630,40 +2912,32 @@ PHP_METHOD(Redis, client) { /* {{{ proto mixed Redis::rawcommand(string $command, [ $arg1 ... $argN]) */ PHP_METHOD(Redis, rawcommand) { - int argc = ZEND_NUM_ARGS(), cmd_len; + int argc, cmd_len; char *cmd = NULL; RedisSock *redis_sock; zval *z_args; - /* Sanity check on arguments */ - if (argc < 1) { - php_error_docref(NULL, E_WARNING, - "Must pass at least one command keyword"); - RETURN_FALSE; - } - z_args = emalloc(argc * sizeof(zval)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - php_error_docref(NULL, E_WARNING, - "Internal PHP error parsing arguments"); - efree(z_args); - RETURN_FALSE; - } else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len) < 0 || + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_VARIADIC('+', z_args, argc) + ZEND_PARSE_PARAMETERS_END(); + + if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len) < 0 || (redis_sock = redis_sock_get(getThis(), 0)) == NULL ) { if (cmd) efree(cmd); - efree(z_args); RETURN_FALSE; } - /* Clean up command array */ - efree(z_args); + if (redis_process_request(redis_sock, cmd, cmd_len) != SUCCESS) { + RETURN_FALSE; + } - /* Execute our command */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if (IS_ATOMIC(redis_sock)) { - redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL,NULL); + redis_read_raw_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock,NULL,NULL); + } else { + REDIS_PROCESS_RESPONSE_CLOSURE(redis_read_variant_reply, NULL); } - REDIS_PROCESS_RESPONSE(redis_read_variant_reply); } /* }}} */ @@ -2677,14 +2951,14 @@ PHP_METHOD(Redis, command) { /* {{{ proto array Redis::copy(string $source, string $destination, array $options = null) */ PHP_METHOD(Redis, copy) { - REDIS_PROCESS_CMD(copy, redis_1_response) + REDIS_PROCESS_CMD(copy, redis_1_response); } /* }}} */ /* Helper to format any combination of SCAN arguments */ -PHP_REDIS_API int +static int redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, - long iter, char *pattern, int pattern_len, int count, + uint64_t cursor, char *pattern, int pattern_len, int count, zend_string *match_type) { smart_string cmdstr = {0}; @@ -2715,7 +2989,7 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, /* Start the command */ redis_cmd_init_sstr(&cmdstr, argc, keyword, strlen(keyword)); if (key_len) redis_cmd_append_sstr(&cmdstr, key, key_len); - redis_cmd_append_sstr_long(&cmdstr, iter); + redis_cmd_append_sstr_u64(&cmdstr, cursor); /* Append COUNT if we've got it */ if(count) { @@ -2739,24 +3013,26 @@ redis_build_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, return cmdstr.len; } -/* {{{ proto redis::scan(&$iterator, [pattern, [count, [type]]]) */ +/* {{{ proto redis::scan(&$cursor, [pattern, [count, [type]]]) */ PHP_REDIS_API void generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { - zval *object, *z_iter; + zval *object, *z_cursor; RedisSock *redis_sock; HashTable *hash; char *pattern = NULL, *cmd, *key = NULL; int cmd_len, num_elements, key_free = 0, pattern_free = 0; size_t key_len = 0, pattern_len = 0; zend_string *match_type = NULL; - zend_long count = 0, iter; + zend_long count = 0; + zend_bool completed; + uint64_t cursor; /* Different prototype depending on if this is a key based scan */ if(type != TYPE_SCAN) { // Requires a key if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os!z/|s!l", &object, redis_ce, &key, - &key_len, &z_iter, &pattern, + &key_len, &z_cursor, &pattern, &pattern_len, &count)==FAILURE) { RETURN_FALSE; @@ -2764,7 +3040,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { } else { // Doesn't require a key if(zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Oz/|s!lS", &object, redis_ce, &z_iter, + "Oz/|s!lS!", &object, redis_ce, &z_cursor, &pattern, &pattern_len, &count, &match_type) == FAILURE) { @@ -2784,20 +3060,10 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { RETURN_FALSE; } - // The iterator should be passed in as NULL for the first iteration, but we - // can treat any NON LONG value as NULL for these purposes as we've - // separated the variable anyway. - if(Z_TYPE_P(z_iter) != IS_LONG || Z_LVAL_P(z_iter) < 0) { - /* Convert to long */ - convert_to_long(z_iter); - iter = 0; - } else if(Z_LVAL_P(z_iter) != 0) { - /* Update our iterator value for the next passthru */ - iter = Z_LVAL_P(z_iter); - } else { - /* We're done, back to iterator zero */ + /* Get our SCAN cursor short circuiting if we're done */ + cursor = redisGetScanCursor(z_cursor, &completed); + if (completed) RETURN_FALSE; - } /* Prefix our key if we've got one and we have a prefix set */ if(key_len) { @@ -2814,24 +3080,26 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { * pattern. phpredis can be set up to abstract this from the user, by * setting OPT_SCAN to REDIS_SCAN_RETRY. Otherwise we will return empty * keys and the user will need to make subsequent calls with an updated - * iterator. + * cursor. */ do { /* Free our previous reply if we're back in the loop. We know we are * if our return_value is an array */ if (Z_TYPE_P(return_value) == IS_ARRAY) { - zval_dtor(return_value); + zval_ptr_dtor_nogc(return_value); ZVAL_NULL(return_value); } // Format our SCAN command - cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, (long)iter, - pattern, pattern_len, count, match_type); + cmd_len = redis_build_scan_cmd(&cmd, type, key, key_len, cursor, + pattern, pattern_len, count, match_type); + + if (redis_process_request(redis_sock, cmd, cmd_len) != SUCCESS) { + RETURN_FALSE; + } - /* Execute our command getting our new iterator value */ - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); if(redis_sock_read_scan_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock,type,&iter) < 0) + redis_sock,type, &cursor) < 0) { if(key_free) efree(key); RETURN_FALSE; @@ -2840,7 +3108,7 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Get the number of elements */ hash = Z_ARRVAL_P(return_value); num_elements = zend_hash_num_elements(hash); - } while (redis_sock->scan & REDIS_SCAN_RETRY && iter != 0 && + } while (redis_sock->scan & REDIS_SCAN_RETRY && cursor != 0 && num_elements == 0); /* Free our pattern if it was prefixed */ @@ -2849,8 +3117,8 @@ generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, REDIS_SCAN_TYPE type) { /* Free our key if it was prefixed */ if(key_free) efree(key); - /* Update our iterator reference */ - Z_LVAL_P(z_iter) = iter; + /* Update our cursor reference */ + redisSetScanCursor(z_cursor, cursor); } PHP_METHOD(Redis, scan) { @@ -2929,6 +3197,71 @@ PHP_METHOD(Redis, geosearchstore) { REDIS_PROCESS_CMD(geosearchstore, redis_long_response); } +PHP_METHOD(Redis, digest) { + REDIS_PROCESS_KW_CMD("DIGEST", redis_key_cmd, redis_ping_response); +} + +/* + * Vectors + */ + +PHP_METHOD(Redis, vadd) { + REDIS_PROCESS_CMD(vadd, redis_long_response); +} + +PHP_METHOD(Redis, vsim) { + REDIS_PROCESS_CMD(vsim, redis_zrange_response); +} + +PHP_METHOD(Redis, vcard) { + REDIS_PROCESS_KW_CMD("VCARD", redis_key_cmd, redis_long_response); +} + +PHP_METHOD(Redis, vdim) { + REDIS_PROCESS_KW_CMD("VDIM", redis_key_cmd, redis_long_response); +} + +PHP_METHOD(Redis, vinfo) { + REDIS_PROCESS_KW_CMD("VINFO", redis_key_cmd, redis_vinfo_reply); +} + +PHP_METHOD(Redis, vismember) { + REDIS_PROCESS_KW_CMD("VISMEMBER", redis_kv_cmd, redis_1_response); +} + +PHP_METHOD(Redis, vemb) { + REDIS_PROCESS_CMD(vemb, redis_vemb_reply); +} + +PHP_METHOD(Redis, vrandmember) { + REDIS_PROCESS_KW_CMD("VRANDMEMBER", redis_randmember_cmd, + redis_randmember_response); +} + +PHP_METHOD(Redis, vrange) { + REDIS_PROCESS_KW_CMD("VRANGE", redis_vrange_cmd, redis_sock_read_multibulk_reply); +} + +PHP_METHOD(Redis, vrem) { + REDIS_PROCESS_KW_CMD("VREM", redis_kv_cmd, redis_long_response); +} + +PHP_METHOD(Redis, vgetattr) { + REDIS_PROCESS_CMD(vgetattr, redis_vgetattr_reply); +} + +PHP_METHOD(Redis, vsetattr) { + REDIS_PROCESS_CMD(vsetattr, redis_long_response); +} + +PHP_METHOD(Redis, vlinks) { + REDIS_PROCESS_CMD(vlinks, redis_vlinks_reply); +} + +PHP_METHOD(Redis, gcra) { + REDIS_PROCESS_CMD(gcra, redis_read_variant_reply); +} + /* * Streams */ @@ -2953,6 +3286,10 @@ PHP_METHOD(Redis, xdel) { REDIS_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, redis_long_response); } +PHP_METHOD(Redis, xdelex) { + REDIS_PROCESS_CMD(xdelex, redis_read_variant_reply); +} + PHP_METHOD(Redis, xgroup) { REDIS_PROCESS_CMD(xgroup, redis_read_variant_reply); } diff --git a/redis.stub.php b/redis.stub.php index 7739d66cc6..6d30144064 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -8,6 +8,8 @@ class Redis { /** + * Returned by `\Redis::type()` when the key does not exist or has a type + * we are not familiar with. * * @var int * @cvalue REDIS_NOT_FOUND @@ -16,6 +18,7 @@ class Redis { public const REDIS_NOT_FOUND = UNKNOWN; /** + * Returned by `\Redis::type()` when the key is a string. * * @var int * @cvalue REDIS_STRING @@ -24,6 +27,7 @@ class Redis { public const REDIS_STRING = UNKNOWN; /** + * Returned by `\Redis::type()` when the key is a set. * * @var int * @cvalue REDIS_SET @@ -32,6 +36,7 @@ class Redis { public const REDIS_SET = UNKNOWN; /** + * Returned by `\Redis::type()` when the key is a list. * * @var int * @cvalue REDIS_LIST @@ -40,6 +45,7 @@ class Redis { public const REDIS_LIST = UNKNOWN; /** + * Returned by `\Redis::type()` when the key is a sorted set. * * @var int * @cvalue REDIS_ZSET @@ -48,6 +54,7 @@ class Redis { public const REDIS_ZSET = UNKNOWN; /** + * Returned by `\Redis::type()` when the key is a hash. * * @var int * @cvalue REDIS_HASH @@ -56,6 +63,7 @@ class Redis { public const REDIS_HASH = UNKNOWN; /** + * Returned by `\Redis::type()` when the key is a stream. * * @var int * @cvalue REDIS_STREAM @@ -64,6 +72,17 @@ class Redis { public const REDIS_STREAM = UNKNOWN; /** + * Returned by `\Redis::type()` when the key is a vector set. + * + * @var int + * @cvalue REDIS_VECTORSET + * + */ + public const REDIS_VECTORSET = UNKNOWN; + + /** + * Returned from `\Redis::getMode()` when we're not in a multi or pipeline + * transaction. * * @var int * @cvalue ATOMIC @@ -72,6 +91,7 @@ class Redis { public const ATOMIC = UNKNOWN; /** + * Returned from `\Redis::getMode()` when we're in a multi transaction. * * @var int * @cvalue MULTI @@ -80,6 +100,7 @@ class Redis { public const MULTI = UNKNOWN; /** + * Returned from `\Redis::getMode()` when we're in a pipeline transaction. * * @var int * @cvalue PIPELINE @@ -88,6 +109,7 @@ class Redis { public const PIPELINE = UNKNOWN; /** + * Used with `\Redis::setOption()` to specify the serializer to use * * @var int * @cvalue REDIS_OPT_SERIALIZER @@ -96,6 +118,7 @@ class Redis { public const OPT_SERIALIZER = UNKNOWN; /** + * Used to set an automatic prefix for keys used in commands. * * @var int * @cvalue REDIS_OPT_PREFIX @@ -104,6 +127,7 @@ class Redis { public const OPT_PREFIX = UNKNOWN; /** + * Used to set the read timeout for the connection. * * @var int * @cvalue REDIS_OPT_READ_TIMEOUT @@ -112,6 +136,7 @@ class Redis { public const OPT_READ_TIMEOUT = UNKNOWN; /** + * Used to enable or disable TCP keepalive on the connection. * * @var int * @cvalue REDIS_OPT_TCP_KEEPALIVE @@ -120,6 +145,7 @@ class Redis { public const OPT_TCP_KEEPALIVE = UNKNOWN; /** + * Used to set the compression algorithm to use for compressing * * @var int * @cvalue REDIS_OPT_COMPRESSION @@ -128,6 +154,9 @@ class Redis { public const OPT_COMPRESSION = UNKNOWN; /** + * Causes PhpRedis to return the actual string in `+OK` style responses + * from Redis. If disabled those replies are just converted to boolean + * true. * * @var int * @cvalue REDIS_OPT_REPLY_LITERAL @@ -136,6 +165,7 @@ class Redis { public const OPT_REPLY_LITERAL = UNKNOWN; /** + * Used to specify the compression level to use when compressing data. * * @var int * @cvalue REDIS_OPT_COMPRESSION_LEVEL @@ -144,6 +174,8 @@ class Redis { public const OPT_COMPRESSION_LEVEL = UNKNOWN; /** + * Tells PhpRedis to return a NULL multi-bulk (`*-1\r\n`) response + * as `null` as opposed to an empty array. * * @var int * @cvalue REDIS_OPT_NULL_MBULK_AS_NULL @@ -152,6 +184,33 @@ class Redis { public const OPT_NULL_MULTIBULK_AS_NULL = UNKNOWN; /** + * When enabled, this option tells PhpRedis to ignore purely numeric values + * when packing and unpacking data. This does not include numeric strings. + * If you want numeric strings to be ignored, typecast them to an int or + * float. + * + * The primary purpose of this option is to make it more ergonomic when + * setting keys that will later be incremented or decremented. + * + * Note: This option incurs a small performance penalty when reading data + * because we have to see if the data is a string representation of an int + * or float. + * + * @var int + * @cvalue REDIS_OPT_PACK_IGNORE_NUMBERS + * + * @example + * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); + * $redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, true); + * $redis->set('answer', 32); + * + * var_dump($redis->incrBy('answer', 10)); // int(42) + * var_dump($redis->get('answer')); // int(42) + */ + public const OPT_PACK_IGNORE_NUMBERS = UNKNOWN; + + /** + * Sets the serializer to none (no serialization). * * @var int * @cvalue REDIS_SERIALIZER_NONE @@ -160,6 +219,7 @@ class Redis { public const SERIALIZER_NONE = UNKNOWN; /** + * Sets the serializer to PHP's built-in `serialize()`/`unserialize()` * * @var int * @cvalue REDIS_SERIALIZER_PHP @@ -169,6 +229,8 @@ class Redis { #ifdef HAVE_REDIS_IGBINARY /** + * Sets the serializer to igbinary. Note that phpredis must be compiled + * with ighbinary support to use this serializer. * * @var int * @cvalue REDIS_SERIALIZER_IGBINARY @@ -179,6 +241,8 @@ class Redis { #ifdef HAVE_REDIS_MSGPACK /** + * Sets the serializer to msgpack. Note that phpredis must be compiled + * with msgpack support to use this serializer. * * @var int * @cvalue REDIS_SERIALIZER_MSGPACK @@ -188,6 +252,7 @@ class Redis { #endif /** + * Sets the serializer to JSON. * * @var int * @cvalue REDIS_SERIALIZER_JSON @@ -196,6 +261,7 @@ class Redis { public const SERIALIZER_JSON = UNKNOWN; /** + * Disables compression. * * @var int * @cvalue REDIS_COMPRESSION_NONE @@ -205,6 +271,8 @@ class Redis { #ifdef HAVE_REDIS_LZF /** + * Sets the compression algorithm to LZF. PhpRedis must be compiled with + * lzf support but this serializer is bundled with the extension. * * @var int * @cvalue REDIS_COMPRESSION_LZF @@ -215,6 +283,9 @@ class Redis { #ifdef HAVE_REDIS_ZSTD /** + * Sets the compression algorithm to ZSTD. PhpRedis must be compiled with + * zstd support to use this serializer. This is often the best balance + * between speed and compression ratio. * * @var int * @cvalue REDIS_COMPRESSION_ZSTD @@ -224,6 +295,9 @@ class Redis { #ifdef ZSTD_CLEVEL_DEFAULT /** + * This constant represents the "default" compression level for ZSTD. If + * PhpRedis is compiled against a new enough ZSTD the value comes from the + * library, otherwise we just set it to 3. * * @var int * @cvalue ZSTD_CLEVEL_DEFAULT @@ -232,6 +306,9 @@ class Redis { public const COMPRESSION_ZSTD_DEFAULT = UNKNOWN; #else /** + * This constant represents the "default" compression level for ZSTD. If + * PhpRedis is compiled against a new enough ZSTD the value comes from the + * library, otherwise we just set it to 3. * * @var int * @@ -239,26 +316,42 @@ class Redis { public const COMPRESSION_ZSTD_DEFAULT = 3; #endif -#ifdef ZSTD_CLEVEL_MAX +#if ZSTD_VERSION_NUMBER >= 10400 /** + * The minimum compression level ZSTD supports, which comes from the + * underlying ZSTD library if new enough. Otherwise we just set it to 1. * * @var int - * @cvalue ZSTD_CLEVEL_MAX + * @cvalue ZSTD_minCLevel() * */ - public const COMPRESSION_ZSTD_MAX = UNKNOWN; + public const COMPRESSION_ZSTD_MIN = UNKNOWN; +#else + /** + * The minimum compression level ZSTD supports, which comes from the + * underlying ZSTD library if new enough. Otherwise we just set it to 1. + * + * @var int + * + */ + public const COMPRESSION_ZSTD_MIN = 1; #endif /** + * The maximum compression level ZSTD supports, which comes from the + * underlying ZSTD library. + * * @var int * @cvalue ZSTD_maxCLevel() */ public const COMPRESSION_ZSTD_MAX = UNKNOWN; - #endif #ifdef HAVE_REDIS_LZ4 /** + * Set the compression algorithm to LZ4. PhpRedis must be compiled with + * lz4 support to use this serializer. This algorithm is generally + * the fastest but has a lower compression ratio than ZSTD. * * @var int * @cvalue REDIS_COMPRESSION_LZ4 @@ -268,6 +361,7 @@ class Redis { #endif /** + * Used with `\Redis::setOption()` to specify scan options. * * @var int * @cvalue REDIS_OPT_SCAN @@ -276,6 +370,9 @@ class Redis { public const OPT_SCAN = UNKNOWN; /** + * When enabled, this option causes PhpRedis to automatically retry `SCAN` + * commands when Redis returns a non-zero cursor but no keys. This can + * happen due to the nature of Redis' scanning algorithm. * * @var int * @cvalue REDIS_SCAN_RETRY @@ -284,6 +381,9 @@ class Redis { public const SCAN_RETRY = UNKNOWN; /** + * Then enabled, this option tells PhpRedis to not retry `SCAN` commands + * when Redis returns a non-zero cursor but no keys. This means that your + * code must handle this case itself. * * @var int * @cvalue REDIS_SCAN_NORETRY @@ -292,6 +392,8 @@ class Redis { public const SCAN_NORETRY = UNKNOWN; /** + * Tells PhpRedis to prefix keys returned from `SCAN` commands with the + * currently set key prefix. * * @var int * @cvalue REDIS_SCAN_PREFIX @@ -300,6 +402,8 @@ class Redis { public const SCAN_PREFIX = UNKNOWN; /** + * Tells PhpRedis to NOT prefix keys returned from `SCAN` commands with + * the currently set key prefix. * * @var int * @cvalue REDIS_SCAN_NOPREFIX @@ -308,6 +412,8 @@ class Redis { public const SCAN_NOPREFIX = UNKNOWN; /** + * This is just the string "before" which is used with various list + * commands to indicate an insertion point. * * @var string * @@ -315,6 +421,8 @@ class Redis { public const BEFORE = "before"; /** + * This is just the string "after" which is used with various list commands + * to indicate an insertion point. * * @var string * @@ -322,6 +430,8 @@ class Redis { public const AFTER = "after"; /** + * This is just the string "left" which is used with various list commands + * such as `LMOVE`. * * @var string * @@ -329,6 +439,8 @@ class Redis { public const LEFT = "left"; /** + * This is just the string "right" which is used with various list commands + * such as `LMOVE`. * * @var string * @@ -336,6 +448,8 @@ class Redis { public const RIGHT = "right"; /** + * How many times should `PhpRedis` attempt to reconnect when we are + * disconnected. * * @var int * @cvalue REDIS_OPT_MAX_RETRIES @@ -344,6 +458,7 @@ class Redis { public const OPT_MAX_RETRIES = UNKNOWN; /** + * Used to specify the backoff algorithm to use when reconnecting. * * @var int * @cvalue REDIS_OPT_BACKOFF_ALGORITHM @@ -352,6 +467,7 @@ class Redis { public const OPT_BACKOFF_ALGORITHM = UNKNOWN; /** + * Default backoff - random delay before the first retry, then constant `base` ms. * * @var int * @cvalue REDIS_BACKOFF_ALGORITHM_DEFAULT @@ -360,6 +476,7 @@ class Redis { public const BACKOFF_ALGORITHM_DEFAULT = UNKNOWN; /** + * Constant backoff - always sleep for exactly `base` ms (capped by `cap`). * * @var int * @cvalue REDIS_BACKOFF_ALGORITHM_CONSTANT @@ -368,6 +485,7 @@ class Redis { public const BACKOFF_ALGORITHM_CONSTANT = UNKNOWN; /** + * Uniform backoff - randomly sleep between 0 and `base` ms for each retry. * * @var int * @cvalue REDIS_BACKOFF_ALGORITHM_UNIFORM @@ -376,6 +494,7 @@ class Redis { public const BACKOFF_ALGORITHM_UNIFORM = UNKNOWN; /** + * Exponential backoff - doubles the delay every retry (up to 2^10) before `cap`. * * @var int * @cvalue REDIS_BACKOFF_ALGORITHM_EXPONENTIAL @@ -384,6 +503,7 @@ class Redis { public const BACKOFF_ALGORITHM_EXPONENTIAL = UNKNOWN; /** + * Full jitter - exponential delay but pick a random value between 0 and that delay. * * @var int * @cvalue REDIS_BACKOFF_ALGORITHM_FULL_JITTER @@ -392,6 +512,7 @@ class Redis { public const BACKOFF_ALGORITHM_FULL_JITTER = UNKNOWN; /** + * Equal jitter - half the exponential delay plus a random amount up to the other half. * * @var int * @cvalue REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER @@ -400,6 +521,7 @@ class Redis { public const BACKOFF_ALGORITHM_EQUAL_JITTER = UNKNOWN; /** + * Decorrelated jitter - pick a random delay between `base` and 3x the previous delay. * * @var int * @cvalue REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER @@ -408,6 +530,7 @@ class Redis { public const BACKOFF_ALGORITHM_DECORRELATED_JITTER = UNKNOWN; /** + * Backoff base - minimum delay in milliseconds that algorithms start from. * * @var int * @cvalue REDIS_OPT_BACKOFF_BASE @@ -416,6 +539,7 @@ class Redis { public const OPT_BACKOFF_BASE = UNKNOWN; /** + * Backoff cap - maximum delay in milliseconds that any algorithm can reach. * * @var int * @cvalue REDIS_OPT_BACKOFF_CAP @@ -430,42 +554,44 @@ class Redis { * * **NOTE**: Below is an example options array with various setting * - * $options = [ - * 'host' => 'localhost', - * 'port' => 6379, - * 'readTimeout' => 2.5, - * 'connectTimeout' => 2.5, - * 'persistent' => true, - * - * // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass'] - * 'auth' => ['phpredis', 'phpredis'], - * - * // See PHP stream options for valid SSL configuration settings. - * 'ssl' => ['verify_peer' => false], - * - * // How quickly to retry a connection after we time out or it closes. - * // Note that this setting is overridden by 'backoff' strategies. - * 'retryInterval' => 100, - * - * // Which backoff algorithm to use. 'decorrelated jitter' is - * // likely the best one for most solution, but there are many - * // to choose from: - * // REDIS_BACKOFF_ALGORITHM_DEFAULT - * // REDIS_BACKOFF_ALGORITHM_CONSTANT - * // REDIS_BACKOFF_ALGORITHM_UNIFORM - * // REDIS_BACKOFF_ALGORITHM_EXPONENTIAL - * // REDIS_BACKOFF_ALGORITHM_FULL_JITTER - * // REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER - * // REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER - * // 'base', and 'cap' are in milliseconds and represent the first - * // delay redis will use when reconnecting, and the maximum delay - * // we will reach while retrying. - * 'backoff' => [ - * 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, - * 'base' => 500, - * 'cap' => 750, - * ] - * ]; + *```php + *$options = [ + * 'host' => 'localhost', + * 'port' => 6379, + * 'readTimeout' => 2.5, + * 'connectTimeout' => 2.5, + * 'persistent' => true, + * + * // Valid formats: NULL, ['user', 'pass'], 'pass', or ['pass'] + * 'auth' => ['phpredis', 'phpredis'], + * + * // See PHP stream options for valid SSL configuration settings. + * 'ssl' => ['verify_peer' => false], + * + * // How quickly to retry a connection after we time out or it closes. + * // Note that this setting is overridden by 'backoff' strategies. + * 'retryInterval' => 100, + * + * // Which backoff algorithm to use. 'decorrelated jitter' is + * // likely the best one for most solution, but there are many + * // to choose from: + * // REDIS_BACKOFF_ALGORITHM_DEFAULT + * // REDIS_BACKOFF_ALGORITHM_CONSTANT + * // REDIS_BACKOFF_ALGORITHM_UNIFORM + * // REDIS_BACKOFF_ALGORITHM_EXPONENTIAL + * // REDIS_BACKOFF_ALGORITHM_FULL_JITTER + * // REDIS_BACKOFF_ALGORITHM_EQUAL_JITTER + * // REDIS_BACKOFF_ALGORITHM_DECORRELATED_JITTER + * // 'base', and 'cap' are in milliseconds and represent the first + * // delay redis will use when reconnecting, and the maximum delay + * // we will reach while retrying. + * 'backoff' => [ + * 'algorithm' => Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER, + * 'base' => 500, + * 'cap' => 750, + * ] + *]; + *``` * * Note: If you do wish to connect via the constructor, only 'host' is * strictly required, which will cause PhpRedis to connect to that @@ -474,35 +600,52 @@ class Redis { * * @see Redis::connect() * @see https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ - * @param array $options + * @param array|null $options * * @return Redis + * + * @example + * $redis = new Redis(['host' => '127.0.0.1', 'port' => 6380]); + * */ - public function __construct(array $options = null); + public function __construct(?array $options = null); + /** + * Destructor to clean up the Redis object. + * + * This method will disconnect from Redis. If the connection is persistento + * it will be stashed for future reuse. + * + */ public function __destruct(); /** - * Compress a value with the currently configured compressor as set with - * Redis::setOption(). + * Compress a value with the currently configured compressor (Redis::OPT_COMPRESSION) + * exactly the same way PhpRedis does before sending data to Redis. * * @see Redis::setOption() * * @param string $value The value to be compressed - * @return string The compressed result + * @return string The compressed result (or the original value if compression is disabled) + * + * @example + * $redis->_compress('payload'); * */ public function _compress(string $value): string; /** - * Uncompress the provided argument that has been compressed with the - * currently configured compressor as set with Redis::setOption(). + * Uncompress the provided argument using the compressor configured via + * Redis::setOption() (Redis::OPT_COMPRESSION). * * @see Redis::setOption() * * @param string $value The compressed value to uncompress. * @return string The uncompressed result. * + * @example + * $redis->_uncompress($redis->_compress('payload')); + * */ public function _uncompress(string $value): string; @@ -513,6 +656,9 @@ public function _uncompress(string $value): string; * @param string $key The key/string to prefix * @return string The prefixed string * + * @example + * $redis->_prefix('user:42'); + * */ public function _prefix(string $key): string; @@ -525,6 +671,9 @@ public function _prefix(string $key): string; * @param mixed $value The value to serialize * @return string The serialized result * + * @example + * $redis->_serialize(['answer' => 42]); + * */ public function _serialize(mixed $value): string; @@ -537,29 +686,68 @@ public function _serialize(mixed $value): string; * @param string $value The value to unserialize * @return mixed The unserialized result * + * @example + * $redis->_unserialize($redis->_serialize(['answer' => 42])); + * */ public function _unserialize(string $value): mixed; /** - * Pack the provided value with the configured serializer and compressor - * as set with Redis::setOption(). + * Pack the provided value by first serializing it (if Redis::OPT_SERIALIZER is set) + * and then compressing the serialized payload (if Redis::OPT_COMPRESSION is set), + * mirroring exactly what PhpRedis transmits to Redis. * * @param mixed $value The value to pack * @return string The packed result having been serialized and * compressed. + * + * @example + * $redis->_pack(['count' => 5]); + * */ public function _pack(mixed $value): string; /** - * Unpack the provided value with the configured compressor and serializer - * as set with Redis::setOption(). + * Compute the XXH3 digest of a PHP value after it has been `_pack`ed, producing + * the same digest Redis' DIGEST command would return for the stored value. + * + * @param mixed $value The value to compute the digest for. + * @return string The computed digest. + * + * @throws RedisException If XXH3 is not supported. + * + * @note This function requires PHP >= 8.1 which is the version PHP + * added support for XXH3 hashing and made the hash extension + * mandatory. + * + * @example + * $redis->_digest(['token' => 'abc']); + * + */ + public function _digest(mixed $value): string; + + /** + * Unpack the provided value by first uncompressing it (if Redis::OPT_COMPRESSION + * is set) and then unserializing it (if Redis::OPT_SERIALIZER is set) to recover + * the original PHP value. * - * @param string $value The value which has been serialized and compressed. - * @return mixed The uncompressed and eserialized value. + * @param string $value The value which has been serialized and compressed. + * @return mixed The uncompressed and deserialized value. + * + * @example + * $redis->_unpack($redis->_pack(['count' => 5])); * */ public function _unpack(string $value): mixed; + /** + * Execute Redis ACL subcommands. + * + * @see https://redis.io/docs/latest/commands/acl/ + * + * @example + * $redis->acl('list'); + */ public function acl(string $subcmd, string ...$args): mixed; /** @@ -570,7 +758,7 @@ public function acl(string $subcmd, string ...$args): mixed; * * @return Redis|int|false The new string length of the key or false on failure. * - * @see https://redis.io/commands/append + * @see https://redis.io/docs/latest/commands/append/ * * @example * $redis->set('foo', 'hello); @@ -585,36 +773,58 @@ public function append(string $key, mixed $value): Redis|int|false; * $redis->auth(['password']); * $redis->auth(['username', 'password']); * - * @see https://redis.io/commands/auth + * @see https://redis.io/docs/latest/commands/auth/ * * @param mixed $credentials A string password, or an array with one or two string elements. * @return Redis|bool Whether the AUTH was successful. * + * @example + * $redis->auth('secret'); + * */ public function auth(#[\SensitiveParameter] mixed $credentials): Redis|bool; /** * Execute a save of the Redis database in the background. * - * @see https://redis.io/commands/bgsave + * @see https://redis.io/docs/latest/commands/bgsave/ * * @return Redis|bool Whether the command was successful. + * + * @example + * $redis->bgSave(); + * */ public function bgSave(): Redis|bool; /** * Asynchronously rewrite Redis' append-only file * - * @see https://redis.io/commands/bgrewriteaof + * @see https://redis.io/docs/latest/commands/bgrewriteaof/ * * @return Redis|bool Whether the command was successful. + * + * @example + * $redis->bgrewriteaof(); + * */ public function bgrewriteaof(): Redis|bool; + /** + * @see https://redis.io/docs/latest/commands/waitaof/ + * + * @return Redis|array|false + * + * @example + * $redis->waitaof(1, 1, 5000); + * + */ + public function waitaof(int $numlocal, int $numreplicas, int $timeout): Redis|array|false; + /** * Count the number of set bits in a Redis string. * - * @see https://redis.io/commands/bitcount/ + * @see https://redis.io/docs/latest/commands/bitcount/ * * @param string $key The key in question (must be a string key) * @param int $start The index where Redis should start counting. If omitted it @@ -627,6 +837,9 @@ public function bgrewriteaof(): Redis|bool; * * @return Redis|int|false The number of bits set in the requested range. * + * @example + * $redis->bitcount('bitmap', 0, -1); + * */ public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false): Redis|int|false; @@ -635,7 +848,7 @@ public function bitop(string $operation, string $deskey, string $srckey, string /** * Return the position of the first bit set to 0 or 1 in a string. * - * @see https://redis.io/commands/bitpos/ + * @see https://redis.io/docs/latest/commands/bitpos/ * * @param string $key The key to check (must be a string) * @param bool $bit Whether to look for an unset (0) or set (1) bit. @@ -645,6 +858,10 @@ public function bitop(string $operation, string $deskey, string $srckey, string * was 0 and end was 2, Redis would only search the first two bits. * * @return Redis|int|false The position of the first set or unset bit. + * + * @example + * $redis->bitpos('bitmap', true, 0, -1); + * **/ public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false): Redis|int|false; @@ -652,7 +869,7 @@ public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bo * Pop an element off the beginning of a Redis list or lists, potentially blocking up to a specified * timeout. This method may be called in two distinct ways, of which examples are provided below. * - * @see https://redis.io/commands/blpop/ + * @see https://redis.io/docs/latest/commands/blpop/ * * @param string|array $key_or_keys This can either be a string key or an array of one or more * keys. @@ -672,9 +889,12 @@ public function blPop(string|array $key_or_keys, string|float|int $timeout_or_ke * Pop an element off of the end of a Redis list or lists, potentially blocking up to a specified timeout. * The calling convention is identical to Redis::blPop() so see that documentation for more details. * - * @see https://redis.io/commands/brpop/ + * @see https://redis.io/docs/latest/commands/brpop/ * @see Redis::blPop() * + * @example + * $redis->brPop(['queue:critical', 'queue:default'], 5); + * */ public function brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args): Redis|array|null|false; @@ -682,13 +902,16 @@ public function brPop(string|array $key_or_keys, string|float|int $timeout_or_ke * Pop an element from the end of a Redis list, pushing it to the beginning of another Redis list, * optionally blocking up to a specified timeout. * - * @see https://redis.io/commands/brpoplpush/ + * @see https://redis.io/docs/latest/commands/brpoplpush/ * * @param string $src The source list * @param string $dst The destination list * @param int|float $timeout The number of seconds to wait. Note that you must be connected * to Redis >= 6.0.0 to send a floating point timeout. * + * @example + * $redis->brpoplpush('queue:pending', 'queue:processing', 5); + * */ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis|string|false; @@ -698,16 +921,16 @@ public function brpoplpush(string $src, string $dst, int|float $timeout): Redis| * * Following are examples of the two main ways to call this method. * - * **NOTE**: We reccomend calling this function with an array and a timeout as the other strategy + * **NOTE**: We recommend calling this function with an array and a timeout as the other strategy * may be deprecated in future versions of PhpRedis * - * @see https://redis.io/commands/bzpopmax + * @see https://redis.io/docs/latest/commands/bzpopmax/ * - * @param string|array $key_or_keys Either a string key or an array of one or more keys. - * @param string|int $timeout_or_key If the previous argument was an array, this argument + * @param string|array $key Either a string key or an array of one or more keys. + * @param string|int $timeout_or_key If the previous argument was an array, this argument * must be a timeout value. Otherwise it could also be * another key. - * @param mixed $extra_args Can consist of additional keys, until the last argument + * @param mixed ...$extra_args Can consist of additional keys, until the last argument * which needs to be a timeout. * * @return Redis|array|false The popped elements. @@ -724,9 +947,12 @@ public function bzPopMax(string|array $key, string|int $timeout_or_key, mixed .. * * This command is identical in semantics to bzPopMax so please see that method for more information. * - * @see https://redis.io/commands/bzpopmin + * @see https://redis.io/docs/latest/commands/bzpopmin/ * @see Redis::bzPopMax() * + * @example + * $redis->bzPopMin(['scores:high', 'scores:low'], 1.5); + * */ public function bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): Redis|array|false; @@ -746,13 +972,19 @@ public function bzPopMin(string|array $key, string|int $timeout_or_key, mixed .. * * NOTE: If Redis::OPT_NULL_MULTIBULK_AS_NULL is set to true via Redis::setOption(), this method will * instead return NULL when Redis doesn't pop any elements. + * + * @see https://redis.io/docs/latest/commands/bzmpop/ + * + * @example + * $redis->bzmpop(1.5, ['scores:high', 'scores:low'], 'MIN', 2); + * */ public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false; /** * POP one or more of the highest or lowest scoring elements from one or more sorted sets. * - * @see https://redis.io/commands/zmpop + * @see https://redis.io/docs/latest/commands/zmpop/ * * @param array $keys One or more sorted sets * @param string $from The string 'MIN' or 'MAX' (case insensitive) telling Redis whether you want to @@ -760,6 +992,10 @@ public function bzmpop(float $timeout, array $keys, string $from, int $count = 1 * @param int $count Pop up to how many elements at once. * * @return Redis|array|null|false An array of popped elements or false if none could be popped. + * + * @example + * $redis->zmpop(['scores:high', 'scores:low'], 'MAX', 2); + * */ public function zmpop(array $keys, string $from, int $count = 1): Redis|array|null|false; @@ -767,7 +1003,7 @@ public function zmpop(array $keys, string $from, int $count = 1): Redis|array|nu * Pop one or more elements from one or more Redis LISTs, blocking up to a specified timeout when * no elements are available. * - * @see https://redis.io/commands/blmpop + * @see https://redis.io/docs/latest/commands/blmpop/ * * @param float $timeout The number of seconds Redis will block when no elements are available. * @param array $keys One or more Redis LISTs to pop from. @@ -777,13 +1013,17 @@ public function zmpop(array $keys, string $from, int $count = 1): Redis|array|nu * * @return Redis|array|null|false One or more elements popped from the list(s) or false if all LISTs * were empty. + * + * @example + * $redis->blmpop(1.5, ['queue:critical', 'queue:default'], 'LEFT', 2); + * */ public function blmpop(float $timeout, array $keys, string $from, int $count = 1): Redis|array|null|false; /** * Pop one or more elements off of one or more Redis LISTs. * - * @see https://redis.io/commands/lmpop + * @see https://redis.io/docs/latest/commands/lmpop/ * * @param array $keys An array with one or more Redis LIST key names. * @param string $from The string 'LEFT' or 'RIGHT' (case insensitive), telling Redis whether to pop\ @@ -793,6 +1033,9 @@ public function blmpop(float $timeout, array $keys, string $from, int $count = 1 * @return Redis|array|null|false One or more elements popped from the LIST(s) or false if all the LISTs * were empty. * + * @example + * $redis->lmpop(['queue:critical', 'queue:default'], 'RIGHT', 2); + * */ public function lmpop(array $keys, string $from, int $count = 1): Redis|array|null|false; @@ -812,11 +1055,50 @@ public function lmpop(array $keys, string $from, int $count = 1): Redis|array|nu */ public function clearLastError(): bool; + /** + * Execute Redis CLIENT subcommands. + * + * @param string $opt The CLIENT subcommand to execute. + * @param mixed ...$args Additional arguments depending on the subcommand. + * + * @see https://redis.io/docs/latest/commands/client/ + * + * @example + * $redis->client('list'); + */ public function client(string $opt, mixed ...$args): mixed; + /** + * Closes the connection to Redis + * + * This function will close the connection whether it is persistent or not. + * + * @return bool Whether the connection was successfully closed. + * + * @example + * $redis = new Redis; + * $redis->pconnect('localhost', 6379); + * $id1 = $redis->client('id'); + * $redis->close(); + * + * $redis = new Redis; + * $redis->pconnect('localhost', 6379); + * $id2 = $redis->client('id'); + * + * // Will print "id is different" + * printf("ID is %s\n", $id1 == $id2 ? 'the same' : 'different'); + */ public function close(): bool; - public function command(string $opt = null, mixed ...$args): mixed; + /** + * Execute Redis COMMAND subcommands. + * + * @see https://redis.io/docs/latest/commands/command/ + * + * @example + * $redis->command('command'); + */ + public function command(?string $opt = null, mixed ...$args): mixed; /** * Execute the Redis CONFIG command in a variety of ways. @@ -824,10 +1106,10 @@ public function command(string $opt = null, mixed ...$args): mixed; * What the command does in particular depends on the `$operation` qualifier. * Operations that PhpRedis supports are: RESETSTAT, REWRITE, GET, and SET. * - * @param string $operation The CONFIG operation to execute (e.g. GET, SET, REWRITE). + * @param string $operation The CONFIG operation to execute (e.g. GET, SET, REWRITE). * @param array|string|null $key_or_settings One or more keys or values. - * @param string $value The value if this is a `CONFIG SET` operation. - * @see https://redis.io/commands/config + * @param string|null $value The value if this is a `CONFIG SET` operation. + * @see https://redis.io/docs/latest/commands/config/ * * @example * $redis->config('GET', 'timeout'); @@ -835,29 +1117,62 @@ public function command(string $opt = null, mixed ...$args): mixed; * $redis->config('SET', 'timeout', 30); * $redis->config('SET', ['timeout' => 30, 'loglevel' => 'warning']); */ - public function config(string $operation, array|string|null $key_or_settings = NULL, ?string $value = NULL): mixed; + public function config(string $operation, array|string|null $key_or_settings = null, ?string $value = null): mixed; - public function connect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = null, - int $retry_interval = 0, float $read_timeout = 0, array $context = null): bool; + /** + * Connect to a Redis server + * + * @param string $host The Redis server hostname or IP + * address. + * @param int $port The Redis server port. Defaults to + * 6379. + * @param float $timeout The connection timeout in seconds. + * Defaults to 0 (no timeout). + * @param string|null $persistent_id If set, a persistent connection will + * be made with this ID. + * @param int $retry_interval The number of milliseconds to wait + * between connection attempts. + * @param float $read_timeout The read timeout in seconds. + * Defaults to 0 (no timeout). + * @param array|null $context An optional stream context to use + * when connecting. + * See `\Redis::__construct()` for more + * details. + * @return bool Whether the connection was successful. + * + * @throws RedisException On connection errors. + * + * @example + * $redis = new \Redis; + * try { + * $redis->connect('localhost', 6379, 2.5, null, 100, 2.5); + * $foo = $redis->get('foo'); + * printf("foo: %s\n", $foo); + * } catch (Exception $ex) { + * fprintf(STDERR, "Error: {$ex->getMessage()}\n"); + * } + */ + public function connect(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, + int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Make a copy of a key. * * $redis = new Redis(['host' => 'localhost']); * - * @param string $src The key to copy - * @param string $dst The name of the new key created from the source key. - * @param array $options An array with modifiers on how COPY should operate. - * - * $options = [ - * 'REPLACE' => true|false # Whether to replace an existing key. - * 'DB' => int # Copy key to specific db. - * ]; - * + * @param string $src The key to copy + * @param string $dst The name of the new key created from the source key. + * @param array|null $options An array with modifiers on how COPY should operate. + * ```php + * $options = [ + * 'REPLACE' => true|false # Whether to replace an existing key. + * 'DB' => int # Copy key to specific db. + * ]; + * ``` * * @return Redis|bool True if the copy was completed and false if not. * - * @see https://redis.io/commands/copy + * @see https://redis.io/docs/latest/commands/copy/ * * @example * $redis->pipeline() @@ -873,14 +1188,14 @@ public function connect(string $host, int $port = 6379, float $timeout = 0, stri * var_dump($redis->copy('source1', 'exists')); * var_dump($redis->copy('source1', 'exists', ['REPLACE' => true])); */ - public function copy(string $src, string $dst, array $options = null): Redis|bool; + public function copy(string $src, string $dst, ?array $options = null): Redis|bool; /** * Return the number of keys in the currently selected Redis database. * - * @see https://redis.io/commands/dbsize + * @see https://redis.io/docs/latest/commands/dbsize/ * - * @return Redis|int The number of keys or false on failure. + * @return Redis|int|false The number of keys or false on failure. * * @example * $redis = new Redis(['host' => 'localhost']); @@ -892,6 +1207,18 @@ public function copy(string $src, string $dst, array $options = null): Redis|boo */ public function dbSize(): Redis|int|false; + /** + * Execute the Redis `DEBUG` command. Note that this is disabled by default + * and can be very dangerous, even allowing you to crash the server. Use + * with caution + * + * @note The command has greatly increased in complexity since it was first + * added to PhpRedis, so you may need to use it via `Redis::rawCommand()` + * for certain subcommands. + * + * @param string $key The DEBUG subcommand to execute. + * @return Redis|string The result of the DEBUG command. + */ public function debug(string $key): Redis|string; /** @@ -905,8 +1232,8 @@ public function debug(string $key): Redis|string; * * @return Redis|int|false The new value of the key or false on failure. * - * @see https://redis.io/commands/decr - * @see https://redis.io/commands/decrby + * @see https://redis.io/docs/latest/commands/decr/ + * @see https://redis.io/docs/latest/commands/decrby/ * * @example $redis->decr('counter'); * @example $redis->decr('counter', 2); @@ -921,7 +1248,7 @@ public function decr(string $key, int $by = 1): Redis|int|false; * * @return Redis|int|false The new value of the key or false on failure. * - * @see https://redis.io/commands/decrby + * @see https://redis.io/docs/latest/commands/decrby/ * * @example $redis->decrby('counter', 1); * @example $redis->decrby('counter', 2); @@ -935,22 +1262,60 @@ public function decrBy(string $key, int $value): Redis|int|false; * of keys to delete, and the second is to pass N arguments, all names of keys. See * below for an example of both strategies. * - * @param array|string $key_or_keys Either an array with one or more key names or a string with - * the name of a key. - * @param string $other_keys One or more additional keys passed in a variadic fashion. + * @param array|string $key Either an array with one or more key names or a string with + * the name of a key. + * @param string ...$other_keys One or more additional keys passed in a variadic fashion. * * @return Redis|int|false The number of keys that were deleted * - * @see https://redis.io/commands/del + * @see https://redis.io/docs/latest/commands/del/ * * @example $redis->del('key:0', 'key:1'); * @example $redis->del(['key:2', 'key:3', 'key:4']); */ public function del(array|string $key, string ...$other_keys): Redis|int|false; + /** + * Delete a key conditionally based on its value or hash digest + * + * @param string $key The key to delete + * @param array|null $options An array with options to modify how DELX works. + * + * @return Redis|int|false Returns 1 if the key was deleted, 0 if it was not. + * + * @see https://redis.io/docs/latest/commands/delex/ + * + * @example + * $redis->delex('session:42'); + * + */ + public function delex(string $key, ?array $options = null): Redis|int|false; + + /** + * Delete a key if it's equal to the specified value. This command is + * specific to Valkey >= 9.0 + * + * @param string $key The key to delete + * @param mixed $value The value to compare against the key's value. + * @return Redis|int|false Returns 1 if the key was deleted, 0 if it was not. + * + * @see https://valkey.io/commands/delifeq/ + * + * @example + * $redis->delifeq('session:42', 'token'); + * + */ + public function delifeq(string $key, mixed $value): Redis|int|false; + /** * @deprecated * @alias Redis::del + * + * @see https://redis.io/docs/latest/commands/del/ + * + * @example + * $redis->delete('legacy:key'); + * */ public function delete(array|string $key, string ...$other_keys): Redis|int|false; @@ -959,6 +1324,8 @@ public function delete(array|string $key, string ...$other_keys): Redis|int|fals * * @return Redis|bool True if we could discard the transaction. * + * @see https://redis.io/docs/latest/commands/discard/ + * * @example * $redis->getMode(); * $redis->set('foo', 'bar'); @@ -970,21 +1337,18 @@ public function discard(): Redis|bool; /** * Dump Redis' internal binary representation of a key. * - * $redis->zRange('new-zset', 0, -1, true); - *
    - * * @param string $key The key to dump. * - * @return Redis|string A binary string representing the key's value. + * @return Redis|string|false A binary string representing the key's value. * - * @see https://redis.io/commands/dump + * @see https://redis.io/docs/latest/commands/dump/ * * @example * $redis->zadd('zset', 0, 'zero', 1, 'one', 2, 'two'); * $binary = $redis->dump('zset'); * $redis->restore('new-zset', 0, $binary); */ - public function dump(string $key): Redis|string; + public function dump(string $key): Redis|string|false; /** * Have Redis repeat back an arbitrary string to the client. @@ -993,7 +1357,7 @@ public function dump(string $key): Redis|string; * * @return Redis|string|false The string sent to Redis or false on failure. * - * @see https://redis.io/commands/echo + * @see https://redis.io/docs/latest/commands/echo/ * * @example $redis->echo('Hello, World'); */ @@ -1002,7 +1366,7 @@ public function echo(string $str): Redis|string|false; /** * Execute a LUA script on the redis server. * - * @see https://redis.io/commands/eval/ + * @see https://redis.io/docs/latest/commands/eval/ * * @param string $script A string containing the LUA script * @param array $args An array of arguments to pass to this script @@ -1012,6 +1376,10 @@ public function echo(string $str): Redis|string|false; * * @return mixed LUA scripts may return arbitrary data so this method can return * strings, arrays, nested arrays, etc. + * + * @example + * $redis->eval('return redis.call("set", KEYS[1], ARGV[1])', ['counter', 1], 1); + * */ public function eval(string $script, array $args = [], int $num_keys = 0): mixed; @@ -1020,6 +1388,11 @@ public function eval(string $script, array $args = [], int $num_keys = 0): mixed * may not modify data in redis. * * @see Redis::eval_ro() + * @see https://redis.io/docs/latest/commands/eval_ro/ + * + * @example + * $redis->eval_ro('return redis.call("get", KEYS[1])', ['counter'], 1); + * */ public function eval_ro(string $script_sha, array $args = [], int $num_keys = 0): mixed; @@ -1027,18 +1400,22 @@ public function eval_ro(string $script_sha, array $args = [], int $num_keys = 0) * Execute a LUA script on the server but instead of sending the script, send * the SHA1 hash of the script. * - * @param string $script_sha The SHA1 hash of the lua code. Note that the script - * must already exist on the server, either having been - * loaded with `SCRIPT LOAD` or having been executed directly - * with `EVAL` first. - * @param array $args Arguments to send to the script. - * @param int $num_keys The number of arguments that are keys + * @param string $sha1 The SHA1 hash of the lua code. Note that the script + * must already exist on the server, either having been + * loaded with `SCRIPT LOAD` or having been executed directly + * with `EVAL` first. + * @param array $args Arguments to send to the script. + * @param int $num_keys The number of arguments that are keys * * @return mixed Returns whatever the specific script does. * - * @see https://redis.io/commands/evalsha/ + * @see https://redis.io/docs/latest/commands/evalsha/ * @see Redis::eval(); * + * @example + * $sha = $redis->script('load', 'return redis.call("incr", KEYS[1])'); + * $redis->evalsha($sha, ['counter'], 1); + * */ public function evalsha(string $sha1, array $args = [], int $num_keys = 0): mixed; @@ -1047,6 +1424,12 @@ public function evalsha(string $sha1, array $args = [], int $num_keys = 0): mixe * may not modify data in redis. * * @see Redis::evalsha() + * @see https://redis.io/docs/latest/commands/evalsha_ro/ + * + * @example + * $sha = $redis->script('load', 'return redis.call("get", KEYS[1])'); + * $redis->evalsha_ro($sha, ['counter'], 1); + * */ public function evalsha_ro(string $sha1, array $args = [], int $num_keys = 0): mixed; @@ -1055,8 +1438,8 @@ public function evalsha_ro(string $sha1, array $args = [], int $num_keys = 0): m * * @return Redis|array|false The array of pipeline'd or multi replies or false on failure. * - * @see https://redis.io/commands/exec - * @see https://redis.io/commands/multi + * @see https://redis.io/docs/latest/commands/exec/ + * @see https://redis.io/docs/latest/commands/multi/ * @see Redis::pipeline() * @see Redis::multi() * @@ -1073,13 +1456,13 @@ public function exec(): Redis|array|false; /** * Test if one or more keys exist. * - * @param mixed $key Either an array of keys or a string key - * @param mixed $other_keys If the previous argument was a string, you may send any number of - * additional keys to test. + * @param mixed $key Either an array of keys or a string key + * @param mixed ...$other_keys If the previous argument was a string, you may send any number of + * additional keys to test. * * @return Redis|int|bool The number of keys that do exist and false on failure * - * @see https://redis.io/commands/exists + * @see https://redis.io/docs/latest/commands/exists/ * * @example $redis->exists(['k1', 'k2', 'k3']); * @example $redis->exists('k4', 'k5', 'notakey'); @@ -1091,8 +1474,9 @@ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; * redis-server >= 7.0.0 you may send an additional "mode" argument which * modifies how the command will execute. * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the + * @param string $key The key to set an expiration on. + * @param int $timeout The number of seconds after which key will be automatically deleted. + * @param string|null $mode A two character modifier that changes how the * command works. * * NX - Set expiry only if key has no expiry @@ -1102,10 +1486,13 @@ public function exists(mixed $key, mixed ...$other_keys): Redis|int|bool; * * * @return Redis|bool True if an expiration was set and false otherwise. - * @see https://redis.io/commands/expire + * @see https://redis.io/docs/latest/commands/expire/ + * + * @example + * $redis->expire('session:42', 60); * */ - public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|bool; + public function expire(string $key, int $timeout, ?string $mode = null): Redis|bool; /* * Set a key's expiration to a specific Unix timestamp in seconds. @@ -1122,16 +1509,20 @@ public function expire(string $key, int $timeout, ?string $mode = NULL): Redis|b /** * Set a key to expire at an exact unix timestamp. * - * @param string $key The key to set an expiration on. - * @param int $timestamp The unix timestamp to expire at. - * @param string $mode An option 'mode' that modifies how the command acts (see {@link Redis::expire}). + * @param string $key The key to set an expiration on. + * @param int $timestamp The unix timestamp to expire at. + * @param string|null $mode An option 'mode' that modifies how the command acts (see {@link Redis::expire}). * @return Redis|bool True if an expiration was set, false if not. * - * @see https://redis.io/commands/expireat - * @see https://redis.io/commands/expire + * @see https://redis.io/docs/latest/commands/expireat/ + * @see https://redis.io/docs/latest/commands/expire/ * @see Redis::expire() + * + * @example + * $redis->expireAt('session:42', time() + 300); + * */ - public function expireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool; + public function expireAt(string $key, int $timestamp, ?string $mode = null): Redis|bool; public function failover(?array $to = null, bool $abort = false, int $timeout = 0): Redis|bool; @@ -1143,7 +1534,7 @@ public function failover(?array $to = null, bool $abort = false, int $timeout = * @return Redis|int|false The timestamp when the key expires, or -1 if the key has no expiry * and -2 if the key doesn't exist. * - * @see https://redis.io/commands/expiretime + * @see https://redis.io/docs/latest/commands/expiretime/ * * @example * $redis->setEx('mykey', 60, 'myval'); @@ -1152,15 +1543,19 @@ public function failover(?array $to = null, bool $abort = false, int $timeout = public function expiretime(string $key): Redis|int|false; /** - * Get the expriation timestamp of a given Redis key but in milliseconds. + * Get the expiration timestamp of a given Redis key but in milliseconds. * - * @see https://redis.io/commands/pexpiretime + * @see https://redis.io/docs/latest/commands/pexpiretime/ * @see Redis::expiretime() * * @param string $key The key to check * * @return Redis|int|false The expiration timestamp of this key (in milliseconds) or -1 if the * key has no expiration, and -2 if it does not exist. + * + * @example + * $redis->pexpiretime('session:42'); + * */ public function pexpiretime(string $key): Redis|int|false; @@ -1174,7 +1569,11 @@ public function pexpiretime(string $key): Redis|int|false; * @return mixed Function may return arbitrary data so this method can return * strings, arrays, nested arrays, etc. * - * @see https://redis.io/commands/fcall + * @see https://redis.io/docs/latest/commands/fcall/ + * + * @example + * $redis->fcall('mylib.increment', ['counter'], [1]); + * */ public function fcall(string $fn, array $keys = [], array $args = []): mixed; @@ -1188,59 +1587,73 @@ public function fcall(string $fn, array $keys = [], array $args = []): mixed; * @return mixed Function may return arbitrary data so this method can return * strings, arrays, nested arrays, etc. * - * @see https://redis.io/commands/fcall_ro + * @see https://redis.io/docs/latest/commands/fcall_ro/ + * + * @example + * $redis->fcall_ro('mylib.peek', ['counter']); + * */ public function fcall_ro(string $fn, array $keys = [], array $args = []): mixed; /** * Deletes every key in all Redis databases * - * @param bool $sync Whether to perform the task in a blocking or non-blocking way. - * @return bool + * @param bool $sync Whether to perform the task in a blocking or non-blocking way. + * + * @see https://redis.io/docs/latest/commands/flushall/ + * + * @example + * $redis->flushAll(true); * - * @see https://redis.io/commands/flushall */ public function flushAll(?bool $sync = null): Redis|bool; /** * Deletes all the keys of the currently selected database. * - * @param bool $sync Whether to perform the task in a blocking or non-blocking way. - * @return bool + * @param bool $sync Whether to perform the task in a blocking or non-blocking way. + * + * @see https://redis.io/docs/latest/commands/flushdb/ + * + * @example + * $redis->flushDB(true); * - * @see https://redis.io/commands/flushdb */ public function flushDB(?bool $sync = null): Redis|bool; /** * Functions is an API for managing code to be executed on the server. * - * @param string $operation The subcommand you intend to execute. Valid options are as follows - * 'LOAD' - Create a new library with the given library name and code. - * 'DELETE' - Delete the given library. - * 'LIST' - Return general information on all the libraries - * 'STATS' - Return information about the current function running - * 'KILL' - Kill the current running function - * 'FLUSH' - Delete all the libraries - * 'DUMP' - Return a serialized payload representing the current libraries - * 'RESTORE' - Restore the libraries represented by the given payload - * @param member $args Additional arguments + * @param string $operation The subcommand you intend to execute. Valid options are as follows + * 'LOAD' - Create a new library with the given library name and code. + * 'DELETE' - Delete the given library. + * 'LIST' - Return general information on all the libraries + * 'STATS' - Return information about the current function running + * 'KILL' - Kill the current running function + * 'FLUSH' - Delete all the libraries + * 'DUMP' - Return a serialized payload representing the current libraries + * 'RESTORE' - Restore the libraries represented by the given payload + * @param mixed ...$args Additional arguments * * @return Redis|bool|string|array Depends on subcommand. * - * @see https://redis.io/commands/function + * @see https://redis.io/docs/latest/commands/function/ + * + * @example + * $redis->function('LIST'); + * */ public function function(string $operation, mixed ...$args): Redis|bool|string|array; /** * Add one or more members to a geospacial sorted set * - * @param string $key The sorted set to add data to. - * @param float $lng The longitude of the first member - * @param float $lat The lattitude of the first member. - * @param member $other_triples_and_options You can continue to pass longitude, lattitude, and member - * arguments to add as many members as you wish. Optionally, the final argument may be - * a string with options for the command @see Redis documentation for the options. + * @param string $key The sorted set to add data to. + * @param float $lng The longitude of the first member + * @param float $lat The latitude of the first member. + * @param mixed ...$other_triples_and_options You can continue to pass longitude, latitude, and member + * arguments to add as many members as you wish. Optionally, the final argument may be + * a string with options for the command @see Redis documentation for the options. * * @return Redis|int|false The number of added elements is returned. If the 'CH' option is specified, * the return value is the number of members *changed*. @@ -1248,7 +1661,7 @@ public function function(string $operation, mixed ...$args): Redis|bool|string|a * @example $redis->geoAdd('cities', -121.8374, 39.7284, 'Chico', -122.03218, 37.322, 'Cupertino'); * @example $redis->geoadd('cities', -121.837478, 39.728494, 'Chico', ['XX', 'CH']); * - * @see https://redis.io/commands/geoadd + * @see https://redis.io/docs/latest/commands/geoadd/ */ public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options): Redis|int|false; @@ -1256,36 +1669,36 @@ public function geoadd(string $key, float $lng, float $lat, string $member, mixe /** * Get the distance between two members of a geospacially encoded sorted set. * - * @param string $key The Sorted set to query. - * @param string $src The first member. - * @param string $dst The second member. - * @param string $unit Which unit to use when computing distance, defaulting to meters. - * - * M - meters - * KM - kilometers - * FT - feet - * MI - miles - * + * @param string $key The Sorted set to query. + * @param string $src The first member. + * @param string $dst The second member. + * @param string|null $unit Which unit to use when computing distance, defaulting to meters. + * + * M - meters + * KM - kilometers + * FT - feet + * MI - miles + * * * @return Redis|float|false The calculated distance in whichever units were specified or false * if one or both members did not exist. * * @example $redis->geodist('cities', 'Chico', 'Cupertino', 'mi'); * - * @see https://redis.io/commands/geodist + * @see https://redis.io/docs/latest/commands/geodist/ */ public function geodist(string $key, string $src, string $dst, ?string $unit = null): Redis|float|false; /** * Retrieve one or more GeoHash encoded strings for members of the set. * - * @param string $key The key to query - * @param string $member The first member to request - * @param string $other_members One or more additional members to request. + * @param string $key The key to query + * @param string $member The first member to request + * @param string ...$other_members One or more additional members to request. * * @return Redis|array|false An array of GeoHash encoded values. * - * @see https://redis.io/commands/geohash + * @see https://redis.io/docs/latest/commands/geohash/ * @see https://en.wikipedia.org/wiki/Geohash * * @example $redis->geohash('cities', 'Chico', 'Cupertino'); @@ -1293,15 +1706,15 @@ public function geodist(string $key, string $src, string $dst, ?string $unit = n public function geohash(string $key, string $member, string ...$other_members): Redis|array|false; /** - * Return the longitude and lattitude for one or more members of a geospacially encoded sorted set. + * Return the longitude and latitude for one or more members of a geospacially encoded sorted set. * - * @param string $key The set to query. - * @param string $member The first member to query. - * @param string $other_members One or more members to query. + * @param string $key The set to query. + * @param string $member The first member to query. + * @param string ...$other_members One or more members to query. * - * @return An array of longitude and lattitude pairs. + * @return Redis|array|false An array of longitude and latitude pairs. * - * @see https://redis.io/commands/geopos + * @see https://redis.io/docs/latest/commands/geopos/ * * @example $redis->geopos('cities', 'Seattle', 'New York'); */ @@ -1317,7 +1730,7 @@ public function geopos(string $key, string $member, string ...$other_members): R * @param string $unit The unit of the provided radius (defaults to 'meters). * See {@link Redis::geodist} for possible units. * @param array $options An array of options that modifies how the command behaves. - * + * ```php * $options = [ * 'WITHCOORD', # Return members and their coordinates. * 'WITHDIST', # Return members and their distances from the center. @@ -1334,11 +1747,11 @@ public function geopos(string $key, string $member, string ...$other_members): R * # Store the distances in the specified key * 'STOREDIST' => * ]; - * + * ``` * * @return mixed This command can return various things, depending on the options passed. * - * @see https://redis.io/commands/georadius + * @see https://redis.io/docs/latest/commands/georadius/ * * @example $redis->georadius('cities', 47.608013, -122.335167, 1000, 'km'); */ @@ -1348,6 +1761,11 @@ public function georadius(string $key, float $lng, float $lat, float $radius, st * A readonly variant of `GEORADIUS` that may be executed on replicas. * * @see Redis::georadius + * @see https://redis.io/docs/latest/commands/georadius_ro/ + * + * @example + * $redis->georadius_ro('cities', -122.335167, 47.608013, 100, 'km'); + * */ public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed; @@ -1364,12 +1782,20 @@ public function georadius_ro(string $key, float $lng, float $lat, float $radius, * * @return mixed This command can return various things depending on options. * + * @see https://redis.io/docs/latest/commands/georadiusbymember/ + * * @example $redis->georadiusbymember('cities', 'Seattle', 200, 'mi'); */ public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): mixed; /** * This is the read-only variant of `GEORADIUSBYMEMBER` that can be run on replicas. + * + * @see https://redis.io/docs/latest/commands/georadiusbymember_ro/ + * + * @example + * $redis->georadiusbymember_ro('cities', 'Seattle', 200, 'mi'); + * */ public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): mixed; @@ -1377,7 +1803,7 @@ public function georadiusbymember_ro(string $key, string $member, float $radius, * Search a geospacial sorted set for members in various ways. * * @param string $key The set to query. - * @param array|string $position Either a two element array with longitude and lattitude, or + * @param array|string $position Either a two element array with longitude and latitude, or * a string representing a member of the set. * @param array|int|float $shape Either a number representine the radius of a circle to search, or * a two element array representing the width and height of a box @@ -1385,6 +1811,12 @@ public function georadiusbymember_ro(string $key, string $member, float $radius, * @param string $unit The unit of our shape. See {@link Redis::geodist} for possible units. * @param array $options @see {@link Redis::georadius} for options. Note that the `STORE` * options are not allowed for this command. + * + * @see https://redis.io/docs/latest/commands/geosearch/ + * + * @example + * $redis->geosearch('cities', 'Seattle', 50, 'km', ['WITHDIST']); + * */ public function geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []): array; @@ -1394,14 +1826,14 @@ public function geosearch(string $key, array|string $position, array|int|float $ * * @param string $dst The destination where results will be stored. * @param string $src The key to query. - * @param array|string $position Either a two element array with longitude and lattitude, or + * @param array|string $position Either a two element array with longitude and latitude, or * a string representing a member of the set. * @param array|int|float $shape Either a number representine the radius of a circle to search, or * a two element array representing the width and height of a box * to search. * @param string $unit The unit of our shape. See {@link Redis::geodist} for possible units. * @param array $options - * + * ```php * $options = [ * 'ASC' | 'DESC', # The sort order of returned members * 'WITHDIST' # Also store distances. @@ -1410,7 +1842,13 @@ public function geosearch(string $key, array|string $position, array|int|float $ * # passed as the `LIMIT` argument, and the `ANY` argument. * 'COUNT' => [], or [, ] * ]; - * + * ``` + * + * @see https://redis.io/docs/latest/commands/geosearchstore/ + * + * @example + * $redis->geosearchstore('west:cities', 'cities', 'Seattle', 50, 'km', ['DESC']); + * */ public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): Redis|array|int|false; @@ -1420,18 +1858,32 @@ public function geosearchstore(string $dst, string $src, array|string $position, * @param string $key The key to query * @return mixed The keys value or false if it did not exist. * - * @see https://redis.io/commands/get + * @see https://redis.io/docs/latest/commands/get/ * * @example $redis->get('foo'); */ public function get(string $key): mixed; + /** + * Retrieve a value and metadata of key. + * + * @param string $key The key to query + * @return Redis|array|false + * + * @example $redis->getWithMeta('foo'); + */ + public function getWithMeta(string $key): Redis|array|false; + /** * Get the authentication information on the connection, if any. * * @return mixed The authentication information used to authenticate the connection. * * @see Redis::auth() + * + * @example + * $redis->getAuth(); + * */ public function getAuth(): mixed; @@ -1443,7 +1895,7 @@ public function getAuth(): mixed; * * @example $redis->getbit('bitmap', 1337); * - * @see https://redis.io/commands/getbit + * @see https://redis.io/docs/latest/commands/getbit/ */ public function getBit(string $key, int $idx): Redis|int|false; @@ -1452,7 +1904,7 @@ public function getBit(string $key, int $idx): Redis|int|false; * * @param string $key The key to query * @param array $options Options to modify how the command works. - * + * ```php * $options = [ * 'EX' => # Expire in N seconds * 'PX' => # Expire in N milliseconds @@ -1460,11 +1912,11 @@ public function getBit(string $key, int $idx): Redis|int|false; * 'PXAT' => # Expire at a unix timestamp (in milliseconds); * 'PERSIST' # Remove any configured expiration on the key. * ]; - * + * ``` * * @return Redis|string|bool The key's value or false if it didn't exist. * - * @see https://redis.io/comands/getex + * @see https://redis.io/docs/latest/commands/getex/ * * @example $redis->getEx('mykey', ['EX' => 60]); */ @@ -1475,10 +1927,14 @@ public function getEx(string $key, array $options = []): Redis|string|bool; * * This value is updated internally in PhpRedis each time {@link Redis::select} is called. * - * @return The database we're connected to. + * @return int The database we're connected to. * * @see Redis::select() - * @see https://redis.io/commands/select + * @see https://redis.io/docs/latest/commands/select/ + * + * @example + * $redis->getDBNum(); + * */ public function getDBNum(): int; @@ -1488,7 +1944,7 @@ public function getDBNum(): int; * @param string $key The key to get/delete. * @return Redis|string|bool The value of the key or false if it didn't exist. * - * @see https://redis.io/commands/getdel + * @see https://redis.io/docs/latest/commands/getdel/ * * @example $redis->getdel('token:123'); */ @@ -1498,21 +1954,32 @@ public function getDel(string $key): Redis|string|bool; * Return the host or Unix socket we are connected to. * * @return string The host or Unix socket. + * + * @example + * $redis->getHost(); + * */ public function getHost(): string; /** * Get the last error returned to us from Redis, if any. * - * @return string The error string or NULL if there is none. + * @return string|null The error string or NULL if there is none. + * + * @example + * $redis->getLastError(); + * */ - public function getLastError(): ?string; + public function getLastError(): string|null; /** * Returns whether the connection is in ATOMIC, MULTI, or PIPELINE mode * * @return int The mode we're in. * + * @example + * $redis->getMode(); + * */ public function getMode(): int; @@ -1522,23 +1989,57 @@ public function getMode(): int; * @see Redis::setOption() for a detailed list of options and their values. * * @return mixed The setting itself or false on failure + * + * @example + * $redis->getOption(Redis::OPT_PREFIX); + * */ public function getOption(int $option): mixed; /** * Get the persistent connection ID, if there is one. * - * @return string The ID or NULL if we don't have one. + * @return string|null The ID or NULL if we don't have one. + * + * @example + * $redis->getPersistentID(); + * */ - public function getPersistentID(): ?string; + public function getPersistentID(): string|null; /** * Get the port we are connected to. This number will be zero if we are connected to a unix socket. * * @return int The port. + * + * @example + * $redis->getPort(); + * */ public function getPort(): int; + /** + * Get the server name as reported by the `HELLO` response. + * + * @return string|false + * + * @example + * $redis->serverName(); + * + */ + public function serverName(): string|false; + + /** + * Get the server version as reported by the `HELLO` response. + * + * @return string|false + * + * @example + * $redis->serverVersion(); + * + */ + public function serverVersion(): string|false; + /** * Retrieve a substring of a string by index. * @@ -1548,7 +2049,7 @@ public function getPort(): int; * * @return Redis|string|false The substring or false on failure. * - * @see https://redis.io/commands/getrange + * @see https://redis.io/docs/latest/commands/getrange/ * * @example * $redis->set('silly-word', 'Supercalifragilisticexpialidocious'); @@ -1559,42 +2060,46 @@ public function getRange(string $key, int $start, int $end): Redis|string|false; /** * Get the longest common subsequence between two string keys. * - * @param string $key1 The first key to check - * @param string $key2 The second key to check - * @param array $options An optional array of modifiers for the comand. + * @param string $key1 The first key to check + * @param string $key2 The second key to check + * @param array|null $options An optional array of modifiers for the command. * - * - * $options = [ - * 'MINMATCHLEN' => int # Exclude matching substrings that are less than this value + * ```php + * $options = [ + * 'MINMATCHLEN' => int # Exclude matching substrings that are less than this value * - * 'WITHMATCHLEN' => bool # Whether each match should also include its length. + * 'WITHMATCHLEN' => bool # Whether each match should also include its length. * - * 'LEN' # Return the length of the longest subsequence + * 'LEN' # Return the length of the longest subsequence * - * 'IDX' # Each returned match will include the indexes where the - * # match occurs in each string. - * ]; - * + * 'IDX' # Each returned match will include the indexes where the + * # match occurs in each string. + * ]; + * ``` * - * NOTE: 'LEN' cannot be used with 'IDX'. + * NOTE: 'LEN' cannot be used with 'IDX'. * * @return Redis|string|array|int|false Various reply types depending on options. * - * @see https://redis.io/commands/lcs + * @see https://redis.io/docs/latest/commands/lcs/ * * @example * $redis->set('seq1', 'gtaggcccgcacggtctttaatgtatccctgtttaccatgccatacctgagcgcatacgc'); * $redis->set('seq2', 'aactcggcgcgagtaccaggccaaggtcgttccagagcaaagactcgtgccccgctgagc'); * echo $redis->lcs('seq1', 'seq2') . "\n"; */ - public function lcs(string $key1, string $key2, ?array $options = NULL): Redis|string|array|int|false; + public function lcs(string $key1, string $key2, ?array $options = null): Redis|string|array|int|false; /** * Get the currently set read timeout on the connection. * * @return float The timeout. - */ - public function getReadTimeout(): float; + * + * @example + * $redis->getReadTimeout(); + * + */ + public function getReadTimeout(): float; /** * Sets a key and returns any previously set value, if the key already existed. @@ -1604,7 +2109,7 @@ public function getReadTimeout(): float; * * @return Redis|string|false The old value of the key or false if it didn't exist. * - * @see https://redis.io/commands/getset + * @see https://redis.io/docs/latest/commands/getset/ * * @example * $redis->getset('captain', 'Pike'); @@ -1615,7 +2120,11 @@ public function getset(string $key, mixed $value): Redis|string|false; /** * Retrieve any set connection timeout * - * @return float The currently set timeout or false on failure (e.g. we aren't connected). + * @return float|false The currently set timeout or false on failure (e.g. we aren't connected). + * + * @example + * $redis->getTimeout(); + * */ public function getTimeout(): float|false; @@ -1623,6 +2132,10 @@ public function getTimeout(): float|false; * Get the number of bytes sent and received on the socket. * * @return array An array in the form [$sent_bytes, $received_bytes] + * + * @example + * $redis->getTransferredBytes(); + * */ public function getTransferredBytes(): array; @@ -1630,19 +2143,23 @@ public function getTransferredBytes(): array; * Reset the number of bytes sent and received on the socket. * * @return void + * + * @example + * $redis->clearTransferredBytes(); + * */ public function clearTransferredBytes(): void; /** * Remove one or more fields from a hash. * - * @param string $key The hash key in question. - * @param string $field The first field to remove - * @param string $other_fields One or more additional fields to remove. + * @param string $key The hash key in question. + * @param string $field The first field to remove + * @param string ...$other_fields One or more additional fields to remove. * * @return Redis|int|false The number of fields actually removed. * - * @see https://redis.io/commands/hdel + * @see https://redis.io/docs/latest/commands/hdel/ * * @example $redis->hDel('communication', 'Alice', 'Bob'); */ @@ -1656,7 +2173,7 @@ public function hDel(string $key, string $field, string ...$other_fields): Redis * * @return Redis|bool True if it exists, false if not. * - * @see https://redis.io/commands/hexists + * @see https://redis.io/docs/latest/commands/hexists/ * * @example $redis->hExists('communication', 'Alice'); */ @@ -1668,14 +2185,26 @@ public function hGet(string $key, string $member): mixed; * Read every field and value from a hash. * * @param string $key The hash to query. - * @return Redis|array|false All fields and values or false if the key didn't exist. + * @return Redis|array|false All fields and values or false if the key didn't exist. * - * @see https://redis.io/commands/hgetall + * @see https://redis.io/docs/latest/commands/hgetall/ * - * @example $redis->hgetall('myhash'); + * @example + * $redis->hgetall('myhash'); */ public function hGetAll(string $key): Redis|array|false; + /** + * Retrieve a value and metadata of hash field. + * + * @param string $key The key to query + * @param string $member The key to query + * @return mixed + * + * @example $redis->hgetWithMeta('foo', 'field'); + */ + public function hGetWithMeta(string $key, string $member): mixed; + /** * Increment a hash field's value by an integer * @@ -1685,7 +2214,7 @@ public function hGetAll(string $key): Redis|array|false; * * @return Redis|int|false The new value of the field. * - * @see https://redis.io/commands/hincrby + * @see https://redis.io/docs/latest/commands/hincrby/ * * @example * $redis->hMSet('player:1', ['name' => 'Alice', 'score' => 0]); @@ -1702,7 +2231,7 @@ public function hIncrBy(string $key, string $field, int $value): Redis|int|false * * @return Redis|float|false The field value after incremented. * - * @see https://redis.io/commands/hincrbyfloat + * @see https://redis.io/docs/latest/commands/hincrbyfloat/ * * @example * $redis->hincrbyfloat('numbers', 'tau', 2 * 3.1415926); @@ -1714,9 +2243,9 @@ public function hIncrByFloat(string $key, string $field, float $value): Redis|fl * * @param string $key The hash to query. * - * @return Redis|array|false The fields in the hash or false if the hash doesn't exist. + * @return Redis|list|false The fields in the hash or false if the hash doesn't exist. * - * @see https://redis.io/commands/hkeys + * @see https://redis.io/docs/latest/commands/hkeys/ * * @example $redis->hkeys('myhash'); */ @@ -1725,7 +2254,7 @@ public function hKeys(string $key): Redis|array|false; /** * Get the number of fields in a hash. * - * @see https://redis.io/commands/hlen + * @see https://redis.io/docs/latest/commands/hlen/ * * @param string $key The hash to check. * @@ -1743,12 +2272,63 @@ public function hLen(string $key): Redis|int|false; * * @return Redis|array|false The fields and values or false if the key didn't exist. * - * @see https://redis.io/commands/hmget + * @see https://redis.io/docs/latest/commands/hmget/ * * @example $redis->hMGet('player:1', ['name', 'score']); */ public function hMget(string $key, array $fields): Redis|array|false; + /** + * Get one or more fields of a hash while optionally setting expiration + * information + * + * @param string $key The hash to query. + * @param array $fields One or more fields to query in the hash. + * @param string|array|null $expiry Info about the expiration + * + * @return Redis|array|false The fields and values or false if the key didn't exist. + * + * @see https://redis.io/docs/latest/commands/hgetex/ + * + * @example + * $redis->hgetex('profiles', ['name', 'email'], ['EX' => 60]); + * + */ + public function hgetex(string $key, array $fields, string|array|null $expiry = null): Redis|array|false; + + /** + * Set one or more fields in a hash with optional expiration information. + * + * @param string $key The hash to create/update. + * @param array $fields An array with fields values. + * @param array|null $expiry Info about the expiration + * + * @return Redis|int|false One if fields were set zero if not. + * + * @see https://redis.io/docs/latest/commands/hsetex/ + * + * @example + * $redis->hsetex('profiles', ['token' => 'abc123'], ['EX' => 60]); + * + */ + public function hsetex(string $key, array $fields, ?array $expiry = null): Redis|int|false; + + /** + * Get one or more fields and delete them + * + * @param string $key The hash in question + * @param array $fields One or more fields + * + * @return Redis|array|false The field and values or false on failure + * + * @see https://redis.io/docs/latest/commands/hgetdel/ + * + * @example + * $redis->hgetdel('profiles', ['token']); + * + */ + public function hgetdel(string $key, array $fields): Redis|array|false; + /** * Add or update one or more hash fields and values * @@ -1757,7 +2337,7 @@ public function hMget(string $key, array $fields): Redis|array|false; * * @return Redis|bool True if the operation was successful * - * @see https://redis.io/commands/hmset + * @see https://redis.io/docs/latest/commands/hmset/ * * @example $redis->hmset('updates', ['status' => 'starting', 'elapsed' => 0]); */ @@ -1766,26 +2346,40 @@ public function hMset(string $key, array $fieldvals): Redis|bool; /** * Get one or more random field from a hash. * - * @param string $key The hash to query. - * @param array $options An array of options to modify how the command behaves. + * @param string $key The hash to query. + * @param array|null $options An array of options to modify how the command behaves. * - * - * $options = [ - * 'COUNT' => int # An optional number of fields to return. - * 'WITHVALUES' => bool # Also return the field values. - * ]; - * + * ```php + * $options = [ + * 'COUNT' => int # An optional number of fields to return. + * 'WITHVALUES' => bool # Also return the field values. + * ]; + * ``` * - * @return Redis|array|string One or more random fields (and possibly values). + * @return Redis|string|array|false One or more random fields (and possibly values). * - * @see https://redis.io/commands/hrandfield + * @see https://redis.io/docs/latest/commands/hrandfield/ * * @example $redis->hrandfield('settings'); * @example $redis->hrandfield('settings', ['count' => 2, 'withvalues' => true]); */ - public function hRandField(string $key, array $options = null): Redis|string|array; + public function hRandField(string $key, ?array $options = null): Redis|string|array|false; - public function hSet(string $key, string $member, mixed $value): Redis|int|false; + /** + * Add or update one or more hash fields and values. + * + * @param string $key The hash to create/update. + * @param mixed ...$fields_and_vals Argument pairs of fields and values. Alternatively, an associative array with the + * fields and their values. + * + * @return Redis|int|false The number of fields that were added, or false on failure. + * + * @see https://redis.io/docs/latest/commands/hset/ + * + * @example $redis->hSet('player:1', 'name', 'Kim', 'score', 78); + * @example $redis->hSet('player:1', ['name' => 'Kim', 'score' => 78]); + */ + public function hSet(string $key, mixed ...$fields_and_vals): Redis|int|false; /** * Set a hash field and value, but only if that field does not exist @@ -1795,13 +2389,13 @@ public function hSet(string $key, string $member, mixed $value): Redis|int|false * * @return Redis|bool True if the field was set and false if not. * - * @see https://redis.io/commands/hsetnx + * @see https://redis.io/docs/latest/commands/hsetnx/ * * @example * $redis->hsetnx('player:1', 'lock', 'enabled'); * $redis->hsetnx('player:1', 'lock', 'enabled'); */ - public function hSetNx(string $key, string $field, string $value): Redis|bool; + public function hSetNx(string $key, string $field, mixed $value): Redis|bool; /** * Get the string length of a hash field @@ -1817,7 +2411,7 @@ public function hSetNx(string $key, string $field, string $value): Redis|bool; * $redis->hmset('hash', ['50bytes' => str_repeat('a', 50)]); * $redis->hstrlen('hash', '50bytes'); * - * @see https://redis.io/commands/hstrlen + * @see https://redis.io/docs/latest/commands/hstrlen/ */ public function hStrLen(string $key, string $field): Redis|int|false; @@ -1826,27 +2420,182 @@ public function hStrLen(string $key, string $field): Redis|int|false; * * @param string $key The hash to query. * - * @return Redis|array|false The values from the hash. + * @return Redis|list|false The values from the hash. * - * @see https://redis.io/commands/hvals + * @see https://redis.io/docs/latest/commands/hvals/ * * @example $redis->hvals('player:1'); */ public function hVals(string $key): Redis|array|false; + /** + * Set the expiration on one or more fields in a hash. + * + * @param string $key The hash to update. + * @param int $ttl The time to live in seconds. + * @param array $fields The fields to set the expiration on. + * @param string|null $mode An optional mode (NX, XX, ETC) + * + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/hexpire/ + * + * @example + * $redis->hexpire('profiles', 300, ['token'], 'NX'); + * + */ + public function hexpire(string $key, int $ttl, array $fields, + ?string $mode = NULL): Redis|array|false; + + /** + * Set the expiration on one or more fields in a hash in milliseconds. + * + * @param string $key The hash to update. + * @param int $ttl The time to live in milliseconds. + * @param array $fields The fields to set the expiration on. + * @param string|null $mode An optional mode (NX, XX, ETC) + * + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/hexpire/ + * + * @example + * $redis->hpexpire('profiles', 1500, ['token']); + * + */ + public function hpexpire(string $key, int $ttl, array $fields, + ?string $mode = NULL): Redis|array|false; + + /** + * Set the expiration time on one or more fields of a hash. + * + * @param string $key The hash to update. + * @param int $time The time to live in seconds. + * @param array $fields The fields to set the expiration on. + * @param string|null $mode An optional mode (NX, XX, ETC) + * + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/hexpire/ + * + * @example + * $redis->hexpireat('profiles', time() + 600, ['token']); + * + */ + public function hexpireat(string $key, int $time, array $fields, + ?string $mode = NULL): Redis|array|false; + + /** + * Set the expiration time on one or more fields of a hash in milliseconds. + * + * @param string $key The hash to update. + * @param int $mstime The time to live in milliseconds. + * @param array $fields The fields to set the expiration on. + * @param string|null $mode An optional mode (NX, XX, ETC) + * + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/hexpire/ + * + * @example + * $redis->hpexpireat('profiles', (int) (microtime(true) * 1000) + 60000, ['token']); + * + */ + public function hpexpireat(string $key, int $mstime, array $fields, + ?string $mode = NULL): Redis|array|false; + + /** + * Get the TTL of one or more fields in a hash + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/httl/ + * + * @example + * $redis->httl('profiles', ['token']); + * + */ + public function httl(string $key, array $fields): Redis|array|false; + + /** + * Get the millisecond TTL of one or more fields in a hash + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/hpttl/ + * + * @example + * $redis->hpttl('profiles', ['token']); + * + */ + public function hpttl(string $key, array $fields): Redis|array|false; + + /** + * Get the expiration time of one or more fields in a hash + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/hexpiretime/ + * + * @example + * $redis->hexpiretime('profiles', ['token']); + * + */ + public function hexpiretime(string $key, array $fields): Redis|array|false; + + /** + * Get the expiration time in milliseconds of one or more fields in a hash + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/hpexpiretime/ + * + * @example + * $redis->hpexpiretime('profiles', ['token']); + * + */ + public function hpexpiretime(string $key, array $fields): Redis|array|false; + + /** + * Persist one or more hash fields + * + * @param string $key The hash to query. + * @param array $fields The fields to query. + * + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/hpersist/ + * + * @example + * $redis->hpersist('profiles', ['token']); + * + */ + public function hpersist(string $key, array $fields): Redis|array|false; /** * Iterate over the fields and values of a hash in an incremental fashion. * - * @see https://redis.io/commands/hscan - * @see https://redis.io/commands/scan + * @see https://redis.io/docs/latest/commands/hscan/ + * @see https://redis.io/docs/latest/commands/scan/ * - * @param string $key The hash to query. - * @param int $iterator The scan iterator, which should be initialized to NULL before the first call. - * This value will be updated after every call to hscan, until it reaches zero - * meaning the scan is complete. - * @param string $pattern An optional glob-style pattern to filter fields with. - * @param int $count An optional hint to Redis about how many fields and values to return per HSCAN. + * @param string $key The hash to query. + * @param int|string|null $iterator The scan iterator, which should be initialized to NULL before the first call. + * This value will be updated after every call to hscan, until it reaches zero + * meaning the scan is complete. + * @param string|null $pattern An optional glob-style pattern to filter fields with. + * @param int $count An optional hint to Redis about how many fields and values to return per HSCAN. * * @return Redis|array|bool An array with a subset of fields and values. * @@ -1861,7 +2610,7 @@ public function hVals(string $key): Redis|array|false; * * $redis->hmset('big-hash', $fields); * - * $it = NULL; + * $it = null; * * do { * // Scan the hash but limit it to fields that match '*:1?3' @@ -1872,13 +2621,45 @@ public function hVals(string $key): Redis|array|false; * } * } while ($it != 0); */ - public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|bool; + public function hscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): Redis|array|bool; /** - * Increment a key's value, optionally by a specifc amount. + * Set an expiration on a key member (KeyDB only). + * + * @see https://docs.keydb.dev/docs/commands/#expiremember + * @see https://redis.io/docs/latest/commands/expiremember/ * - * @see https://redis.io/commands/incr - * @see https://redis.io/commands/incrby + * @param string $key The key to expire + * @param string $field The field to expire + * @param string|null $unit The unit of the ttl (s, or ms). + * + * @example + * $redis->expiremember('profiles', 'token', 60); + * + */ + public function expiremember(string $key, string $field, int $ttl, ?string $unit = null): Redis|int|false; + + /** + * Set an expiration on a key membert to a specific unix timestamp (KeyDB only). + * + * @see https://docs.keydb.dev/docs/commands/#expirememberat + * @see https://redis.io/docs/latest/commands/expirememberat/ + * + * @param string $key The key to expire + * @param string $field The field to expire + * @param int $timestamp The unix timestamp to expire at. + * + * @example + * $redis->expirememberat('profiles', 'token', time() + 300); + * + */ + public function expirememberat(string $key, string $field, int $timestamp): Redis|int|false; + + /** + * Increment a key's value, optionally by a specific amount. + * + * @see https://redis.io/docs/latest/commands/incr/ + * @see https://redis.io/docs/latest/commands/incrby/ * * @param string $key The key to increment * @param int $by An optional amount to increment by. @@ -1893,7 +2674,7 @@ public function incr(string $key, int $by = 1): Redis|int|false; /** * Increment a key by a specific integer value * - * @see https://redis.io/commands/incrby + * @see https://redis.io/docs/latest/commands/incrby/ * * @param string $key The key to increment. * @param int $value The amount to increment. @@ -1910,11 +2691,13 @@ public function incrBy(string $key, int $value): Redis|int|false; /** * Increment a numeric key by a floating point value. * - * @param string $key The key to increment - * @param floag $value How much to increment (or decrement) the value. + * @param string $key The key to increment + * @param float $value How much to increment (or decrement) the value. * * @return Redis|float|false The new value of the key or false if the key didn't contain a string. * + * @see https://redis.io/docs/latest/commands/incrbyfloat/ + * * @example * $redis->incrbyfloat('tau', 3.1415926); * $redis->incrbyfloat('tau', 3.1415926); @@ -1929,11 +2712,15 @@ public function incrByFloat(string $key, float $value): Redis|float|false; * * If connected to Redis server >= 7.0.0 you may pass multiple optional sections. * - * @see https://redis.io/commands/info/ + * @see https://redis.io/docs/latest/commands/info/ * - * @param string $sections Optional section(s) you wish Redis server to return. + * @param string ...$sections Optional section(s) you wish Redis server to return. * * @return Redis|array|false + * + * @example + * $redis->info('server', 'stats'); + * */ public function info(string ...$sections): Redis|array|false; @@ -1941,24 +2728,48 @@ public function info(string ...$sections): Redis|array|false; * Check if we are currently connected to a Redis instance. * * @return bool True if we are, false if not + * + * @example + * $redis->isConnected(); + * */ public function isConnected(): bool; - /** @return Redis|array|false */ + /** + * @param string $pattern + * @return Redis|list|false + * + * @see https://redis.io/docs/latest/commands/keys/ + * + * @example + * $redis->keys('session:*'); + * + */ public function keys(string $pattern); /** - * @param mixed $elements * @return Redis|int|false + * + * @see https://redis.io/docs/latest/commands/linsert/ + * + * @example + * $redis->lInsert('letters', Redis::AFTER, 'b', 'beta'); + * */ public function lInsert(string $key, string $pos, mixed $pivot, mixed $value); /** - * Retrieve the lenght of a list. + * Retrieve the length of a list. * * @param string $key The list * * @return Redis|int|false The number of elements in the list or false on failure. + * + * @see https://redis.io/docs/latest/commands/llen/ + * + * @example + * $redis->lLen('queue'); + * */ public function lLen(string $key): Redis|int|false; @@ -1973,6 +2784,8 @@ public function lLen(string $key): Redis|int|false; * - `Redis::LEFT`, or `Redis::RIGHT`. * @return Redis|string|false The element removed from the source list. * + * @see https://redis.io/docs/latest/commands/lmove/ + * * @example * $redis->rPush('numbers', 'one', 'two', 'three'); * $redis->lMove('numbers', 'odds', Redis::LEFT, Redis::LEFT); @@ -1992,6 +2805,8 @@ public function lMove(string $src, string $dst, string $wherefrom, string $where * * @return Redis|string|false; * + * @see https://redis.io/docs/latest/commands/blmove/ + * * @example * @redis->lPush('numbers', 'one'); * @redis->blmove('numbers', 'odds', Redis::LEFT, Redis::LEFT 1.0); @@ -2005,10 +2820,11 @@ public function blmove(string $src, string $dst, string $wherefrom, string $wher * * @param string $key The list to pop from. * @param int $count Optional number of elements to remove. By default one element is popped. - * @return Redis|null|bool|int|array Will return the element(s) popped from the list or false/NULL - * if none was removed. * - * @see https://redis.io/commands/lpop + * @return Redis|bool|string|array Will return the element(s) popped from the list or false/NULL + * if none was removed. + * + * @see https://redis.io/docs/latest/commands/lpop/ * * @example $redis->lpop('mylist'); * @example $redis->lpop('mylist', 4); @@ -2018,40 +2834,46 @@ public function lPop(string $key, int $count = 0): Redis|bool|string|array; /** * Retrieve the index of an element in a list. * - * @param string $key The list to query. - * @param mixed $value The value to search for. - * @param array $options Options to configure how the command operates - * - * $options = [ - * # How many matches to return. By default a single match is returned. - * # If count is set to zero, it means unlimited. - * 'COUNT' => - * - * # Specify which match you want returned. `RANK` 1 means "the first match" - * # 2 means the second, and so on. If passed as a negative number the - * # RANK is computed right to left, so a `RANK` of -1 means "the last match". - * 'RANK' => - * - * # This argument allows you to limit how many elements Redis will search before - * # returning. This is useful to prevent Redis searching very long lists while - * # blocking the client. - * 'MAXLEN => - * ]; - * + * @param string $key The list to query. + * @param mixed $value The value to search for. + * @param array|null $options Options to configure how the command operates + * ```php + * $options = [ + * # How many matches to return. By default a single match is returned. + * # If count is set to zero, it means unlimited. + * 'COUNT' => + * + * # Specify which match you want returned. `RANK` 1 means "the first match" + * # 2 means the second, and so on. If passed as a negative number the + * # RANK is computed right to left, so a `RANK` of -1 means "the last match". + * 'RANK' => + * + * # This argument allows you to limit how many elements Redis will search before + * # returning. This is useful to prevent Redis searching very long lists while + * # blocking the client. + * 'MAXLEN => + * ]; + * ``` * * @return Redis|null|bool|int|array Returns one or more of the matching indexes, or null/false if none were found. + * + * @see https://redis.io/docs/latest/commands/lpos/ + * + * @example + * $redis->lPos('queue', 'job-42'); + * */ - public function lPos(string $key, mixed $value, array $options = null): Redis|null|bool|int|array; + public function lPos(string $key, mixed $value, ?array $options = null): Redis|null|bool|int|array; /** * Prepend one or more elements to a list. * - * @param string $key The list to prepend. - * @param mixed $elements One or more elements to prepend. + * @param string $key The list to prepend. + * @param mixed ...$elements One or more elements to prepend. * - * @return Redis|int The new length of the list after prepending. + * @return Redis|int|false The new length of the list after prepending. * - * @see https://redis.io/commands/lpush + * @see https://redis.io/docs/latest/commands/lpush/ * * @example $redis->lPush('mylist', 'cat', 'bear', 'aligator'); */ @@ -2060,12 +2882,12 @@ public function lPush(string $key, mixed ...$elements): Redis|int|false; /** * Append one or more elements to a list. * - * @param string $key The list to append to. - * @param mixed $elements one or more elements to append. + * @param string $key The list to append to. + * @param mixed ...$elements one or more elements to append. * * @return Redis|int|false The new length of the list * - * @see https://redis.io/commands/rpush + * @see https://redis.io/docs/latest/commands/rpush/ * * @example $redis->rPush('mylist', 'xray', 'yankee', 'zebra'); */ @@ -2079,6 +2901,11 @@ public function rPush(string $key, mixed ...$elements): Redis|int|false; * * @return Redis|int|false The new length of the list. * + * @see https://redis.io/docs/latest/commands/lpushx/ + * + * @example + * $redis->lPushx('queue', 'job-42'); + * */ public function lPushx(string $key, mixed $value): Redis|int|false; @@ -2090,6 +2917,11 @@ public function lPushx(string $key, mixed $value): Redis|int|false; * * @return Redis|int|false The new length of the list. * + * @see https://redis.io/docs/latest/commands/rpushx/ + * + * @example + * $redis->rPushx('queue', 'job-99'); + * */ public function rPushx(string $key, mixed $value): Redis|int|false; @@ -2102,7 +2934,11 @@ public function rPushx(string $key, mixed $value): Redis|int|false; * * @return Redis|bool True if the list was modified. * - * @see https://redis.io/commands/lset + * @see https://redis.io/docs/latest/commands/lset/ + * + * @example + * $redis->lSet('queue', 0, 'job-42'); + * */ public function lSet(string $key, int $index, mixed $value): Redis|bool; @@ -2111,7 +2947,11 @@ public function lSet(string $key, int $index, mixed $value): Redis|bool; * * @return int The unix timestamp of the last save time * - * @see https://redis.io/commands/lastsave + * @see https://redis.io/docs/latest/commands/lastsave/ + * + * @example + * $redis->lastSave(); + * */ public function lastSave(): int; @@ -2121,6 +2961,12 @@ public function lastSave(): int; * @param string $key The key to query * @param int $index The index to check. * @return mixed The index or NULL/false if the element was not found. + * + * @see https://redis.io/docs/latest/commands/lindex/ + * + * @example + * $redis->lindex('queue', 0); + * */ public function lindex(string $key, int $index): mixed; @@ -2135,6 +2981,8 @@ public function lindex(string $key, int $index): mixed; * * @return Redis|array|false The range of elements between the indexes. * + * @see https://redis.io/docs/latest/commands/lrange/ + * * @example $redis->lrange('mylist', 0, -1); // the whole list * @example $redis->lrange('mylist', -2, -1); // the last two elements in the list. */ @@ -2149,7 +2997,11 @@ public function lrange(string $key, int $start , int $end): Redis|array|false; * * @return Redis|int|false The number of elements removed. * - * @see https://redis.io/commands/lrem + * @see https://redis.io/docs/latest/commands/lrem/ + * + * @example + * $redis->lrem('queue', 0, 'expired-job'); + * */ public function lrem(string $key, mixed $value, int $count = 0): Redis|int|false; @@ -2162,52 +3014,101 @@ public function lrem(string $key, mixed $value, int $count = 0): Redis|int|false * * @return Redis|bool true if the list was trimmed. * + * @see https://redis.io/docs/latest/commands/ltrim/ + * * @example $redis->ltrim('mylist', 0, 3); // Keep the first four elements */ public function ltrim(string $key, int $start , int $end): Redis|bool; /** - * Get one ore more string keys. + * Get one or more string keys. * * @param array $keys The keys to retrieve - * @return Redis|array an array of keys with their values. + * @return Redis|array|false an array of keys with their values. + * + * @see https://redis.io/docs/latest/commands/mget/ * * @example $redis->mget(['key1', 'key2']); */ - public function mget(array $keys): Redis|array; + public function mget(array $keys): Redis|array|false; + /** + * Proxy for the Redis MIGRATE command. + * + * @param string $host The destination redis host. + * @param int $port The destination redis port. + * @param string|array $key The key or array of keys to migrate. + * @param int $dstdb The destination database index. + * @param int $timeout The timeout for the operation in + * milliseconds. + * @param bool $copy Whether to copy the key(s) or move + * them. + * @param bool $replace Whether to replace existing keys on + * the destination. + * @param mixed|null $credentials Optional credentials for + * authenticating to the destination + * server. + * + * @see https://redis.io/docs/latest/commands/migrate/ + * + * @example + * $redis->connect('localhost', 6379); + * $redis->set('foo', '6379_key'); + * + * // Move the key to localhost:9999 with a 5 second timeout + * var_dump($redis->migrate('localhost', 9999, 'foo', 0, 5000)); + */ public function migrate(string $host, int $port, string|array $key, int $dstdb, int $timeout, bool $copy = false, bool $replace = false, - #[\SensitiveParameter] mixed $credentials = NULL): Redis|bool; + #[\SensitiveParameter] mixed $credentials = null): Redis|bool; /** * Move a key to a different database on the same redis instance. * * @param string $key The key to move * @return Redis|bool True if the key was moved + * + * @see https://redis.io/docs/latest/commands/move/ + * + * @example + * $redis->move('cart:42', 1); + * */ public function move(string $key, int $index): Redis|bool; /** - * Set one ore more string keys. + * Set one or more string keys. * * @param array $key_values An array with keys and their values. * @return Redis|bool True if the keys could be set. * - * @see https://redis.io/commands/mset + * @see https://redis.io/docs/latest/commands/mset/ * * @example $redis->mSet(['foo' => 'bar', 'baz' => 'bop']); */ public function mset(array $key_values): Redis|bool; /** - * Set one ore more string keys but only if none of the key exist. + * Set one or more keys and values with optional expiry information. + * + * @param array $key_vals An array of keys with their values. + * @param int|float|array|null $expiry An optional array with expiry information. + * @return Redis|int|false 1 if all keys were set, 0 if none were. + * + * @see https://redis.io/commands/msetex + * + * @example $redis->msetex(['foo' => 'bar', 'baz' => 'bop'], ['EX' => 60]); + */ + public function msetex(array $key_vals, int|float|array|null $expiry = null): Redis|int|false; + + /** + * Set one or more string keys but only if none of the key exist. * * @param array $key_values An array of keys with their values. * * @return Redis|bool True if the keys were set and false if not. * - * @see https://redis.io/commands/msetnx + * @see https://redis.io/docs/latest/commands/msetnx/ * * @example $redis->msetnx(['foo' => 'bar', 'baz' => 'bop']); */ @@ -2221,7 +3122,7 @@ public function msetnx(array $key_values): Redis|bool; * * @return Redis|bool True if the transaction could be started. * - * @see https://redis.io/commands/multi + * @see https://redis.io/docs/latest/commands/multi/ * * @example * $redis->multi(); @@ -2231,15 +3132,56 @@ public function msetnx(array $key_values): Redis|bool; */ public function multi(int $value = Redis::MULTI): bool|Redis; + /** + * Get encoding and other information about a key. + * + * @param string $subcommand The subcommand to execute. This can be either 'encoding', 'freq', or 'idle'. + * @param string $key The key to query. + * + * @return Redis|int|string|false The requested information about the key. + * + * @example + * $redis->del('list1'); + * $redis->rPush('list1', 'a', 'b', 'c'); + * echo $redis->object('encoding', 'list1'); + * + */ public function object(string $subcommand, string $key): Redis|int|string|false; /** * @deprecated * @alias Redis::connect + * + * @example + * $redis->open('127.0.0.1', 6379); + * */ - public function open(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; + public function open(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; - public function pconnect(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; + /** + * Connects to a Redis server creating or reusing a persistent connection. + * + * @param string $host The Redis server hostname. + * @param int $port The Redis server port. + * @param float $timeout Connection timeout in seconds. + * @param string|null $persistent_id An optional persistent ID to use for the connection. + * @param int $retry_interval The number of microseconds to wait before retrying a connection. + * @param float $read_timeout Read timeout in seconds. + * @param array|null $context An optional stream context array. + * + * @return bool True if the connection was successful. + * + * @throws RedisException + * + * @example + * try { + * $redis = new Redis(); + * $redis->pconnect('localhost', 6379); + * } catch (Exception $ex) { + * echo "Could not connect to Redis: ", $ex->getMessage(), "\n"; + * } + */ + public function pconnect(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Remove the expiration from a key. @@ -2247,79 +3189,109 @@ public function pconnect(string $host, int $port = 6379, float $timeout = 0, str * @param string $key The key to operate against. * * @return Redis|bool True if a timeout was removed and false if it was not or the key didn't exist. + * + * @see https://redis.io/docs/latest/commands/persist/ + * + * @example + * $redis->persist('session:42'); + * */ public function persist(string $key): Redis|bool; /** - * Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 - * you can pass an optional mode argument that modifies how the command will execute. + * Sets an expiration in milliseconds on a given key. If connected to Redis >= 7.0.0 + * you can pass an optional mode argument that modifies how the command will execute. * - * @see Redis::expire() for a description of the mode argument. + * @see Redis::expire() for a description of the mode argument. + * @see https://redis.io/docs/latest/commands/pexpire/ * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the - * command works. + * @param string $key The key to set an expiration on. + * @param int $timeout The number of milliseconds after which key will be automatically deleted. + * @param string|null $mode A two character modifier that changes how the + * command works. + * + * @return bool True if an expiry was set on the key, and false otherwise. + * + * @example + * $redis->pexpire('session:42', 5000); * - * @return Redis|bool True if an expiry was set on the key, and false otherwise. */ - public function pexpire(string $key, int $timeout, ?string $mode = NULL): bool; + public function pexpire(string $key, int $timeout, ?string $mode = null): bool; /** * Set a key's expiration to a specific Unix Timestamp in milliseconds. If connected to * Redis >= 7.0.0 you can pass an optional 'mode' argument. * * @see Redis::expire() For a description of the mode argument. + * @see https://redis.io/docs/latest/commands/pexpireat/ * - * @param string $key The key to set an expiration on. - * @param string $mode A two character modifier that changes how the + * @param string $key The key to set an expiration on. + * @param int $timestamp The unix timestamp to expire at. + * @param string|null $mode A two character modifier that changes how the * command works. * * @return Redis|bool True if an expiration was set on the key, false otherwise. + * + * @example + * $redis->pexpireAt('session:42', (int) (microtime(true) * 1000) + 60000); + * */ - public function pexpireAt(string $key, int $timestamp, ?string $mode = NULL): Redis|bool; + public function pexpireAt(string $key, int $timestamp, ?string $mode = null): Redis|bool; /** * Add one or more elements to a Redis HyperLogLog key * - * @see https://redis.io/commands/pfadd + * @see https://redis.io/docs/latest/commands/pfadd/ * * @param string $key The key in question. * * @param array $elements One or more elements to add. * * @return Redis|int Returns 1 if the set was altered, and zero if not. + * + * @example + * $redis->pfadd('visitors', ['alice', 'bob']); + * */ public function pfadd(string $key, array $elements): Redis|int; /** * Retrieve the cardinality of a Redis HyperLogLog key. * - * @see https://redis.io/commands/pfcount + * @see https://redis.io/docs/latest/commands/pfcount/ + * + * @param array|string $key_or_keys Either one key or an array of keys * - * @param string $key_or_keys Either one key or an array of keys + * @return Redis|int|false The estimated cardinality of the set. + * + * @example + * $redis->pfcount(['visitors:today', 'visitors:yesterday']); * - * @return Redis|int The estimated cardinality of the set. */ public function pfcount(array|string $key_or_keys): Redis|int|false; /** * Merge one or more source HyperLogLog sets into a destination set. * - * @see https://redis.io/commands/pfmerge + * @see https://redis.io/docs/latest/commands/pfmerge/ * * @param string $dst The destination key. * @param array $srckeys One or more source keys. * * @return Redis|bool Always returns true. + * + * @example + * $redis->pfmerge('visitors:all', ['visitors:today', 'visitors:yesterday']); + * */ public function pfmerge(string $dst, array $srckeys): Redis|bool; /** * PING the redis server with an optional string argument. * - * @see https://redis.io/commands/ping + * @see https://redis.io/docs/latest/commands/ping/ * - * @param string $message An optional string message that Redis will reply with, if passed. + * @param string|null $message An optional string message that Redis will reply with, if passed. * * @return Redis|string|false If passed no message, this command will simply return `true`. * If a message is passed, it will return the message. @@ -2327,7 +3299,7 @@ public function pfmerge(string $dst, array $srckeys): Redis|bool; * @example $redis->ping(); * @example $redis->ping('beep boop'); */ - public function ping(string $message = NULL): Redis|string|bool; + public function ping(?string $message = null): Redis|string|bool; /** * Enter into pipeline mode. @@ -2338,7 +3310,7 @@ public function ping(string $message = NULL): Redis|string|bool; * * NOTE: That this is shorthand for Redis::multi(Redis::PIPELINE) * - * @return Redis The redis object is returned, to facilitate method chaining. + * @return bool|Redis The redis object is returned, to facilitate method chaining. * * @example * $redis->pipeline() @@ -2352,8 +3324,12 @@ public function pipeline(): bool|Redis; /** * @deprecated * @alias Redis::pconnect + * + * @example + * $redis->popen('127.0.0.1', 6379, 0.0, 'cache'); + * */ - public function popen(string $host, int $port = 6379, float $timeout = 0, string $persistent_id = NULL, int $retry_interval = 0, float $read_timeout = 0, array $context = NULL): bool; + public function popen(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null): bool; /** * Set a key with an expiration time in milliseconds @@ -2364,6 +3340,8 @@ public function popen(string $host, int $port = 6379, float $timeout = 0, string * * @return Redis|bool True if the key could be set. * + * @see https://redis.io/docs/latest/commands/psetex/ + * * @example $redis->psetex('mykey', 1000, 'myval'); */ public function psetex(string $key, int $expire, mixed $value): Redis|bool; @@ -2374,13 +3352,19 @@ public function psetex(string $key, int $expire, mixed $value): Redis|bool; * @param array $patterns One or more patterns to subscribe to. * @param callable $cb A callback with the following prototype: * - * + * ```php * function ($redis, $channel, $message) { } - * + * ``` * - * @see https://redis.io/commands/psubscribe + * @see https://redis.io/docs/latest/commands/psubscribe/ * * @return bool True if we were subscribed. + * + * @example + * $redis->psubscribe(['user:*'], function (Redis $client, string $pattern, string $channel, string $message): void { + * printf('[%s] %s' . PHP_EOL, $channel, $message); + * }); + * */ public function psubscribe(array $patterns, callable $cb): bool; @@ -2395,7 +3379,7 @@ public function psubscribe(array $patterns, callable $cb): bool; * -2 - The key did not exist. *
    * - * @see https://redis.io/commands/pttl + * @see https://redis.io/docs/latest/commands/pttl/ * * @example $redis->pttl('ttl-key'); */ @@ -2404,27 +3388,47 @@ public function pttl(string $key): Redis|int|false; /** * Publish a message to a pubsub channel * - * @see https://redis.io/commands/publish + * @see https://redis.io/docs/latest/commands/publish/ * * @param string $channel The channel to publish to. * @param string $message The message itself. * - * @return Redis|int The number of subscribed clients to the given channel. + * @return Redis|int|false The number of subscribed clients to the given channel. + * + * @example + * $redis->publish('updates', 'build complete'); + * + */ + public function publish(string $channel, string $message): Redis|int|false; + + /** + * Interact with the Redis PubSub subsystem. + * + * @param string $command The PubSub command to execute. This can be one of: + * @param mixed $arg An optional argument to the command. + * + * @return mixed Can return any number of things depending on the command executed. + * @see https://redis.io/docs/latest/commands/pubsub/ + * + * @example + * $redis->pubsub('channels'); */ - public function publish(string $channel, string $message): Redis|int|false; - public function pubsub(string $command, mixed $arg = null): mixed; /** * Unsubscribe from one or more channels by pattern * - * @see https://redis.io/commands/punsubscribe - * @see https://redis.io/commands/subscribe + * @see https://redis.io/docs/latest/commands/punsubscribe/ + * @see https://redis.io/docs/latest/commands/subscribe/ * @see Redis::subscribe() * * @param array $patterns One or more glob-style patterns of channel names. * * @return Redis|array|bool The array of subscribed patterns or false on failure. + * + * @example + * $redis->punsubscribe(['user:*', 'room:*']); + * */ public function punsubscribe(array $patterns): Redis|array|bool; @@ -2435,9 +3439,9 @@ public function punsubscribe(array $patterns): Redis|array|bool; * @param int $count The maximum number of elements to pop at once. * NOTE: The `count` argument requires Redis >= 6.2.0 * - * @return Redis|array|string|bool One ore more popped elements or false if all were empty. + * @return Redis|array|string|bool One or more popped elements or false if all were empty. * - * @see https://redis.io/commands/rpop + * @see https://redis.io/docs/latest/commands/rpop/ * * @example $redis->rPop('mylist'); * @example $redis->rPop('mylist', 4); @@ -2447,10 +3451,13 @@ public function rPop(string $key, int $count = 0): Redis|array|string|bool; /** * Return a random key from the current database * - * @see https://redis.io/commands/randomkey + * @see https://redis.io/docs/latest/commands/randomkey/ * * @return Redis|string|false A random key name or false if no keys exist * + * @example + * $redis->randomKey(); + * */ public function randomKey(): Redis|string|false; @@ -2458,7 +3465,7 @@ public function randomKey(): Redis|string|false; * Execute any arbitrary Redis command by name. * * @param string $command The command to execute - * @param mixed $args One or more arguments to pass to the command. + * @param mixed ...$args One or more arguments to pass to the command. * * @return mixed Can return any number of things depending on command executed. * @@ -2471,19 +3478,23 @@ public function rawcommand(string $command, mixed ...$args): mixed; /** * Unconditionally rename a key from $old_name to $new_name * - * @see https://redis.io/commands/rename + * @see https://redis.io/docs/latest/commands/rename/ * * @param string $old_name The original name of the key * @param string $new_name The new name for the key * * @return Redis|bool True if the key was renamed or false if not. + * + * @example + * $redis->rename('config:pending', 'config:active'); + * */ public function rename(string $old_name, string $new_name): Redis|bool; /** * Renames $key_src to $key_dst but only if newkey does not exist. * - * @see https://redis.io/commands/renamenx + * @see https://redis.io/docs/latest/commands/renamenx/ * * @param string $key_src The source key name * @param string $key_dst The destination key name. @@ -2503,37 +3514,43 @@ public function renameNx(string $key_src, string $key_dst): Redis|bool; * Reset the state of the connection. * * @return Redis|bool Should always return true unless there is an error. + * + * @see https://redis.io/docs/latest/commands/reset/ + * + * @example + * $redis->reset(); + * */ public function reset(): Redis|bool; /** * Restore a key by the binary payload generated by the DUMP command. * - * @param string $key The name of the key you wish to create. - * @param int $ttl What Redis should set the key's TTL (in milliseconds) to once it is created. - * Zero means no TTL at all. - * @param string $value The serialized binary value of the string (generated by DUMP). - * @param array $options An array of additional options that modifies how the command operates. + * @param string $key The name of the key you wish to create. + * @param int $ttl What Redis should set the key's TTL (in milliseconds) to once it is created. + * Zero means no TTL at all. + * @param string $value The serialized binary value of the string (generated by DUMP). + * @param array|null $options An array of additional options that modifies how the command operates. * - * - * $options = [ - * 'ABSTTL' # If this is present, the `$ttl` provided by the user should - * # be an absolute timestamp, in milliseconds() + * ```php + * $options = [ + * 'ABSTTL' # If this is present, the `$ttl` provided by the user should + * # be an absolute timestamp, in milliseconds() * - * 'REPLACE' # This flag instructs Redis to store the key even if a key with - * # that name already exists. + * 'REPLACE' # This flag instructs Redis to store the key even if a key with + * # that name already exists. * - * 'IDLETIME' => int # Tells Redis to set the keys internal 'idletime' value to a - * # specific number (see the Redis command OBJECT for more info). - * 'FREQ' => int # Tells Redis to set the keys internal 'FREQ' value to a specific - * # number (this relates to Redis' LFU eviction algorithm). - * ]; - * + * 'IDLETIME' => int # Tells Redis to set the keys internal 'idletime' value to a + * # specific number (see the Redis command OBJECT for more info). + * 'FREQ' => int # Tells Redis to set the keys internal 'FREQ' value to a specific + * # number (this relates to Redis' LFU eviction algorithm). + * ]; + * ``` * * @return Redis|bool True if the key was stored, false if not. * - * @see https://redis.io/commands/restore - * @see https://redis.io/commands/dump + * @see https://redis.io/docs/latest/commands/restore/ + * @see https://redis.io/docs/latest/commands/dump/ * @see Redis::dump() * * @example @@ -2542,13 +3559,19 @@ public function reset(): Redis|bool; * * $redis->restore('captains-backup', 0, $serialized); */ - public function restore(string $key, int $ttl, string $value, ?array $options = NULL): Redis|bool; + public function restore(string $key, int $ttl, string $value, ?array $options = null): Redis|bool; /** * Query whether the connected instance is a primary or replica * * @return mixed Will return an array with the role of the connected instance unless there is * an error. + * + * @see https://redis.io/docs/latest/commands/role/ + * + * @example + * $redis->role(); + * */ public function role(): mixed; @@ -2561,7 +3584,7 @@ public function role(): mixed; * * @return Redis|string|false The popped element or false if the source key was empty. * - * @see https://redis.io/commands/rpoplpush + * @see https://redis.io/docs/latest/commands/rpoplpush/ * * @example * $redis->pipeline() @@ -2577,13 +3600,13 @@ public function rpoplpush(string $srckey, string $dstkey): Redis|string|false; /** * Add one or more values to a Redis SET key. * - * @param string $key The key name - * @param mixed $member A value to add to the set. - * @param mixed $other_members One or more additional values to add + * @param string $key The key name + * @param mixed $value A value to add to the set. + * @param mixed ...$other_values One or more additional values to add * * @return Redis|int|false The number of values added to the set. * - * @see https://redis.io/commands/sadd + * @see https://redis.io/docs/latest/commands/sadd/ * * @example * $redis->del('myset'); @@ -2594,15 +3617,16 @@ public function rpoplpush(string $srckey, string $dstkey): Redis|string|false; public function sAdd(string $key, mixed $value, mixed ...$other_values): Redis|int|false; /** - * Add one ore more values to a Redis SET key. This is an alternative to Redis::sadd() but + * Add one or more values to a Redis SET key. This is an alternative to Redis::sadd() but * instead of being variadic, takes a single array of values. * - * @see https://redis.io/commands/sadd + * @see https://redis.io/docs/latest/commands/sadd/ * @see Redis::sadd() * - * @param string $key The set to add values to. - * @param array $values One or more members to add to the set. - * @return Redis|int|false The number of members added to the set. + * @param string $key The set to add values to. + * @param array $values One or more members to add to the set. + * + * @return int The number of members added to the set. * * @example * $redis->del('myset'); @@ -2616,13 +3640,13 @@ public function sAddArray(string $key, array $values): int; * Given one or more Redis SETS, this command returns all of the members from the first * set that are not in any subsequent set. * - * @param string $key The first set - * @param string $other_keys One or more additional sets + * @param string $key The first set + * @param string ...$other_keys One or more additional sets * * @return Redis|array|false Returns the elements from keys 2..N that don't exist in the * first sorted set, or false on failure. * - * @see https://redis.io/commands/sdiff + * @see https://redis.io/docs/latest/commands/sdiff/ * * @example * $redis->pipeline() @@ -2640,14 +3664,18 @@ public function sDiff(string $key, string ...$other_keys): Redis|array|false; * This method performs the same operation as SDIFF except it stores the resulting diff * values in a specified destination key. * - * @see https://redis.io/commands/sdiffstore + * @see https://redis.io/docs/latest/commands/sdiffstore/ * @see Redis::sdiff() * - * @param string $dst The key where to store the result - * @param string $key The first key to perform the DIFF on - * @param string $other_keys One or more additional keys. + * @param string $dst The key where to store the result + * @param string $key The first key to perform the DIFF on + * @param string ...$other_keys One or more additional keys. * * @return Redis|int|false The number of values stored in the destination set or false on failure. + * + * @example + * $redis->sDiffStore('diff:set', 'set:all', 'set:archived'); + * */ public function sDiffStore(string $dst, string $key, string ...$other_keys): Redis|int|false; @@ -2655,10 +3683,10 @@ public function sDiffStore(string $dst, string $key, string ...$other_keys): Red * Given one or more Redis SET keys, this command will return all of the elements that are * in every one. * - * @see https://redis.io/commands/sinter + * @see https://redis.io/docs/latest/commands/sinter/ * - * @param string $key The first SET key to intersect. - * @param string $other_keys One or more Redis SET keys. + * @param array|string $key The first SET key to intersect. + * @param string ...$other_keys One or more Redis SET keys. * * @example * $redis->pipeline() @@ -2669,7 +3697,6 @@ public function sDiffStore(string $dst, string $key, string ...$other_keys): Red * ->exec(); * * var_dump($redis->sinter('alice_likes', 'bob_likes', 'bill_likes')); - *
    */ public function sInter(array|string $key, string ...$other_keys): Redis|array|false; @@ -2682,7 +3709,7 @@ public function sInter(array|string $key, string ...$other_keys): Redis|array|fa * * @return Redis|int|false The * - * @see https://redis.io/commands/sintercard + * @see https://redis.io/docs/latest/commands/sintercard/ * * @example * $redis->sAdd('set1', 'apple', 'pear', 'banana', 'carrot'); @@ -2690,8 +3717,6 @@ public function sInter(array|string $key, string ...$other_keys): Redis|array|fa * $redis->sAdd('set3', 'pear', 'banana'); * * $redis->sInterCard(['set1', 'set2', 'set3']); - * ?> - *
    */ public function sintercard(array $keys, int $limit = -1): Redis|int|false; @@ -2699,21 +3724,20 @@ public function sintercard(array $keys, int $limit = -1): Redis|int|false; * Perform the intersection of one or more Redis SETs, storing the result in a destination * key, rather than returning them. * - * @param array|string $key_or_keys Either a string key, or an array of keys (with at least two - * elements, consisting of the destination key name and one - * or more source keys names. - * @param string $other_keys If the first argument was a string, subsequent arguments should - * be source key names. + * @param array|string $key Either a string key, or an array of keys (with at least two + * elements, consisting of the destination key name and one + * or more source keys names. + * @param string ...$other_keys If the first argument was a string, subsequent arguments should + * be source key names. * - * @return Redis|int|false The number of values stored in the destination key or false on failure. + * @return Redis|int|false The number of values stored in the destination key or false on failure. * - * @see https://redis.io/commands/sinterstore + * @see https://redis.io/docs/latest/commands/sinterstore/ * @see Redis::sinter() * - * @example $redis->sInterStore(['dst', 'src1', 'src2', 'src3']); - * @example $redis->sInterStore('dst', 'src1', 'src'2', 'src3'); - * ?> - *
    + * @example + * $redis->sInterStore(['dst', 'src1', 'src2', 'src3']); + * $redis->sInterStore('dst', 'src1', 'src'2', 'src3'); */ public function sInterStore(array|string $key, string ...$other_keys): Redis|int|false; @@ -2724,7 +3748,7 @@ public function sInterStore(array|string $key, string ...$other_keys): Redis|int * * @return Redis|array|false Every element in the set or false on failure. * - * @see https://redis.io/commands/smembers + * @see https://redis.io/docs/latest/commands/smembers/ * * @example * $redis->sAdd('tng-crew', ...['Picard', 'Riker', 'Data', 'Worf', 'La Forge', 'Troi', 'Crusher', 'Broccoli']); @@ -2735,13 +3759,13 @@ public function sMembers(string $key): Redis|array|false; /** * Check if one or more values are members of a set. * - * @see https://redis.io/commands/smismember - * @see https://redis.io/commands/smember + * @see https://redis.io/docs/latest/commands/smismember/ + * @see https://redis.io/docs/latest/commands/smember/ * @see Redis::smember() * - * @param string $key The set to query. - * @param string $member The first value to test if exists in the set. - * @param string $other_members Any number of additional values to check. + * @param string $key The set to query. + * @param string $member The first value to test if exists in the set. + * @param string ...$other_members Any number of additional values to check. * * @return Redis|array|false An array of integers representing whether each passed value * was a member of the set. @@ -2756,7 +3780,7 @@ public function sMisMember(string $key, string $member, string ...$other_members * Pop a member from one set and push it onto another. This command will create the * destination set if it does not currently exist. * - * @see https://redis.io/commands/smove + * @see https://redis.io/docs/latest/commands/smove/ * * @param string $src The source set. * @param string $dst The destination set. @@ -2775,7 +3799,7 @@ public function sMove(string $src, string $dst, mixed $value): Redis|bool; /** * Remove one or more elements from a set. * - * @see https://redis.io/commands/spop + * @see https://redis.io/docs/latest/commands/spop/ * * @param string $key The set in question. * @param int $count An optional number of members to pop. This defaults to @@ -2796,28 +3820,28 @@ public function sPop(string $key, int $count = 0): Redis|string|array|false; * * If this value is positive, Redis will return *up to* the requested * number but with unique elements that will never repeat. This means - * you may recieve fewer then `$count` replies. + * you may receive fewer then `$count` replies. * * If the number is negative, Redis will return the exact number requested * but the result may contain duplicate elements. * * @return Redis|array|string|false One or more random members or false on failure. * - * @see https://redis.io/commands/srandmember + * @see https://redis.io/docs/latest/commands/srandmember/ * * @example $redis->sRandMember('myset'); * @example $redis->sRandMember('myset', 10); * @example $redis->sRandMember('myset', -10); */ - public function sRandMember(string $key, int $count = 0): Redis|string|array|false; + public function sRandMember(string $key, int $count = 0): mixed; /** * Returns the union of one or more Redis SET keys. * - * @see https://redis.io/commands/sunion + * @see https://redis.io/docs/latest/commands/sunion/ * - * @param string $key The first SET to do a union with - * @param string $other_keys One or more subsequent keys + * @param string $key The first SET to do a union with + * @param string ...$other_keys One or more subsequent keys * * @return Redis|array|false The union of the one or more input sets or false on failure. * @@ -2828,15 +3852,19 @@ public function sUnion(string $key, string ...$other_keys): Redis|array|false; /** * Perform a union of one or more Redis SET keys and store the result in a new set * - * @see https://redis.io/commands/sunionstore + * @see https://redis.io/docs/latest/commands/sunionstore/ * @see Redis::sunion() * - * @param string $dst The destination key - * @param string $key The first source key - * @param string $other_keys One or more additional source keys + * @param string $dst The destination key + * @param string $key The first source key + * @param string ...$other_keys One or more additional source keys * * @return Redis|int|false The number of elements stored in the destination SET or * false on failure. + * + * @example + * $redis->sUnionStore('union:set', 'set:a', 'set:b'); + * */ public function sUnionStore(string $dst, string $key, string ...$other_keys): Redis|int|false; @@ -2844,10 +3872,14 @@ public function sUnionStore(string $dst, string $key, string ...$other_keys): Re * Persist the Redis database to disk. This command will block the server until the save is * completed. For a nonblocking alternative, see Redis::bgsave(). * - * @see https://redis.io/commands/save + * @see https://redis.io/docs/latest/commands/save/ * @see Redis::bgsave() * * @return Redis|bool Returns true unless an error occurs. + * + * @example + * $redis->save(); + * */ public function save(): Redis|bool; @@ -2861,21 +3893,18 @@ public function save(): Redis|bool; * keys match inside a key space with a great many keys. The following example demonstrates how * to use Redis::scan() with the option disabled and enabled. * - * @param int $iterator The cursor returned by Redis for every subsequent call to SCAN. On - * the initial invocation of the call, it should be initialized by the - * caller to NULL. Each time SCAN is invoked, the iterator will be - * updated to a new number, until finally Redis will set the value to - * zero, indicating that the scan is complete. - * - * @param string $pattern An optional glob-style pattern for matching key names. If passed as - * NULL, it is the equivalent of sending '*' (match every key). - * - * @param int $count A hint to redis that tells it how many keys to return in a single - * call to SCAN. The larger the number, the longer Redis may block - * clients while iterating the key space. - * - * @param string $type An optional argument to specify which key types to scan (e.g. - * 'STRING', 'LIST', 'SET') + * @param int|string|null $iterator The cursor returned by Redis for every subsequent call to SCAN. On + * the initial invocation of the call, it should be initialized by the + * caller to NULL. Each time SCAN is invoked, the iterator will be + * updated to a new number, until finally Redis will set the value to + * zero, indicating that the scan is complete. + * @param string|null $pattern An optional glob-style pattern for matching key names. If passed as + * NULL, it is the equivalent of sending '*' (match every key). + * @param int $count A hint to redis that tells it how many keys to return in a single + * call to SCAN. The larger the number, the longer Redis may block + * clients while iterating the key space. + * @param string|null $type An optional argument to specify which key types to scan (e.g. + * 'STRING', 'LIST', 'SET') * * @return array|false An array of keys, or false if no keys were returned for this * invocation of scan. Note that it is possible for Redis to return @@ -2883,7 +3912,7 @@ public function save(): Redis|bool; * should instead continue to SCAN until the iterator reference is * returned to zero. * - * @see https://redis.io/commands/scan + * @see https://redis.io/docs/latest/commands/scan/ * @see Redis::setOption() * * @example @@ -2891,7 +3920,7 @@ public function save(): Redis|bool; * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); * - * $it = NULL; + * $it = null; * * do { * $keys = $redis->scan($it, '*zorg*'); @@ -2902,7 +3931,7 @@ public function save(): Redis|bool; * * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); * - * $it = NULL; + * $it = null; * * // When Redis::SCAN_RETRY is enabled, we can use simpler logic, as we will never receive an * // empty array of keys when the iterator is nonzero. @@ -2912,7 +3941,7 @@ public function save(): Redis|bool; * } * } */ - public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, string $type = NULL): array|false; + public function scan(null|int|string &$iterator, ?string $pattern = null, int $count = 0, ?string $type = null): array|false; /** * Retrieve the number of members in a Redis set. @@ -2921,20 +3950,19 @@ public function scan(?int &$iterator, ?string $pattern = null, int $count = 0, s * * @return Redis|int|false The cardinality of the set or false on failure. * - * @see https://redis.io/commands/scard + * @see https://redis.io/docs/latest/commands/scard/ * * @example $redis->scard('set'); - * */ public function scard(string $key): Redis|int|false; /** * An administrative command used to interact with LUA scripts stored on the server. * - * @see https://redis.io/commands/script + * @see https://redis.io/docs/latest/commands/script/ * * @param string $command The script suboperation to execute. - * @param mixed $args One ore more additional argument + * @param mixed ...$args One or more additional argument * * @return mixed This command returns various things depending on the specific operation executed. * @@ -2950,7 +3978,7 @@ public function script(string $command, mixed ...$args): mixed; * * @return Redis|bool true on success and false on failure * - * @see https://redis.io/commands/select + * @see https://redis.io/docs/latest/commands/select/ * * @example $redis->select(1); */ @@ -2979,18 +4007,18 @@ public function select(int $db): Redis|bool; * * @return Redis|string|bool True if the key was set or false on failure. * - * @see https://redis.io/commands/set - * @see https://redis.io/commands/setex + * @see https://redis.io/docs/latest/commands/set/ + * @see https://redis.io/docs/latest/commands/setex/ * * @example $redis->set('key', 'value'); * @example $redis->set('key', 'expires_in_60_seconds', 60); */ - public function set(string $key, mixed $value, mixed $options = NULL): Redis|string|bool; + public function set(string $key, mixed $value, mixed $options = null): Redis|string|bool; /** * Set a specific bit in a Redis string to zero or one * - * @see https://redis.io/commands/setbit + * @see https://redis.io/docs/latest/commands/setbit/ * * @param string $key The Redis STRING key to modify * @param bool $value Whether to set the bit to zero or one. @@ -3006,7 +4034,7 @@ public function setBit(string $key, int $idx, bool $value): Redis|int|false; /** * Update or append to a Redis string at a specific starting index * - * @see https://redis.io/commands/setrange + * @see https://redis.io/docs/latest/commands/setrange/ * * @param string $key The key to update * @param int $index Where to insert the provided value @@ -3049,6 +4077,9 @@ public function setRange(string $key, int $index, string $value): Redis|int|fals * * @return bool true if the setting was updated, false if not. * + * @example + * $redis->setOption(Redis::OPT_PREFIX, 'app:'); + * */ public function setOption(int $option, mixed $value): bool; @@ -3061,6 +4092,8 @@ public function setOption(int $option, mixed $value): bool; * * @return Redis|bool True on success or false on failure. * + * @see https://redis.io/docs/latest/commands/setex/ + * * @example $redis->setex('60s-ttl', 60, 'some-value'); */ public function setex(string $key, int $expire, mixed $value); @@ -3068,7 +4101,7 @@ public function setex(string $key, int $expire, mixed $value); /** * Set a key to a value, but only if that key does not already exist. * - * @see https://redis.io/commands/setnx + * @see https://redis.io/docs/latest/commands/setnx/ * * @param string $key The key name to set. * @param mixed $value What to set the key to. @@ -3088,6 +4121,8 @@ public function setnx(string $key, mixed $value): Redis|bool; * * @return Redis|bool True if the member exists and false if not. * + * @see https://redis.io/docs/latest/commands/sismember/ + * * @example $redis->sismember('myset', 'mem1', 'mem2'); */ public function sismember(string $key, mixed $value): Redis|bool; @@ -3102,25 +4137,29 @@ public function sismember(string $key, mixed $value): Redis|bool; * * @deprecated * - * @see https://redis.io/commands/slaveof - * @see https://redis.io/commands/replicaof + * @see https://redis.io/docs/latest/commands/slaveof/ + * @see https://redis.io/docs/latest/commands/replicaof/ * @see Redis::replicaof() + * + * @example + * $redis->slaveof('10.0.0.5', 6380); + * */ - public function slaveof(string $host = NULL, int $port = 6379): Redis|bool; + public function slaveof(?string $host = null, int $port = 6379): Redis|bool; /** * Used to turn a Redis instance into a replica of another, or to remove * replica status promoting the instance to a primary. * - * @see https://redis.io/commands/replicaof - * @see https://redis.io/commands/slaveof + * @see https://redis.io/docs/latest/commands/replicaof/ + * @see https://redis.io/docs/latest/commands/slaveof/ * @see Redis::slaveof() * - * @param string $host The host of the primary to start replicating. - * @param string $port The port of the primary to start replicating. + * @param string|null $host The host of the primary to start replicating. + * @param int $port The port of the primary to start replicating. * * @return Redis|bool Success if we were successfully able to start replicating a primary or - * were able to promote teh replicat to a primary. + * were able to promote the replicat to a primary. * * @example * $redis = new Redis(['host' => 'localhost']); @@ -3132,19 +4171,23 @@ public function slaveof(string $host = NULL, int $port = 6379): Redis|bool; * // attempting to promote the instance to a primary. * $redis->replicaof(); */ - public function replicaof(string $host = NULL, int $port = 6379): Redis|bool; + public function replicaof(?string $host = null, int $port = 6379): Redis|bool; /** * Update one or more keys last modified metadata. * - * @see https://redis.io/commands/touch/ + * @see https://redis.io/docs/latest/commands/touch/ * - * @param array|string $key Either the first key or if passed as the only argument - * an array of keys. - * @param string $more_keys One or more keys to send to the command. + * @param array|string $key_or_array Either the first key or if passed as the only argument + * an array of keys. + * @param string ...$more_keys One or more keys to send to the command. * * @return Redis|int|false This command returns the number of keys that exist and * had their last modified time reset + * + * @example + * $redis->touch('cache:1', 'cache:2'); + * */ public function touch(array|string $key_or_array, string ...$more_keys): Redis|int|false; @@ -3168,7 +4211,7 @@ public function touch(array|string $key_or_array, string ...$more_keys): Redis|i * * @return mixed * - * @see https://redis.io/commands/slowlog/ + * @see https://redis.io/docs/latest/commands/slowlog/ * * @example $redis->slowlog('get', -1); // Retrieve all slowlog entries. * @example $redis->slowlog('len'); // Retrieve slowlog length. @@ -3179,12 +4222,12 @@ public function slowlog(string $operation, int $length = 0): mixed; /** * Sort the contents of a Redis key in various ways. * - * @see https://redis.io/commands/sort/ + * @see https://redis.io/docs/latest/commands/sort/ * - * @param string $key The key you wish to sort - * @param array $options Various options controlling how you would like the - * data sorted. See blow for a detailed description - * of this options array. + * @param string $key The key you wish to sort + * @param array|null $options Various options controlling how you would like the + * data sorted. See blow for a detailed description + * of this options array. * * @return mixed This command can either return an array with the sorted data * or the number of elements placed in a destination set when @@ -3209,37 +4252,66 @@ public function sort(string $key, ?array $options = null): mixed; * This is simply a read-only variant of the sort command * * @see Redis::sort() + * @see https://redis.io/docs/latest/commands/sort_ro/ + * + * @example + * $redis->sort_ro('numbers', ['LIMIT' => [0, 5]]); + * */ public function sort_ro(string $key, ?array $options = null): mixed; /** * @deprecated + * + * @see https://redis.io/docs/latest/commands/sort/ + * + * @example + * $redis->sortAsc('numbers'); + * */ public function sortAsc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array; /** * @deprecated + * + * @see https://redis.io/docs/latest/commands/sort/ + * + * @example + * $redis->sortAscAlpha('tags'); + * */ public function sortAscAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array; /** * @deprecated + * + * @see https://redis.io/docs/latest/commands/sort/ + * + * @example + * $redis->sortDesc('numbers'); + * */ public function sortDesc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array; /** * @deprecated + * + * @see https://redis.io/docs/latest/commands/sort/ + * + * @example + * $redis->sortDescAlpha('tags'); + * */ public function sortDescAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null): array; /** * Remove one or more values from a Redis SET key. * - * @see https://redis.io/commands/srem + * @see https://redis.io/docs/latest/commands/srem/ * - * @param string $key The Redis SET key in question. - * @param mixed $value The first value to remove. - * @param mixed $more_values One or more additional values to remove. + * @param string $key The Redis SET key in question. + * @param mixed $value The first value to remove. + * @param mixed ...$other_values One or more additional values to remove. * * @return Redis|int|false The number of values removed from the set or false on failure. * @@ -3250,19 +4322,19 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i /** * Scan the members of a redis SET key. * - * @see https://redis.io/commands/sscan - * @see https://redis.io/commands/scan + * @see https://redis.io/docs/latest/commands/sscan/ + * @see https://redis.io/docs/latest/commands/scan/ * @see Redis::setOption() * - * @param string $key The Redis SET key in question. - * @param int $iterator A reference to an iterator which should be initialized to NULL that - * PhpRedis will update with the value returned from Redis after each - * subsequent call to SSCAN. Once this cursor is zero you know all - * members have been traversed. - * @param string $pattern An optional glob style pattern to match against, so Redis only - * returns the subset of members matching this pattern. - * @param int $count A hint to Redis as to how many members it should scan in one command - * before returning members for that iteration. + * @param string $key The Redis SET key in question. + * @param int|string|null $iterator A reference to an iterator which should be initialized to NULL that + * PhpRedis will update with the value returned from Redis after each + * subsequent call to SSCAN. Once this cursor is zero you know all + * members have been traversed. + * @param string|null $pattern An optional glob style pattern to match against, so Redis only + * returns the subset of members matching this pattern. + * @param int $count A hint to Redis as to how many members it should scan in one command + * before returning members for that iteration. * * @example * $redis->del('myset'); @@ -3274,7 +4346,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); * * $scanned = 0; - * $it = NULL; + * $it = null; * * // Without Redis::SCAN_RETRY we may receive empty results and * // a nonzero iterator. @@ -3291,7 +4363,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); * * $scanned = 0; - * $it = NULL; + * $it = null; * * // With Redis::SCAN_RETRY PhpRedis will never return an empty array * // when the cursor is non-zero @@ -3302,7 +4374,7 @@ public function srem(string $key, mixed $value, mixed ...$other_values): Redis|i * } * } */ - public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false; + public function sscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): array|false; /** * Subscribes the client to the specified shard channels. @@ -3314,7 +4386,7 @@ public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int * @return bool True on success, false on faiilure. Note that this command will block the * client in a subscribe loop, waiting for messages to arrive. * - * @see https://redis.io/commands/ssubscribe + * @see https://redis.io/docs/latest/commands/ssubscribe/ * * @example * $redis = new Redis(['host' => 'localhost']); @@ -3343,7 +4415,7 @@ public function ssubscribe(array $channels, callable $cb): bool; * @return Redis|int|false The length of the string key if it exists, zero if it does not, and * false on failure. * - * @see https://redis.io/commands/strlen + * @see https://redis.io/docs/latest/commands/strlen/ * * @example $redis->strlen('mykey'); */ @@ -3359,7 +4431,7 @@ public function strlen(string $key): Redis|int|false; * @return bool True on success, false on faiilure. Note that this command will block the * client in a subscribe loop, waiting for messages to arrive. * - * @see https://redis.io/commands/subscribe + * @see https://redis.io/docs/latest/commands/subscribe/ * * @example * $redis = new Redis(['host' => 'localhost']); @@ -3387,7 +4459,7 @@ public function subscribe(array $channels, callable $cb): bool; * @param array $channels One or more channels to unsubscribe from. * @return Redis|array|bool The array of unsubscribed channels. * - * @see https://redis.io/commands/sunsubscribe + * @see https://redis.io/docs/latest/commands/sunsubscribe/ * @see Redis::ssubscribe() * * @example @@ -3416,7 +4488,7 @@ public function sunsubscribe(array $channels): Redis|array|bool; * * @return Redis|bool Success if the databases could be swapped and false on failure. * - * @see https://redis.io/commands/swapdb + * @see https://redis.io/docs/latest/commands/swapdb/ * @see Redis::del() * * @example @@ -3430,10 +4502,10 @@ public function swapdb(int $src, int $dst): Redis|bool; /** * Retrieve the server time from the connected Redis instance. * - * @see https://redis.io/commands/time + * @see https://redis.io/docs/latest/commands/time/ * - * @return A two element array consisting of a Unix Timestamp and the number of microseconds - * elapsed since the second. + * @return Redis|array A two element array consisting of a Unix Timestamp and the number of microseconds + * elapsed since the second. * * @example $redis->time(); */ @@ -3447,7 +4519,7 @@ public function time(): Redis|array; * no expiration, and -2 if the key does not exist. In the event of an * error, this command will return false. * - * @see https://redis.io/commands/ttl + * @see https://redis.io/docs/latest/commands/ttl/ * * @example $redis->ttl('mykey'); */ @@ -3456,7 +4528,7 @@ public function ttl(string $key): Redis|int|false; /** * Get the type of a given Redis key. * - * @see https://redis.io/commands/type + * @see https://redis.io/docs/latest/commands/type/ * * @param string $key The key to check * @return Redis|int|false The Redis type constant or false on failure. @@ -3470,6 +4542,7 @@ public function ttl(string $key): Redis|int|false; * Redis::REDIS_ZSET * Redis::REDIS_HASH * Redis::REDIS_STREAM + * Redis::REDIS_VECTORSET * * @example * foreach ($redis->keys('*') as $key) { @@ -3483,15 +4556,15 @@ public function type(string $key): Redis|int|false; * deletion is asynchronous, meaning it is safe to delete large keys without fear of * Redis blocking for a long period of time. * - * @param array|string $key_or_keys Either an array with one or more keys or a string with - * the first key to delete. - * @param string $other_keys If the first argument passed to this method was a string - * you may pass any number of additional key names. + * @param array|string $key Either an array with one or more keys or a string with + * the first key to delete. + * @param string ...$other_keys If the first argument passed to this method was a string + * you may pass any number of additional key names. * * @return Redis|int|false The number of keys deleted or false on failure. * - * @see https://redis.io/commands/unlink - * @see https://redis.io/commands/del + * @see https://redis.io/docs/latest/commands/unlink/ + * @see https://redis.io/docs/latest/commands/del/ * @see Redis::del() * * @example $redis->unlink('key1', 'key2', 'key3'); @@ -3505,7 +4578,7 @@ public function unlink(array|string $key, string ...$other_keys): Redis|int|fals * @param array $channels One or more channels to unsubscribe from. * @return Redis|array|bool The array of unsubscribed channels. * - * @see https://redis.io/commands/unsubscribe + * @see https://redis.io/docs/latest/commands/unsubscribe/ * @see Redis::subscribe() * * @example @@ -3525,25 +4598,29 @@ public function unsubscribe(array $channels): Redis|array|bool; /** * Remove any previously WATCH'ed keys in a transaction. * - * @see https://redis.io/commands/unwatch - * @see https://redis.io/commands/unwatch + * @see https://redis.io/docs/latest/commands/unwatch/ + * @see https://redis.io/docs/latest/commands/unwatch/ * @see Redis::watch() * - * @return True on success and false on failure. + * @return Redis|bool True on success and false on failure. + * + * @example + * $redis->unwatch(); + * */ public function unwatch(): Redis|bool; /** * Watch one or more keys for conditional execution of a transaction. * - * @param array|string $key_or_keys Either an array with one or more key names, or a string key name - * @param string $other_keys If the first argument was passed as a string, any number of additional - * string key names may be passed variadically. - * @return Redis|bool + * @param array|string $key Either an array with one or more key names, or a string key name + * @param string ...$other_keys If the first argument was passed as a string, any number of additional + * string key names may be passed variadically. * + * @return Redis|bool * - * @see https://redis.io/commands/watch - * @see https://redis.io/commands/unwatch + * @see https://redis.io/docs/latest/commands/watch/ + * @see https://redis.io/docs/latest/commands/unwatch/ * * @example * $redis1 = new Redis(['host' => 'localhost']); @@ -3575,20 +4652,23 @@ public function watch(array|string $key, string ...$other_keys): Redis|bool; /** * Block the client up to the provided timeout until a certain number of replicas have confirmed - * recieving them. + * receiving them. * - * @see https://redis.io/commands/wait + * @see https://redis.io/docs/latest/commands/wait/ * - * @param int $numreplicas The number of replicas we want to confirm write operaions + * @param int $numreplicas The number of replicas we want to confirm write operations * @param int $timeout How long to wait (zero meaning forever). * - * @return Redis|int|false The number of replicas that have confirmed or false on failure. + * @return int|false The number of replicas that have confirmed or false on failure. + * + * @example + * $redis->wait(1, 1000); * */ public function wait(int $numreplicas, int $timeout): int|false; /** - * Acknowledge one ore more messages that are pending (have been consumed using XREADGROUP but + * Acknowledge one or more messages that are pending (have been consumed using XREADGROUP but * not yet acknowledged by XACK.) * * @param string $key The stream to query. @@ -3597,8 +4677,8 @@ public function wait(int $numreplicas, int $timeout): int|false; * * @return int|false The number of acknowledged messages * - * @see https://redis.io/commands/xack - * @see https://redis.io/commands/xreadgroup + * @see https://redis.io/docs/latest/commands/xack/ + * @see https://redis.io/docs/latest/commands/xreadgroup/ * @see Redis::xack() * * @example @@ -3635,7 +4715,7 @@ public function xack(string $key, string $group, array $ids): int|false; * @param string $id The ID for the message we want to add. This can be the special value '*' * which means Redis will generate the ID that appends the message to the * end of the stream. It can also be a value in the form -* which will - * generate an ID that appends to the end ot entries with the same value + * generate an ID that appends to the end of entries with the same value * (if any exist). * @param int $maxlen If specified Redis will append the new message but trim any number of the * oldest messages in the stream until the length is <= $maxlen. @@ -3644,7 +4724,7 @@ public function xack(string $key, string $group, array $ids): int|false; * `$maxlen` values. * @param bool $nomkstream If passed as `TRUE`, the stream must exist for Redis to append the message. * - * @see https://redis.io/commands/xadd + * @see https://redis.io/docs/latest/commands/xadd/ * * @example $redis->xAdd('ds9-season-1', '1-1', ['title' => 'Emissary Part 1']); * @example $redis->xAdd('ds9-season-1', '1-2', ['title' => 'A Man Alone']); @@ -3655,8 +4735,8 @@ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bo * This command allows a consumer to claim pending messages that have been idle for a specified period of time. * Its purpose is to provide a mechanism for picking up messages that may have had a failed consumer. * - * @see https://redis.io/commands/xautoclaim - * @see https://redis.io/commands/xclaim + * @see https://redis.io/docs/latest/commands/xautoclaim/ + * @see https://redis.io/docs/latest/commands/xclaim/ * @see https://redis.io/docs/data-types/streams-tutorial/ * * @param string $key The stream to check. @@ -3681,7 +4761,7 @@ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bo * $pending = $redis->xPending('ships', 'combatants'); * var_dump($pending); * - * // Asssume control of the pending message with a different consumer. + * // Assume control of the pending message with a different consumer. * $res = $redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); * * // Now the 'Sisko' consumer owns the message @@ -3694,34 +4774,34 @@ public function xautoclaim(string $key, string $group, string $consumer, int $mi * This method allows a consumer to take ownership of pending stream entries, by ID. Another * command that does much the same thing but does not require passing specific IDs is `Redis::xAutoClaim`. * - * @see https://redis.io/commands/xclaim - * @see https://redis.io/commands/xautoclaim. - * - * @param string $key The stream we wish to claim messages for. - * @param string $group Our consumer group. - * @param string $consumer Our consumer. - * @param int $min_idle_time The minimum idle-time in milliseconds a message must have for ownership to be transferred. - * @param array $options An options array that modifies how the command operates. - * - * - * # Following is an options array describing every option you can pass. Note that - * # 'IDLE', and 'TIME' are mutually exclusive. - * $options = [ - * 'IDLE' => 3 # Set the idle time of the message to a 3. By default - * # the idle time is set to zero. - * 'TIME' => 1000*time() # Same as IDLE except it takes a unix timestamp in - * # milliseconds. - * 'RETRYCOUNT' => 0 # Set the retry counter to zero. By default XCLAIM - * # doesn't modify the counter. - * 'FORCE' # Creates the pending message entry even if IDs are - * # not already - * # in the PEL with another client. - * 'JUSTID' # Return only an array of IDs rather than the messages - * # themselves. - * ]; - * - * - * @return Redis|array|bool An array of claimed messags or false on failure. + * @see https://redis.io/docs/latest/commands/xclaim/ + * @see https://redis.io/docs/latest/commands/xautoclaim./ + * + * @param string $key The stream we wish to claim messages for. + * @param string $group Our consumer group. + * @param string $consumer Our consumer. + * @param int $min_idle The minimum idle-time in milliseconds a message must have for ownership to be transferred. + * @param array $options An options array that modifies how the command operates. + * + * ```php + * # Following is an options array describing every option you can pass. Note that + * # 'IDLE', and 'TIME' are mutually exclusive. + * $options = [ + * 'IDLE' => 3 # Set the idle time of the message to a 3. By default + * # the idle time is set to zero. + * 'TIME' => 1000*time() # Same as IDLE except it takes a unix timestamp in + * # milliseconds. + * 'RETRYCOUNT' => 0 # Set the retry counter to zero. By default XCLAIM + * # doesn't modify the counter. + * 'FORCE' # Creates the pending message entry even if IDs are + * # not already + * # in the PEL with another client. + * 'JUSTID' # Return only an array of IDs rather than the messages + * # themselves. + * ]; + * ``` + * + * @return Redis|array|bool An array of claimed messages or false on failure. * * @example * $redis->xGroup('CREATE', 'ships', 'combatants', '0-0', true); @@ -3755,60 +4835,84 @@ public function xclaim(string $key, string $group, string $consumer, int $min_id * * @return Redis|int|false The number of messages removed or false on failure. * + * @see https://redis.io/docs/latest/commands/xdel/ + * * @example $redis->xDel('stream', ['1-1', '2-1', '3-1']); */ public function xdel(string $key, array $ids): Redis|int|false; + /** + * Remove one or more IDs from a stream with extended options. + * + * @param string $key The stream to modify. + * @param array $ids One or more message IDs to remove. + * @param string|null $mode An optional mode argument. Valid modes + * are as follows: KEEPREF | DELREF | ACKED + * + * @return Redis|array|false An array corresponding to IDs. 1 if the id was + * deleted and 0 if not. + * + * @see https://redis.io/docs/latest/commands/xdelex/ + * + * @example + * $redis->xadd('s', '*', ['field' => 'value1']); + * $redis->xdelex('s', ['1-0'], 'KEEPREF'); + */ + public function xdelex(string $key, array $ids, ?string $mode = null): Redis|array|false; + /** * XGROUP * * Perform various operation on consumer groups for a particular Redis STREAM. What the command does * is primarily based on which operation is passed. * - * @see https://redis.io/commands/xgroup/ - * - * @param string $operation The subcommand you intend to execute. Valid options are as follows - * 'HELP' - Redis will return information about the command - * Requires: none - * 'CREATE' - Create a consumer group. - * Requires: Key, group, consumer. - * 'SETID' - Set the ID of an existing consumer group for the stream. - * Requires: Key, group, id. - * 'CREATECONSUMER' - Create a new consumer group for the stream. You must - * also pass key, group, and the consumer name you wish to - * create. - * Requires: Key, group, consumer. - * 'DELCONSUMER' - Delete a consumer from group attached to the stream. - * Requires: Key, group, consumer. - * 'DESTROY' - Delete a consumer group from a stream. - * Requires: Key, group. - * @param string $key The STREAM we're operating on. - * @param string $group The consumer group we want to create/modify/delete. - * @param string $id_or_consumer The STREAM id (e.g. '$') or consumer group. See the operation section - * for information about which to send. - * @param bool $mkstream This flag may be sent in combination with the 'CREATE' operation, and - * cause Redis to also create the STREAM if it doesn't currently exist. - * - * @param bool $entriesread Allows you to set Redis' 'entries-read' STREAM value. This argument is - * only relevant to the 'CREATE' and 'SETID' operations. - * Note: Requires Redis >= 7.0.0. - * - * @return mixed This command return various results depending on the operation performed. - */ - public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, + * @see https://redis.io/docs/latest/commands/xgroup/ + * + * @param string $operation The subcommand you intend to execute. Valid options are as follows + * 'HELP' - Redis will return information about the command + * Requires: none + * 'CREATE' - Create a consumer group. + * Requires: Key, group, consumer. + * 'SETID' - Set the ID of an existing consumer group for the stream. + * Requires: Key, group, id. + * 'CREATECONSUMER' - Create a new consumer group for the stream. You must + * also pass key, group, and the consumer name you wish to + * create. + * Requires: Key, group, consumer. + * 'DELCONSUMER' - Delete a consumer from group attached to the stream. + * Requires: Key, group, consumer. + * 'DESTROY' - Delete a consumer group from a stream. + * Requires: Key, group. + * @param string|null $key The STREAM we're operating on. + * @param string|null $group The consumer group we want to create/modify/delete. + * @param string|null $id_or_consumer The STREAM id (e.g. '$') or consumer group. See the operation section + * for information about which to send. + * @param bool $mkstream This flag may be sent in combination with the 'CREATE' operation, and + * cause Redis to also create the STREAM if it doesn't currently exist. + * @param int $entries_read Allows you to set Redis' 'entries-read' STREAM value. This argument is + * only relevant to the 'CREATE' and 'SETID' operations. + * Note: Requires Redis >= 7.0.0. + * + * @return mixed This command return various results depending on the operation performed. + * + * @example + * $redis->xgroup('CREATE', 'mystream', 'workers', '$'); + * + */ + public function xgroup(string $operation, ?string $key = null, ?string $group = null, ?string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2): mixed; /** * Retrieve information about a stream key. * - * @param string $operation The specific info operation to perform. - * @param string $arg1 The first argument (depends on operation) - * @param string $arg2 The second argument - * @param int $count The COUNT argument to `XINFO STREAM` + * @param string $operation The specific info operation to perform. + * @param string|null $arg1 The first argument (depends on operation) + * @param string|null $arg2 The second argument + * @param int $count The COUNT argument to `XINFO STREAM` * * @return mixed This command can return different things depending on the operation being called. * - * @see https://redis.io/commands/xinfo + * @see https://redis.io/docs/latest/commands/xinfo/ * * @example $redis->xInfo('CONSUMERS', 'stream'); * @example $redis->xInfo('GROUPS', 'stream'); @@ -3824,7 +4928,7 @@ public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = n * * @return Redis|int|false The number of messages or false on failure. * - * @see https://redis.io/commands/xlen + * @see https://redis.io/docs/latest/commands/xlen/ * * @example $redis->xLen('stream'); */ @@ -3834,18 +4938,21 @@ public function xlen(string $key): Redis|int|false; * Interact with stream messages that have been consumed by a consumer group but not yet * acknowledged with XACK. * - * @see https://redis.io/commands/xpending - * @see https://redis.io/commands/xreadgroup + * @see https://redis.io/docs/latest/commands/xpending/ + * @see https://redis.io/docs/latest/commands/xreadgroup/ * - * @param string $key The stream to inspect. - * @param string $group The user group we want to see pending messages from. - * @param string $start The minimum ID to consider. - * @param string $string The maximum ID to consider. - * @param string $count Optional maximum number of messages to return. - * @param string $consumer If provided, limit the returned messages to a specific consumer. + * @param string $key The stream to inspect. + * @param string $group The user group we want to see pending messages from. + * @param string|null $start The minimum ID to consider. + * @param string|null $end The maximum ID to consider. + * @param int $count Optional maximum number of messages to return. + * @param string|null $consumer If provided, limit the returned messages to a specific consumer. * * @return Redis|array|false The pending messages belonging to the stream or false on failure. * + * @example + * $redis->xpending('mystream', 'workers', '-', '+', 10); + * */ public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): Redis|array|false; @@ -3859,7 +4966,7 @@ public function xpending(string $key, string $group, ?string $start = null, ?str * * @return Redis|array|bool The entries in the stream within the requested range or false on failure. * - * @see https://redis.io/commands/xrange + * @see https://redis.io/docs/latest/commands/xrange/ * * @example $redis->xRange('stream', '0-1', '0-2'); * @example $redis->xRange('stream', '-', '+'); @@ -3870,13 +4977,13 @@ public function xrange(string $key, string $start, string $end, int $count = -1) * Consume one or more unconsumed elements in one or more streams. * * @param array $streams An associative array with stream name keys and minimum id values. - * @param int $count An optional limit to how many entries are returnd *per stream* + * @param int $count An optional limit to how many entries are returned *per stream* * @param int $block An optional maximum number of milliseconds to block the caller if no * data is available on any of the provided streams. * * @return Redis|array|bool An array of read elements or false if there aren't any. * - * @see https://redis.io/commands/xread + * @see https://redis.io/docs/latest/commands/xread/ * * @example * $redis->xAdd('s03', '3-1', ['title' => 'The Search, Part I']); @@ -3901,7 +5008,7 @@ public function xread(array $streams, int $count = -1, int $block = -1): Redis|a * * @return Redis|array|bool Zero or more unread messages or false on failure. * - * @see https://redis.io/commands/xreadgroup + * @see https://redis.io/docs/latest/commands/xreadgroup/ * * @example * $redis->xGroup('CREATE', 'episodes', 'ds9', '0-0', true); @@ -3926,7 +5033,7 @@ public function xread(array $streams, int $count = -1, int $block = -1): Redis|a public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): Redis|array|bool; /** - * Get a range of entries from a STREAM ke in reverse cronological order. + * Get a range of entries from a STREAM key in reverse chronological order. * * @param string $key The stream key to query. * @param string $end The maximum message ID to include. @@ -3935,14 +5042,252 @@ public function xreadgroup(string $group, string $consumer, array $streams, int * * @return Redis|array|bool The entries within the requested range, from newest to oldest. * - * @see https://redis.io/commands/xrevrange - * @see https://redis.io/commands/xrange + * @see https://redis.io/docs/latest/commands/xrevrange/ + * @see https://redis.io/docs/latest/commands/xrange/ * * @example $redis->xRevRange('stream', '0-2', '0-1'); * @example $redis->xRevRange('stream', '+', '-'); */ public function xrevrange(string $key, string $end, string $start, int $count = -1): Redis|array|bool; + /** + * Add to a vector set + * + * @param string $key The vector set to add to. + * @param array $values A non-empty array of floating point values + * @param mixed $element The element to add to the vector set. + * @param array|null $options An optional options array + * + * @return Redis|int|false One if the key was added zero if not. + * + * @see https://redis.io/docs/latest/commands/vadd/ + * + * @example + * $redis->vadd('embeddings', [0.12, 0.04, 0.88], 'doc:1'); + * + */ + public function vadd(string $key, array $values, mixed $element, array|null $options = null): Redis|int|false; + + /** + * Query similarity of a vector by element or scores + * + * @param string $key The vector set to query. + * @param mixed $member Either an element or array of scores. PhpRedis + * will attempt to infer which it is, but since + * there can be some ambiguity here due to + * serialization you can also explicitly specify + * `ELE`, `VALUES`, or `FP32` in the options + * array. + * @param array|null $options An optional options array + * + * @return Redis|array|false An array of elements and their similarity scores, or false on failure. + * + * @see https://redis.io/docs/latest/commands/vsim/ + * + * @example + * $redis->vsim('embeddings', 'doc:1', ['COUNT' => 3]); + * + */ + public function vsim(string $key, mixed $member, array|null $options = null): Redis|array|false; + + /** + * Get the length of a vector set + * + * @param string $key The vector set to query. + * + * @return Redis|int|false The number of elements in the vector set or false on failure. + * + * @see https://redis.io/docs/latest/commands/vcard/ + * + * @example + * $redis->vcard('embeddings'); + * + */ + public function vcard(string $key): Redis|int|false; + + /** + * Get the dimensions of a vector set + * + * @param string $key The vector set to query. + * + * @return Redis|int|false The number of dimensions in the vector set or false on failure. + * + * @see https://redis.io/docs/latest/commands/vdim/ + * + * @example + * $redis->vdim('embeddings'); + * + */ + public function vdim(string $key): Redis|int|false; + + /** + * Get various bits of information about a vector set + * + * @param string $key The vector set to query. + * + * @return Redis|array|false An array of information about the vector set or false on failure. + * + * @see https://redis.io/docs/latest/commands/vinfo/ + * + * @example + * $redis->vinfo('embeddings'); + * + */ + public function vinfo(string $key): Redis|array|false; + + /** + * Check if an element is a member of a vectorset + * + * @param string $key The vector set to query. + * @param mixed $member The member to check for. + * + * @return Redis|bool true if the member exists, false if it does not. + * + * @see https://redis.io/docs/latest/commands/vismember/ + * + * @example + * $redis->vismember('embeddings', 'doc:1'); + * + */ + public function vismember(string $key, mixed $member): Redis|bool; + + /** + * Get the embeddings for a specific member + * + * @param string $key The vector set to query. + * @param mixed $member The member to query. + * @param bool $raw If set to `true`, the raw embeddings will be returned + * + * @return Redis|array|false An array of embeddings for the member or false on failure. + * + * @see https://redis.io/docs/latest/commands/vemb/ + * + * @example + * $redis->vemb('embeddings', 'doc:1'); + * + */ + public function vemb(string $key, mixed $member, bool $raw = false): Redis|array|false; + + /** + * Get one or more random members from a vector set + * + * @param string $key The vector set to query. + * @param int $count The number of random members to return. + * + * @see https://redis.io/docs/latest/commands/vrandmember/ + * + * @example + * $redis->vrandmember('embeddings', 2); + * + */ + public function vrandmember(string $key, int $count = 0): Redis|array|string|false; + + /** + * Retreive a lexographical range of elements from a vector set + * + * @param string $key The vector set to query. + * @param string $min The minimum element to return. + * @param string $max The maximum element to return. + * @param int $count An optional maximum number of elements to return. + * + * @return Redis|array|false An array of elements in the requested range or false on failure. + * + * @see https://redis.io/docs/latest/commands/vrange/ + * + * @example + * $redis->vrange('embeddings', '-', '+', 5); + * + */ + public function vrange(string $key, string $min, string $max, int $count = -1): Redis|array|false; + + /** + * Remove an element from a vector set + * + * @param string $key The vector set to remove from. + * @param mixed $member The member to remove. + * + * @return Redis|int|faslse 1 if the member was removed, 0 if it was not. + * + * @see https://redis.io/docs/latest/commands/vrem/ + * + * @example + * $redis->vrem('embeddings', 'doc:1'); + * + */ + public function vrem(string $key, mixed $member): Redis|int|false; + + /** + * Set the attributes of a vector set element + * + * @param string $key The vector set to modify. + * @param mixed $member The member to modify. + * @param array|string $attributes The attributes to set. This should either + * be a json encoded string or an array which + * will be json encoded. + * + * @return Redis|int|false 1 if the attributes were set, 0 if they were not. + * + * @see https://redis.io/docs/latest/commands/vsetattr/ + * + * @example + * $redis->vsetattr('embeddings', 'doc:1', ['topic' => 'tech']); + * + */ + public function vsetattr(string $key, mixed $member, array|string $attributes): Redis|int|false; + + /** + * Get the attributes of a vector set element + * + * @param string $key The vector set to query. + * @param mixed $member The member to query. + * @param bool $decode Whether to automatically deserialize any returned json. + * + * @return Redis|array|string|false An array of attributes for the member or false on failure. + * + * @see https://redis.io/docs/latest/commands/vgetattr/ + * + * @example + * $redis->vgetattr('embeddings', 'doc:1'); + * + */ + public function vgetattr(string $key, mixed $member, bool $decode = true): Redis|array|string|false; + + /** + * Get any adajcent values for a member of a vector set. + * + * @param string $key The vector set to query. + * @param mixed $member The member to query. + * @param bool $withscores If set to `true`, the scores of the adjacent values will be returned. + * + * @return Redis|array|false An array of adjacent values and their scores, or false on failure. + * + * @see https://redis.io/docs/latest/commands/vlinks/ + * + * @example + * $redis->vlinks('embeddings', 'doc:1', true); + * + */ + public function vlinks(string $key, mixed $member, bool $withscores = false): Redis|array|false; + + /** + * Get rate limiting information + * + * @param string $key + * @param int $maxBurst + * @param int $requestsPerPeriod + * @param int $period + * @param int $tokens = 0 + * @return Redis|array|false + * + * @see https://redis.io/docs/latest/commands/gcra/ + * + * @example + * $redis->gcra('user:123', 10, 100, 3600); + */ + public function gcra(string $key, int $maxBurst, int $requestsPerPeriod, + int $period, int $tokens = 0): Redis|array|false; + + /** * Truncate a STREAM key in various ways. * @@ -3958,7 +5303,7 @@ public function xrevrange(string $key, string $end, string $start, int $count = * * @return Redis|int|false The number of entries deleted from the stream. * - * @see https://redis.io/commands/xtrim + * @see https://redis.io/docs/latest/commands/xtrim/ * * @example $redis->xTrim('stream', 3); * @example $redis->xTrim('stream', '2-1', false, true); @@ -3968,52 +5313,52 @@ public function xtrim(string $key, string $threshold, bool $approx = false, bool /** * Add one or more elements and scores to a Redis sorted set. * - * @param string $key The sorted set in question. - * @param array|float $score_or_options Either the score for the first element, or an array of options. - * - * $options = [ - * 'NX', # Only update elements that already exist - * 'NX', # Only add new elements but don't update existing ones. - * - * 'LT' # Only update existing elements if the new score is - * # less than the existing one. - * 'GT' # Only update existing elements if the new score is - * # greater than the existing one. - * - * 'CH' # Instead of returning the number of elements added, - * # Redis will return the number Of elements that were - * # changed in the operation. - * - * 'INCR' # Instead of setting each element to the provide score, - * # increment the element by the - * # provided score, much like ZINCRBY. When this option - * # is passed, you may only send a single score and member. - * ]; - * - * Note: 'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis - * will send whichever one is last in the options array. - * - * @param mixed $more_scores_and_mems A variadic number of additional scores and members. - * - * @return Redis|int|false The return value varies depending on the options passed. + * @param string $key The sorted set in question. + * @param array|float $score_or_options Either the score for the first element, or an array of options. + * ```php + * $options = [ + * 'NX', # Only update elements that already exist + * 'NX', # Only add new elements but don't update existing ones. + * + * 'LT' # Only update existing elements if the new score is + * # less than the existing one. + * 'GT' # Only update existing elements if the new score is + * # greater than the existing one. + * + * 'CH' # Instead of returning the number of elements added, + * # Redis will return the number Of elements that were + * # changed in the operation. + * + * 'INCR' # Instead of setting each element to the provide score, + * # increment the element by the + * # provided score, much like ZINCRBY. When this option + * # is passed, you may only send a single score and member. + * ]; + * ``` + * Note: 'GX', 'LT', and 'NX' cannot be passed together, and PhpRedis + * will send whichever one is last in the options array. + * @param mixed ...$more_scores_and_mems A variadic number of additional scores and members. + * + * @return Redis|int|float|false The return value varies depending on the options passed. * * Following is information about the options that may be passed as the second argument: * - * @see https://redis.io/commands/zadd + * @see https://redis.io/docs/latest/commands/zadd/ * - * @example $redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third'); - * @example $redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element'); + * @example + * $redis->zadd('zs', 1, 'first', 2, 'second', 3, 'third'); + * $redis->zAdd('zs', ['XX'], 8, 'second', 99, 'new-element'); */ public function zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): Redis|int|float|false; /** * Return the number of elements in a sorted set. * - * @param string $key The sorted set to retreive cardinality from. + * @param string $key The sorted set to retrieve cardinality from. * * @return Redis|int|false The number of elements in the set or false on failure * - * @see https://redis.io/commands/zcard + * @see https://redis.io/docs/latest/commands/zcard/ * * @example $redis->zCard('zs'); */ @@ -4022,20 +5367,21 @@ public function zCard(string $key): Redis|int|false; /** * Count the number of members in a sorted set with scores inside a provided range. * - * @param string $key The sorted set to check. - * @param string $min The minimum score to include in the count - * @param string $max The maximum score to include in the count + * @param string $key The sorted set to check. + * @param int|string $start The minimum score to include in the count + * @param int|string $end The maximum score to include in the count * * NOTE: In addition to a floating point score you may pass the special values of '-inf' and * '+inf' meaning negative and positive infinity, respectively. * - * @see https://redis.io/commands/zcount + * @see https://redis.io/docs/latest/commands/zcount/ * - * @example $redis->zCount('fruit-rankings', '0', '+inf'); - * @example $redis->zCount('fruit-rankings', 50, 60); - * @example $redis->zCount('fruit-rankings', '-inf', 0); + * @example + * $redis->zCount('fruit-rankings', '0', '+inf'); + * $redis->zCount('fruit-rankings', 50, 60); + * $redis->zCount('fruit-rankings', '-inf', 0); */ - public function zCount(string $key, string $start, string $end): Redis|int|false; + public function zCount(string $key, int|string $start, int|string $end): Redis|int|false; /** * Create or increment the score of a member in a Redis sorted set @@ -4045,15 +5391,16 @@ public function zCount(string $key, string $start, string $end): Redis|int|false * * @return Redis|float|false The new score of the member or false on failure. * - * @see https://redis.io/commands/zincrby + * @see https://redis.io/docs/latest/commands/zincrby/ * - * @example $redis->zIncrBy('zs', 5.0, 'bananas'); - * @example $redis->zIncrBy('zs', 2.0, 'eggplants'); + * @example + * $redis->zIncrBy('zs', 5.0, 'bananas'); + * $redis->zIncrBy('zs', 2.0, 'eggplants'); */ public function zIncrBy(string $key, float $value, mixed $member): Redis|float|false; /** - * Count the number of elements in a sorted set whos members fall within the provided + * Count the number of elements in a sorted set whose members fall within the provided * lexographical range. * * @param string $key The sorted set to check. @@ -4062,7 +5409,7 @@ public function zIncrBy(string $key, float $value, mixed $member): Redis|float|f * * @return Redis|int|false The number of members that fall within the range or false on failure. * - * @see https://redis.io/commands/zlexcount + * @see https://redis.io/docs/latest/commands/zlexcount/ * * @example * $redis->zAdd('captains', 0, 'Janeway', 0, 'Kirk', 0, 'Picard', 0, 'Sisko', 0, 'Archer'); @@ -4073,11 +5420,11 @@ public function zLexCount(string $key, string $min, string $max): Redis|int|fals /** * Retrieve the score of one or more members in a sorted set. * - * @see https://redis.io/commands/zmscore + * @see https://redis.io/docs/latest/commands/zmscore/ * - * @param string $key The sorted set - * @param mixed $member The first member to return the score from - * @param mixed $other_members One or more additional members to return the scores of. + * @param string $key The sorted set + * @param mixed $member The first member to return the score from + * @param mixed ...$other_members One or more additional members to return the scores of. * * @return Redis|array|false An array of the scores of the requested elements. * @@ -4092,12 +5439,12 @@ public function zMscore(string $key, mixed $member, mixed ...$other_members): Re /** * Pop one or more of the highest scoring elements from a sorted set. * - * @param string $key The sorted set to pop elements from. - * @param int $count An optional count of elements to pop. + * @param string $key The sorted set to pop elements from. + * @param int|null $count An optional count of elements to pop. * - * @return Redis|array|false All of the popped elements with scores or false on fialure. + * @return Redis|array|false All of the popped elements with scores or false on failure * - * @see https://redis.io/commands/zpopmax + * @see https://redis.io/docs/latest/commands/zpopmax/ * * @example * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three'); @@ -4105,17 +5452,17 @@ public function zMscore(string $key, mixed $member, mixed ...$other_members): Re * $redis->zPopMax('zs'); * $redis->zPopMax('zs', 2);. */ - public function zPopMax(string $key, int $count = null): Redis|array|false; + public function zPopMax(string $key, ?int $count = null): Redis|array|false; /** * Pop one or more of the lowest scoring elements from a sorted set. * - * @param string $key The sorted set to pop elements from. - * @param int $count An optional count of elements to pop. + * @param string $key The sorted set to pop elements from. + * @param int|null $count An optional count of elements to pop. * * @return Redis|array|false The popped elements with their scores or false on failure. * - * @see https://redis.io/commands/zpopmin + * @see https://redis.io/docs/latest/commands/zpopmin/ * * @example * $redis->zAdd('zs', 0, 'zero', 1, 'one', 2, 'two', 3, 'three'); @@ -4123,7 +5470,7 @@ public function zPopMax(string $key, int $count = null): Redis|array|false; * $redis->zPopMin('zs'); * $redis->zPopMin('zs', 2); */ - public function zPopMin(string $key, int $count = null): Redis|array|false; + public function zPopMin(string $key, ?int $count = null): Redis|array|false; /** * Retrieve a range of elements of a sorted set between a start and end point. @@ -4137,7 +5484,7 @@ public function zPopMin(string $key, int $count = null): Redis|array|false; * @param array|bool|null $options This value may either be an array of options to pass to * the command, or for historical purposes a boolean which * controls just the 'WITHSCORES' option. - * + * ```php * $options = [ * 'WITHSCORES' => true, # Return both scores and members. * 'LIMIT' => [10, 10], # Start at offset 10 and return 10 elements. @@ -4145,25 +5492,26 @@ public function zPopMin(string $key, int $count = null): Redis|array|false; * 'BYSCORE', # Treat `start` and `end` as scores instead * 'BYLEX' # Treat `start` and `end` as lexicographical values. * ]; - * + * ``` * - * Note: 'BYLEX' and 'BYSCORE' are mutually exclusive. + * Note: `BYLEX` and `BYSCORE` are mutually exclusive. * * * @return Redis|array|false An array with matching elements or false on failure. * - * @see https://redis.io/commands/zrange/ + * @see https://redis.io/docs/latest/commands/zrange/ * @category zset * - * @example $redis->zRange('zset', 0, -1); - * @example $redis->zRange('zset', '-inf', 'inf', ['byscore']); + * @example + * $redis->zRange('zset', 0, -1); + * $redis->zRange('zset', '-inf', 'inf', ['byscore']); */ public function zRange(string $key, string|int $start, string|int $end, array|bool|null $options = null): Redis|array|false; /** * Retrieve a range of elements from a sorted set by legographical range. * - * @param string $key The sorted set to retreive elements from + * @param string $key The sorted set to retrieve elements from * @param string $min The minimum legographical value to return * @param string $max The maximum legographical value to return * @param int $offset An optional offset within the matching values to return @@ -4171,7 +5519,7 @@ public function zRange(string $key, string|int $start, string|int $end, array|bo * * @return Redis|array|false An array of matching elements or false on failure. * - * @see https://redis.io/commands/zrangebylex + * @see https://redis.io/docs/latest/commands/zrangebylex/ * * @example * $redis = new Redis(['host' => 'localhost']); @@ -4196,10 +5544,11 @@ public function zRangeByLex(string $key, string $min, string $max, int $offset = * * @return Redis|array|false The number of matching elements or false on failure. * - * @see https://redis.io/commands/zrangebyscore + * @see https://redis.io/docs/latest/commands/zrangebyscore/ * - * @example $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]); - * @example $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]); + * @example + * $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true]); + * $redis->zRangeByScore('zs', 20, 30, ['WITHSCORES' => true, 'LIMIT' => [5, 5]]); */ public function zRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false; @@ -4215,59 +5564,65 @@ public function zRangeByScore(string $key, string $start, string $end, array $op * * @return Redis|int|false The number of elements stored in $dstkey or false on failure. * - * @see https://redis.io/commands/zrange/ + * @see https://redis.io/docs/latest/commands/zrange/ * @see Redis::zRange * @category zset * * See {@link Redis::zRange} for a full description of the possible options. + * + * @example + * $redis->zrangestore('recent:leaders', 'leaders', '0', '9'); + * */ public function zrangestore(string $dstkey, string $srckey, string $start, string $end, - array|bool|null $options = NULL): Redis|int|false; + array|bool|null $options = null): Redis|int|false; /** * Retrieve one or more random members from a Redis sorted set. * - * @param string $key The sorted set to pull random members from. - * @param array $options One or more options that determine exactly how the command operates. - * - * OPTION TYPE MEANING - * 'COUNT' int The number of random members to return. - * 'WITHSCORES' bool Whether to return scores and members instead of + * @param string $key The sorted set to pull random members from. + * @param array|null $options One or more options that determine exactly how the command operates. + * OPTION TYPE MEANING + * 'COUNT' int The number of random members to return. + * 'WITHSCORES' bool Whether to return scores and members instead of * - * @return Redis|string|array One ore more random elements. + * @return Redis|string|array One or more random elements. * - * @see https://redis.io/commands/zrandmember + * @see https://redis.io/docs/latest/commands/zrandmember/ * - * @example $redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]); + * @example + * $redis->zRandMember('zs', ['COUNT' => 2, 'WITHSCORES' => true]); */ - public function zRandMember(string $key, array $options = null): Redis|string|array; + public function zRandMember(string $key, ?array $options = null): Redis|string|array; /** * Get the rank of a member of a sorted set, by score. * * @param string $key The sorted set to check. - * @param mixed $memeber The member to test. + * @param mixed $member The member to test. * * @return Redis|int|false The rank of the requested member. - * @see https://redis.io/commands/zrank + * @see https://redis.io/docs/latest/commands/zrank/ * - * @example $redis->zRank('zs', 'zero'); - * @example $redis->zRank('zs', 'three'); + * @example + * $redis->zRank('zs', 'zero'); + * $redis->zRank('zs', 'three'); */ public function zRank(string $key, mixed $member): Redis|int|false; /** * Remove one or more members from a Redis sorted set. * - * @param mixed $key The sorted set in question. - * @param mixed $member The first member to remove. - * @param mixed $other_members One or more members to remove passed in a variadic fashion. + * @param mixed $key The sorted set in question. + * @param mixed $member The first member to remove. + * @param mixed ...$other_members One or more members to remove passed in a variadic fashion. * * @return Redis|int|false The number of members that were actually removed or false on failure. * - * @see https://redis.io/commands/zrem + * @see https://redis.io/docs/latest/commands/zrem/ * - * @example $redis->zRem('zs', 'mem:0', 'mem:1', 'mem:2', 'mem:6', 'mem:7', 'mem:8', 'mem:9'); + * @example + * $redis->zRem('zs', 'mem:0', 'mem:1', 'mem:2', 'mem:6', 'mem:7', 'mem:8', 'mem:9'); */ public function zRem(mixed $key, mixed $member, mixed ...$other_members): Redis|int|false; @@ -4280,39 +5635,41 @@ public function zRem(mixed $key, mixed $member, mixed ...$other_members): Redis| * * @return Redis|int|false The number of elements removed from the set or false on failure. * - * @see https://redis.io/commands/zremrangebylex + * @see https://redis.io/docs/latest/commands/zremrangebylex/ * @see Redis::zrangebylex() * - * @example $redis->zRemRangeByLex('zs', '[a', '(b'); - * @example $redis->zRemRangeByLex('zs', '(banana', '(eggplant'); + * @example + * $redis->zRemRangeByLex('zs', '[a', '(b'); + * $redis->zRemRangeByLex('zs', '(banana', '(eggplant'); */ public function zRemRangeByLex(string $key, string $min, string $max): Redis|int|false; /** * Remove one or more members of a sorted set by their rank. * - * @param string $key The sorted set where we wnat to remove members. + * @param string $key The sorted set where we want to remove members. * @param int $start The rank when we want to start removing members * @param int $end The rank we want to stop removing membersk. * * @return Redis|int|false The number of members removed from the set or false on failure. * - * @see https://redis.io/commands/zremrangebyrank + * @see https://redis.io/docs/latest/commands/zremrangebyrank/ * - * @example $redis->zRemRangeByRank('zs', 0, 3); + * @example + * $redis->zRemRangeByRank('zs', 0, 3); */ public function zRemRangeByRank(string $key, int $start, int $end): Redis|int|false; /** * Remove one or more members of a sorted set by their score. * - * @param string $key The sorted set where we wnat to remove members. - * @param int $start The lowest score to remove. - * @param int $end The highest score to remove. + * @param string $key The sorted set where we want to remove members. + * @param string $start The lowest score to remove. + * @param string $end The highest score to remove. * * @return Redis|int|false The number of members removed from the set or false on failure. * - * @see https://redis.io/commands/zremrangebyrank + * @see https://redis.io/docs/latest/commands/zremrangebyrank/ * * @example * $redis->zAdd('zs', 2, 'two', 4, 'four', 6, 'six'); @@ -4323,21 +5680,22 @@ public function zRemRangeByScore(string $key, string $start, string $end): Redis /** * List the members of a Redis sorted set in reverse order * - * @param string $key The sorted set in question. - * @param int $start The index to start listing elements - * @param int $end The index to stop listing elements. - * @param mixed $withscores Whether or not Redis should also return each members score. See + * @param string $key The sorted set in question. + * @param int $start The index to start listing elements + * @param int $end The index to stop listing elements. + * @param mixed|null $scores Whether or not Redis should also return each members score. See * the example below demonstrating how it may be used. * * @return Redis|array|false The members (and possibly scores) of the matching elements or false * on failure. * - * @see https://redis.io/commands/zrevrange + * @see https://redis.io/docs/latest/commands/zrevrange/ * - * @example $redis->zRevRange('zs', 0, -1); - * @example $redis->zRevRange('zs', 2, 3); - * @example $redis->zRevRange('zs', 0, -1, true); - * @example $redis->zRevRange('zs', 0, -1, ['withscores' => true]); + * @example + * $redis->zRevRange('zs', 0, -1); + * $redis->zRevRange('zs', 2, 3); + * $redis->zRevRange('zs', 0, -1, true); + * $redis->zRevRange('zs', 0, -1, ['withscores' => true]); */ public function zRevRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false; @@ -4345,42 +5703,43 @@ public function zRevRange(string $key, int $start, int $end, mixed $scores = nul * List members of a Redis sorted set within a legographical range, in reverse order. * * @param string $key The sorted set to list - * @param string $min The maximum legographical element to include in the result. + * @param string $max The maximum legographical element to include in the result. * @param string $min The minimum lexographical element to include in the result. - * @param string $offset An option offset within the matching elements to start at. - * @param string $count An optional count to limit the replies to. + * @param int $offset An option offset within the matching elements to start at. + * @param int $count An optional count to limit the replies to. * * @return Redis|array|false The matching members or false on failure. * - * @see https://redis.io/commands/zrevrangebylex + * @see https://redis.io/docs/latest/commands/zrevrangebylex/ * @see Redis::zrangebylex() * - * @example $redis->zRevRangeByLex('captains', '[Q', '[J'); - * @example $redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2); + * @example + * $redis->zRevRangeByLex('captains', '[Q', '[J'); + * $redis->zRevRangeByLex('captains', '[Q', '[J', 1, 2); */ public function zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1): Redis|array|false; /** * List elements from a Redis sorted set by score, highest to lowest * - * @param string $key The sorted set to query. - * @param string $max The highest score to include in the results. - * @param string $min The lowest score to include in the results. - * @param array $options An options array that modifies how the command executes. + * @param string $key The sorted set to query. + * @param string $max The highest score to include in the results. + * @param string $min The lowest score to include in the results. + * @param array|bool $options An options array that modifies how the command executes. * - * - * $options = [ - * 'WITHSCORES' => true|false # Whether or not to return scores - * 'LIMIT' => [offset, count] # Return a subset of the matching members - * ]; - * + * ```php + * $options = [ + * 'WITHSCORES' => true|false # Whether or not to return scores + * 'LIMIT' => [offset, count] # Return a subset of the matching members + * ]; + * ``` * - * NOTE: For legacy reason, you may also simply pass `true` for the - * options argument, to mean `WITHSCORES`. + * NOTE: For legacy reason, you may also simply pass `true` for the + * options argument, to mean `WITHSCORES`. * * @return Redis|array|false The matching members in reverse order of score or false on failure. * - * @see https://redis.io/commands/zrevrangebyscore + * @see https://redis.io/docs/latest/commands/zrevrangebyscore/ * * @example * $redis->zadd('oldest-people', 122.4493, 'Jeanne Calment', 119.2932, 'Kane Tanaka', @@ -4403,7 +5762,7 @@ public function zRevRangeByScore(string $key, string $max, string $min, array|bo * * @return Redis|int|false The reverse rank (the rank if counted high to low) of the member or * false on failure. - * @see https://redis.io/commands/zrevrank + * @see https://redis.io/docs/latest/commands/zrevrank/ * * @example * $redis->zAdd('ds9-characters', 10, 'Sisko', 9, 'Garak', 8, 'Dax', 7, 'Odo'); @@ -4419,9 +5778,9 @@ public function zRevRank(string $key, mixed $member): Redis|int|false; * @param string $key The sorted set to query. * @param mixed $member The member we wish to query. * - * @return The score of the requested element or false if it is not found. + * @return Redis|float|false The score of the requested element or false if it is not found. * - * @see https://redis.io/commands/zscore + * @see https://redis.io/docs/latest/commands/zscore/ * * @example * $redis->zAdd('telescopes', 11.9, 'LBT', 10.4, 'GTC', 10, 'HET'); @@ -4433,13 +5792,13 @@ public function zScore(string $key, mixed $member): Redis|float|false; * Given one or more sorted set key names, return every element that is in the first * set but not any of the others. * - * @param array $keys One ore more sorted sets. - * @param array $options An array which can contain ['WITHSCORES' => true] if you want Redis to - * return members and scores. + * @param array $keys One or more sorted sets. + * @param array|null $options An array which can contain ['WITHSCORES' => true] if you want Redis to + * return members and scores. * * @return Redis|array|false An array of members or false on failure. * - * @see https://redis.io/commands/zdiff + * @see https://redis.io/docs/latest/commands/zdiff/ * * @example * $redis->zAdd('primes', 1, 'one', 3, 'three', 5, 'five'); @@ -4448,36 +5807,40 @@ public function zScore(string $key, mixed $member): Redis|float|false; * * $redis->zDiff(['primes', 'evens', 'mod3']); */ - public function zdiff(array $keys, array $options = null): Redis|array|false; + public function zdiff(array $keys, ?array $options = null): Redis|array|false; /** * Store the difference of one or more sorted sets in a destination sorted set. * * See {@link Redis::zdiff} for a more detailed description of how the diff operation works. * - * @param string $key The destination set name. + * @param string $dst The destination set name. * @param array $keys One or more source key names * * @return Redis|int|false The number of elements stored in the destination set or false on * failure. * - * @see https://redis.io/commands/zdiff + * @see https://redis.io/docs/latest/commands/zdiff/ * @see Redis::zdiff() + * + * @example + * $redis->zdiffstore('only:new', ['all:users', 'inactive:users']); + * */ public function zdiffstore(string $dst, array $keys): Redis|int|false; /** * Compute the intersection of one or more sorted sets and return the members * - * @param array $keys One ore more sorted sets. - * @param array $weights An optional array of weights to be applied to each set when performing - * the intersection. - * @param array $options Options for how Redis should combine duplicate elements when performing the - * intersection. See Redis::zunion() for details. + * @param array $keys One or more sorted sets. + * @param array|null $weights An optional array of weights to be applied to each set when performing + * the intersection. + * @param array|null $options Options for how Redis should combine duplicate elements when performing the + * intersection. See Redis::zunion() for details. * * @return Redis|array|false All of the members that exist in every set. * - * @see https://redis.io/commands/zinter + * @see https://redis.io/docs/latest/commands/zinter/ * * @example * $redis->zAdd('TNG', 2, 'Worf', 2.5, 'Data', 4.0, 'Picard'); @@ -4493,11 +5856,11 @@ public function zinter(array $keys, ?array $weights = null, ?array $options = nu * Similar to ZINTER but instead of returning the intersected values, this command returns the * cardinality of the intersected set. * - * @see https://redis.io/commands/zintercard - * @see https://redis.io/commands/zinter + * @see https://redis.io/docs/latest/commands/zintercard/ + * @see https://redis.io/docs/latest/commands/zinter/ * @see Redis::zinter() * - * @param array $keys One ore more sorted set key names. + * @param array $keys One or more sorted set key names. * @param int $limit An optional upper bound on the returned cardinality. If set to a value * greater than zero, Redis will stop processing the intersection once the * resulting cardinality reaches this limit. @@ -4513,21 +5876,20 @@ public function zinter(array $keys, ?array $weights = null, ?array $options = nu public function zintercard(array $keys, int $limit = -1): Redis|int|false; /** - * Compute the intersection of one ore more sorted sets storing the result in a new sorted set. - * - * @param string $dst The destination sorted set to store the intersected values. - * @param array $keys One ore more sorted set key names. - * @param array $weights An optional array of floats to weight each passed input set. - * @param string $aggregate An optional aggregation method to use. + * Compute the intersection of one or more sorted sets storing the result in a new sorted set. * - * 'SUM' - Store sum of all intersected members (this is the default). - * 'MIN' - Store minimum value for each intersected member. - * 'MAX' - Store maximum value for each intersected member. + * @param string $dst The destination sorted set to store the intersected values. + * @param array $keys One or more sorted set key names. + * @param array|null $weights An optional array of floats to weight each passed input set. + * @param string|null $aggregate An optional aggregation method to use. + * 'SUM' - Store sum of all intersected members (this is the default). + * 'MIN' - Store minimum value for each intersected member. + * 'MAX' - Store maximum value for each intersected member. * * @return Redis|int|false The total number of members writtern to the destination set or false on failure. * - * @see https://redis.io/commands/zinterstore - * @see https://redis.io/commands/zinter + * @see https://redis.io/docs/latest/commands/zinterstore/ + * @see https://redis.io/docs/latest/commands/zinter/ * * @example * $redis->zAdd('zs1', 3, 'apples', 2, 'pears'); @@ -4542,50 +5904,60 @@ public function zinterstore(string $dst, array $keys, ?array $weights = null, ?s /** * Scan the members of a sorted set incrementally, using a cursor * - * @param string $key The sorted set to scan. - * @param int $iterator A reference to an iterator that should be initialized to NULL initially, that - * will be updated after each subsequent call to ZSCAN. Once the iterator - * has returned to zero the scan is complete - * @param string $pattern An optional glob-style pattern that limits which members are returned during - * the scanning process. - * @param int $count A hint for Redis that tells it how many elements it should test before returning - * from the call. The higher the more work Redis may do in any one given call to - * ZSCAN potentially blocking for longer periods of time. + * @param string $key The sorted set to scan. + * @param int|string|null $iterator A reference to an iterator that should be initialized to NULL initially, that + * will be updated after each subsequent call to ZSCAN. Once the iterator + * has returned to zero the scan is complete + * @param string|null $pattern An optional glob-style pattern that limits which members are returned during + * the scanning process. + * @param int $count A hint for Redis that tells it how many elements it should test before returning + * from the call. The higher the more work Redis may do in any one given call to + * ZSCAN potentially blocking for longer periods of time. * * @return Redis|array|false An array of elements or false on failure. * - * @see https://redis.io/commands/zscan - * @see https://redis.io/commands/scan + * @see https://redis.io/docs/latest/commands/zscan/ + * @see https://redis.io/docs/latest/commands/scan/ * @see Redis::scan() * * NOTE: See Redis::scan() for detailed example code on how to call SCAN like commands. * + * @example + * $it = null; + * while ($members = $redis->zscan('leaders', $it)) { + * foreach ($members as $member => $score) { + * printf('%s => %s' . PHP_EOL, $member, $score); + * } + * } + * */ - public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): Redis|array|false; + public function zscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): Redis|array|false; /** * Retrieve the union of one or more sorted sets * - * @param array $keys One ore more sorted set key names - * @param array $weights An optional array with floating point weights used when performing the union. - * Note that if this argument is passed, it must contain the same number of - * elements as the $keys array. - * @param array $options An array that modifies how this command functions. + * @param array $keys One or more sorted set key names + * @param array|null $weights An optional array with floating point weights used when performing the union. + * Note that if this argument is passed, it must contain the same number of + * elements as the $keys array. + * @param array|null $options An array that modifies how this command functions. * - * - * $options = [ - * # By default when members exist in more than one set Redis will SUM - * # total score for each match. Instead, it can return the AVG, MIN, - * # or MAX value based on this option. - * 'AGGREGATE' => 'sum' | 'min' | 'max' + * ```php + * $options = [ + * # By default when members exist in more than one set Redis will SUM + * # total score for each match. Instead, it can return the AVG, MIN, + * # or MAX value based on this option. + * 'AGGREGATE' => 'sum' | 'min' | 'max' * - * # Whether Redis should also return each members aggregated score. - * 'WITHSCORES' => true | false - * ] - * + * # Whether Redis should also return each members aggregated score. + * 'WITHSCORES' => true | false + * ] + * ``` * * @return Redis|array|false The union of each sorted set or false on failure * + * @see https://redis.io/docs/latest/commands/zunion/ + * * @example * $redis->del('store1', 'store2', 'store3'); * $redis->zAdd('store1', 1, 'apples', 3, 'pears', 6, 'bananas'); @@ -4601,15 +5973,15 @@ public function zunion(array $keys, ?array $weights = null, ?array $options = nu /** * Perform a union on one or more Redis sets and store the result in a destination sorted set. * - * @param string $dst The destination set to store the union. - * @param array $keys One or more input keys on which to perform our union. - * @param array $weights An optional weights array used to weight each input set. - * @param string $aggregate An optional modifier in how Redis will combine duplicate members. - * Valid: 'MIN', 'MAX', 'SUM'. + * @param string $dst The destination set to store the union. + * @param array $keys One or more input keys on which to perform our union. + * @param array|null $weights An optional weights array used to weight each input set. + * @param string|null $aggregate An optional modifier in how Redis will combine duplicate members. + * Valid: 'MIN', 'MAX', 'SUM'. * * @return Redis|int|false The number of members stored in the destination set or false on failure. * - * @see https://redis.io/commands/zunionstore + * @see https://redis.io/docs/latest/commands/zunionstore/ * @see Redis::zunion() * * @example @@ -4619,7 +5991,23 @@ public function zunion(array $keys, ?array $weights = null, ?array $options = nu * * $redis->zUnionStore('dst', ['zs1', 'zs2', 'zs3']); */ - public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): Redis|int|false; + public function zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): Redis|int|false; + + /** + * Ask the server for the XXH3 digest of a given key's value + * + * @param strinig $key The key to retrieve the digest for. + * @return Redis|string|false The XXH3 digest as a string or false on failure. + * + * @see https://redis.io/docs/latest/commands/digest/ + * + * @example + * $redis->digest('session:42'); + * + */ + public function digest(string $key): Redis|string|false; } + + class RedisException extends RuntimeException {} diff --git a/redis_arginfo.h b/redis_arginfo.h index 8429b51ff4..73acd29d8f 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,8 +1,8 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ + * Stub hash: 3ee3118802fef67d6bd7176f2a72f0568d962c8b */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___destruct, 0, 0, 0) @@ -28,6 +28,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis__pack arginfo_class_Redis__serialize +#define arginfo_class_Redis__digest arginfo_class_Redis__serialize + #define arginfo_class_Redis__unpack arginfo_class_Redis__unserialize ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_acl, 0, 1, IS_MIXED, 0) @@ -49,6 +51,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis_bgSave +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_waitaof, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, numlocal, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, numreplicas, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_bitcount, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, start, IS_LONG, 0, "0") @@ -121,30 +129,30 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_close arginfo_class_Redis_clearLastError ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_command, 0, 0, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, opt, IS_STRING, 1, "null") ZEND_ARG_VARIADIC_TYPE_INFO(0, args, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_config, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) - ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_MASK(0, key_or_settings, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_NULL, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_connect, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_copy, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_dbSize, 0, 0, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -169,11 +177,20 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_del, 0, 1, Redis ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_delex, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_delifeq arginfo_class_Redis_append + #define arginfo_class_Redis_delete arginfo_class_Redis_del #define arginfo_class_Redis_discard arginfo_class_Redis_bgSave -#define arginfo_class_Redis_dump arginfo_class_Redis_debug +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_dump, 0, 1, Redis, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_echo, 0, 1, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) @@ -210,13 +227,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expire, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expireAt, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_failover, 0, 0, Redis, MAY_BE_BOOL) @@ -315,6 +332,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_get, 0, 1, IS_MIXED, ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getWithMeta, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getAuth, 0, 0, IS_MIXED, 0) ZEND_END_ARG_INFO() @@ -351,6 +372,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_getPort arginfo_class_Redis_getDBNum +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_serverName, 0, 0, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_serverVersion arginfo_class_Redis_serverName + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_getRange, 0, 3, Redis, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) @@ -360,7 +386,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lcs, 0, 2, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_getReadTimeout, 0, 0, IS_DOUBLE, 0) @@ -396,9 +422,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_hGet, 0, 2, IS_MIXED ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hGetAll, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Redis_hGetAll arginfo_class_Redis_getWithMeta + +#define arginfo_class_Redis_hGetWithMeta arginfo_class_Redis_hGet ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrBy, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -412,7 +438,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hIncrByFloat, 0, ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_hKeys arginfo_class_Redis_hGetAll +#define arginfo_class_Redis_hKeys arginfo_class_Redis_getWithMeta #define arginfo_class_Redis_hLen arginfo_class_Redis_expiretime @@ -421,26 +447,39 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMget, 0, 2, Red ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hgetex, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_MASK(0, expiry, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_NULL, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hsetex, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, expiry, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_hgetdel arginfo_class_Redis_hMget + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hMset, 0, 2, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, fieldvals, IS_ARRAY, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hRandField, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSet, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSet, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) + ZEND_ARG_VARIADIC_TYPE_INFO(0, fields_and_vals, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hSetNx, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hStrLen, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -448,15 +487,61 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hStrLen, 0, 2, R ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_hVals arginfo_class_Redis_hGetAll +#define arginfo_class_Redis_hVals arginfo_class_Redis_getWithMeta + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hexpire, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_hpexpire arginfo_class_Redis_hexpire + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hexpireat, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, time, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hpexpireat, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, mstime, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_httl arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpttl arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hexpiretime arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpexpiretime arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpersist arginfo_class_Redis_hMget ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_hscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expiremember, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_expirememberat, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_Redis_incr arginfo_class_Redis_decr #define arginfo_class_Redis_incrBy arginfo_class_Redis_decrBy @@ -508,7 +593,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_lPush, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -553,7 +638,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ltrim, 0, 3, Red ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_mget, 0, 1, Redis, MAY_BE_ARRAY) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_mget, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -565,7 +650,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_migrate, 0, 5, R ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, copy, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, replace, _IS_BOOL, 0, "false") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, credentials, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_move, 0, 2, Redis, MAY_BE_BOOL) @@ -577,6 +662,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_mset, 0, 1, Redi ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_msetex, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key_vals, IS_ARRAY, 0) + ZEND_ARG_TYPE_MASK(0, expiry, MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_NULL, "null") +ZEND_END_ARG_INFO() + #define arginfo_class_Redis_msetnx arginfo_class_Redis_mset ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_multi, 0, 0, Redis, MAY_BE_BOOL) @@ -588,17 +678,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_object, 0, 2, Re ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_open, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent_id, IS_STRING, 0, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, retry_interval, IS_LONG, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, read_timeout, IS_DOUBLE, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL") -ZEND_END_ARG_INFO() +#define arginfo_class_Redis_open arginfo_class_Redis_connect -#define arginfo_class_Redis_pconnect arginfo_class_Redis_open +#define arginfo_class_Redis_pconnect arginfo_class_Redis_connect ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_persist, 0, 1, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -607,7 +689,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_pexpire, 0, 2, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_pexpireAt arginfo_class_Redis_expireAt @@ -627,13 +709,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pfmerge, 0, 2, R ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_ping, 0, 0, Redis, MAY_BE_STRING|MAY_BE_BOOL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_pipeline, 0, 0, Redis, MAY_BE_BOOL) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_popen arginfo_class_Redis_open +#define arginfo_class_Redis_popen arginfo_class_Redis_connect ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_psetex, 0, 3, Redis, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -691,7 +773,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_restore, 0, 3, R ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_role arginfo_class_Redis_getAuth @@ -735,7 +817,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_sInterStore arginfo_class_Redis_del -#define arginfo_class_Redis_sMembers arginfo_class_Redis_hGetAll +#define arginfo_class_Redis_sMembers arginfo_class_Redis_getWithMeta #define arginfo_class_Redis_sMisMember arginfo_class_Redis_geohash @@ -750,7 +832,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_sPop, 0, 1, Redi ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -#define arginfo_class_Redis_sRandMember arginfo_class_Redis_sPop +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_sRandMember, 0, 1, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() #define arginfo_class_Redis_sUnion arginfo_class_Redis_sDiff @@ -759,10 +844,10 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_save arginfo_class_Redis_bgSave ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_scan, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, type, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_scard arginfo_class_Redis_expiretime @@ -776,7 +861,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_set, 0, 2, Redis, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_setBit, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -810,7 +895,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_sismember arginfo_class_Redis_setnx ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_slaveof, 0, 0, Redis, MAY_BE_BOOL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, host, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, port, IS_LONG, 0, "6379") ZEND_END_ARG_INFO() @@ -852,7 +937,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_Redis_sscan, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -937,11 +1022,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xdel, 0, 2, Redi ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xdelex, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xgroup, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") ZEND_END_ARG_INFO() @@ -992,6 +1083,79 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xrevrange, 0, 3, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vadd, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, element, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vsim, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_vcard arginfo_class_Redis_expiretime + +#define arginfo_class_Redis_vdim arginfo_class_Redis_expiretime + +#define arginfo_class_Redis_vinfo arginfo_class_Redis_getWithMeta + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vismember, 0, 2, Redis, MAY_BE_BOOL) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vemb, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, raw, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vrandmember, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vrange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vrem, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vsetattr, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_MASK(0, attributes, MAY_BE_ARRAY|MAY_BE_STRING, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vgetattr, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, decode, _IS_BOOL, 0, "true") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_vlinks, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, withscores, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_gcra, 0, 4, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, maxBurst, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, requestsPerPeriod, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, period, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, tokens, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_xtrim, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, threshold, IS_STRING, 0) @@ -1010,8 +1174,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zCount, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) + ZEND_ARG_TYPE_MASK(0, start, MAY_BE_LONG|MAY_BE_STRING, NULL) + ZEND_ARG_TYPE_MASK(0, end, MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zIncrBy, 0, 3, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE) @@ -1034,7 +1198,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zPopMax, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zPopMin arginfo_class_Redis_zPopMax @@ -1066,16 +1230,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zrangestore, 0, ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) - ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "NULL") + ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() -#define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField - -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRank, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRandMember, 0, 1, Redis, MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() +#define arginfo_class_Redis_zRank arginfo_class_Redis_vrem + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRem, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) @@ -1090,7 +1254,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRemRangeByRank, ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_zCount +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRemRangeByScore, 0, 3, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -1114,7 +1282,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRangeByScore ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL, "[]") ZEND_END_ARG_INFO() -#define arginfo_class_Redis_zRevRank arginfo_class_Redis_zRank +#define arginfo_class_Redis_zRevRank arginfo_class_Redis_vrem ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zScore, 0, 2, Redis, MAY_BE_DOUBLE|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -1123,7 +1291,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiff, 0, 1, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zdiffstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -1148,20 +1316,16 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zscan, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #define arginfo_class_Redis_zunion arginfo_class_Redis_zinter -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zunionstore, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "NULL") -ZEND_END_ARG_INFO() +#define arginfo_class_Redis_zunionstore arginfo_class_Redis_zinterstore +#define arginfo_class_Redis_digest arginfo_class_Redis_dump ZEND_METHOD(Redis, __construct); ZEND_METHOD(Redis, __destruct); @@ -1171,12 +1335,14 @@ ZEND_METHOD(Redis, _prefix); ZEND_METHOD(Redis, _serialize); ZEND_METHOD(Redis, _unserialize); ZEND_METHOD(Redis, _pack); +ZEND_METHOD(Redis, _digest); ZEND_METHOD(Redis, _unpack); ZEND_METHOD(Redis, acl); ZEND_METHOD(Redis, append); ZEND_METHOD(Redis, auth); ZEND_METHOD(Redis, bgSave); ZEND_METHOD(Redis, bgrewriteaof); +ZEND_METHOD(Redis, waitaof); ZEND_METHOD(Redis, bitcount); ZEND_METHOD(Redis, bitop); ZEND_METHOD(Redis, bitpos); @@ -1201,6 +1367,8 @@ ZEND_METHOD(Redis, debug); ZEND_METHOD(Redis, decr); ZEND_METHOD(Redis, decrBy); ZEND_METHOD(Redis, del); +ZEND_METHOD(Redis, delex); +ZEND_METHOD(Redis, delifeq); ZEND_METHOD(Redis, discard); ZEND_METHOD(Redis, dump); ZEND_METHOD(Redis, echo); @@ -1231,6 +1399,7 @@ ZEND_METHOD(Redis, georadiusbymember_ro); ZEND_METHOD(Redis, geosearch); ZEND_METHOD(Redis, geosearchstore); ZEND_METHOD(Redis, get); +ZEND_METHOD(Redis, getWithMeta); ZEND_METHOD(Redis, getAuth); ZEND_METHOD(Redis, getBit); ZEND_METHOD(Redis, getEx); @@ -1242,6 +1411,8 @@ ZEND_METHOD(Redis, getMode); ZEND_METHOD(Redis, getOption); ZEND_METHOD(Redis, getPersistentID); ZEND_METHOD(Redis, getPort); +ZEND_METHOD(Redis, serverName); +ZEND_METHOD(Redis, serverVersion); ZEND_METHOD(Redis, getRange); ZEND_METHOD(Redis, lcs); ZEND_METHOD(Redis, getReadTimeout); @@ -1253,18 +1424,33 @@ ZEND_METHOD(Redis, hDel); ZEND_METHOD(Redis, hExists); ZEND_METHOD(Redis, hGet); ZEND_METHOD(Redis, hGetAll); +ZEND_METHOD(Redis, hGetWithMeta); ZEND_METHOD(Redis, hIncrBy); ZEND_METHOD(Redis, hIncrByFloat); ZEND_METHOD(Redis, hKeys); ZEND_METHOD(Redis, hLen); ZEND_METHOD(Redis, hMget); +ZEND_METHOD(Redis, hgetex); +ZEND_METHOD(Redis, hsetex); +ZEND_METHOD(Redis, hgetdel); ZEND_METHOD(Redis, hMset); ZEND_METHOD(Redis, hRandField); ZEND_METHOD(Redis, hSet); ZEND_METHOD(Redis, hSetNx); ZEND_METHOD(Redis, hStrLen); ZEND_METHOD(Redis, hVals); +ZEND_METHOD(Redis, hexpire); +ZEND_METHOD(Redis, hpexpire); +ZEND_METHOD(Redis, hexpireat); +ZEND_METHOD(Redis, hpexpireat); +ZEND_METHOD(Redis, httl); +ZEND_METHOD(Redis, hpttl); +ZEND_METHOD(Redis, hexpiretime); +ZEND_METHOD(Redis, hpexpiretime); +ZEND_METHOD(Redis, hpersist); ZEND_METHOD(Redis, hscan); +ZEND_METHOD(Redis, expiremember); +ZEND_METHOD(Redis, expirememberat); ZEND_METHOD(Redis, incr); ZEND_METHOD(Redis, incrBy); ZEND_METHOD(Redis, incrByFloat); @@ -1291,6 +1477,7 @@ ZEND_METHOD(Redis, mget); ZEND_METHOD(Redis, migrate); ZEND_METHOD(Redis, move); ZEND_METHOD(Redis, mset); +ZEND_METHOD(Redis, msetex); ZEND_METHOD(Redis, msetnx); ZEND_METHOD(Redis, multi); ZEND_METHOD(Redis, object); @@ -1374,6 +1561,7 @@ ZEND_METHOD(Redis, xadd); ZEND_METHOD(Redis, xautoclaim); ZEND_METHOD(Redis, xclaim); ZEND_METHOD(Redis, xdel); +ZEND_METHOD(Redis, xdelex); ZEND_METHOD(Redis, xgroup); ZEND_METHOD(Redis, xinfo); ZEND_METHOD(Redis, xlen); @@ -1382,6 +1570,20 @@ ZEND_METHOD(Redis, xrange); ZEND_METHOD(Redis, xread); ZEND_METHOD(Redis, xreadgroup); ZEND_METHOD(Redis, xrevrange); +ZEND_METHOD(Redis, vadd); +ZEND_METHOD(Redis, vsim); +ZEND_METHOD(Redis, vcard); +ZEND_METHOD(Redis, vdim); +ZEND_METHOD(Redis, vinfo); +ZEND_METHOD(Redis, vismember); +ZEND_METHOD(Redis, vemb); +ZEND_METHOD(Redis, vrandmember); +ZEND_METHOD(Redis, vrange); +ZEND_METHOD(Redis, vrem); +ZEND_METHOD(Redis, vsetattr); +ZEND_METHOD(Redis, vgetattr); +ZEND_METHOD(Redis, vlinks); +ZEND_METHOD(Redis, gcra); ZEND_METHOD(Redis, xtrim); ZEND_METHOD(Redis, zAdd); ZEND_METHOD(Redis, zCard); @@ -1414,7 +1616,7 @@ ZEND_METHOD(Redis, zinterstore); ZEND_METHOD(Redis, zscan); ZEND_METHOD(Redis, zunion); ZEND_METHOD(Redis, zunionstore); - +ZEND_METHOD(Redis, digest); static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC) @@ -1425,12 +1627,14 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, _digest, arginfo_class_Redis__digest, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC) ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC) ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC) ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgSave, arginfo_class_Redis_bgSave, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgrewriteaof, arginfo_class_Redis_bgrewriteaof, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, waitaof, arginfo_class_Redis_waitaof, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitcount, arginfo_class_Redis_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitpos, arginfo_class_Redis_bitpos, ZEND_ACC_PUBLIC) @@ -1455,7 +1659,13 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, decr, arginfo_class_Redis_decr, ZEND_ACC_PUBLIC) ZEND_ME(Redis, decrBy, arginfo_class_Redis_decrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, del, arginfo_class_Redis_del, ZEND_ACC_PUBLIC) - ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) + ZEND_ME(Redis, delex, arginfo_class_Redis_delex, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, delifeq, arginfo_class_Redis_delifeq, ZEND_ACC_PUBLIC) +#if (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("delete", zim_Redis_del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, NULL) +#else + ZEND_RAW_FENTRY("delete", zim_Redis_del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#endif ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC) ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC) @@ -1486,6 +1696,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, getWithMeta, arginfo_class_Redis_getWithMeta, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getEx, arginfo_class_Redis_getEx, ZEND_ACC_PUBLIC) @@ -1497,6 +1708,8 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, serverName, arginfo_class_Redis_serverName, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, serverVersion, arginfo_class_Redis_serverVersion, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC) @@ -1508,18 +1721,33 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGetAll, arginfo_class_Redis_hGetAll, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hGetWithMeta, arginfo_class_Redis_hGetWithMeta, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hIncrBy, arginfo_class_Redis_hIncrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hIncrByFloat, arginfo_class_Redis_hIncrByFloat, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hKeys, arginfo_class_Redis_hKeys, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hLen, arginfo_class_Redis_hLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hMget, arginfo_class_Redis_hMget, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hgetex, arginfo_class_Redis_hgetex, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hsetex, arginfo_class_Redis_hsetex, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hgetdel, arginfo_class_Redis_hgetdel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hMset, arginfo_class_Redis_hMset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hRandField, arginfo_class_Redis_hRandField, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hSet, arginfo_class_Redis_hSet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpire, arginfo_class_Redis_hexpire, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpire, arginfo_class_Redis_hpexpire, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpireat, arginfo_class_Redis_hexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpireat, arginfo_class_Redis_hpexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, httl, arginfo_class_Redis_httl, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpttl, arginfo_class_Redis_hpttl, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpiretime, arginfo_class_Redis_hexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpiretime, arginfo_class_Redis_hpexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpersist, arginfo_class_Redis_hpersist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, expiremember, arginfo_class_Redis_expiremember, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, expirememberat, arginfo_class_Redis_expirememberat, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC) @@ -1546,10 +1774,15 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, migrate, arginfo_class_Redis_migrate, ZEND_ACC_PUBLIC) ZEND_ME(Redis, move, arginfo_class_Redis_move, ZEND_ACC_PUBLIC) ZEND_ME(Redis, mset, arginfo_class_Redis_mset, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, msetex, arginfo_class_Redis_msetex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, msetnx, arginfo_class_Redis_msetnx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC) ZEND_ME(Redis, object, arginfo_class_Redis_object, ZEND_ACC_PUBLIC) - ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#if (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("open", zim_Redis_connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, NULL) +#else + ZEND_RAW_FENTRY("open", zim_Redis_connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#endif ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC) ZEND_ME(Redis, persist, arginfo_class_Redis_persist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpire, arginfo_class_Redis_pexpire, ZEND_ACC_PUBLIC) @@ -1559,7 +1792,11 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, pfmerge, arginfo_class_Redis_pfmerge, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC) - ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#if (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("popen", zim_Redis_pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, NULL) +#else + ZEND_RAW_FENTRY("popen", zim_Redis_pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#endif ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, psubscribe, arginfo_class_Redis_psubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pttl, arginfo_class_Redis_pttl, ZEND_ACC_PUBLIC) @@ -1631,6 +1868,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, xautoclaim, arginfo_class_Redis_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xclaim, arginfo_class_Redis_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xdel, arginfo_class_Redis_xdel, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, xdelex, arginfo_class_Redis_xdelex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xgroup, arginfo_class_Redis_xgroup, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xinfo, arginfo_class_Redis_xinfo, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xlen, arginfo_class_Redis_xlen, ZEND_ACC_PUBLIC) @@ -1639,6 +1877,20 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, xread, arginfo_class_Redis_xread, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xreadgroup, arginfo_class_Redis_xreadgroup, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xrevrange, arginfo_class_Redis_xrevrange, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vadd, arginfo_class_Redis_vadd, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vsim, arginfo_class_Redis_vsim, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vcard, arginfo_class_Redis_vcard, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vdim, arginfo_class_Redis_vdim, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vinfo, arginfo_class_Redis_vinfo, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vismember, arginfo_class_Redis_vismember, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vemb, arginfo_class_Redis_vemb, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vrandmember, arginfo_class_Redis_vrandmember, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vrange, arginfo_class_Redis_vrange, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vrem, arginfo_class_Redis_vrem, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vsetattr, arginfo_class_Redis_vsetattr, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vgetattr, arginfo_class_Redis_vgetattr, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vlinks, arginfo_class_Redis_vlinks, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, gcra, arginfo_class_Redis_gcra, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xtrim, arginfo_class_Redis_xtrim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zAdd, arginfo_class_Redis_zAdd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zCard, arginfo_class_Redis_zCard, ZEND_ACC_PUBLIC) @@ -1671,11 +1923,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, zscan, arginfo_class_Redis_zscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zunion, arginfo_class_Redis_zunion, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zunionstore, arginfo_class_Redis_zunionstore, ZEND_ACC_PUBLIC) - ZEND_FE_END -}; - - -static const zend_function_entry class_RedisException_methods[] = { + ZEND_ME(Redis, digest, arginfo_class_Redis_digest, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -1684,7 +1932,11 @@ static zend_class_entry *register_class_Redis(void) zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "Redis", class_Redis_methods); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif zval const_REDIS_NOT_FOUND_value; ZVAL_LONG(&const_REDIS_NOT_FOUND_value, REDIS_NOT_FOUND); @@ -1728,6 +1980,12 @@ static zend_class_entry *register_class_Redis(void) zend_declare_class_constant_ex(class_entry, const_REDIS_STREAM_name, &const_REDIS_STREAM_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_STREAM_name); + zval const_REDIS_VECTORSET_value; + ZVAL_LONG(&const_REDIS_VECTORSET_value, REDIS_VECTORSET); + zend_string *const_REDIS_VECTORSET_name = zend_string_init_interned("REDIS_VECTORSET", sizeof("REDIS_VECTORSET") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_VECTORSET_name, &const_REDIS_VECTORSET_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_VECTORSET_name); + zval const_ATOMIC_value; ZVAL_LONG(&const_ATOMIC_value, ATOMIC); zend_string *const_ATOMIC_name = zend_string_init_interned("ATOMIC", sizeof("ATOMIC") - 1, 1); @@ -1794,6 +2052,12 @@ static zend_class_entry *register_class_Redis(void) zend_declare_class_constant_ex(class_entry, const_OPT_NULL_MULTIBULK_AS_NULL_name, &const_OPT_NULL_MULTIBULK_AS_NULL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_NULL_MULTIBULK_AS_NULL_name); + zval const_OPT_PACK_IGNORE_NUMBERS_value; + ZVAL_LONG(&const_OPT_PACK_IGNORE_NUMBERS_value, REDIS_OPT_PACK_IGNORE_NUMBERS); + zend_string *const_OPT_PACK_IGNORE_NUMBERS_name = zend_string_init_interned("OPT_PACK_IGNORE_NUMBERS", sizeof("OPT_PACK_IGNORE_NUMBERS") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_PACK_IGNORE_NUMBERS_name, &const_OPT_PACK_IGNORE_NUMBERS_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_PACK_IGNORE_NUMBERS_name); + zval const_SERIALIZER_NONE_value; ZVAL_LONG(&const_SERIALIZER_NONE_value, REDIS_SERIALIZER_NONE); zend_string *const_SERIALIZER_NONE_name = zend_string_init_interned("SERIALIZER_NONE", sizeof("SERIALIZER_NONE") - 1, 1); @@ -1865,13 +2129,21 @@ static zend_class_entry *register_class_Redis(void) zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); #endif -#if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_MAX) +#if defined(HAVE_REDIS_ZSTD) && ZSTD_VERSION_NUMBER >= 10400 - zval const_COMPRESSION_ZSTD_MAX_value; - ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_CLEVEL_MAX); - zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release(const_COMPRESSION_ZSTD_MAX_name); + zval const_COMPRESSION_ZSTD_MIN_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MIN_value, ZSTD_minCLevel()); + zend_string *const_COMPRESSION_ZSTD_MIN_name = zend_string_init_interned("COMPRESSION_ZSTD_MIN", sizeof("COMPRESSION_ZSTD_MIN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MIN_name, &const_COMPRESSION_ZSTD_MIN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MIN_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && !(ZSTD_VERSION_NUMBER >= 10400) + + zval const_COMPRESSION_ZSTD_MIN_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MIN_value, 1); + zend_string *const_COMPRESSION_ZSTD_MIN_name = zend_string_init_interned("COMPRESSION_ZSTD_MIN", sizeof("COMPRESSION_ZSTD_MIN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MIN_name, &const_COMPRESSION_ZSTD_MIN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MIN_name); #endif #if defined(HAVE_REDIS_ZSTD) @@ -2013,13 +2285,15 @@ static zend_class_entry *register_class_Redis(void) zend_string *const_OPT_BACKOFF_CAP_name = zend_string_init_interned("OPT_BACKOFF_CAP", sizeof("OPT_BACKOFF_CAP") - 1, 1); zend_declare_class_constant_ex(class_entry, const_OPT_BACKOFF_CAP_name, &const_OPT_BACKOFF_CAP_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_BACKOFF_CAP_name); -#if (PHP_VERSION_ID >= 80200) - zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "auth", sizeof("auth") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + zend_string *attribute_name_SensitiveParameter_func_auth_arg0_0 = zend_string_init_interned("SensitiveParameter", sizeof("SensitiveParameter") - 1, 1); + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "auth", sizeof("auth") - 1), 0, attribute_name_SensitiveParameter_func_auth_arg0_0, 0); + zend_string_release(attribute_name_SensitiveParameter_func_auth_arg0_0); - zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "migrate", sizeof("migrate") - 1), 7, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); -#endif + zend_string *attribute_name_SensitiveParameter_func_migrate_arg7_0 = zend_string_init_interned("SensitiveParameter", sizeof("SensitiveParameter") - 1, 1); + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "migrate", sizeof("migrate") - 1), 7, attribute_name_SensitiveParameter_func_migrate_arg7_0, 0); + zend_string_release(attribute_name_SensitiveParameter_func_migrate_arg7_0); return class_entry; } @@ -2028,8 +2302,12 @@ static zend_class_entry *register_class_RedisException(zend_class_entry *class_e { zend_class_entry ce, *class_entry; - INIT_CLASS_ENTRY(ce, "RedisException", class_RedisException_methods); + INIT_CLASS_ENTRY(ce, "RedisException", NULL); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_RuntimeException, 0); +#else class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException); +#endif return class_entry; } diff --git a/redis_array.c b/redis_array.c index 53ad4eb2ca..ee4b8d78ad 100644 --- a/redis_array.c +++ b/redis_array.c @@ -66,17 +66,17 @@ redis_array_free(RedisArray *ra) /* Redis objects */ for(i = 0; i< ra->count; i++) { - zval_dtor(&ra->redis[i]); + zval_ptr_dtor_nogc(&ra->redis[i]); zend_string_release(ra->hosts[i]); } efree(ra->redis); efree(ra->hosts); /* delete hash function */ - zval_dtor(&ra->z_fun); + zval_ptr_dtor_nogc(&ra->z_fun); /* Distributor */ - zval_dtor(&ra->z_dist); + zval_ptr_dtor_nogc(&ra->z_dist); /* Hashing algorithm */ if (ra->algorithm) zend_string_release(ra->algorithm); @@ -121,6 +121,7 @@ create_redis_array_object(zend_class_entry *ce) memcpy(&redis_array_object_handlers, zend_get_std_object_handlers(), sizeof(redis_array_object_handlers)); redis_array_object_handlers.offset = XtOffsetOf(redis_array_object, std); redis_array_object_handlers.free_obj = free_redis_array_object; + redis_array_object_handlers.clone_obj = NULL; obj->std.handlers = &redis_array_object_handlers; return &obj->std; @@ -214,8 +215,8 @@ PHP_METHOD(RedisArray, __construct) if (algorithm) zend_string_release(algorithm); if (user) zend_string_release(user); if (pass) zend_string_release(pass); - zval_dtor(&z_dist); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_dist); + zval_ptr_dtor_nogc(&z_fun); finish: @@ -277,10 +278,10 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, /* multi/exec */ if(ra->z_multi_exec) { call_user_function(&redis_ce->function_table, ra->z_multi_exec, &z_fun, return_value, argc, z_callargs); - zval_dtor(return_value); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(return_value); + zval_ptr_dtor_nogc(&z_fun); for (i = 0; i < argc; ++i) { - zval_dtor(&z_callargs[i]); + zval_ptr_dtor_nogc(&z_callargs[i]); } efree(z_callargs); RETURN_ZVAL(getThis(), 1, 0); @@ -295,7 +296,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, ra_index_multi(redis_inst, MULTI); /* call using discarded temp value and extract exec results after. */ call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, argc, z_callargs); - zval_dtor(return_value); + zval_ptr_dtor_nogc(return_value); /* add keys to index. */ ra_index_key(key, key_len, redis_inst); @@ -309,7 +310,7 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, /* check if we have an error. */ if (ra->prev && RA_CALL_FAILED(return_value, cmd)) { /* there was an error reading, try with prev ring. */ /* Free previous return value */ - zval_dtor(return_value); + zval_ptr_dtor_nogc(return_value); /* ERROR, FALLBACK TO PREVIOUS RING and forward a reference to the first redis instance we were looking at. */ ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra->prev, cmd, cmd_len, z_args, z_new_target ? z_new_target : redis_inst); @@ -323,9 +324,9 @@ ra_forward_call(INTERNAL_FUNCTION_PARAMETERS, RedisArray *ra, const char *cmd, } /* cleanup */ - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); for (i = 0; i < argc; ++i) { - zval_dtor(&z_callargs[i]); + zval_ptr_dtor_nogc(&z_callargs[i]); } efree(z_callargs); } @@ -543,7 +544,7 @@ multihost_distribute(INTERNAL_FUNCTION_PARAMETERS, const char *method_name) multihost_distribute_call(ra, return_value, &z_fun, 0, NULL); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); } static void @@ -568,7 +569,7 @@ multihost_distribute_flush(INTERNAL_FUNCTION_PARAMETERS, const char *method_name multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); } PHP_METHOD(RedisArray, info) @@ -629,8 +630,8 @@ PHP_METHOD(RedisArray, keys) multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); - zval_dtor(&z_args[0]); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_args[0]); + zval_ptr_dtor_nogc(&z_fun); } PHP_METHOD(RedisArray, getOption) @@ -656,7 +657,7 @@ PHP_METHOD(RedisArray, getOption) multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); } PHP_METHOD(RedisArray, setOption) @@ -685,8 +686,8 @@ PHP_METHOD(RedisArray, setOption) multihost_distribute_call(ra, return_value, &z_fun, 2, z_args); - zval_dtor(&z_args[1]); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_args[1]); + zval_ptr_dtor_nogc(&z_fun); } PHP_METHOD(RedisArray, select) @@ -712,7 +713,7 @@ PHP_METHOD(RedisArray, select) multihost_distribute_call(ra, return_value, &z_fun, 1, z_args); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); } #define HANDLE_MULTI_EXEC(ra, cmd, cmdlen) do { \ @@ -732,7 +733,7 @@ PHP_METHOD(RedisArray, select) } \ /* call */\ ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmdlen, &z_arg_array, NULL); \ - zval_dtor(&z_arg_array); \ + zval_ptr_dtor_nogc(&z_arg_array); \ return; \ } \ } while(0) @@ -825,13 +826,13 @@ PHP_METHOD(RedisArray, mget) call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_arg); /* cleanup args array */ - zval_dtor(&z_arg); + zval_ptr_dtor_nogc(&z_arg); /* Error out if we didn't get a proper response */ if (Z_TYPE(z_ret) != IS_ARRAY) { /* cleanup */ - zval_dtor(&z_ret); - zval_dtor(&z_tmp_array); + zval_ptr_dtor_nogc(&z_ret); + zval_ptr_dtor_nogc(&z_tmp_array); RETVAL_FALSE; goto cleanup; } @@ -842,10 +843,10 @@ PHP_METHOD(RedisArray, mget) ZVAL_ZVAL(&z_arg, z_cur, 1, 0); add_index_zval(&z_tmp_array, i, &z_arg); } - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); } - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); array_init(return_value); /* copy temp array in the right order to return_value */ @@ -857,7 +858,7 @@ PHP_METHOD(RedisArray, mget) } /* cleanup */ - zval_dtor(&z_tmp_array); + zval_ptr_dtor_nogc(&z_tmp_array); cleanup: efree(argv); efree(pos); @@ -954,7 +955,7 @@ PHP_METHOD(RedisArray, mset) } if(!found) { - zval_dtor(&z_argarray); + zval_ptr_dtor_nogc(&z_argarray); continue; /* don't run empty MSETs */ } @@ -967,11 +968,11 @@ PHP_METHOD(RedisArray, mset) call_user_function(&redis_ce->function_table, &ra->redis[n], &z_fun, &z_ret, 1, &z_argarray); } - zval_dtor(&z_argarray); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_argarray); + zval_ptr_dtor_nogc(&z_ret); } - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); /* Free any keys that we needed to allocate memory for, because they weren't strings */ for(i = 0; i < argc; i++) { @@ -1027,7 +1028,7 @@ ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) /* init data structures */ h_keys = Z_ARRVAL(z_keys); if ((argc = zend_hash_num_elements(h_keys)) == 0) { - if (free_zkeys) zval_dtor(&z_keys); + if (free_zkeys) zval_ptr_dtor_nogc(&z_keys); efree(z_args); RETURN_FALSE; } @@ -1075,7 +1076,7 @@ ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } if(!found) { /* don't run empty DEL or UNLINK commands */ - zval_dtor(&z_argarray); + zval_ptr_dtor_nogc(&z_argarray); continue; } @@ -1089,11 +1090,11 @@ ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) } total += Z_LVAL(z_ret); /* increment total */ - zval_dtor(&z_argarray); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_argarray); + zval_ptr_dtor_nogc(&z_ret); } - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); RETVAL_LONG(total); @@ -1103,7 +1104,7 @@ ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) efree(argc_each); if(free_zkeys) { - zval_dtor(&z_keys); + zval_ptr_dtor_nogc(&z_keys); } efree(z_args); } @@ -1123,11 +1124,11 @@ ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, int kw_len) { RedisArray *ra; zend_string *key, *pattern = NULL; - zval *object, *redis_inst, *z_iter, z_fun, z_args[4]; + zval *object, *redis_inst, *z_cursor, z_fun, z_args[4]; zend_long count = 0; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "OSz/|S!l", - &object, redis_array_ce, &key, &z_iter, &pattern, &count) == FAILURE) { + &object, redis_array_ce, &key, &z_cursor, &pattern, &count) == FAILURE) { RETURN_FALSE; } @@ -1141,15 +1142,15 @@ ra_generic_scan_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, int kw_len) } ZVAL_STR(&z_args[0], key); - ZVAL_NEW_REF(&z_args[1], z_iter); + ZVAL_NEW_REF(&z_args[1], z_cursor); if (pattern) ZVAL_STR(&z_args[2], pattern); ZVAL_LONG(&z_args[3], count); ZVAL_STRINGL(&z_fun, kw, kw_len); call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, ZEND_NUM_ARGS(), z_args); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); - ZVAL_ZVAL(z_iter, &z_args[1], 0, 1); + ZVAL_ZVAL(z_cursor, &z_args[1], 0, 1); } PHP_METHOD(RedisArray, hscan) @@ -1193,7 +1194,7 @@ PHP_METHOD(RedisArray, scan) ZVAL_STRING(&z_fun, "SCAN"); call_user_function(&redis_ce->function_table, redis_inst, &z_fun, return_value, ZEND_NUM_ARGS() - 1, z_args); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_fun); ZVAL_ZVAL(z_iter, &z_args[0], 0, 1); } diff --git a/redis_array.stub.php b/redis_array.stub.php index 8fca8ab95a..7f3a4cc290 100644 --- a/redis_array.stub.php +++ b/redis_array.stub.php @@ -6,11 +6,265 @@ * @generate-class-entries */ +/** + * @method mixed acl(string $subcmd, string ...$args) + * @method RedisArray|int|false append(string $key, mixed $value) + * @method RedisArray|bool auth(#[\SensitiveParameter] mixed $credentials) + * @method RedisArray|bool bgrewriteaof() + * @method RedisArray|array|false waitaof(int $numlocal, int $numreplicas, int $timeout) + * @method RedisArray|int|false bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false) + * @method RedisArray|int|false bitop(string $operation, string $deskey, string $srckey, string ...$other_keys) + * @method RedisArray|int|false bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false) + * @method RedisArray|array|null|false blPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) + * @method RedisArray|array|null|false brPop(string|array $key_or_keys, string|float|int $timeout_or_key, mixed ...$extra_args) + * @method RedisArray|string|false brpoplpush(string $src, string $dst, int|float $timeout) + * @method RedisArray|array|false bzPopMax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + * @method RedisArray|array|false bzPopMin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args) + * @method RedisArray|array|null|false bzmpop(float $timeout, array $keys, string $from, int $count = 1) + * @method RedisArray|array|null|false zmpop(array $keys, string $from, int $count = 1) + * @method RedisArray|array|null|false blmpop(float $timeout, array $keys, string $from, int $count = 1) + * @method RedisArray|array|null|false lmpop(array $keys, string $from, int $count = 1) + * @method bool clearLastError() + * @method mixed client(string $opt, mixed ...$args) + * @method bool close() + * @method mixed command(?string $opt = null, mixed ...$args) + * @method mixed config(string $operation, array|string|null $key_or_settings = null, ?string $value = null) + * @method RedisArray|bool copy(string $src, string $dst, ?array $options = null) + * @method RedisArray|int|false dbSize() + * @method RedisArray|string debug(string $key) + * @method RedisArray|int|false decr(string $key, int $by = 1) + * @method RedisArray|int|false decrBy(string $key, int $value) + * @method RedisArray|int|false delex(string $key, ?array $options = null) + * @method RedisArray|int|false delifeq(string $key, mixed $value) + * @method RedisArray|int|false delete(array|string $key, string ...$other_keys) + * @method RedisArray|string|false dump(string $key) + * @method RedisArray|string|false echo(string $str) + * @method mixed eval(string $script, array $args = [], int $num_keys = 0) + * @method mixed eval_ro(string $script_sha, array $args = [], int $num_keys = 0) + * @method mixed evalsha(string $sha1, array $args = [], int $num_keys = 0) + * @method mixed evalsha_ro(string $sha1, array $args = [], int $num_keys = 0) + * @method RedisArray|int|bool exists(mixed $key, mixed ...$other_keys) + * @method RedisArray|bool expire(string $key, int $timeout, ?string $mode = null) + * @method RedisArray|bool expireAt(string $key, int $timestamp, ?string $mode = null) + * @method RedisArray|bool failover(?array $to = null, bool $abort = false, int $timeout = 0) + * @method RedisArray|int|false expiretime(string $key) + * @method RedisArray|int|false pexpiretime(string $key) + * @method mixed fcall(string $fn, array $keys = [], array $args = []) + * @method mixed fcall_ro(string $fn, array $keys = [], array $args = []) + * @method RedisArray|bool|string|array function(string $operation, mixed ...$args) + * @method RedisArray|int|false geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options) + * @method RedisArray|float|false geodist(string $key, string $src, string $dst, ?string $unit = null) + * @method RedisArray|array|false geohash(string $key, string $member, string ...$other_members) + * @method RedisArray|array|false geopos(string $key, string $member, string ...$other_members) + * @method mixed georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + * @method mixed georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []) + * @method mixed georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []) + * @method mixed georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []) + * @method array geosearch(string $key, array|string $position, array|int|float $shape, string $unit, array $options = []) + * @method RedisArray|array|int|false geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []) + * @method mixed get(string $key) + * @method RedisArray|array|false getWithMeta(string $key) + * @method mixed getAuth() + * @method RedisArray|int|false getBit(string $key, int $idx) + * @method RedisArray|string|bool getEx(string $key, array $options = []) + * @method int getDBNum() + * @method RedisArray|string|bool getDel(string $key) + * @method string getHost() + * @method string|null getLastError() + * @method int getMode() + * @method string|null getPersistentID() + * @method int getPort() + * @method string|false serverName() + * @method string|false serverVersion() + * @method RedisArray|string|false getRange(string $key, int $start, int $end) + * @method RedisArray|string|array|int|false lcs(string $key1, string $key2, ?array $options = null) + * @method float getReadTimeout() + * @method RedisArray|string|false getset(string $key, mixed $value) + * @method float|false getTimeout() + * @method array getTransferredBytes() + * @method void clearTransferredBytes() + * @method RedisArray|int|false hDel(string $key, string $field, string ...$other_fields) + * @method RedisArray|bool hExists(string $key, string $field) + * @method mixed hGet(string $key, string $member) + * @method RedisArray|array|false hGetAll(string $key) + * @method mixed hGetWithMeta(string $key, string $member) + * @method RedisArray|int|false hIncrBy(string $key, string $field, int $value) + * @method RedisArray|float|false hIncrByFloat(string $key, string $field, float $value) + * @method RedisArray|array|false hKeys(string $key) + * @method RedisArray|int|false hLen(string $key) + * @method RedisArray|array|false hMget(string $key, array $fields) + * @method RedisArray|array|false hgetex(string $key, array $fields, string|array|null $expiry = null) + * @method RedisArray|int|false hsetex(string $key, array $fields, ?array $expiry = null) + * @method RedisArray|array|false hgetdel(string $key, array $fields) + * @method RedisArray|bool hMset(string $key, array $fieldvals) + * @method RedisArray|string|array|false hRandField(string $key, ?array $options = null) + * @method RedisArray|int|false hSet(string $key, mixed ...$fields_and_vals) + * @method RedisArray|bool hSetNx(string $key, string $field, mixed $value) + * @method RedisArray|int|false hStrLen(string $key, string $field) + * @method RedisArray|array|false hVals(string $key) + * @method RedisArray|array|false httl(string $key, array $fields) + * @method RedisArray|array|false hpttl(string $key, array $fields) + * @method RedisArray|array|false hexpiretime(string $key, array $fields) + * @method RedisArray|array|false hpexpiretime(string $key, array $fields) + * @method RedisArray|array|false hpersist(string $key, array $fields) + * @method RedisArray|int|false expiremember(string $key, string $field, int $ttl, ?string $unit = null) + * @method RedisArray|int|false expirememberat(string $key, string $field, int $timestamp) + * @method RedisArray|int|false incr(string $key, int $by = 1) + * @method RedisArray|int|false incrBy(string $key, int $value) + * @method RedisArray|float|false incrByFloat(string $key, float $value) + * @method bool isConnected() + * @method lInsert(string $key, string $pos, mixed $pivot, mixed $value) + * @method RedisArray|int|false lLen(string $key) + * @method RedisArray|string|false lMove(string $src, string $dst, string $wherefrom, string $whereto) + * @method RedisArray|string|false blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout) + * @method RedisArray|bool|string|array lPop(string $key, int $count = 0) + * @method RedisArray|null|bool|int|array lPos(string $key, mixed $value, ?array $options = null) + * @method RedisArray|int|false lPush(string $key, mixed ...$elements) + * @method RedisArray|int|false rPush(string $key, mixed ...$elements) + * @method RedisArray|int|false lPushx(string $key, mixed $value) + * @method RedisArray|int|false rPushx(string $key, mixed $value) + * @method RedisArray|bool lSet(string $key, int $index, mixed $value) + * @method int lastSave() + * @method mixed lindex(string $key, int $index) + * @method RedisArray|array|false lrange(string $key, int $start , int $end) + * @method RedisArray|int|false lrem(string $key, mixed $value, int $count = 0) + * @method RedisArray|bool ltrim(string $key, int $start , int $end) + * @method RedisArray|bool move(string $key, int $index) + * @method RedisArray|int|false msetex(array $key_vals, int|float|array|null $expiry = null) + * @method RedisArray|bool msetnx(array $key_values) + * @method RedisArray|int|string|false object(string $subcommand, string $key) + * @method bool open(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null) + * @method bool pconnect(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null) + * @method RedisArray|bool persist(string $key) + * @method bool pexpire(string $key, int $timeout, ?string $mode = null) + * @method RedisArray|bool pexpireAt(string $key, int $timestamp, ?string $mode = null) + * @method RedisArray|int pfadd(string $key, array $elements) + * @method RedisArray|int|false pfcount(array|string $key_or_keys) + * @method RedisArray|bool pfmerge(string $dst, array $srckeys) + * @method bool|RedisArray pipeline() + * @method bool popen(string $host, int $port = 6379, float $timeout = 0, ?string $persistent_id = null, int $retry_interval = 0, float $read_timeout = 0, ?array $context = null) + * @method RedisArray|bool psetex(string $key, int $expire, mixed $value) + * @method bool psubscribe(array $patterns, callable $cb) + * @method RedisArray|int|false pttl(string $key) + * @method RedisArray|int|false publish(string $channel, string $message) + * @method mixed pubsub(string $command, mixed $arg = null) + * @method RedisArray|array|bool punsubscribe(array $patterns) + * @method RedisArray|array|string|bool rPop(string $key, int $count = 0) + * @method RedisArray|string|false randomKey() + * @method mixed rawcommand(string $command, mixed ...$args) + * @method RedisArray|bool rename(string $old_name, string $new_name) + * @method RedisArray|bool renameNx(string $key_src, string $key_dst) + * @method RedisArray|bool reset() + * @method RedisArray|bool restore(string $key, int $ttl, string $value, ?array $options = null) + * @method mixed role() + * @method RedisArray|string|false rpoplpush(string $srckey, string $dstkey) + * @method RedisArray|int|false sAdd(string $key, mixed $value, mixed ...$other_values) + * @method int sAddArray(string $key, array $values) + * @method RedisArray|array|false sDiff(string $key, string ...$other_keys) + * @method RedisArray|int|false sDiffStore(string $dst, string $key, string ...$other_keys) + * @method RedisArray|array|false sInter(array|string $key, string ...$other_keys) + * @method RedisArray|int|false sintercard(array $keys, int $limit = -1) + * @method RedisArray|int|false sInterStore(array|string $key, string ...$other_keys) + * @method RedisArray|array|false sMembers(string $key) + * @method RedisArray|array|false sMisMember(string $key, string $member, string ...$other_members) + * @method RedisArray|bool sMove(string $src, string $dst, mixed $value) + * @method RedisArray|string|array|false sPop(string $key, int $count = 0) + * @method mixed sRandMember(string $key, int $count = 0) + * @method RedisArray|array|false sUnion(string $key, string ...$other_keys) + * @method RedisArray|int|false sUnionStore(string $dst, string $key, string ...$other_keys) + * @method RedisArray|int|false scard(string $key) + * @method mixed script(string $command, mixed ...$args) + * @method RedisArray|string|bool set(string $key, mixed $value, mixed $options = null) + * @method RedisArray|int|false setBit(string $key, int $idx, bool $value) + * @method RedisArray|int|false setRange(string $key, int $index, string $value) + * @method setex(string $key, int $expire, mixed $value) + * @method RedisArray|bool setnx(string $key, mixed $value) + * @method RedisArray|bool sismember(string $key, mixed $value) + * @method RedisArray|bool slaveof(?string $host = null, int $port = 6379) + * @method RedisArray|bool replicaof(?string $host = null, int $port = 6379) + * @method RedisArray|int|false touch(array|string $key_or_array, string ...$more_keys) + * @method mixed slowlog(string $operation, int $length = 0) + * @method mixed sort(string $key, ?array $options = null) + * @method mixed sort_ro(string $key, ?array $options = null) + * @method array sortAsc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) + * @method array sortAscAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) + * @method array sortDesc(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) + * @method array sortDescAlpha(string $key, ?string $pattern = null, mixed $get = null, int $offset = -1, int $count = -1, ?string $store = null) + * @method RedisArray|int|false srem(string $key, mixed $value, mixed ...$other_values) + * @method bool ssubscribe(array $channels, callable $cb) + * @method RedisArray|int|false strlen(string $key) + * @method bool subscribe(array $channels, callable $cb) + * @method RedisArray|array|bool sunsubscribe(array $channels) + * @method RedisArray|bool swapdb(int $src, int $dst) + * @method RedisArray|array time() + * @method RedisArray|int|false ttl(string $key) + * @method RedisArray|int|false type(string $key) + * @method RedisArray|array|bool unsubscribe(array $channels) + * @method RedisArray|bool watch(array|string $key, string ...$other_keys) + * @method int|false wait(int $numreplicas, int $timeout) + * @method int|false xack(string $key, string $group, array $ids) + * @method RedisArray|string|false xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false, bool $nomkstream = false) + * @method RedisArray|bool|array xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false) + * @method RedisArray|array|bool xclaim(string $key, string $group, string $consumer, int $min_idle, array $ids, array $options) + * @method RedisArray|int|false xdel(string $key, array $ids) + * @method Relay|array|false xdelex(string $key, array $ids, ?string $mode = null) + * @method mixed xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1) + * @method RedisArray|int|false xlen(string $key) + * @method RedisArray|array|false xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null) + * @method RedisArray|array|bool xrange(string $key, string $start, string $end, int $count = -1) + * @method RedisArray|array|bool xread(array $streams, int $count = -1, int $block = -1) + * @method RedisArray|array|bool xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1) + * @method RedisArray|array|bool xrevrange(string $key, string $end, string $start, int $count = -1) + * @method RedisArray|int|false vadd(string $key, array $values, mixed $element, array|null $options = null) + * @method RedisArray|array|false vsim(string $key, mixed $member, array|null $options = null) + * @method RedisArray|int|false vcard(string $key) + * @method RedisArray|int|false vdim(string $key) + * @method RedisArray|array|false vinfo(string $key) + * @method RedisArray|bool vismember(string $key, mixed $member) + * @method RedisArray|array|false vemb(string $key, mixed $member, bool $raw = false) + * @method RedisArray|array|string|false vrandmember(string $key, int $count = 0) + * @method RedisArray|array|false vrange(string $key, string $min, string $max, int $count = -1) + * @method RedisArray|int|false vrem(string $key, mixed $member) + * @method RedisArray|int|false vsetattr(string $key, mixed $member, array|string $attributes) + * @method RedisArray|array|string|false vgetattr(string $key, mixed $member, bool $decode = true) + * @method RedisArray|array|false vlinks(string $key, mixed $member, bool $withscores = false) + * @method RedisArray|int|false xtrim(string $key, string $threshold, bool $approx = false, bool $minid = false, int $limit = -1) + * @method RedisArray|int|float|false zAdd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems) + * @method RedisArray|int|false zCard(string $key) + * @method RedisArray|int|false zCount(string $key, int|string $start, int|string $end) + * @method RedisArray|float|false zIncrBy(string $key, float $value, mixed $member) + * @method RedisArray|int|false zLexCount(string $key, string $min, string $max) + * @method RedisArray|array|false zMscore(string $key, mixed $member, mixed ...$other_members) + * @method RedisArray|array|false zPopMax(string $key, ?int $count = null) + * @method RedisArray|array|false zPopMin(string $key, ?int $count = null) + * @method RedisArray|array|false zRange(string $key, string|int $start, string|int $end, array|bool|null $options = null) + * @method RedisArray|array|false zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1) + * @method RedisArray|array|false zRangeByScore(string $key, string $start, string $end, array $options = []) + * @method RedisArray|string|array zRandMember(string $key, ?array $options = null) + * @method RedisArray|int|false zRank(string $key, mixed $member) + * @method RedisArray|int|false zRem(mixed $key, mixed $member, mixed ...$other_members) + * @method RedisArray|int|false zRemRangeByLex(string $key, string $min, string $max) + * @method RedisArray|int|false zRemRangeByRank(string $key, int $start, int $end) + * @method RedisArray|int|false zRemRangeByScore(string $key, string $start, string $end) + * @method RedisArray|array|false zRevRange(string $key, int $start, int $end, mixed $scores = null) + * @method RedisArray|array|false zRevRangeByLex(string $key, string $max, string $min, int $offset = -1, int $count = -1) + * @method RedisArray|array|false zRevRangeByScore(string $key, string $max, string $min, array|bool $options = []) + * @method RedisArray|int|false zRevRank(string $key, mixed $member) + * @method RedisArray|float|false zScore(string $key, mixed $member) + * @method RedisArray|array|false zdiff(array $keys, ?array $options = null) + * @method RedisArray|int|false zdiffstore(string $dst, array $keys) + * @method RedisArray|array|false zinter(array $keys, ?array $weights = null, ?array $options = null) + * @method RedisArray|int|false zintercard(array $keys, int $limit = -1) + * @method RedisArray|int|false zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null) + * @method RedisArray|array|false zunion(array $keys, ?array $weights = null, ?array $options = null) + * @method RedisArray|int|false zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null) + * @method RedisArray|string|false digest(string $key) + */ class RedisArray { public function __call(string $function_name, array $arguments): mixed; - public function __construct(string|array $name_or_hosts, array $options = NULL); + public function __construct(string|array $name_or_hosts, ?array $options = null); public function _continuum(): bool|array; @@ -22,7 +276,7 @@ public function _hosts(): bool|array; public function _instance(string $host): bool|null|Redis; - public function _rehash(callable $fn = NULL): bool|null; + public function _rehash(?callable $fn = null): bool|null; public function _target(string $key): bool|string|null; @@ -32,7 +286,7 @@ public function del(string|array $key, string ...$otherkeys): bool|int; public function discard(): bool|null; - public function exec(): bool|null; + public function exec(): bool|null|array; public function flushall(): bool|array; @@ -40,7 +294,7 @@ public function flushdb(): bool|array; public function getOption(int $opt): bool|array; - public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array; + public function hscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): bool|array; public function info(): bool|array; @@ -50,23 +304,23 @@ public function mget(array $keys): bool|array; public function mset(array $pairs): bool; - public function multi(string $host, int $mode = NULL): bool|RedisArray; + public function multi(string $host, ?int $mode = null): bool|RedisArray; public function ping(): bool|array; public function save(): bool|array; - public function scan(?int &$iterator, string $node, ?string $pattern = null, int $count = 0): bool|array; + public function scan(null|int|string &$iterator, string $node, ?string $pattern = null, int $count = 0): bool|array; public function select(int $index): bool|array; public function setOption(int $opt, string $value): bool|array; - public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array; + public function sscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): bool|array; public function unlink(string|array $key, string ...$otherkeys): bool|int; public function unwatch(): bool|null; - public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): bool|array; + public function zscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): bool|array; } diff --git a/redis_array_arginfo.h b/redis_array_arginfo.h index bfacd08609..313bdc183a 100644 --- a/redis_array_arginfo.h +++ b/redis_array_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fb17c785beccf1dbeedaa48afb4aa7d48fd8b655 */ + * Stub hash: 1293a0ccef1e700da87d9ace76c3a05d91c0ffc7 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray___call, 0, 2, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, function_name, IS_STRING, 0) @@ -8,7 +8,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___construct, 0, 0, 1) ZEND_ARG_TYPE_MASK(0, name_or_hosts, MAY_BE_STRING|MAY_BE_ARRAY, NULL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray__continuum, 0, 0, MAY_BE_BOOL|MAY_BE_ARRAY) @@ -26,7 +26,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisArray__instance, ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray__rehash, 0, 0, _IS_BOOL, 1) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fn, IS_CALLABLE, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fn, IS_CALLABLE, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray__target, 0, 1, MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_NULL) @@ -44,7 +44,8 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisArray_discard, 0, 0, _IS_BOOL, 1) ZEND_END_ARG_INFO() -#define arginfo_class_RedisArray_exec arginfo_class_RedisArray_discard +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_exec, 0, 0, MAY_BE_BOOL|MAY_BE_NULL|MAY_BE_ARRAY) +ZEND_END_ARG_INFO() #define arginfo_class_RedisArray_flushall arginfo_class_RedisArray__continuum @@ -56,7 +57,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_hscan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -77,7 +78,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisArray_multi, 0, 1, RedisArray, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisArray_ping arginfo_class_RedisArray__continuum @@ -85,7 +86,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisArray_save arginfo_class_RedisArray__continuum ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisArray_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO(0, node, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") @@ -108,7 +109,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisArray_zscan arginfo_class_RedisArray_hscan - ZEND_METHOD(RedisArray, __call); ZEND_METHOD(RedisArray, __construct); ZEND_METHOD(RedisArray, _continuum); @@ -141,7 +141,6 @@ ZEND_METHOD(RedisArray, unlink); ZEND_METHOD(RedisArray, unwatch); ZEND_METHOD(RedisArray, zscan); - static const zend_function_entry class_RedisArray_methods[] = { ZEND_ME(RedisArray, __call, arginfo_class_RedisArray___call, ZEND_ACC_PUBLIC) ZEND_ME(RedisArray, __construct, arginfo_class_RedisArray___construct, ZEND_ACC_PUBLIC) @@ -182,7 +181,11 @@ static zend_class_entry *register_class_RedisArray(void) zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisArray", class_RedisArray_methods); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif return class_entry; } diff --git a/redis_array_impl.c b/redis_array_impl.c index 8c1bc6eef2..90e592fe77 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -90,38 +90,43 @@ ra_init_function_table(RedisArray *ra) ALLOC_HASHTABLE(ra->pure_cmds); zend_hash_init(ra->pure_cmds, 0, NULL, NULL, 0); - zend_hash_str_update_ptr(ra->pure_cmds, "EXISTS", sizeof("EXISTS") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "GET", sizeof("GET") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "GETBIT", sizeof("GETBIT") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "GETRANGE", sizeof("GETRANGE") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "HEXISTS", sizeof("HEXISTS") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "HGET", sizeof("HGET") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "HGETALL", sizeof("HGETALL") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "HKEYS", sizeof("HKEYS") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "HLEN", sizeof("HLEN") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "HMGET", sizeof("HMGET") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "HVALS", sizeof("HVALS") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "LINDEX", sizeof("LINDEX") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "LLEN", sizeof("LLEN") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "LRANGE", sizeof("LRANGE") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "OBJECT", sizeof("OBJECT") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "SCARD", sizeof("SCARD") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "SDIFF", sizeof("SDIFF") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "SINTER", sizeof("SINTER") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "SISMEMBER", sizeof("SISMEMBER") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "SMEMBERS", sizeof("SMEMBERS") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "SRANDMEMBER", sizeof("SRANDMEMBER") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "STRLEN", sizeof("STRLEN") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "SUNION", sizeof("SUNION") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "TYPE", sizeof("TYPE") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "ZCARD", sizeof("ZCARD") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "ZCOUNT", sizeof("ZCOUNT") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "ZRANGE", sizeof("ZRANGE") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "ZRANK", sizeof("ZRANK") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANGE", sizeof("ZREVRANGE") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANGEBYSCORE", sizeof("ZREVRANGEBYSCORE") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "ZREVRANK", sizeof("ZREVRANK") - 1, NULL); - zend_hash_str_update_ptr(ra->pure_cmds, "ZSCORE", sizeof("ZSCORE") - 1, NULL); + #define ra_add_pure_cmd(cmd) \ + zend_hash_str_add_empty_element(ra->pure_cmds, cmd, sizeof(cmd) - 1); + + ra_add_pure_cmd("EXISTS"); + ra_add_pure_cmd("GET"); + ra_add_pure_cmd("GETBIT"); + ra_add_pure_cmd("GETRANGE"); + ra_add_pure_cmd("HEXISTS"); + ra_add_pure_cmd("HGET"); + ra_add_pure_cmd("HGETALL"); + ra_add_pure_cmd("HKEYS"); + ra_add_pure_cmd("HLEN"); + ra_add_pure_cmd("HMGET"); + ra_add_pure_cmd("HVALS"); + ra_add_pure_cmd("LINDEX"); + ra_add_pure_cmd("LLEN"); + ra_add_pure_cmd("LRANGE"); + ra_add_pure_cmd("OBJECT"); + ra_add_pure_cmd("SCARD"); + ra_add_pure_cmd("SDIFF"); + ra_add_pure_cmd("SINTER"); + ra_add_pure_cmd("SISMEMBER"); + ra_add_pure_cmd("SMEMBERS"); + ra_add_pure_cmd("SRANDMEMBER"); + ra_add_pure_cmd("STRLEN"); + ra_add_pure_cmd("SUNION"); + ra_add_pure_cmd("TYPE"); + ra_add_pure_cmd("ZCARD"); + ra_add_pure_cmd("ZCOUNT"); + ra_add_pure_cmd("ZRANGE"); + ra_add_pure_cmd("ZRANK"); + ra_add_pure_cmd("ZREVRANGE"); + ra_add_pure_cmd("ZREVRANGEBYSCORE"); + ra_add_pure_cmd("ZREVRANK"); + ra_add_pure_cmd("ZSCORE"); + + #undef ra_add_pure_cmd } static int @@ -134,7 +139,7 @@ ra_find_name(const char *name) { for(p = ini_names; p;) { next = strchr(p, ','); if(next) { - if(strncmp(p, name, next - p) == 0) { + if(redis_strncmp(p, name, next - p) == 0) { return 1; } } else { @@ -197,7 +202,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zval(Z_ARRVAL(z_tmp), name, name_len, &z_fun, 1, 0); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find distributor */ @@ -205,7 +210,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zval(Z_ARRVAL(z_tmp), name, name_len, &z_dist, 1, 0); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find hash algorithm */ @@ -213,7 +218,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_string(Z_ARRVAL(z_tmp), name, name_len, &algorithm); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find index option */ @@ -221,7 +226,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_index); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find autorehash option */ @@ -229,7 +234,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_autorehash); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find retry interval option */ @@ -237,7 +242,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_long(Z_ARRVAL(z_tmp), name, name_len, &l_retry_interval); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find pconnect option */ @@ -245,7 +250,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_pconnect); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find lazy connect option */ @@ -253,7 +258,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_zend_bool(Z_ARRVAL(z_tmp), name, name_len, &b_lazy_connect); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find connect timeout option */ @@ -261,7 +266,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &d_connect_timeout); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find read timeout option */ @@ -269,7 +274,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &read_timeout); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find consistent option */ @@ -277,9 +282,10 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); if ((z_data = zend_hash_str_find(Z_ARRVAL(z_tmp), name, name_len)) != NULL) { - consistent = Z_TYPE_P(z_data) == IS_STRING && strncmp(Z_STRVAL_P(z_data), "1", 1) == 0; + consistent = Z_TYPE_P(z_data) == IS_STRING && + redis_strncmp(Z_STRVAL_P(z_data), ZEND_STRL("1")) == 0; } - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* find auth option */ @@ -287,7 +293,7 @@ RedisArray *ra_load_array(const char *name) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_auth(Z_ARRVAL(z_tmp), name, name_len, &user, &pass); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* create RedisArray object */ @@ -303,10 +309,10 @@ RedisArray *ra_load_array(const char *name) { if (user) zend_string_release(user); if (pass) zend_string_release(pass); - zval_dtor(&z_params_hosts); - zval_dtor(&z_params_prev); - zval_dtor(&z_dist); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_params_hosts); + zval_ptr_dtor_nogc(&z_params_prev); + zval_ptr_dtor_nogc(&z_dist); + zval_ptr_dtor_nogc(&z_fun); return ra; } @@ -379,7 +385,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev if (ra_load_hosts(ra, hosts, user, pass, retry_interval, b_lazy_connect) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { - zval_dtor(&ra->redis[i]); + zval_ptr_dtor_nogc(&ra->redis[i]); zend_string_release(ra->hosts[i]); } efree(ra->redis); @@ -429,8 +435,8 @@ ra_call_extractor(RedisArray *ra, const char *key, int key_len) out = zval_get_string(&z_ret); } - zval_dtor(&z_argv); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_argv); + zval_ptr_dtor_nogc(&z_ret); return out; } @@ -468,8 +474,8 @@ ra_call_distributor(RedisArray *ra, const char *key, int key_len) ret = (Z_TYPE(z_ret) == IS_LONG) ? Z_LVAL(z_ret) : -1; - zval_dtor(&z_argv); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_argv); + zval_ptr_dtor_nogc(&z_ret); return ret; } @@ -567,8 +573,8 @@ ra_index_multi(zval *z_redis, long multi_value) { ZVAL_STRINGL(&z_fun_multi, "MULTI", 5); ZVAL_LONG(&z_args[0], multi_value); call_user_function(&redis_ce->function_table, z_redis, &z_fun_multi, &z_ret, 1, z_args); - zval_dtor(&z_fun_multi); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun_multi); + zval_ptr_dtor_nogc(&z_ret); } static void @@ -598,9 +604,9 @@ ra_index_change_keys(const char *cmd, zval *z_keys, zval *z_redis) { /* run cmd */ call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, argc, z_args); - zval_dtor(&z_args[0]); - zval_dtor(&z_fun); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_args[0]); + zval_ptr_dtor_nogc(&z_fun); + zval_ptr_dtor_nogc(&z_ret); efree(z_args); /* free container */ } @@ -637,7 +643,7 @@ ra_index_keys(zval *z_pairs, zval *z_redis) { ra_index_change_keys("SADD", &z_keys, z_redis); /* cleanup */ - zval_dtor(&z_keys); + zval_ptr_dtor_nogc(&z_keys); } void @@ -653,10 +659,10 @@ ra_index_key(const char *key, int key_len, zval *z_redis) { /* run SADD */ call_user_function(&redis_ce->function_table, z_redis, &z_fun_sadd, &z_ret, 2, z_args); - zval_dtor(&z_fun_sadd); - zval_dtor(&z_args[1]); - zval_dtor(&z_args[0]); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun_sadd); + zval_ptr_dtor_nogc(&z_args[1]); + zval_ptr_dtor_nogc(&z_args[0]); + zval_ptr_dtor_nogc(&z_ret); } void @@ -667,7 +673,7 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all) { /* run EXEC */ ZVAL_STRINGL(&z_fun_exec, "EXEC", 4); call_user_function(&redis_ce->function_table, z_redis, &z_fun_exec, &z_ret, 0, NULL); - zval_dtor(&z_fun_exec); + zval_ptr_dtor_nogc(&z_fun_exec); /* extract first element of exec array and put into return_value. */ if(Z_TYPE(z_ret) == IS_ARRAY) { @@ -680,7 +686,7 @@ ra_index_exec(zval *z_redis, zval *return_value, int keep_all) { } } } - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); /* zval *zptr = &z_ret; */ /* php_var_dump(&zptr, 0); */ @@ -695,8 +701,8 @@ ra_index_discard(zval *z_redis, zval *return_value) { ZVAL_STRINGL(&z_fun_discard, "DISCARD", 7); call_user_function(&redis_ce->function_table, z_redis, &z_fun_discard, &z_ret, 0, NULL); - zval_dtor(&z_fun_discard); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun_discard); + zval_ptr_dtor_nogc(&z_ret); } void @@ -708,8 +714,8 @@ ra_index_unwatch(zval *z_redis, zval *return_value) { ZVAL_STRINGL(&z_fun_unwatch, "UNWATCH", 7); call_user_function(&redis_ce->function_table, z_redis, &z_fun_unwatch, &z_ret, 0, NULL); - zval_dtor(&z_fun_unwatch); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun_unwatch); + zval_ptr_dtor_nogc(&z_ret); } zend_bool @@ -747,15 +753,15 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TYPE", 4); call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); - zval_dtor(&z_fun); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun); + zval_ptr_dtor_nogc(&z_ret); /* run TYPE */ ZVAL_NULL(&z_ret); ZVAL_STRINGL(&z_fun, "TTL", 3); call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_arg); - zval_dtor(&z_fun); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun); + zval_ptr_dtor_nogc(&z_ret); /* Get the result from the pipeline. */ ra_index_exec(z_from, &z_ret, 1); @@ -769,8 +775,8 @@ ra_get_key_type(zval *z_redis, const char *key, int key_len, zval *z_from, long res[i++] = Z_LVAL_P(z_data); } ZEND_HASH_FOREACH_END(); } - zval_dtor(&z_arg); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_arg); + zval_ptr_dtor_nogc(&z_ret); return success; } @@ -788,10 +794,10 @@ ra_remove_from_index(zval *z_redis, const char *key, int key_len) { call_user_function(&redis_ce->function_table, z_redis, &z_fun_srem, &z_ret, 2, z_args); /* cleanup */ - zval_dtor(&z_fun_srem); - zval_dtor(&z_args[1]); - zval_dtor(&z_args[0]); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun_srem); + zval_ptr_dtor_nogc(&z_args[1]); + zval_ptr_dtor_nogc(&z_args[0]); + zval_ptr_dtor_nogc(&z_ret); } @@ -808,9 +814,9 @@ ra_del_key(const char *key, int key_len, zval *z_from) { ZVAL_STRINGL(&z_fun_del, "DEL", 3); ZVAL_STRINGL(&z_args[0], key, key_len); call_user_function(&redis_ce->function_table, z_from, &z_fun_del, &z_ret, 1, z_args); - zval_dtor(&z_fun_del); - zval_dtor(&z_args[0]); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun_del); + zval_ptr_dtor_nogc(&z_args[0]); + zval_ptr_dtor_nogc(&z_ret); /* remove key from index */ ra_remove_from_index(z_from, key, key_len); @@ -833,9 +839,9 @@ ra_expire_key(const char *key, int key_len, zval *z_to, long ttl) { ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_LONG(&z_args[1], ttl); call_user_function(&redis_ce->function_table, z_to, &z_fun_expire, &z_ret, 2, z_args); - zval_dtor(&z_fun_expire); - zval_dtor(&z_args[0]); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun_expire); + zval_ptr_dtor_nogc(&z_args[0]); + zval_ptr_dtor_nogc(&z_ret); } return 1; @@ -857,15 +863,15 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { ZVAL_STRINGL(&z_args[2], "-1", 2); ZVAL_BOOL(&z_args[3], 1); call_user_function(&redis_ce->function_table, z_from, &z_fun_zrange, &z_ret, 4, z_args); - zval_dtor(&z_fun_zrange); - zval_dtor(&z_args[2]); - zval_dtor(&z_args[1]); - zval_dtor(&z_args[0]); + zval_ptr_dtor_nogc(&z_fun_zrange); + zval_ptr_dtor_nogc(&z_args[2]); + zval_ptr_dtor_nogc(&z_args[1]); + zval_ptr_dtor_nogc(&z_args[0]); if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); return 0; } @@ -900,13 +906,13 @@ ra_move_zset(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { ra_expire_key(key, key_len, z_to, ttl); /* cleanup */ - zval_dtor(&z_fun_zadd); - zval_dtor(&z_ret_dest); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun_zadd); + zval_ptr_dtor_nogc(&z_ret_dest); + zval_ptr_dtor_nogc(&z_ret); /* Free the array itself */ for (i = 0; i < 1 + 2 * count; i++) { - zval_dtor(&z_zadd_args[i]); + zval_ptr_dtor_nogc(&z_zadd_args[i]); } efree(z_zadd_args); @@ -922,12 +928,12 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) ZVAL_STRINGL(&z_fun_get, "GET", 3); ZVAL_STRINGL(&z_args[0], key, key_len); call_user_function(&redis_ce->function_table, z_from, &z_fun_get, &z_ret, 1, z_args); - zval_dtor(&z_fun_get); + zval_ptr_dtor_nogc(&z_fun_get); if(Z_TYPE(z_ret) != IS_STRING) { /* key not found or replaced */ /* TODO: report? */ - zval_dtor(&z_args[0]); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_args[0]); + zval_ptr_dtor_nogc(&z_ret); return 0; } @@ -936,21 +942,21 @@ ra_move_string(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) ZVAL_STRINGL(&z_fun_set, "SETEX", 5); ZVAL_LONG(&z_args[1], ttl); ZVAL_STRINGL(&z_args[2], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ - zval_dtor(&z_ret); /* free memory from our previous call */ + zval_ptr_dtor_nogc(&z_ret); /* free memory from our previous call */ call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 3, z_args); /* cleanup */ - zval_dtor(&z_args[2]); + zval_ptr_dtor_nogc(&z_args[2]); } else { ZVAL_STRINGL(&z_fun_set, "SET", 3); ZVAL_STRINGL(&z_args[1], Z_STRVAL(z_ret), Z_STRLEN(z_ret)); /* copy z_ret to arg 1 */ - zval_dtor(&z_ret); /* free memory from our previous return value */ + zval_ptr_dtor_nogc(&z_ret); /* free memory from our previous return value */ call_user_function(&redis_ce->function_table, z_to, &z_fun_set, &z_ret, 2, z_args); /* cleanup */ - zval_dtor(&z_args[1]); + zval_ptr_dtor_nogc(&z_args[1]); } - zval_dtor(&z_fun_set); - zval_dtor(&z_args[0]); - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_fun_set); + zval_ptr_dtor_nogc(&z_args[0]); + zval_ptr_dtor_nogc(&z_ret); return 1; } @@ -963,27 +969,27 @@ ra_move_hash(const char *key, int key_len, zval *z_from, zval *z_to, long ttl) { ZVAL_STRINGL(&z_args[0], key, key_len); ZVAL_STRINGL(&z_fun_hgetall, "HGETALL", 7); call_user_function(&redis_ce->function_table, z_from, &z_fun_hgetall, &z_args[1], 1, z_args); - zval_dtor(&z_fun_hgetall); + zval_ptr_dtor_nogc(&z_fun_hgetall); if (Z_TYPE(z_args[1]) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ - zval_dtor(&z_args[1]); - zval_dtor(&z_args[0]); + zval_ptr_dtor_nogc(&z_args[1]); + zval_ptr_dtor_nogc(&z_args[0]); return 0; } /* run HMSET on target */ ZVAL_STRINGL(&z_fun_hmset, "HMSET", 5); call_user_function(&redis_ce->function_table, z_to, &z_fun_hmset, &z_ret_dest, 2, z_args); - zval_dtor(&z_fun_hmset); - zval_dtor(&z_ret_dest); + zval_ptr_dtor_nogc(&z_fun_hmset); + zval_ptr_dtor_nogc(&z_ret_dest); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl); /* cleanup */ - zval_dtor(&z_args[1]); - zval_dtor(&z_args[0]); + zval_ptr_dtor_nogc(&z_args[1]); + zval_ptr_dtor_nogc(&z_args[0]); return 1; } @@ -1012,15 +1018,15 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, call_user_function(&redis_ce->function_table, z_from, &z_fun_retrieve, &z_ret, list_count, z_retrieve_args); /* cleanup */ - zval_dtor(&z_fun_retrieve); + zval_ptr_dtor_nogc(&z_fun_retrieve); for(i = 0; i < list_count; ++i) { - zval_dtor(&z_retrieve_args[i]); + zval_ptr_dtor_nogc(&z_retrieve_args[i]); } efree(z_retrieve_args); if(Z_TYPE(z_ret) != IS_ARRAY) { /* key not found or replaced */ /* TODO: report? */ - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); return 0; } @@ -1039,19 +1045,19 @@ ra_move_collection(const char *key, int key_len, zval *z_from, zval *z_to, } ZEND_HASH_FOREACH_END(); /* Clean up our input return value */ - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); call_user_function(&redis_ce->function_table, z_to, &z_fun_sadd, &z_ret, count, z_sadd_args); /* cleanup */ - zval_dtor(&z_fun_sadd); + zval_ptr_dtor_nogc(&z_fun_sadd); for (i = 0; i < count; i++) { - zval_dtor(&z_sadd_args[i]); + zval_ptr_dtor_nogc(&z_sadd_args[i]); } efree(z_sadd_args); /* Clean up our output return value */ - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); /* Expire if needed */ ra_expire_key(key, key_len, z_to, ttl); @@ -1144,8 +1150,8 @@ zval_rehash_callback(zend_fcall_info *z_cb, zend_fcall_info_cache *z_cb_cache, zend_call_function(z_cb, z_cb_cache); /* cleanup */ - zval_dtor(&z_args[0]); - zval_dtor(z_ret); + zval_ptr_dtor_nogc(&z_args[0]); + zval_ptr_dtor_nogc(z_ret); } static void @@ -1166,8 +1172,8 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool } ZVAL_NULL(&z_ret); call_user_function(&redis_ce->function_table, z_redis, &z_fun, &z_ret, 1, &z_argv); - zval_dtor(&z_argv); - zval_dtor(&z_fun); + zval_ptr_dtor_nogc(&z_argv); + zval_ptr_dtor_nogc(&z_fun); if (Z_TYPE(z_ret) == IS_ARRAY) { h_keys = Z_ARRVAL(z_ret); @@ -1175,7 +1181,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool } if (!count) { - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); return; } @@ -1197,7 +1203,7 @@ ra_rehash_server(RedisArray *ra, zval *z_redis, zend_string *hostname, zend_bool } ZEND_HASH_FOREACH_END(); /* cleanup */ - zval_dtor(&z_ret); + zval_ptr_dtor_nogc(&z_ret); } void diff --git a/redis_array_legacy_arginfo.h b/redis_array_legacy_arginfo.h index 1f2174ef0b..cdb20349c1 100644 --- a/redis_array_legacy_arginfo.h +++ b/redis_array_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: fb17c785beccf1dbeedaa48afb4aa7d48fd8b655 */ + * Stub hash: 1293a0ccef1e700da87d9ace76c3a05d91c0ffc7 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisArray___call, 0, 0, 2) ZEND_ARG_INFO(0, function_name) @@ -105,7 +105,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisArray_zscan arginfo_class_RedisArray_hscan - ZEND_METHOD(RedisArray, __call); ZEND_METHOD(RedisArray, __construct); ZEND_METHOD(RedisArray, _continuum); @@ -138,7 +137,6 @@ ZEND_METHOD(RedisArray, unlink); ZEND_METHOD(RedisArray, unwatch); ZEND_METHOD(RedisArray, zscan); - static const zend_function_entry class_RedisArray_methods[] = { ZEND_ME(RedisArray, __call, arginfo_class_RedisArray___call, ZEND_ACC_PUBLIC) ZEND_ME(RedisArray, __construct, arginfo_class_RedisArray___construct, ZEND_ACC_PUBLIC) @@ -179,7 +177,11 @@ static zend_class_entry *register_class_RedisArray(void) zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisArray", class_RedisArray_methods); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif return class_entry; } diff --git a/redis_cluster.c b/redis_cluster.c index ae7e2d2e73..71a5003e7b 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -43,6 +43,119 @@ zend_class_entry *redis_cluster_exception_ce; #include "redis_cluster_arginfo.h" #endif +static void +cluster_enqueue_response(redisCluster *c, short slot, cluster_cb cb, void *ctx) +{ + clusterFoldItem *item; + + item = emalloc(sizeof(clusterFoldItem)); + item->callback = cb; + item->slot = slot; + item->ctx = ctx; + item->next = NULL; + item->flags = c->flags->flags; + + if (UNEXPECTED(c->multi_head == NULL)) { + c->multi_head = item; + c->multi_curr = item; + } else { + c->multi_curr->next = item; + c->multi_curr = item; + } +} + +static void cluster_free_queue(redisCluster *c) { + clusterFoldItem *item = c->multi_head, *tmp; + + while (item) { + tmp = item->next; + efree(item); + item = tmp; + } + + c->multi_head = NULL; + c->multi_curr = NULL; +} + +static void cluster_reset_multi(redisCluster *c) { + redisClusterNode *node; + ZEND_HASH_FOREACH_PTR(c->nodes, node) { + if (node == NULL) + continue; + node->sock->watching = 0; + node->sock->mode = ATOMIC; + } ZEND_HASH_FOREACH_END(); + + c->flags->watching = 0; + c->flags->mode = ATOMIC; +} + +void +cluster_process_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + redis_cmd_cb cmd_cb, cluster_cb resp_cb, int readonly) +{ + void *ctx = NULL; + int cmd_len; + short slot; + char *cmd; + + c->readonly = readonly && CLUSTER_IS_ATOMIC(c); + + if (cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, &cmd_len, &slot, + &ctx) == FAILURE) + { + RETURN_FALSE; + } + + if (cluster_send_command(c, slot, cmd, cmd_len) < 0 || c->err != NULL) { + efree(cmd); + RETURN_FALSE; + } + + efree(cmd); + + if (c->flags->mode == MULTI) { + cluster_enqueue_response(c, slot, resp_cb, ctx); + RETURN_ZVAL(getThis(), 1, 0); + } + + resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); +} + +void +cluster_process_kw_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + const char *kw, redis_kw_cmd_cb cmd_cb, cluster_cb resp_cb, + int readonly) +{ + void *ctx = NULL; + int cmd_len; + short slot; + char *cmd; + + c->readonly = readonly && CLUSTER_IS_ATOMIC(c); + + /* TODO: Update kw commands to take a const char * (and len to avoid strlen) */ + if (cmd_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, (char*)kw, &cmd, &cmd_len, + &slot, &ctx) == FAILURE) + { + RETURN_FALSE; + } + + if (cluster_send_command(c, slot, cmd, cmd_len) < 0 || c->err != NULL) { + efree(cmd); + RETURN_FALSE; + } + + efree(cmd); + + if (c->flags->mode == MULTI) { + cluster_enqueue_response(c, slot, resp_cb, ctx); + RETURN_ZVAL(getThis(), 1, 0); + } + + resp_cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); +} + PHP_MINIT_FUNCTION(redis_cluster) { redis_cluster_ce = register_class_RedisCluster(); @@ -96,6 +209,7 @@ zend_object * create_cluster_context(zend_class_entry *class_type) { memcpy(&RedisCluster_handlers, zend_get_std_object_handlers(), sizeof(RedisCluster_handlers)); RedisCluster_handlers.offset = XtOffsetOf(redisCluster, std); RedisCluster_handlers.free_obj = free_cluster_context; + RedisCluster_handlers.clone_obj = NULL; cluster->std.handlers = &RedisCluster_handlers; @@ -132,14 +246,13 @@ static void redis_cluster_init(redisCluster *c, HashTable *ht_seeds, double time c->flags->user = zend_string_copy(user); if (pass && ZSTR_LEN(pass)) c->flags->pass = zend_string_copy(pass); - if (context) { - redis_sock_set_stream_context(c->flags, context); - } + if (context) + redis_sock_set_context_zval(c->flags, context); c->flags->timeout = timeout; c->flags->read_timeout = read_timeout; c->flags->persistent = persistent; - c->waitms = timeout * 1000L; + c->waitms = (long)(1000 * (timeout + read_timeout)); /* Attempt to load slots from cache if caching is enabled */ if (CLUSTER_CACHING_ENABLED()) { @@ -179,7 +292,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) { if ((z_value = zend_hash_str_find(Z_ARRVAL(z_seeds), name, name_len)) != NULL) { ht_seeds = Z_ARRVAL_P(z_value); } else { - zval_dtor(&z_seeds); + zval_ptr_dtor_nogc(&z_seeds); CLUSTER_THROW_EXCEPTION("Couldn't find seeds for cluster", 0); return; } @@ -189,7 +302,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &timeout); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* Read timeout */ @@ -197,7 +310,7 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_double(Z_ARRVAL(z_tmp), name, name_len, &read_timeout); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* Persistent connections */ @@ -205,21 +318,21 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_bool(Z_ARRVAL(z_tmp), name, name_len, &persistent); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } if ((iptr = INI_STR("redis.clusters.auth"))) { array_init(&z_tmp); sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_tmp); redis_conf_auth(Z_ARRVAL(z_tmp), name, name_len, &user, &pass); - zval_dtor(&z_tmp); + zval_ptr_dtor_nogc(&z_tmp); } /* Attempt to create/connect to the cluster */ redis_cluster_init(c, ht_seeds, timeout, read_timeout, persistent, user, pass, NULL); /* Clean up */ - zval_dtor(&z_seeds); + zval_ptr_dtor_nogc(&z_seeds); if (user) zend_string_release(user); if (pass) zend_string_release(pass); } @@ -240,7 +353,7 @@ PHP_METHOD(RedisCluster, __construct) { // Parse arguments if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Os!|addbza!", &object, redis_cluster_ce, &name, + "Os!|a!ddbza!", &object, redis_cluster_ce, &name, &name_len, &z_seeds, &timeout, &read_timeout, &persistent, &z_auth, &context) == FAILURE) { @@ -248,7 +361,7 @@ PHP_METHOD(RedisCluster, __construct) { } /* If we've got a string try to load from INI */ - if (ZEND_NUM_ARGS() < 2) { + if (ZEND_NUM_ARGS() < 2 || z_seeds == NULL) { if (name_len == 0) { // Require a name CLUSTER_THROW_EXCEPTION("You must specify a name or pass seeds!", 0); } @@ -281,6 +394,19 @@ PHP_METHOD(RedisCluster, get) { } /* }}} */ +/* {{{ proto string RedisCluster::getdel(string key) */ +PHP_METHOD(RedisCluster, getdel) { + CLUSTER_PROCESS_KW_CMD("GETDEL", redis_key_cmd, cluster_bulk_resp, 1); +} +/* }}} */ + +/* {{{ proto array|false RedisCluster::getWithMeta(string key) */ +PHP_METHOD(RedisCluster, getWithMeta) { + CLUSTER_PROCESS_KW_CMD("GET", redis_key_cmd, cluster_bulk_withmeta_resp, 1); +} +/* }}} */ + + /* {{{ proto bool RedisCluster::set(string key, string value) */ PHP_METHOD(RedisCluster, set) { CLUSTER_PROCESS_CMD(set, cluster_set_resp, 0); @@ -311,9 +437,9 @@ distcmd_resp_handler(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, short slot, if (CLUSTER_IS_ATOMIC(c)) { // Process response now - cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, (void*)ctx); + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); } else { - CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); + cluster_enqueue_response(c, slot, cb, ctx); } // Clear out our command but retain allocated memory @@ -671,7 +797,15 @@ static void cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS, /* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */ PHP_METHOD(RedisCluster, del) { - cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL") - 1); + cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("DEL")); +} + +PHP_METHOD(RedisCluster, delex) { + CLUSTER_PROCESS_CMD(delex, cluster_long_resp, 0); +} + +PHP_METHOD(RedisCluster, delifeq) { + CLUSTER_PROCESS_KW_CMD("DELIFEQ", redis_kv_cmd, cluster_long_resp, 0); } /* {{{ proto array RedisCluster::unlink(string key1, string key2, ... keyN) */ @@ -679,6 +813,10 @@ PHP_METHOD(RedisCluster, unlink) { cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1); } +PHP_METHOD(RedisCluster, msetex) { + CLUSTER_PROCESS_CMD(msetex, cluster_long_resp, 0); +} + /* {{{ proto array RedisCluster::mget(array keys) */ PHP_METHOD(RedisCluster, mget) { zval *z_ret = emalloc(sizeof(*z_ret)); @@ -689,7 +827,7 @@ PHP_METHOD(RedisCluster, mget) { if (cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MGET", sizeof("MGET")-1, z_ret, cluster_mbulk_mget_resp) < 0) { - zval_dtor(z_ret); + zval_ptr_dtor_nogc(z_ret); efree(z_ret); RETURN_FALSE; } @@ -720,13 +858,17 @@ PHP_METHOD(RedisCluster, msetnx) { if (cluster_mset_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "MSETNX", sizeof("MSETNX")-1, z_ret, cluster_msetnx_resp) ==-1) { - zval_dtor(z_ret); + zval_ptr_dtor_nogc(z_ret); efree(z_ret); RETURN_FALSE; } } /* }}} */ +PHP_METHOD(RedisCluster, getex) { + CLUSTER_PROCESS_CMD(getex, cluster_bulk_resp, 0); +} + /* {{{ proto bool RedisCluster::setex(string key, string value, int expiry) */ PHP_METHOD(RedisCluster, setex) { CLUSTER_PROCESS_KW_CMD("SETEX", redis_key_long_val_cmd, cluster_bool_resp, 0); @@ -794,7 +936,7 @@ PHP_METHOD(RedisCluster, keys) { { php_error_docref(0, E_ERROR, "Can't send KEYS to %s:%d", ZSTR_VAL(node->sock->host), node->sock->port); - zval_dtor(return_value); + zval_ptr_dtor_nogc(return_value); efree(cmd); RETURN_FALSE; } @@ -862,14 +1004,15 @@ PHP_METHOD(RedisCluster, spop) { } else if (ZEND_NUM_ARGS() == 2) { CLUSTER_PROCESS_KW_CMD("SPOP", redis_key_long_cmd, cluster_mbulk_resp, 0); } else { - ZEND_WRONG_PARAM_COUNT(); + zend_wrong_param_count(); } } /* }}} */ /* {{{ proto string|array RedisCluster::srandmember(string key, [long count]) */ PHP_METHOD(RedisCluster, srandmember) { - CLUSTER_PROCESS_CMD(srandmember, cluster_srandmember_resp, 1); + CLUSTER_PROCESS_KW_CMD("SRANDMEMBER", redis_randmember_cmd, + cluster_randmember_resp, 1); } /* {{{ proto string RedisCluster::strlen(string key) */ @@ -1042,7 +1185,7 @@ PHP_METHOD(RedisCluster, sdiffstore) { } /* }}} */ -/* {{{ proto bool RedisCluster::smove(sting src, string dst, string mem) */ +/* {{{ proto bool RedisCluster::smove(string src, string dst, string mem) */ PHP_METHOD(RedisCluster, smove) { CLUSTER_PROCESS_CMD(smove, cluster_1_resp, 0); } @@ -1143,6 +1286,12 @@ PHP_METHOD(RedisCluster, hget) { } /* }}} */ +/* {{{ proto string RedisCluster::hgetWithMeta(string key, string mem) */ +PHP_METHOD(RedisCluster, hgetWithMeta) { + CLUSTER_PROCESS_KW_CMD("HGET", redis_key_str_cmd, cluster_bulk_withmeta_resp, 1); +} +/* }}} */ + /* {{{ proto bool RedisCluster::hset(string key, string mem, string val) */ PHP_METHOD(RedisCluster, hset) { CLUSTER_PROCESS_CMD(hset, cluster_long_resp, 0); @@ -1186,6 +1335,49 @@ PHP_METHOD(RedisCluster, hmset) { } /* }}} */ +PHP_METHOD(RedisCluster, hexpire) { + CLUSTER_PROCESS_KW_CMD("HEXPIRE", + redis_hexpire_cmd, cluster_variant_resp, 0); +} + +PHP_METHOD(RedisCluster, hpexpire) { + CLUSTER_PROCESS_KW_CMD("HPEXPIRE", + redis_hexpire_cmd, cluster_variant_resp, 0); +} + +PHP_METHOD(RedisCluster, hexpireat) { + CLUSTER_PROCESS_KW_CMD("HEXPIREAT", + redis_hexpire_cmd, cluster_variant_resp, 0); +} + +PHP_METHOD(RedisCluster, hpexpireat) { + CLUSTER_PROCESS_KW_CMD("HPEXPIREAT", + redis_hexpire_cmd, cluster_variant_resp, 0); +} + +PHP_METHOD(RedisCluster, httl) { + CLUSTER_PROCESS_KW_CMD("HTTL", redis_httl_cmd, cluster_variant_resp, 1); +} + +PHP_METHOD(RedisCluster, hpttl) { + CLUSTER_PROCESS_KW_CMD("HPTTL", redis_httl_cmd, cluster_variant_resp, 1); +} + + +PHP_METHOD(RedisCluster, hexpiretime) { + CLUSTER_PROCESS_KW_CMD("HEXPIRETIME", redis_httl_cmd, + cluster_variant_resp, 1); +} + +PHP_METHOD(RedisCluster, hpexpiretime) { + CLUSTER_PROCESS_KW_CMD("HPEXPIRETIME", redis_httl_cmd, + cluster_variant_resp, 1); +} + +PHP_METHOD(RedisCluster, hpersist) { + CLUSTER_PROCESS_KW_CMD("HPERSIST", redis_httl_cmd, cluster_variant_resp, 0); +} + /* {{{ proto bool RedisCluster::hrandfield(string key, [array $options]) */ PHP_METHOD(RedisCluster, hrandfield) { CLUSTER_PROCESS_CMD(hrandfield, cluster_hrandfield_resp, 1); @@ -1204,6 +1396,18 @@ PHP_METHOD(RedisCluster, hmget) { } /* }}} */ +PHP_METHOD(RedisCluster, hgetex) { + CLUSTER_PROCESS_CMD(hgetex, cluster_mbulk_assoc_resp, 0); +} + +PHP_METHOD(RedisCluster, hsetex) { + CLUSTER_PROCESS_CMD(hsetex, cluster_long_resp, 0); +} + +PHP_METHOD(RedisCluster, hgetdel) { + CLUSTER_PROCESS_CMD(hgetdel, cluster_mbulk_assoc_resp, 0); +} + /* {{{ proto array RedisCluster::hstrlen(string key, string field) */ PHP_METHOD(RedisCluster, hstrlen) { CLUSTER_PROCESS_CMD(hstrlen, cluster_long_resp, 1); @@ -1299,6 +1503,14 @@ PHP_METHOD(RedisCluster, getbit) { } /* }}} */ +PHP_METHOD(RedisCluster, expiremember) { + CLUSTER_PROCESS_CMD(expiremember, cluster_long_resp, 0); +} + +PHP_METHOD(RedisCluster, expirememberat) { + CLUSTER_PROCESS_CMD(expiremember, cluster_long_resp, 0); +} + /* {{{ proto long RedisCluster::setbit(string key, long offset, bool onoff) */ PHP_METHOD(RedisCluster, setbit) { CLUSTER_PROCESS_CMD(setbit, cluster_long_resp, 0); @@ -1358,7 +1570,7 @@ PHP_METHOD(RedisCluster, zmpop) { } /* }}} */ -/* {{{ proto Redis|array|false Redis::bzmpop(double $timeout, array $keys, sring $from, int $count = 1) */ +/* {{{ proto Redis|array|false Redis::bzmpop(double $timeout, array $keys, string $from, int $count = 1) */ PHP_METHOD(RedisCluster, bzmpop) { CLUSTER_PROCESS_KW_CMD("BZMPOP", redis_mpop_cmd, cluster_mpop_resp, 0); } @@ -1552,7 +1764,7 @@ PHP_METHOD(RedisCluster, zpopmax) { } else if (ZEND_NUM_ARGS() == 2) { CLUSTER_PROCESS_KW_CMD("ZPOPMAX", redis_key_long_cmd, cluster_mbulk_zipdbl_resp, 0); } else { - ZEND_WRONG_PARAM_COUNT(); + zend_wrong_param_count(); } } /* }}} */ @@ -1564,7 +1776,7 @@ PHP_METHOD(RedisCluster, zpopmin) { } else if (ZEND_NUM_ARGS() == 2) { CLUSTER_PROCESS_KW_CMD("ZPOPMIN", redis_key_long_cmd, cluster_mbulk_zipdbl_resp, 0); } else { - ZEND_WRONG_PARAM_COUNT(); + zend_wrong_param_count(); } } /* }}} */ @@ -1819,6 +2031,13 @@ PHP_METHOD(RedisCluster, _pack) { redis_pack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags); } +PHP_METHOD(RedisCluster, _digest) { + redisCluster *c = GET_CONTEXT(); + + redis_digest_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, + redis_cluster_exception_ce); +} + PHP_METHOD(RedisCluster, _unpack) { redisCluster *c = GET_CONTEXT(); redis_unpack_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags); @@ -2020,8 +2239,8 @@ PHP_METHOD(RedisCluster, exec) { CLUSTER_THROW_EXCEPTION("Error processing EXEC across the cluster", 0); // Free our queue, reset MULTI state - CLUSTER_FREE_QUEUE(c); - CLUSTER_RESET_MULTI(c); + cluster_free_queue(c); + cluster_reset_multi(c); RETURN_FALSE; } @@ -2036,8 +2255,8 @@ PHP_METHOD(RedisCluster, exec) { // Free our callback queue, any enqueued distributed command context items // and reset our MULTI state. - CLUSTER_FREE_QUEUE(c); - CLUSTER_RESET_MULTI(c); + cluster_free_queue(c); + cluster_reset_multi(c); } /* {{{ proto bool RedisCluster::discard() */ @@ -2050,10 +2269,10 @@ PHP_METHOD(RedisCluster, discard) { } if (cluster_abort_exec(c) < 0) { - CLUSTER_RESET_MULTI(c); + cluster_reset_multi(c); } - CLUSTER_FREE_QUEUE(c); + cluster_free_queue(c); RETURN_TRUE; } @@ -2201,7 +2420,7 @@ static void cluster_raw_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) short slot; int i, argc = ZEND_NUM_ARGS(); - /* Commands using this pass-thru don't need to be enabled in MULTI mode */ + /* Commands using this pass-through don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { php_error_docref(0, E_WARNING, "Command can't be issued in MULTI mode"); @@ -2266,8 +2485,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, short slot; zval *z_it; HashTable *hash; - long it, num_ele; + long num_ele; zend_long count = 0; + zend_bool completed; + uint64_t cursor; // Can't be in MULTI mode if (!CLUSTER_IS_ATOMIC(c)) { @@ -2285,16 +2506,10 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, /* Treat as readonly */ c->readonly = 1; - // Convert iterator to long if it isn't, update our long iterator if it's - // set and >0, and finish if it's back to zero - if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) { - convert_to_long(z_it); - it = 0; - } else if (Z_LVAL_P(z_it) != 0) { - it = Z_LVAL_P(z_it); - } else { + /* Get our scan cursor and return early if we're done */ + cursor = redisGetScanCursor(z_it, &completed); + if (completed) RETURN_FALSE; - } // Apply any key prefix we have, get the slot key_free = redis_key_prefix(c->flags, &key, &key_len); @@ -2309,12 +2524,12 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, do { /* Free our return value if we're back in the loop */ if (Z_TYPE_P(return_value) == IS_ARRAY) { - zval_dtor(return_value); + zval_ptr_dtor_nogc(return_value); ZVAL_NULL(return_value); } // Create command - cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, it, pat, pat_len, + cmd_len = redis_fmt_scan_cmd(&cmd, type, key, key_len, cursor, pat, pat_len, count); // Send it off @@ -2328,7 +2543,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, // Read response if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, type, - &it) == FAILURE) + &cursor) == FAILURE) { CLUSTER_THROW_EXCEPTION("Couldn't read SCAN response", 0); if (key_free) efree(key); @@ -2342,7 +2557,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, // Free our command efree(cmd); - } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0); + } while (c->flags->scan & REDIS_SCAN_RETRY && cursor != 0 && num_ele == 0); // Free our pattern if (pat_free) efree(pat); @@ -2351,7 +2566,7 @@ static void cluster_kscan_cmd(INTERNAL_FUNCTION_PARAMETERS, if (key_free) efree(key); // Update iterator reference - Z_LVAL_P(z_it) = it; + redisSetScanCursor(z_it, cursor); } static int redis_acl_op_readonly(zend_string *op) { @@ -2379,7 +2594,7 @@ PHP_METHOD(RedisCluster, acl) { /* ACL in cluster needs a slot argument, and then at least the op */ if (argc < 2) { - WRONG_PARAM_COUNT; + zend_wrong_param_count(); RETURN_FALSE; } @@ -2394,7 +2609,7 @@ PHP_METHOD(RedisCluster, acl) { REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc - 1, "ACL"); - /* Read the op, determin if it's readonly, and add it */ + /* Read the op, determine if it's readonly, and add it */ zs = zval_get_string(&zargs[1]); readonly = redis_acl_op_readonly(zs); redis_cmd_append_sstr_zstr(&cmdstr, zs); @@ -2431,7 +2646,7 @@ PHP_METHOD(RedisCluster, acl) { if (CLUSTER_IS_ATOMIC(c)) { cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { - CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); + cluster_enqueue_response(c, slot, cb, ctx); } efree(cmdstr.c); @@ -2445,9 +2660,11 @@ PHP_METHOD(RedisCluster, scan) { size_t pat_len = 0; int cmd_len; short slot; - zval *z_it, *z_node; - long it, num_ele, pat_free = 0; + zval *zcursor, *z_node; + long num_ele, pat_free = 0; zend_long count = 0; + zend_bool completed; + uint64_t cursor; /* Treat as read-only */ c->readonly = CLUSTER_IS_ATOMIC(c); @@ -2459,21 +2676,16 @@ PHP_METHOD(RedisCluster, scan) { } /* Parse arguments */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/z|s!l", &z_it, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/z|s!l", &zcursor, &z_node, &pat, &pat_len, &count) == FAILURE) { RETURN_FALSE; } - /* Convert or update iterator */ - if (Z_TYPE_P(z_it) != IS_LONG || Z_LVAL_P(z_it) < 0) { - convert_to_long(z_it); - it = 0; - } else if (Z_LVAL_P(z_it) != 0) { - it = Z_LVAL_P(z_it); - } else { + /* Get the scan cursor and return early if we're done */ + cursor = redisGetScanCursor(zcursor, &completed); + if (completed) RETURN_FALSE; - } if (c->flags->scan & REDIS_SCAN_PREFIX) { pat_free = redis_key_prefix(c->flags, &pat, &pat_len); @@ -2484,12 +2696,12 @@ PHP_METHOD(RedisCluster, scan) { do { /* Free our return value if we're back in the loop */ if (Z_TYPE_P(return_value) == IS_ARRAY) { - zval_dtor(return_value); + zval_ptr_dtor_nogc(return_value); ZVAL_NULL(return_value); } /* Construct our command */ - cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, it, pat, pat_len, + cmd_len = redis_fmt_scan_cmd(&cmd, TYPE_SCAN, NULL, 0, cursor, pat, pat_len, count); if ((slot = cluster_cmd_get_slot(c, z_node)) < 0) { @@ -2505,7 +2717,7 @@ PHP_METHOD(RedisCluster, scan) { } if (cluster_scan_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, TYPE_SCAN, - &it) == FAILURE || Z_TYPE_P(return_value)!=IS_ARRAY) + &cursor) == FAILURE || Z_TYPE_P(return_value) != IS_ARRAY) { CLUSTER_THROW_EXCEPTION("Couldn't process SCAN response from node", 0); efree(cmd); @@ -2515,11 +2727,11 @@ PHP_METHOD(RedisCluster, scan) { efree(cmd); num_ele = zend_hash_num_elements(Z_ARRVAL_P(return_value)); - } while (c->flags->scan & REDIS_SCAN_RETRY && it != 0 && num_ele == 0); + } while (c->flags->scan & REDIS_SCAN_RETRY && cursor != 0 && num_ele == 0); if (pat_free) efree(pat); - Z_LVAL_P(z_it) = it; + redisSetScanCursor(zcursor, cursor); } /* }}} */ @@ -2639,7 +2851,7 @@ PHP_METHOD(RedisCluster, info) { if (CLUSTER_IS_ATOMIC(c)) { cluster_info_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { - CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_info_resp, ctx); + cluster_enqueue_response(c, slot, cluster_info_resp, ctx); } efree(cmdstr.c); @@ -2714,7 +2926,7 @@ PHP_METHOD(RedisCluster, client) { cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { void *ctx = NULL; - CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); + cluster_enqueue_response(c, slot, cb, ctx); } efree(cmd); @@ -2754,7 +2966,7 @@ PHP_METHOD(RedisCluster, script) { short slot; int argc = ZEND_NUM_ARGS(); - /* Commands using this pass-thru don't need to be enabled in MULTI mode */ + /* Commands using this pass-through don't need to be enabled in MULTI mode */ if (!CLUSTER_IS_ATOMIC(c)) { php_error_docref(0, E_WARNING, "Command can't be issued in MULTI mode"); @@ -2876,6 +3088,73 @@ PHP_METHOD(RedisCluster, randomkey) { } /* }}} */ +static +void cluster_gen_wait_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, + size_t kwlen, zend_bool has_local, int reply_type) +{ + zend_long numreplicas, timeout, numlocal = 0; + redisCluster *c = GET_CONTEXT(); + smart_string cmdstr = {0}; + void *ctx = NULL; + short slot; + zval *node; + int argc; + + argc = 3 + !!has_local; + + ZEND_PARSE_PARAMETERS_START(argc, argc) + Z_PARAM_ZVAL(node) + if (has_local) { + Z_PARAM_LONG(numlocal) + } + Z_PARAM_LONG(numreplicas) + Z_PARAM_LONG(timeout) + ZEND_PARSE_PARAMETERS_END(); + + if (numreplicas < 0 || timeout < 0 || numlocal < 0) { + php_error_docref(NULL, E_WARNING, "No arguments can be negative"); + RETURN_FALSE; + } + + slot = cluster_cmd_get_slot(c, node); + if (slot < 0) { + RETURN_FALSE; + } + + redis_cmd_init_sstr(&cmdstr, argc - 1, (char*)kw, kwlen); + if (has_local) { + redis_cmd_append_sstr_long(&cmdstr, numlocal); + } + redis_cmd_append_sstr_long(&cmdstr, numreplicas); + redis_cmd_append_sstr_long(&cmdstr, timeout); + + c->readonly = 0; + + if (cluster_send_slot(c, slot, cmdstr.c, cmdstr.len, reply_type) < 0) { + CLUSTER_THROW_EXCEPTION("Unable to send command at the specified node", 0); + smart_string_free(&cmdstr); + RETURN_FALSE; + } + + if (CLUSTER_IS_ATOMIC(c)) { + cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + cluster_enqueue_response(c, slot, cluster_variant_resp, ctx); + } + + smart_string_free(&cmdstr); +} + +PHP_METHOD(RedisCluster, wait) { + cluster_gen_wait_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("WAIT"), 0, + TYPE_INT); +} + +PHP_METHOD(RedisCluster, waitaof) { + cluster_gen_wait_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_STRL("WAITAOF"), 1, + TYPE_MULTIBULK); +} + /* {{{ proto bool RedisCluster::ping(string key| string msg) * proto bool RedisCluster::ping(array host_port| string msg) */ PHP_METHOD(RedisCluster, ping) { @@ -2932,9 +3211,9 @@ PHP_METHOD(RedisCluster, ping) { } } else { if (arg != NULL) { - CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_bulk_resp, ctx); + cluster_enqueue_response(c, slot, cluster_bulk_resp, ctx); } else { - CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); + cluster_enqueue_response(c, slot, cluster_variant_resp, ctx); } RETURN_ZVAL(getThis(), 1, 0); @@ -2942,6 +3221,63 @@ PHP_METHOD(RedisCluster, ping) { } /* }}} */ +PHP_METHOD(RedisCluster, vadd) { + CLUSTER_PROCESS_CMD(vadd, cluster_long_resp, 0); +} + +PHP_METHOD(RedisCluster, vsim) { + CLUSTER_PROCESS_CMD(vsim, cluster_zrange_resp, 1); +} + +PHP_METHOD(RedisCluster, vcard) { + CLUSTER_PROCESS_KW_CMD("VCARD", redis_key_cmd, cluster_long_resp, 1); +} + +PHP_METHOD(RedisCluster, vdim) { + CLUSTER_PROCESS_KW_CMD("VDIM", redis_key_cmd, cluster_long_resp, 1); +} + +PHP_METHOD(RedisCluster, vinfo) { + CLUSTER_PROCESS_KW_CMD("VINFO", redis_key_cmd, cluster_vinfo_resp, 1); +} + +PHP_METHOD(RedisCluster, vismember) { + CLUSTER_PROCESS_KW_CMD("VISMEMBER", redis_kv_cmd, cluster_1_resp, 1); +} + +PHP_METHOD(RedisCluster, vemb) { + CLUSTER_PROCESS_CMD(vemb, cluster_vemb_resp, 1); +} + +PHP_METHOD(RedisCluster, vrandmember) { + CLUSTER_PROCESS_KW_CMD("VRANDMEMBER", redis_randmember_cmd, + cluster_randmember_resp, 1); +} + +PHP_METHOD(RedisCluster, vrange) { + CLUSTER_PROCESS_KW_CMD("VRANGE", redis_vrange_cmd, cluster_mbulk_resp, 1); +} + +PHP_METHOD(RedisCluster, vrem) { + CLUSTER_PROCESS_KW_CMD("VREM", redis_kv_cmd, cluster_long_resp, 0); +} + +PHP_METHOD(RedisCluster, vlinks) { + CLUSTER_PROCESS_CMD(vlinks, cluster_vlinks_resp, 1); +} + +PHP_METHOD(RedisCluster, vgetattr) { + CLUSTER_PROCESS_CMD(vgetattr, cluster_vgetattr_resp, 1); +} + +PHP_METHOD(RedisCluster, vsetattr) { + CLUSTER_PROCESS_CMD(vsetattr, cluster_long_resp, 0); +} + +PHP_METHOD(RedisCluster, gcra) { + CLUSTER_PROCESS_CMD(gcra, cluster_variant_resp, 0); +} + /* {{{ proto long RedisCluster::xack(string key, string group, array ids) }}} */ PHP_METHOD(RedisCluster, xack) { CLUSTER_PROCESS_CMD(xack, cluster_long_resp, 0); @@ -2966,6 +3302,10 @@ PHP_METHOD(RedisCluster, xdel) { CLUSTER_PROCESS_KW_CMD("XDEL", redis_key_str_arr_cmd, cluster_long_resp, 0); } +PHP_METHOD(RedisCluster, xdelex) { + CLUSTER_PROCESS_CMD(xdelex, cluster_variant_resp, 0); +} + /* {{{ proto variant RedisCluster::xgroup(string op, [string key, string arg1, string arg2]) }}} */ PHP_METHOD(RedisCluster, xgroup) { CLUSTER_PROCESS_CMD(xgroup, cluster_variant_resp, 0); @@ -3049,7 +3389,7 @@ PHP_METHOD(RedisCluster, echo) { cluster_bulk_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { void *ctx = NULL; - CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_bulk_resp, ctx); + cluster_enqueue_response(c, slot, cluster_bulk_resp, ctx); } efree(cmd); @@ -3102,7 +3442,7 @@ PHP_METHOD(RedisCluster, rawcommand) { cluster_variant_raw_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); } else { void *ctx = NULL; - CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_raw_resp, ctx); + cluster_enqueue_response(c, slot, cluster_variant_raw_resp, ctx); } efree(cmd); @@ -3117,7 +3457,11 @@ PHP_METHOD(RedisCluster, command) { } PHP_METHOD(RedisCluster, copy) { - CLUSTER_PROCESS_CMD(copy, cluster_1_resp, 0) + CLUSTER_PROCESS_CMD(copy, cluster_1_resp, 0); +} + +PHP_METHOD(RedisCluster, digest) { + CLUSTER_PROCESS_KW_CMD("DIGEST", redis_key_cmd, cluster_bulk_raw_resp, 1); } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/redis_cluster.h b/redis_cluster.h index ebef92184e..443d4160bc 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -2,94 +2,26 @@ #define REDIS_CLUSTER_H #include "cluster_library.h" +#include "redis_commands.h" #include #include /* Get attached object context */ #define GET_CONTEXT() PHPREDIS_ZVAL_GET_OBJECT(redisCluster, getThis()) -/* Command building/processing is identical for every command */ -#define CLUSTER_BUILD_CMD(name, c, cmd, cmd_len, slot) \ - redis_##name##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, &cmd, \ - &cmd_len, &slot) +void cluster_process_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + redis_cmd_cb cmd_cb, cluster_cb resp_cb, int readonly); -/* Append information required to handle MULTI commands to the tail of our MULTI - * linked list. */ -#define CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx) \ - clusterFoldItem *_item; \ - _item = emalloc(sizeof(clusterFoldItem)); \ - _item->callback = cb; \ - _item->slot = slot; \ - _item->ctx = ctx; \ - _item->next = NULL; \ - if(c->multi_head == NULL) { \ - c->multi_head = _item; \ - c->multi_curr = _item; \ - } else { \ - c->multi_curr->next = _item; \ - c->multi_curr = _item; \ - } \ - -/* Simple macro to free our enqueued callbacks after we EXEC */ -#define CLUSTER_FREE_QUEUE(c) \ - clusterFoldItem *_item = c->multi_head, *_tmp; \ - while(_item) { \ - _tmp = _item->next; \ - efree(_item); \ - _item = _tmp; \ - } \ - c->multi_head = c->multi_curr = NULL; \ - -/* Reset anything flagged as MULTI */ -#define CLUSTER_RESET_MULTI(c) \ - redisClusterNode *_node; \ - ZEND_HASH_FOREACH_PTR(c->nodes, _node) { \ - if (_node == NULL) break; \ - _node->sock->watching = 0; \ - _node->sock->mode = ATOMIC; \ - } ZEND_HASH_FOREACH_END(); \ - c->flags->watching = 0; \ - c->flags->mode = ATOMIC; \ - -/* Simple 1-1 command -> response macro */ #define CLUSTER_PROCESS_CMD(cmdname, resp_func, readcmd) \ - redisCluster *c = GET_CONTEXT(); \ - c->readonly = CLUSTER_IS_ATOMIC(c) && readcmd; \ - char *cmd; int cmd_len; short slot; void *ctx=NULL; \ - if(redis_##cmdname##_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU,c->flags, &cmd, \ - &cmd_len, &slot, &ctx)==FAILURE) { \ - RETURN_FALSE; \ - } \ - if(cluster_send_command(c,slot,cmd,cmd_len)<0 || c->err!=NULL) {\ - efree(cmd); \ - RETURN_FALSE; \ - } \ - efree(cmd); \ - if(c->flags->mode == MULTI) { \ - CLUSTER_ENQUEUE_RESPONSE(c, slot, resp_func, ctx); \ - RETURN_ZVAL(getThis(), 1, 0); \ - } \ - resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); + cluster_process_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), \ + redis_##cmdname##_cmd, resp_func, readcmd) + +void cluster_process_kw_cmd(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + const char *kw, redis_kw_cmd_cb cmd_cb, cluster_cb resp_cb, int readonly); -/* More generic processing, where only the keyword differs */ -#define CLUSTER_PROCESS_KW_CMD(kw, cmdfunc, resp_func, readcmd) \ - redisCluster *c = GET_CONTEXT(); \ - c->readonly = CLUSTER_IS_ATOMIC(c) && readcmd; \ - char *cmd; int cmd_len; short slot; void *ctx=NULL; \ - if(cmdfunc(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len,\ - &slot,&ctx)==FAILURE) { \ - RETURN_FALSE; \ - } \ - if(cluster_send_command(c,slot,cmd,cmd_len)<0 || c->err!=NULL) { \ - efree(cmd); \ - RETURN_FALSE; \ - } \ - efree(cmd); \ - if(c->flags->mode == MULTI) { \ - CLUSTER_ENQUEUE_RESPONSE(c, slot, resp_func, ctx); \ - RETURN_ZVAL(getThis(), 1, 0); \ - } \ - resp_func(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); +#define CLUSTER_PROCESS_KW_CMD(kw, cmd_cb, resp_cb, readonly) \ + cluster_process_kw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, GET_CONTEXT(), \ + kw, cmd_cb, resp_cb, readonly) extern zend_class_entry *redis_cluster_ce; extern zend_class_entry *redis_cluster_exception_ce; diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index c2ab9f4809..7f7d93fbb0 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -8,6 +8,8 @@ class RedisCluster { /** + * Used to configure how `PhpRedis` will failover to replica nodes when a + * primary node fails to respond. * * @var int * @cvalue REDIS_OPT_FAILOVER @@ -16,6 +18,7 @@ class RedisCluster { public const OPT_SLAVE_FAILOVER = UNKNOWN; /** + * Never read from replicas. * * @var int * @cvalue REDIS_FAILOVER_NONE @@ -24,6 +27,7 @@ class RedisCluster { public const FAILOVER_NONE = UNKNOWN; /** + * Attempt to read from replicas when the primary errors out or is down. * * @var int * @cvalue REDIS_FAILOVER_ERROR @@ -32,6 +36,8 @@ class RedisCluster { public const FAILOVER_ERROR = UNKNOWN; /** + * Distribute readonly commands at random between the primary and + * replica(s). * * @var int * @cvalue REDIS_FAILOVER_DISTRIBUTE @@ -40,6 +46,7 @@ class RedisCluster { public const FAILOVER_DISTRIBUTE = UNKNOWN; /** + * Distribute readonly commands between the replicas only. * * @var int * @cvalue REDIS_FAILOVER_DISTRIBUTE_SLAVES @@ -47,40 +54,45 @@ class RedisCluster { */ public const FAILOVER_DISTRIBUTE_SLAVES = UNKNOWN; - public function __construct(string|null $name, array $seeds = NULL, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, #[\SensitiveParameter] mixed $auth = NULL, array $context = NULL); + public function __construct(string|null $name, ?array $seeds = null, int|float $timeout = 0, int|float $read_timeout = 0, bool $persistent = false, #[\SensitiveParameter] mixed $auth = null, ?array $context = null); /** - * @see Redis::_compress() + * {@see \Redis::_compress()} */ public function _compress(string $value): string; /** - * @see Redis::_uncompress() + * @see \Redis::_uncompress() */ public function _uncompress(string $value): string; /** - * @see Redis::_serialize() + * @see \Redis::_serialize() */ public function _serialize(mixed $value): bool|string; /** - * @see Redis::_unserialize() + * @see \Redis::_unserialize() */ public function _unserialize(string $value): mixed; /** - * @see Redis::_pack() + * @see \Redis::_pack() */ public function _pack(mixed $value): string; /** - * @see Redis::_unpack() + * @see \Redis::_digest() + */ + public function _digest(mixed $value): string; + + /** + * @see \Redis::_unpack() */ public function _unpack(string $value): mixed; /** - * @see Redis::_prefix() + * @see \Redis::_prefix() */ public function _prefix(string $key): bool|string; @@ -89,32 +101,43 @@ public function _masters(): array; public function _redir(): string|null; /** - * @see Redis::acl + * @see \Redis::acl() */ public function acl(string|array $key_or_address, string $subcmd, string ...$args): mixed; /** - * @see Redis::append() + * @see \Redis::append() */ public function append(string $key, mixed $value): RedisCluster|bool|int; /** - * @see Redis::bgrewriteaof + * @see \Redis::bgrewriteaof() */ public function bgrewriteaof(string|array $key_or_address): RedisCluster|bool; /** - * @see Redis::bgsave + * @see \Redis::wait() + */ + public function wait(string|array $key_or_address, int $numreplicas, int $timeout): RedisCluster|int|false; + + /** + * @see \Redis::waitaof() + */ + public function waitaof(string|array $key_or_address, int $numlocal, + int $numreplicas, int $timeout): RedisCluster|array|false; + + /** + * @see \Redis::bgSave() */ public function bgsave(string|array $key_or_address): RedisCluster|bool; /** - * @see Redis::bitcount + * @see \Redis::bitcount() */ public function bitcount(string $key, int $start = 0, int $end = -1, bool $bybit = false): RedisCluster|bool|int; /** - * @see Redis::bitop + * @see \Redis::bitop() */ public function bitop(string $operation, string $deskey, string $srckey, string ...$otherkeys): RedisCluster|bool|int; @@ -133,242 +156,262 @@ public function bitop(string $operation, string $deskey, string $srckey, string public function bitpos(string $key, bool $bit, int $start = 0, int $end = -1, bool $bybit = false): RedisCluster|int|false; /** - * See Redis::blpop() + * @see \Redis::blPop() */ public function blpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): RedisCluster|array|null|false; /** - * See Redis::brpop() + * @see \Redis::brPop() */ public function brpop(string|array $key, string|float|int $timeout_or_key, mixed ...$extra_args): RedisCluster|array|null|false; /** - * See Redis::brpoplpush() + * @see \Redis::brpoplpush() */ public function brpoplpush(string $srckey, string $deskey, int $timeout): mixed; /** * Move an element from one list into another. * - * @see Redis::lmove + * @see \Redis::lMove() */ - public function lmove(string $src, string $dst, string $wherefrom, string $whereto): Redis|string|false; + public function lmove(string $src, string $dst, string $wherefrom, string $whereto): RedisCluster|string|false; /** * Move an element from one list to another, blocking up to a timeout until an element is available. * - * @see Redis::blmove + * @see \Redis::blmove() * */ - public function blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout): Redis|string|false; + public function blmove(string $src, string $dst, string $wherefrom, string $whereto, float $timeout): RedisCluster|string|false; /** - * @see Redis::bzpopmax + * @see \Redis::bzPopMax() */ public function bzpopmax(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array; /** - * @see Redis::bzpopmin + * @see \Redis::bzPopMin() */ public function bzpopmin(string|array $key, string|int $timeout_or_key, mixed ...$extra_args): array; /** - * @see Redis::bzmpop + * @see \Redis::bzmpop() */ public function bzmpop(float $timeout, array $keys, string $from, int $count = 1): RedisCluster|array|null|false; /** - * @see Redis::zmpop + * @see \Redis::zmpop() */ public function zmpop(array $keys, string $from, int $count = 1): RedisCluster|array|null|false; /** - * @see Redis::blmpop() + * @see \Redis::blmpop() */ public function blmpop(float $timeout, array $keys, string $from, int $count = 1): RedisCluster|array|null|false; /** - * @see Redis::lmpop() + * @see \Redis::lmpop() */ public function lmpop(array $keys, string $from, int $count = 1): RedisCluster|array|null|false; /** - * @see Redis::clearlasterror() + * @see \Redis::clearLastError() */ public function clearlasterror(): bool; /** - * @see Redis::client + * @see \Redis::client() */ - public function client(string|array $key_or_address, string $subcommand, ?string $arg = NULL): array|string|bool; + public function client(string|array $key_or_address, string $subcommand, ?string $arg = null): array|string|bool; /** - * @see Redis::close + * @see \Redis::close() */ public function close(): bool; /** - * @see Redis::cluster + * @see \Redis::cluster() */ public function cluster(string|array $key_or_address, string $command, mixed ...$extra_args): mixed; /** - * @see Redis::command + * @see \Redis::command() */ public function command(mixed ...$extra_args): mixed; /** - * @see Redis::config() + * @see \Redis::config() */ public function config(string|array $key_or_address, string $subcommand, mixed ...$extra_args): mixed; /** - * @see Redis::dbsize() + * @see \Redis::dbSize() */ public function dbsize(string|array $key_or_address): RedisCluster|int; /** * @see https://redis.io/commands/copy */ - public function copy(string $src, string $dst, array $options = null): RedisCluster|bool; + public function copy(string $src, string $dst, ?array $options = null): RedisCluster|bool; /** - * @see Redis::decr() + * @see \Redis::decr() */ public function decr(string $key, int $by = 1): RedisCluster|int|false; /** - * @see Redis::decrby() + * @see \Redis::decrBy() */ public function decrby(string $key, int $value): RedisCluster|int|false; /** - * @see Redis::decrbyfloat + * @see \Redis::decrbyfloat() */ public function decrbyfloat(string $key, float $value): float; /** - * @see Redis::del() + * @see \Redis::del() */ public function del(array|string $key, string ...$other_keys): RedisCluster|int|false; /** - * @see Redis::discard + * Delete a key conditionally based on its value or hash digest + * + * @param string $key The key to delete + * @param array|null $options An array with options to modify how DELX works. + * + * @return RedisCluster|int|false Returns 1 if the key was deleted, 0 if it was not. + */ + public function delex(string $key, ?array $options = null): RedisCluster|int|false; + + /** + * Delete a key if it's equal to the specified value. This command is + * specific to Valkey >= 9.0 + * + * @param string $key The key to delete + * @param mixed $value The value to compare against the key's value. + * @return RedisCluster|int|false Returns 1 if the key was deleted, 0 if it was not. + */ + public function delifeq(string $key, mixed $value): RedisCluster|int|false; + + /** + * @see \Redis::discard() */ public function discard(): bool; /** - * @see Redis::dump + * @see \Redis::dump() */ public function dump(string $key): RedisCluster|string|false; /** - * @see Redis::echo() + * @see \Redis::echo() */ public function echo(string|array $key_or_address, string $msg): RedisCluster|string|false; /** - * @see Redis::eval + * @see \Redis::eval() */ public function eval(string $script, array $args = [], int $num_keys = 0): mixed; /** - * @see Redis::eval_ro + * @see \Redis::eval_ro() */ public function eval_ro(string $script, array $args = [], int $num_keys = 0): mixed; /** - * @see Redis::evalsha + * @see \Redis::evalsha() */ public function evalsha(string $script_sha, array $args = [], int $num_keys = 0): mixed; /** - * @see Redis::evalsha_ro + * @see \Redis::evalsha_ro() */ public function evalsha_ro(string $script_sha, array $args = [], int $num_keys = 0): mixed; /** - * @see Redis::exec() + * @see \Redis::exec() */ public function exec(): array|false; /** - * @see Redis::exists + * @see \Redis::exists() */ public function exists(mixed $key, mixed ...$other_keys): RedisCluster|int|bool; /** - * @see Redis::touch() + * @see \Redis::touch() */ public function touch(mixed $key, mixed ...$other_keys): RedisCluster|int|bool; /** - * @see Redis::expire + * @see \Redis::expire() */ - public function expire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool; + public function expire(string $key, int $timeout, ?string $mode = null): RedisCluster|bool; /** - * @see Redis::expireat + * @see \Redis::expireAt() */ - public function expireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool; + public function expireat(string $key, int $timestamp, ?string $mode = null): RedisCluster|bool; /** - * @see Redis::expiretime() + * @see \Redis::expiretime() */ public function expiretime(string $key): RedisCluster|int|false; /** - * @see Redis::pexpiretime() + * @see \Redis::pexpiretime() */ public function pexpiretime(string $key): RedisCluster|int|false; /** - * @see Redis::flushall + * @see \Redis::flushAll() */ public function flushall(string|array $key_or_address, bool $async = false): RedisCluster|bool; /** - * @see Redis::flushdb + * @see \Redis::flushDB() */ public function flushdb(string|array $key_or_address, bool $async = false): RedisCluster|bool; /** - * @see Redis::geoadd + * @see \Redis::geoadd() */ public function geoadd(string $key, float $lng, float $lat, string $member, mixed ...$other_triples_and_options): RedisCluster|int|false; /** - * @see Redis::geodist + * @see \Redis::geodist() */ public function geodist(string $key, string $src, string $dest, ?string $unit = null): RedisCluster|float|false; /** - * @see Redis::geohash + * @see \Redis::geohash() */ public function geohash(string $key, string $member, string ...$other_members): RedisCluster|array|false; /** - * @see Redis::geopos + * @see \Redis::geopos() */ public function geopos(string $key, string $member, string ...$other_members): RedisCluster|array|false; /** - * @see Redis::georadius + * @see \Redis::georadius() */ public function georadius(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed; /** - * @see Redis::georadius_ro + * @see \Redis::georadius_ro() */ public function georadius_ro(string $key, float $lng, float $lat, float $radius, string $unit, array $options = []): mixed; /** - * @see Redis::georadiusbymember + * @see \Redis::georadiusbymember() */ public function georadiusbymember(string $key, string $member, float $radius, string $unit, array $options = []): mixed; /** - * @see Redis::georadiusbymember_ro + * @see \Redis::georadiusbymember_ro() */ public function georadiusbymember_ro(string $key, string $member, float $radius, string $unit, array $options = []): mixed; @@ -383,147 +426,241 @@ public function geosearch(string $key, array|string $position, array|int|float $ public function geosearchstore(string $dst, string $src, array|string $position, array|int|float $shape, string $unit, array $options = []): RedisCluster|array|int|false; /** - * @see Redis::get + * @see \Redis::get() */ public function get(string $key): mixed; /** - * @see Redis::getbit + * @see \Redis::getDel() + */ + public function getdel(string $key): mixed; + + /** + * @see \Redis::getWithMeta() + */ + public function getWithMeta(string $key): RedisCluster|array|false; + + /** + * @see \Redis::getEx() + */ + public function getex(string $key, array $options = []): RedisCluster|string|false; + + /** + * @see \Redis::getBit() */ public function getbit(string $key, int $value): RedisCluster|int|false; /** - * @see Redis::getlasterror + * @see \Redis::getLastError() */ public function getlasterror(): string|null; /** - * @see Redis::getmode + * @see \Redis::getMode() */ public function getmode(): int; /** - * @see Redis::getoption + * @see \Redis::getOption() */ public function getoption(int $option): mixed; /** - * @see Redis::getrange + * @see \Redis::getRange() */ public function getrange(string $key, int $start, int $end): RedisCluster|string|false; /** - * @see Redis::lcs + * @see \Redis::lcs() */ - public function lcs(string $key1, string $key2, ?array $options = NULL): RedisCluster|string|array|int|false; + public function lcs(string $key1, string $key2, ?array $options = null): RedisCluster|string|array|int|false; /** - * @see Redis::getset + * @see \Redis::getset() */ public function getset(string $key, mixed $value): RedisCluster|string|bool; /** - * @see Redis::gettransferredbytes + * @see \Redis::getTransferredBytes() */ public function gettransferredbytes(): array|false; /** - * @see Redis::cleartransferredbytes + * @see \Redis::clearTransferredBytes() */ public function cleartransferredbytes(): void; /** - * @see Redis::hdel + * @see \Redis::hDel() */ public function hdel(string $key, string $member, string ...$other_members): RedisCluster|int|false; /** - * @see Redis::hexists + * @see \Redis::hExists() */ public function hexists(string $key, string $member): RedisCluster|bool; /** - * @see Redis::hget + * @see \Redis::hGet() */ public function hget(string $key, string $member): mixed; /** - * @see Redis::hgetall + * @see \Redis::hGetAll() */ public function hgetall(string $key): RedisCluster|array|false; /** - * @see Redis::hincrby + * @see \Redis::hGetWithMeta() + */ + public function hgetWithMeta(string $key, string $member): mixed; + + /** + * @see \Redis::hIncrBy() */ public function hincrby(string $key, string $member, int $value): RedisCluster|int|false; /** - * @see Redis::hincrbyfloat + * @see \Redis::hIncrByFloat() */ public function hincrbyfloat(string $key, string $member, float $value): RedisCluster|float|false; /** - * @see Redis::hkeys + * @see \Redis::hKeys() */ public function hkeys(string $key): RedisCluster|array|false; /** - * @see Redis::hlen + * @see \Redis::hLen() */ public function hlen(string $key): RedisCluster|int|false; /** - * @see Redis::hmget + * @see \Redis::hMget() */ public function hmget(string $key, array $keys): RedisCluster|array|false; /** - * @see Redis::hmset + * @see \Redis::hgetex() + */ + public function hgetex(string $key, array $fields, string|array|null $expiry = null): RedisCluster|array|false; + + /** + * @see \Redis::hsetex() + */ + public function hsetex(string $key, array $fields, ?array $expiry = null): RedisCluster|int|false; + + /** + * @see \Redis::hgetdel() + */ + public function hgetdel(string $key, array $fields): RedisCluster|array|false; + + /** + * @see \Redis::hMset() */ public function hmset(string $key, array $key_values): RedisCluster|bool; /** - * @see Redis::hscan + * @see \Redis::hscan() + */ + public function hscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): array|bool; + + /** + * @see \Redis::expiremember() + */ + public function expiremember(string $key, string $field, int $ttl, ?string $unit = null): RedisCluster|int|false; + + /** + * @see \Redis::expirememberat() */ - public function hscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|bool; + public function expirememberat(string $key, string $field, int $timestamp): RedisCluster|int|false; /** * @see https://redis.io/commands/hrandfield */ - public function hrandfield(string $key, array $options = null): RedisCluster|string|array; + public function hrandfield(string $key, ?array $options = null): RedisCluster|string|array; /** - * @see Redis::hset + * @see \Redis::hSet() */ public function hset(string $key, string $member, mixed $value): RedisCluster|int|false; /** - * @see Redis::hsetnx + * @see \Redis::hSetNx() */ public function hsetnx(string $key, string $member, mixed $value): RedisCluster|bool; /** - * @see Redis::hstrlen + * @see \Redis::hStrLen() */ public function hstrlen(string $key, string $field): RedisCluster|int|false; /** - * @see Redis::hvals + * @see \Redis::hexpire() + */ + public function hexpire(string $key, int $ttl, array $fields, + ?string $mode = NULL): RedisCluster|array|false; + + /** + * @see \Redis::hpexpire() + */ + public function hpexpire(string $key, int $ttl, array $fields, + ?string $mode = NULL): RedisCluster|array|false; + + /** + * @see \Redis::hexpireat() + */ + public function hexpireat(string $key, int $time, array $fields, + ?string $mode = NULL): RedisCluster|array|false; + + /** + * @see \Redis::hpexpireat() + */ + public function hpexpireat(string $key, int $mstime, array $fields, + ?string $mode = NULL): RedisCluster|array|false; + + /** + * @see \Redis::httl() + */ + public function httl(string $key, array $fields): RedisCluster|array|false; + + /** + * @see \Redis::hpttl() + */ + public function hpttl(string $key, array $fields): RedisCluster|array|false; + + /** + * @see \Redis::hexpiretime() + */ + public function hexpiretime(string $key, array $fields): RedisCluster|array|false; + + /** + * @see \Redis::hpexpiretime() + */ + public function hpexpiretime(string $key, array $fields): RedisCluster|array|false; + + /** + * @see \Redis::hpexpiretime() + */ + public function hpersist(string $key, array $fields): RedisCluster|array|false; + + /** + * @see \Redis::hVals() */ public function hvals(string $key): RedisCluster|array|false; /** - * @see Redis::incr + * @see \Redis::incr() */ public function incr(string $key, int $by = 1): RedisCluster|int|false; /** - * @see Redis::incrby + * @see \Redis::incrBy() */ public function incrby(string $key, int $value): RedisCluster|int|false; /** - * @see Redis::incrbyfloat + * @see \Redis::incrByFloat() */ public function incrbyfloat(string $key, float $value): RedisCluster|float|false; @@ -546,627 +683,722 @@ public function incrbyfloat(string $key, float $value): RedisCluster|float|false public function info(string|array $key_or_address, string ...$sections): RedisCluster|array|false; /** - * @see Redis::keys + * @see \Redis::keys() */ public function keys(string $pattern): RedisCluster|array|false; /** - * @see Redis::lastsave + * @see \Redis::lastSave() */ public function lastsave(string|array $key_or_address): RedisCluster|int|false; /** - * @see Redis::lget + * @see \Redis::lget() */ public function lget(string $key, int $index): RedisCluster|string|bool; /** - * @see Redis::lindex + * @see \Redis::lindex() */ public function lindex(string $key, int $index): mixed; /** - * @see Redis::linsert + * @see \Redis::lInsert() */ public function linsert(string $key, string $pos, mixed $pivot, mixed $value): RedisCluster|int|false; /** - * @see Redis::llen + * @see \Redis::lLen() */ public function llen(string $key): RedisCluster|int|bool; /** - * @see Redis::lpop + * @see \Redis::lPop() */ public function lpop(string $key, int $count = 0): RedisCluster|bool|string|array; /** - * @see Redis::lpos + * @see \Redis::lPos() */ - public function lpos(string $key, mixed $value, array $options = null): Redis|null|bool|int|array; + public function lpos(string $key, mixed $value, ?array $options = null): RedisCluster|null|bool|int|array; /** - * @see Redis::lpush + * @see \Redis::lPush() */ public function lpush(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|bool; /** - * @see Redis::lpushx + * @see \Redis::lPushx() */ public function lpushx(string $key, mixed $value): RedisCluster|int|bool; /** - * @see Redis::lrange + * @see \Redis::lrange() */ public function lrange(string $key, int $start, int $end): RedisCluster|array|false; /** - * @see Redis::lrem + * @see \Redis::lrem() */ public function lrem(string $key, mixed $value, int $count = 0): RedisCluster|int|bool; /** - * @see Redis::lset + * @see \Redis::lSet() */ public function lset(string $key, int $index, mixed $value): RedisCluster|bool; /** - * @see Redis::ltrim + * @see \Redis::ltrim() */ public function ltrim(string $key, int $start, int $end): RedisCluster|bool; /** - * @see Redis::mget + * @see \Redis::mget() */ public function mget(array $keys): RedisCluster|array|false; /** - * @see Redis::mset + * @see \Redis::mset() */ public function mset(array $key_values): RedisCluster|bool; /** - * @see Redis::msetnx + * @see \Redis::msetnx() */ public function msetnx(array $key_values): RedisCluster|array|false; + /** + * @see \Redis::msetex() + */ + public function msetex(array $key_vals, int|float|array|null $expiry = null): Redis|int|false; + /* We only support Redis::MULTI in RedisCluster but take the argument so we can test MULTI..EXEC with RedisTest.php and in the event we add pipeline support in the future. */ public function multi(int $value = Redis::MULTI): RedisCluster|bool; /** - * @see Redis::object + * @see \Redis::object() */ public function object(string $subcommand, string $key): RedisCluster|int|string|false; /** - * @see Redis::persist + * @see \Redis::persist() */ public function persist(string $key): RedisCluster|bool; /** - * @see Redis::pexpire + * @see \Redis::pexpire() */ - public function pexpire(string $key, int $timeout, ?string $mode = NULL): RedisCluster|bool; + public function pexpire(string $key, int $timeout, ?string $mode = null): RedisCluster|bool; /** - * @see Redis::pexpireat + * @see \Redis::pexpireAt() */ - public function pexpireat(string $key, int $timestamp, ?string $mode = NULL): RedisCluster|bool; + public function pexpireat(string $key, int $timestamp, ?string $mode = null): RedisCluster|bool; /** - * @see Redis::pfadd() + * @see \Redis::pfadd() */ public function pfadd(string $key, array $elements): RedisCluster|bool; /** - * @see Redis::pfcount() + * @see \Redis::pfcount() */ public function pfcount(string $key): RedisCluster|int|false; /** - * @see Redis::pfmerge() + * @see \Redis::pfmerge() */ public function pfmerge(string $key, array $keys): RedisCluster|bool; /** * PING an instance in the redis cluster. * - * @see Redis::ping() + * @see \Redis::ping() * * @param string|array $key_or_address Either a key name or a two element array with host and * address, informing RedisCluster which node to ping. * - * @param string $message An optional message to send. + * @param string|null $message An optional message to send. * * @return mixed This method always returns `true` if no message was sent, and the message itself * if one was. */ - public function ping(string|array $key_or_address, ?string $message = NULL): mixed; + public function ping(string|array $key_or_address, ?string $message = null): mixed; /** - * @see Redis::psetex + * @see \Redis::psetex() */ public function psetex(string $key, int $timeout, string $value): RedisCluster|bool; /** - * @see Redis::psubscribe + * @see \Redis::psubscribe() */ public function psubscribe(array $patterns, callable $callback): void; /** - * @see Redis::pttl + * @see \Redis::pttl() */ public function pttl(string $key): RedisCluster|int|false; /** - * @see Redis::publish + * @see \Redis::publish() */ - public function publish(string $channel, string $message): RedisCluster|bool; + public function publish(string $channel, string $message): RedisCluster|bool|int; /** - * @see Redis::pubsub + * @see \Redis::pubsub() */ public function pubsub(string|array $key_or_address, string ...$values): mixed; /** - * @see Redis::punsubscribe + * @see \Redis::punsubscribe() */ public function punsubscribe(string $pattern, string ...$other_patterns): bool|array; /** - * @see Redis::randomkey + * @see \Redis::randomKey() */ public function randomkey(string|array $key_or_address): RedisCluster|bool|string; /** - * @see Redis::rawcommand + * @see \Redis::rawcommand() */ public function rawcommand(string|array $key_or_address, string $command, mixed ...$args): mixed; /** - * @see Redis::rename + * @see \Redis::rename() */ public function rename(string $key_src, string $key_dst): RedisCluster|bool; /** - * @see Redis::renamenx + * @see \Redis::renameNx() */ public function renamenx(string $key, string $newkey): RedisCluster|bool; /** - * @see Redis::restore + * @see \Redis::restore() */ - public function restore(string $key, int $timeout, string $value, ?array $options = NULL): RedisCluster|bool; + public function restore(string $key, int $timeout, string $value, ?array $options = null): RedisCluster|bool; /** - * @see Redis::role + * @see \Redis::role() */ public function role(string|array $key_or_address): mixed; /** - * @see Redis::rpop() + * @see \Redis::rPop() */ public function rpop(string $key, int $count = 0): RedisCluster|bool|string|array; /** - * @see Redis::rpoplpush() + * @see \Redis::rpoplpush() */ public function rpoplpush(string $src, string $dst): RedisCluster|bool|string; /** - * @see Redis::rpush + * @see \Redis::rPush() */ public function rpush(string $key, mixed ...$elements): RedisCluster|int|false; /** - * @see Redis::rpushx + * @see \Redis::rPushx() */ public function rpushx(string $key, string $value): RedisCluster|bool|int; /** - * @see Redis::sadd() + * @see \Redis::sAdd() */ public function sadd(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|false; /** - * @see Redis::saddarray() + * @see \Redis::sAddArray() */ public function saddarray(string $key, array $values): RedisCluster|bool|int; /** - * @see Redis::save + * @see \Redis::save() */ public function save(string|array $key_or_address): RedisCluster|bool; /** - * @see Redis::scan + * @see \Redis::scan() */ - public function scan(?int &$iterator, string|array $key_or_address, ?string $pattern = null, int $count = 0): bool|array; + public function scan(null|int|string &$iterator, string|array $key_or_address, ?string $pattern = null, int $count = 0): bool|array; /** - * @see Redis::scard + * @see \Redis::scard() */ public function scard(string $key): RedisCluster|int|false; /** - * @see Redis::script + * @see \Redis::script() */ public function script(string|array $key_or_address, mixed ...$args): mixed; /** - * @see Redis::sdiff() + * @see \Redis::sDiff() */ public function sdiff(string $key, string ...$other_keys): RedisCluster|array|false; /** - * @see Redis::sdiffstore() + * @see \Redis::sDiffStore() */ public function sdiffstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false; /** * @see https://redis.io/commands/set */ - public function set(string $key, mixed $value, mixed $options = NULL): RedisCluster|string|bool; + public function set(string $key, mixed $value, mixed $options = null): RedisCluster|string|bool; /** - * @see Redis::setbit + * @see \Redis::setBit() */ public function setbit(string $key, int $offset, bool $onoff): RedisCluster|int|false; /** - * @see Redis::setex + * @see \Redis::setex() */ public function setex(string $key, int $expire, mixed $value): RedisCluster|bool; /** - * @see Redis::setnx + * @see \Redis::setnx() */ public function setnx(string $key, mixed $value): RedisCluster|bool; /** - * @see Redis::setoption + * @see \Redis::setOption() */ public function setoption(int $option, mixed $value): bool; /** - * @see Redis::setrange + * @see \Redis::setRange() */ public function setrange(string $key, int $offset, string $value): RedisCluster|int|false; /** - * @see Redis::sinter() + * @see \Redis::sInter() */ public function sinter(array|string $key, string ...$other_keys): RedisCluster|array|false; /** - * @see Redis::sintercard + * @see \Redis::sintercard() */ public function sintercard(array $keys, int $limit = -1): RedisCluster|int|false; /** - * @see Redis::sinterstore() + * @see \Redis::sInterStore() */ public function sinterstore(array|string $key, string ...$other_keys): RedisCluster|int|false; /** - * @see Redis::sismember + * @see \Redis::sismember() */ public function sismember(string $key, mixed $value): RedisCluster|bool; /** - * @see Redis::smismember + * @see \Redis::sMisMember() */ public function smismember(string $key, string $member, string ...$other_members): RedisCluster|array|false; /** - * @see Redis::slowlog + * @see \Redis::slowlog() */ public function slowlog(string|array $key_or_address, mixed ...$args): mixed; /** - * @see Redis::smembers() + * @see \Redis::sMembers() */ public function smembers(string $key): RedisCluster|array|false; /** - * @see Redis::smove() + * @see \Redis::sMove() */ public function smove(string $src, string $dst, string $member): RedisCluster|bool; /** - * @see Redis::sort() + * @see \Redis::sort() */ - public function sort(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string; + public function sort(string $key, ?array $options = null): RedisCluster|array|bool|int|string; /** - * @see Redis::sort_ro() + * @see \Redis::sort_ro() */ - public function sort_ro(string $key, ?array $options = NULL): RedisCluster|array|bool|int|string; + public function sort_ro(string $key, ?array $options = null): RedisCluster|array|bool|int|string; /** - * @see Redis::spop + * @see \Redis::sPop() */ public function spop(string $key, int $count = 0): RedisCluster|string|array|false; /** - * @see Redis::srandmember + * @see \Redis::sRandMember() */ public function srandmember(string $key, int $count = 0): RedisCluster|string|array|false; /** - * @see Redis::srem + * @see \Redis::srem() */ public function srem(string $key, mixed $value, mixed ...$other_values): RedisCluster|int|false; /** - * @see Redis::sscan + * @see \Redis::sscan() */ - public function sscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): array|false; + public function sscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): array|false; /** - * @see Redis::strlen + * @see \Redis::strlen() */ public function strlen(string $key): RedisCluster|int|false; /** - * @see Redis::subscribe + * @see \Redis::subscribe() */ public function subscribe(array $channels, callable $cb): void; /** - * @see Redis::sunion() + * @see \Redis::sUnion() */ public function sunion(string $key, string ...$other_keys): RedisCluster|bool|array; /** - * @see Redis::sunionstore() + * @see \Redis::sUnionStore() */ public function sunionstore(string $dst, string $key, string ...$other_keys): RedisCluster|int|false; /** - * @see Redis::time + * @see \Redis::time() */ public function time(string|array $key_or_address): RedisCluster|bool|array; /** - * @see Redis::ttl + * @see \Redis::ttl() */ public function ttl(string $key): RedisCluster|int|false; /** - * @see Redis::type + * @see \Redis::type() */ public function type(string $key): RedisCluster|int|false; /** - * @see Redis::unsubscribe + * @see \Redis::unsubscribe() */ public function unsubscribe(array $channels): bool|array; /** - * @see Redis::unlink + * @see \Redis::unlink() */ public function unlink(array|string $key, string ...$other_keys): RedisCluster|int|false; /** - * @see Redis::unwatch + * @see \Redis::unwatch() */ public function unwatch(): bool; /** - * @see Redis::watch + * @see \Redis::watch() */ public function watch(string $key, string ...$other_keys): RedisCluster|bool; /** - * @see Redis::xack + * @see \Redis::vadd() + */ + public function vadd(string $key, array $values, mixed $element, array|null $options = null): RedisCluster|int|false; + + /** + * @see \Redis::vsim() + */ + public function vsim(string $key, mixed $member, array|null $options = null): RedisCluster|array|false; + + /** + * @see \Redis::vcard() + */ + public function vcard(string $key): RedisCluster|int|false; + + /** + * @see \Redis::vdim() + */ + public function vdim(string $key): RedisCluster|int|false; + + /** + * @see \Redis::vinfo() + */ + public function vinfo(string $key): RedisCluster|array|false; + + /** + * Check if an element is a member of a vectorset + * + * @param string $key The vector set to query. + * @param mixed $member The member to check for. + * + * @return RedisCluster|bool true if the member exists, false if it does not. + */ + public function vismember(string $key, mixed $member): RedisCluster|bool; + + /** + * @see \Redis::vemb() + */ + public function vemb(string $key, mixed $member, bool $raw = false): RedisCluster|array|false; + + /** + * @see \Redis::vrandmember() + */ + public function vrandmember(string $key, int $count = 0): RedisCluster|array|string|false; + + /** + * Retreive a lexographical range of elements from a vector set + * + * @param string $key The vector set to query. + * @param string $min The minimum element to return. + * @param string $max The maximum element to return. + * @param int $count An optional maximum number of elements to return. + * + * @return RedisCluster|array|false An array of elements in the specified range.` + */ + public function vrange(string $key, string $min, string $max, int $count = -1): RedisCluster|array|false; + + + /** + * @see \Redis::vrem() + */ + public function vrem(string $key, mixed $member): RedisCluster|int|false; + + /** + * @see \Redis::vlinks() + */ + public function vlinks(string $key, mixed $member, bool $withscores = false): RedisCluster|array|false; + + /** + * @see \Redis::vgetattr() + */ + public function vgetattr(string $key, mixed $member, bool $decode = true): RedisCluster|array|string|false; + + /** + * @see \Redis::vsetattr() + */ + public function vsetattr(string $key, mixed $member, array|string $attributes): RedisCluster|int|false; + + /** + * @see \Redis::gcra() + */ + public function gcra(string $key, int $maxBurst, int $requestsPerPeriod, + int $period, int $tokens = 0): RedisCluster|array|false; + + + /** + * @see \Redis::xack() */ public function xack(string $key, string $group, array $ids): RedisCluster|int|false; /** - * @see Redis::xadd + * @see \Redis::xadd() */ public function xadd(string $key, string $id, array $values, int $maxlen = 0, bool $approx = false): RedisCluster|string|false; /** - * @see Redis::xclaim + * @see \Redis::xclaim() */ public function xclaim(string $key, string $group, string $consumer, int $min_iddle, array $ids, array $options): RedisCluster|string|array|false; /** - * @see Redis::xdel + * @see \Redis::xdel() */ public function xdel(string $key, array $ids): RedisCluster|int|false; /** - * @see Redis::xgroup + * @see \Redis::xdelex() + */ + public function xdelex(string $key, array $ids, ?string $mode = null): RedisCluster|array|false; + + /** + * @see \Redis::xgroup() */ - public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null, + public function xgroup(string $operation, ?string $key = null, ?string $group = null, ?string $id_or_consumer = null, bool $mkstream = false, int $entries_read = -2): mixed; /** - * @see Redis::xautoclaim + * @see \Redis::xautoclaim() */ public function xautoclaim(string $key, string $group, string $consumer, int $min_idle, string $start, int $count = -1, bool $justid = false): RedisCluster|bool|array; /** - * @see Redis::xinfo + * @see \Redis::xinfo() */ public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed; /** - * @see Redis::xlen + * @see \Redis::xlen() */ public function xlen(string $key): RedisCluster|int|false; /** - * @see Redis::xpending + * @see \Redis::xpending() */ public function xpending(string $key, string $group, ?string $start = null, ?string $end = null, int $count = -1, ?string $consumer = null): RedisCluster|array|false; /** - * @see Redis::xrange + * @see \Redis::xrange() */ public function xrange(string $key, string $start, string $end, int $count = -1): RedisCluster|bool|array; /** - * @see Redis::xread + * @see \Redis::xread() */ public function xread(array $streams, int $count = -1, int $block = -1): RedisCluster|bool|array; /** - * @see Redis::xreadgroup + * @see \Redis::xreadgroup() */ public function xreadgroup(string $group, string $consumer, array $streams, int $count = 1, int $block = 1): RedisCluster|bool|array; /** - * @see Redis::xrevrange + * @see \Redis::xrevrange() */ public function xrevrange(string $key, string $start, string $end, int $count = -1): RedisCluster|bool|array; /** - * @see Redis::xtrim + * @see \Redis::xtrim() */ public function xtrim(string $key, int $maxlen, bool $approx = false, bool $minid = false, int $limit = -1): RedisCluster|int|false; /** - * @see Redis::zadd + * @see \Redis::zAdd() */ public function zadd(string $key, array|float $score_or_options, mixed ...$more_scores_and_mems): RedisCluster|int|float|false; /** - * @see Redis::zcard + * @see \Redis::zCard() */ public function zcard(string $key): RedisCluster|int|false; /** - * @see Redis::zcount + * @see \Redis::zCount() */ public function zcount(string $key, string $start, string $end): RedisCluster|int|false; /** - * @see Redis::zincrby + * @see \Redis::zIncrBy() */ public function zincrby(string $key, float $value, string $member): RedisCluster|float|false; /** - * @see Redis::zinterstore + * @see \Redis::zinterstore() */ public function zinterstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): RedisCluster|int|false; /** - * @see Redis::zintercard + * @see \Redis::zintercard() */ public function zintercard(array $keys, int $limit = -1): RedisCluster|int|false; /** - * @see Redis::zlexcount + * @see \Redis::zLexCount() */ public function zlexcount(string $key, string $min, string $max): RedisCluster|int|false; /** - * @see Redis::zpopmax + * @see \Redis::zPopMax() */ - public function zpopmax(string $key, int $value = null): RedisCluster|bool|array; + public function zpopmax(string $key, ?int $value = null): RedisCluster|bool|array; /** - * @see Redis::zpopmin + * @see \Redis::zPopMin() */ - public function zpopmin(string $key, int $value = null): RedisCluster|bool|array; + public function zpopmin(string $key, ?int $value = null): RedisCluster|bool|array; /** - * @see Redis::zrange + * @see \Redis::zRange() */ public function zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null): RedisCluster|array|bool; /** - * @see Redis::zrangestore + * @see \Redis::zrangestore() */ public function zrangestore(string $dstkey, string $srckey, int $start, int $end, array|bool|null $options = null): RedisCluster|int|false; /** - * @see https://redis.io/commands/zRandMember + * @see https://redis.io/commands/zrandmember */ - public function zrandmember(string $key, array $options = null): RedisCluster|string|array; + public function zrandmember(string $key, ?array $options = null): RedisCluster|string|array; /** - * @see Redis::zrangebylex + * @see \Redis::zRangeByLex() */ public function zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1): RedisCluster|array|false; /** - * @see Redis::zrangebyscore + * @see \Redis::zRangeByScore() */ public function zrangebyscore(string $key, string $start, string $end, array $options = []): RedisCluster|array|false; /** - * @see Redis::zrank + * @see \Redis::zRank() */ public function zrank(string $key, mixed $member): RedisCluster|int|false; /** - * @see Redis::zrem + * @see \Redis::zRem() */ public function zrem(string $key, string $value, string ...$other_values): RedisCluster|int|false; /** - * @see Redis::zremrangebylex + * @see \Redis::zRemRangeByLex() */ public function zremrangebylex(string $key, string $min, string $max): RedisCluster|int|false; /** - * @see Redis::zremrangebyrank + * @see \Redis::zRemRangeByRank() */ public function zremrangebyrank(string $key, string $min, string $max): RedisCluster|int|false; /** - * @see Redis::zremrangebyscore + * @see \Redis::zRemRangeByScore() */ public function zremrangebyscore(string $key, string $min, string $max): RedisCluster|int|false; /** - * @see Redis::zrevrange + * @see \Redis::zRevRange() */ - public function zrevrange(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array; + public function zrevrange(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** - * @see Redis::zrevrangebylex + * @see \Redis::zRevRangeByLex() */ - public function zrevrangebylex(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array; + public function zrevrangebylex(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** - * @see Redis::zrevrangebyscore + * @see \Redis::zRevRangeByScore() */ - public function zrevrangebyscore(string $key, string $min, string $max, array $options = null): RedisCluster|bool|array; + public function zrevrangebyscore(string $key, string $min, string $max, ?array $options = null): RedisCluster|bool|array; /** - * @see Redis::zrevrank + * @see \Redis::zRevRank() */ public function zrevrank(string $key, mixed $member): RedisCluster|int|false; /** - * @see Redis::zscan + * @see \Redis::zscan() */ - public function zscan(string $key, ?int &$iterator, ?string $pattern = null, int $count = 0): RedisCluster|bool|array; + public function zscan(string $key, null|int|string &$iterator, ?string $pattern = null, int $count = 0): RedisCluster|bool|array; /** - * @see Redis::zscore + * @see \Redis::zScore() */ public function zscore(string $key, mixed $member): RedisCluster|float|false; /** - * @see https://redis.io/commands/zMscore + * @see https://redis.io/commands/zmscore */ - public function zmscore(string $key, mixed $member, mixed ...$other_members): Redis|array|false; + public function zmscore(string $key, mixed $member, mixed ...$other_members): RedisCluster|array|false; /** - * @see Redis::zunionstore + * @see \Redis::zunionstore() */ - public function zunionstore(string $dst, array $keys, ?array $weights = NULL, ?string $aggregate = NULL): RedisCluster|int|false; + public function zunionstore(string $dst, array $keys, ?array $weights = null, ?string $aggregate = null): RedisCluster|int|false; /** * @see https://redis.io/commands/zinter @@ -1186,7 +1418,12 @@ public function zunion(array $keys, ?array $weights = null, ?array $options = nu /** * @see https://redis.io/commands/zdiff */ - public function zdiff(array $keys, array $options = null): RedisCluster|array|false; + public function zdiff(array $keys, ?array $options = null): RedisCluster|array|false; + + /** + * @see https://redis.io/commands/digest + */ + public function digest(string $key): RedisCluster|string|false; } class RedisClusterException extends RuntimeException {} diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index 2cd817df79..6d255712c8 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,14 +1,14 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 32b24ce215ff4f2299dd838fab7cbc36b81b65eb */ + * Stub hash: 6c7a87611b3bc9039650a3cf2e3c4d4f916611b0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, seeds, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, seeds, IS_ARRAY, 1, "null") ZEND_ARG_TYPE_MASK(0, timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0") ZEND_ARG_TYPE_MASK(0, read_timeout, MAY_BE_LONG|MAY_BE_DOUBLE, "0") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, persistent, _IS_BOOL, 0, "false") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, auth, IS_MIXED, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, context, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__compress, 0, 1, IS_STRING, 0) @@ -29,6 +29,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster__pack, 0, 1, ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster__digest arginfo_class_RedisCluster__pack + #define arginfo_class_RedisCluster__unpack arginfo_class_RedisCluster__unserialize ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster__prefix, 0, 1, MAY_BE_BOOL|MAY_BE_STRING) @@ -56,6 +58,19 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bgrewrite ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_wait, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) + ZEND_ARG_TYPE_INFO(0, numreplicas, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_waitaof, 0, 4, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) + ZEND_ARG_TYPE_INFO(0, numlocal, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, numreplicas, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_RedisCluster_bgsave arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_bitcount, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG) @@ -94,14 +109,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_brpoplpush, 0 ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lmove, 0, 4, Redis, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lmove, 0, 4, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, whereto, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_blmove, 0, 5, Redis, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_blmove, 0, 5, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, wherefrom, IS_STRING, 0) @@ -140,7 +155,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_client, 0, 2, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO(0, subcommand, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_close arginfo_class_RedisCluster_clearlasterror @@ -168,7 +183,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_copy, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_decr, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -191,6 +206,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_del, 0, 1 ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_delex, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_delifeq, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_RedisCluster_discard arginfo_class_RedisCluster_clearlasterror ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_dump, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE) @@ -231,13 +256,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expire, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expireat, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiretime, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -318,6 +343,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_get, 0, 1, IS ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_getdel arginfo_class_RedisCluster_get + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getWithMeta, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getex, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") +ZEND_END_ARG_INFO() + #define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_decrby #define arginfo_class_RedisCluster_getlasterror arginfo_class_RedisCluster__redir @@ -338,7 +374,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lcs, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key2, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_getset, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL) @@ -367,9 +403,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_hget, 0, 2, I ZEND_ARG_TYPE_INFO(0, member, IS_STRING, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hgetall, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_hgetall arginfo_class_RedisCluster_getWithMeta + +#define arginfo_class_RedisCluster_hgetWithMeta arginfo_class_RedisCluster_hget ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hincrby, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -383,7 +419,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hincrbyfl ZEND_ARG_TYPE_INFO(0, value, IS_DOUBLE, 0) ZEND_END_ARG_INFO() -#define arginfo_class_RedisCluster_hkeys arginfo_class_RedisCluster_hgetall +#define arginfo_class_RedisCluster_hkeys arginfo_class_RedisCluster_getWithMeta #define arginfo_class_RedisCluster_hlen arginfo_class_RedisCluster_expiretime @@ -392,6 +428,23 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hmget, 0, ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hgetex, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_MASK(0, expiry, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_NULL, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hsetex, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, expiry, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hgetdel, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hmset, 0, 2, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0) @@ -399,14 +452,27 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_hscan, 0, 2, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expiremember, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, unit, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_expirememberat, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, timestamp, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hrandfield, 0, 1, RedisCluster, MAY_BE_STRING|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hset, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -426,7 +492,40 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hstrlen, ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster_hgetall +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hexpire, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ttl, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_hpexpire arginfo_class_RedisCluster_hexpire + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hexpireat, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, time, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_hpexpireat, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, mstime, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, fields, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "NULL") +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_httl arginfo_class_RedisCluster_hgetdel + +#define arginfo_class_RedisCluster_hpttl arginfo_class_RedisCluster_hgetdel + +#define arginfo_class_RedisCluster_hexpiretime arginfo_class_RedisCluster_hgetdel + +#define arginfo_class_RedisCluster_hpexpiretime arginfo_class_RedisCluster_hgetdel + +#define arginfo_class_RedisCluster_hpersist arginfo_class_RedisCluster_hgetdel + +#define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster_getWithMeta #define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr @@ -476,10 +575,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpop, 0, ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpos, 0, 2, Redis, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpos, 0, 2, RedisCluster, MAY_BE_NULL|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_lpush, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_BOOL) @@ -529,6 +628,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_msetnx, 0 ZEND_ARG_TYPE_INFO(0, key_values, IS_ARRAY, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_msetex, 0, 1, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key_vals, IS_ARRAY, 0) + ZEND_ARG_TYPE_MASK(0, expiry, MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_NULL, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_multi, 0, 0, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "Redis::MULTI") ZEND_END_ARG_INFO() @@ -560,7 +664,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_ping, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_psetex, 0, 3, RedisCluster, MAY_BE_BOOL) @@ -576,7 +680,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_pttl arginfo_class_RedisCluster_expiretime -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_publish, 0, 2, RedisCluster, MAY_BE_BOOL) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_publish, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_LONG) ZEND_ARG_TYPE_INFO(0, channel, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -615,7 +719,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_restore, ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, timeout, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_role, 0, 1, IS_MIXED, 0) @@ -653,7 +757,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_save arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_scan, 0, 2, MAY_BE_BOOL|MAY_BE_ARRAY) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") @@ -680,7 +784,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_set, 0, 2, RedisCluster, MAY_BE_STRING|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_setbit, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) @@ -729,7 +833,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_slowlog arginfo_class_RedisCluster_script -#define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster_hgetall +#define arginfo_class_RedisCluster_smembers arginfo_class_RedisCluster_getWithMeta ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_smove, 0, 3, RedisCluster, MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0) @@ -739,7 +843,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_sort, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_LONG|MAY_BE_STRING) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_sort @@ -755,7 +859,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_RedisCluster_sscan, 0, 2, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -795,6 +899,79 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_watch, 0, ZEND_ARG_VARIADIC_TYPE_INFO(0, other_keys, IS_STRING, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vadd, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, values, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO(0, element, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vsim, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_vcard arginfo_class_RedisCluster_expiretime + +#define arginfo_class_RedisCluster_vdim arginfo_class_RedisCluster_expiretime + +#define arginfo_class_RedisCluster_vinfo arginfo_class_RedisCluster_getWithMeta + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vismember, 0, 2, RedisCluster, MAY_BE_BOOL) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vemb, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, raw, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vrandmember, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vrange, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "-1") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vrem, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vlinks, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, withscores, _IS_BOOL, 0, "false") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vgetattr, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_STRING|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, decode, _IS_BOOL, 0, "true") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_vsetattr, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) + ZEND_ARG_TYPE_MASK(0, attributes, MAY_BE_ARRAY|MAY_BE_STRING, NULL) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_gcra, 0, 4, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, maxBurst, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, requestsPerPeriod, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, period, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, tokens, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xack, 0, 3, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, group, IS_STRING, 0) @@ -823,11 +1000,17 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xdel, 0, ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_xdelex, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, ids, IS_ARRAY, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mode, IS_STRING, 1, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 1, IS_MIXED, 0) ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2") ZEND_END_ARG_INFO() @@ -928,7 +1111,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zpopmax, 0, 1, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_LONG, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zpopmin arginfo_class_RedisCluster_zpopmax @@ -965,10 +1148,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangebys ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrank, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_zrank arginfo_class_RedisCluster_vrem ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrem, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) @@ -986,18 +1166,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrevrange ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, min, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, max, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_zrevrangebylex arginfo_class_RedisCluster_zrevrange #define arginfo_class_RedisCluster_zrevrangebyscore arginfo_class_RedisCluster_zrevrange -#define arginfo_class_RedisCluster_zrevrank arginfo_class_RedisCluster_zrank +#define arginfo_class_RedisCluster_zrevrank arginfo_class_RedisCluster_vrem ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zscan, 0, 2, RedisCluster, MAY_BE_BOOL|MAY_BE_ARRAY) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(1, iterator, IS_LONG, 1) + ZEND_ARG_TYPE_MASK(1, iterator, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_STRING, NULL) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, pattern, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, count, IS_LONG, 0, "0") ZEND_END_ARG_INFO() @@ -1007,18 +1187,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zscore, 0 ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zmscore, 0, 2, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zmscore, 0, 2, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, member, IS_MIXED, 0) ZEND_ARG_VARIADIC_TYPE_INFO(0, other_members, IS_MIXED, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zunionstore, 0, 2, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, weights, IS_ARRAY, 1, "NULL") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, aggregate, IS_STRING, 1, "NULL") -ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_zunionstore arginfo_class_RedisCluster_zinterstore ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zinter, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) @@ -1035,9 +1210,10 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zdiff, 0, 1, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, keys, IS_ARRAY, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_digest arginfo_class_RedisCluster_dump ZEND_METHOD(RedisCluster, __construct); ZEND_METHOD(RedisCluster, _compress); @@ -1045,6 +1221,7 @@ ZEND_METHOD(RedisCluster, _uncompress); ZEND_METHOD(RedisCluster, _serialize); ZEND_METHOD(RedisCluster, _unserialize); ZEND_METHOD(RedisCluster, _pack); +ZEND_METHOD(RedisCluster, _digest); ZEND_METHOD(RedisCluster, _unpack); ZEND_METHOD(RedisCluster, _prefix); ZEND_METHOD(RedisCluster, _masters); @@ -1052,6 +1229,8 @@ ZEND_METHOD(RedisCluster, _redir); ZEND_METHOD(RedisCluster, acl); ZEND_METHOD(RedisCluster, append); ZEND_METHOD(RedisCluster, bgrewriteaof); +ZEND_METHOD(RedisCluster, wait); +ZEND_METHOD(RedisCluster, waitaof); ZEND_METHOD(RedisCluster, bgsave); ZEND_METHOD(RedisCluster, bitcount); ZEND_METHOD(RedisCluster, bitop); @@ -1079,6 +1258,8 @@ ZEND_METHOD(RedisCluster, decr); ZEND_METHOD(RedisCluster, decrby); ZEND_METHOD(RedisCluster, decrbyfloat); ZEND_METHOD(RedisCluster, del); +ZEND_METHOD(RedisCluster, delex); +ZEND_METHOD(RedisCluster, delifeq); ZEND_METHOD(RedisCluster, discard); ZEND_METHOD(RedisCluster, dump); ZEND_METHOD(RedisCluster, echo); @@ -1106,6 +1287,9 @@ ZEND_METHOD(RedisCluster, georadiusbymember_ro); ZEND_METHOD(RedisCluster, geosearch); ZEND_METHOD(RedisCluster, geosearchstore); ZEND_METHOD(RedisCluster, get); +ZEND_METHOD(RedisCluster, getdel); +ZEND_METHOD(RedisCluster, getWithMeta); +ZEND_METHOD(RedisCluster, getex); ZEND_METHOD(RedisCluster, getbit); ZEND_METHOD(RedisCluster, getlasterror); ZEND_METHOD(RedisCluster, getmode); @@ -1119,17 +1303,32 @@ ZEND_METHOD(RedisCluster, hdel); ZEND_METHOD(RedisCluster, hexists); ZEND_METHOD(RedisCluster, hget); ZEND_METHOD(RedisCluster, hgetall); +ZEND_METHOD(RedisCluster, hgetWithMeta); ZEND_METHOD(RedisCluster, hincrby); ZEND_METHOD(RedisCluster, hincrbyfloat); ZEND_METHOD(RedisCluster, hkeys); ZEND_METHOD(RedisCluster, hlen); ZEND_METHOD(RedisCluster, hmget); +ZEND_METHOD(RedisCluster, hgetex); +ZEND_METHOD(RedisCluster, hsetex); +ZEND_METHOD(RedisCluster, hgetdel); ZEND_METHOD(RedisCluster, hmset); ZEND_METHOD(RedisCluster, hscan); +ZEND_METHOD(RedisCluster, expiremember); +ZEND_METHOD(RedisCluster, expirememberat); ZEND_METHOD(RedisCluster, hrandfield); ZEND_METHOD(RedisCluster, hset); ZEND_METHOD(RedisCluster, hsetnx); ZEND_METHOD(RedisCluster, hstrlen); +ZEND_METHOD(RedisCluster, hexpire); +ZEND_METHOD(RedisCluster, hpexpire); +ZEND_METHOD(RedisCluster, hexpireat); +ZEND_METHOD(RedisCluster, hpexpireat); +ZEND_METHOD(RedisCluster, httl); +ZEND_METHOD(RedisCluster, hpttl); +ZEND_METHOD(RedisCluster, hexpiretime); +ZEND_METHOD(RedisCluster, hpexpiretime); +ZEND_METHOD(RedisCluster, hpersist); ZEND_METHOD(RedisCluster, hvals); ZEND_METHOD(RedisCluster, incr); ZEND_METHOD(RedisCluster, incrby); @@ -1152,6 +1351,7 @@ ZEND_METHOD(RedisCluster, ltrim); ZEND_METHOD(RedisCluster, mget); ZEND_METHOD(RedisCluster, mset); ZEND_METHOD(RedisCluster, msetnx); +ZEND_METHOD(RedisCluster, msetex); ZEND_METHOD(RedisCluster, multi); ZEND_METHOD(RedisCluster, object); ZEND_METHOD(RedisCluster, persist); @@ -1216,10 +1416,25 @@ ZEND_METHOD(RedisCluster, unsubscribe); ZEND_METHOD(RedisCluster, unlink); ZEND_METHOD(RedisCluster, unwatch); ZEND_METHOD(RedisCluster, watch); +ZEND_METHOD(RedisCluster, vadd); +ZEND_METHOD(RedisCluster, vsim); +ZEND_METHOD(RedisCluster, vcard); +ZEND_METHOD(RedisCluster, vdim); +ZEND_METHOD(RedisCluster, vinfo); +ZEND_METHOD(RedisCluster, vismember); +ZEND_METHOD(RedisCluster, vemb); +ZEND_METHOD(RedisCluster, vrandmember); +ZEND_METHOD(RedisCluster, vrange); +ZEND_METHOD(RedisCluster, vrem); +ZEND_METHOD(RedisCluster, vlinks); +ZEND_METHOD(RedisCluster, vgetattr); +ZEND_METHOD(RedisCluster, vsetattr); +ZEND_METHOD(RedisCluster, gcra); ZEND_METHOD(RedisCluster, xack); ZEND_METHOD(RedisCluster, xadd); ZEND_METHOD(RedisCluster, xclaim); ZEND_METHOD(RedisCluster, xdel); +ZEND_METHOD(RedisCluster, xdelex); ZEND_METHOD(RedisCluster, xgroup); ZEND_METHOD(RedisCluster, xautoclaim); ZEND_METHOD(RedisCluster, xinfo); @@ -1261,7 +1476,7 @@ ZEND_METHOD(RedisCluster, zinter); ZEND_METHOD(RedisCluster, zdiffstore); ZEND_METHOD(RedisCluster, zunion); ZEND_METHOD(RedisCluster, zdiff); - +ZEND_METHOD(RedisCluster, digest); static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC) @@ -1270,6 +1485,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _pack, arginfo_class_RedisCluster__pack, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, _digest, arginfo_class_RedisCluster__digest, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC) @@ -1277,6 +1493,8 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgrewriteaof, arginfo_class_RedisCluster_bgrewriteaof, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, wait, arginfo_class_RedisCluster_wait, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, waitaof, arginfo_class_RedisCluster_waitaof, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgsave, arginfo_class_RedisCluster_bgsave, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitcount, arginfo_class_RedisCluster_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitop, arginfo_class_RedisCluster_bitop, ZEND_ACC_PUBLIC) @@ -1304,6 +1522,8 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, delex, arginfo_class_RedisCluster_delex, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, delifeq, arginfo_class_RedisCluster_delifeq, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, echo, arginfo_class_RedisCluster_echo, ZEND_ACC_PUBLIC) @@ -1331,6 +1551,9 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, getdel, arginfo_class_RedisCluster_getdel, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, getWithMeta, arginfo_class_RedisCluster_getWithMeta, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, getex, arginfo_class_RedisCluster_getex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC) @@ -1344,17 +1567,32 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hgetall, arginfo_class_RedisCluster_hgetall, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hgetWithMeta, arginfo_class_RedisCluster_hgetWithMeta, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hincrby, arginfo_class_RedisCluster_hincrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hincrbyfloat, arginfo_class_RedisCluster_hincrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hkeys, arginfo_class_RedisCluster_hkeys, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hlen, arginfo_class_RedisCluster_hlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hgetex, arginfo_class_RedisCluster_hgetex, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hsetex, arginfo_class_RedisCluster_hsetex, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hgetdel, arginfo_class_RedisCluster_hgetdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, expiremember, arginfo_class_RedisCluster_expiremember, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, expirememberat, arginfo_class_RedisCluster_expirememberat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hrandfield, arginfo_class_RedisCluster_hrandfield, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpire, arginfo_class_RedisCluster_hexpire, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpire, arginfo_class_RedisCluster_hpexpire, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpireat, arginfo_class_RedisCluster_hexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpireat, arginfo_class_RedisCluster_hpexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, httl, arginfo_class_RedisCluster_httl, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpttl, arginfo_class_RedisCluster_hpttl, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpiretime, arginfo_class_RedisCluster_hexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpiretime, arginfo_class_RedisCluster_hpexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpersist, arginfo_class_RedisCluster_hpersist, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC) @@ -1377,6 +1615,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, mget, arginfo_class_RedisCluster_mget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, mset, arginfo_class_RedisCluster_mset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, msetnx, arginfo_class_RedisCluster_msetnx, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, msetex, arginfo_class_RedisCluster_msetex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, multi, arginfo_class_RedisCluster_multi, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, object, arginfo_class_RedisCluster_object, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, persist, arginfo_class_RedisCluster_persist, ZEND_ACC_PUBLIC) @@ -1441,10 +1680,25 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, unlink, arginfo_class_RedisCluster_unlink, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, unwatch, arginfo_class_RedisCluster_unwatch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, watch, arginfo_class_RedisCluster_watch, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vadd, arginfo_class_RedisCluster_vadd, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vsim, arginfo_class_RedisCluster_vsim, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vcard, arginfo_class_RedisCluster_vcard, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vdim, arginfo_class_RedisCluster_vdim, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vinfo, arginfo_class_RedisCluster_vinfo, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vismember, arginfo_class_RedisCluster_vismember, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vemb, arginfo_class_RedisCluster_vemb, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vrandmember, arginfo_class_RedisCluster_vrandmember, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vrange, arginfo_class_RedisCluster_vrange, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vrem, arginfo_class_RedisCluster_vrem, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vlinks, arginfo_class_RedisCluster_vlinks, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vgetattr, arginfo_class_RedisCluster_vgetattr, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vsetattr, arginfo_class_RedisCluster_vsetattr, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, gcra, arginfo_class_RedisCluster_gcra, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xack, arginfo_class_RedisCluster_xack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xadd, arginfo_class_RedisCluster_xadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, xdelex, arginfo_class_RedisCluster_xdelex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xautoclaim, arginfo_class_RedisCluster_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC) @@ -1486,11 +1740,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, zdiffstore, arginfo_class_RedisCluster_zdiffstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zunion, arginfo_class_RedisCluster_zunion, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zdiff, arginfo_class_RedisCluster_zdiff, ZEND_ACC_PUBLIC) - ZEND_FE_END -}; - - -static const zend_function_entry class_RedisClusterException_methods[] = { + ZEND_ME(RedisCluster, digest, arginfo_class_RedisCluster_digest, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -1499,7 +1749,11 @@ static zend_class_entry *register_class_RedisCluster(void) zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisCluster", class_RedisCluster_methods); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif zval const_OPT_SLAVE_FAILOVER_value; ZVAL_LONG(&const_OPT_SLAVE_FAILOVER_value, REDIS_OPT_FAILOVER); @@ -1530,11 +1784,11 @@ static zend_class_entry *register_class_RedisCluster(void) zend_string *const_FAILOVER_DISTRIBUTE_SLAVES_name = zend_string_init_interned("FAILOVER_DISTRIBUTE_SLAVES", sizeof("FAILOVER_DISTRIBUTE_SLAVES") - 1, 1); zend_declare_class_constant_ex(class_entry, const_FAILOVER_DISTRIBUTE_SLAVES_name, &const_FAILOVER_DISTRIBUTE_SLAVES_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_FAILOVER_DISTRIBUTE_SLAVES_name); -#if (PHP_VERSION_ID >= 80200) - zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "__construct", sizeof("__construct") - 1), 5, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); -#endif + zend_string *attribute_name_SensitiveParameter_func___construct_arg5_0 = zend_string_init_interned("SensitiveParameter", sizeof("SensitiveParameter") - 1, 1); + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "__construct", sizeof("__construct") - 1), 5, attribute_name_SensitiveParameter_func___construct_arg5_0, 0); + zend_string_release(attribute_name_SensitiveParameter_func___construct_arg5_0); return class_entry; } @@ -1543,8 +1797,12 @@ static zend_class_entry *register_class_RedisClusterException(zend_class_entry * { zend_class_entry ce, *class_entry; - INIT_CLASS_ENTRY(ce, "RedisClusterException", class_RedisClusterException_methods); + INIT_CLASS_ENTRY(ce, "RedisClusterException", NULL); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_RuntimeException, 0); +#else class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException); +#endif return class_entry; } diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index 5b509cde6a..3fd44e313b 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 32b24ce215ff4f2299dd838fab7cbc36b81b65eb */ + * Stub hash: 6c7a87611b3bc9039650a3cf2e3c4d4f916611b0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -23,6 +23,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster__pack arginfo_class_RedisCluster__compress +#define arginfo_class_RedisCluster__digest arginfo_class_RedisCluster__compress + #define arginfo_class_RedisCluster__unpack arginfo_class_RedisCluster__compress ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster__prefix, 0, 0, 1) @@ -49,6 +51,19 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bgrewriteaof, 0, 0, 1) ZEND_ARG_INFO(0, key_or_address) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_wait, 0, 0, 3) + ZEND_ARG_INFO(0, key_or_address) + ZEND_ARG_INFO(0, numreplicas) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_waitaof, 0, 0, 4) + ZEND_ARG_INFO(0, key_or_address) + ZEND_ARG_INFO(0, numlocal) + ZEND_ARG_INFO(0, numreplicas) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + #define arginfo_class_RedisCluster_bgsave arginfo_class_RedisCluster_bgrewriteaof ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_bitcount, 0, 0, 1) @@ -171,6 +186,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_del, 0, 0, 1) ZEND_ARG_VARIADIC_INFO(0, other_keys) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_delex, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_delifeq arginfo_class_RedisCluster_append + #define arginfo_class_RedisCluster_discard arginfo_class_RedisCluster__masters #define arginfo_class_RedisCluster_dump arginfo_class_RedisCluster__prefix @@ -288,6 +310,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_get arginfo_class_RedisCluster__prefix +#define arginfo_class_RedisCluster_getdel arginfo_class_RedisCluster__prefix + +#define arginfo_class_RedisCluster_getWithMeta arginfo_class_RedisCluster__prefix + +#define arginfo_class_RedisCluster_getex arginfo_class_RedisCluster_delex + #define arginfo_class_RedisCluster_getbit arginfo_class_RedisCluster_append #define arginfo_class_RedisCluster_getlasterror arginfo_class_RedisCluster__masters @@ -327,6 +355,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_hgetall arginfo_class_RedisCluster__prefix +#define arginfo_class_RedisCluster_hgetWithMeta arginfo_class_RedisCluster_hexists + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hincrby, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, member) @@ -344,6 +374,19 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hmget, 0, 0, 2) ZEND_ARG_INFO(0, keys) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hgetex, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, expiry) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_hsetex arginfo_class_RedisCluster_hgetex + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hgetdel, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, fields) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hmset, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, key_values) @@ -356,11 +399,21 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hscan, 0, 0, 2) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hrandfield, 0, 0, 1) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expiremember, 0, 0, 3) ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, options) + ZEND_ARG_INFO(0, field) + ZEND_ARG_INFO(0, ttl) + ZEND_ARG_INFO(0, unit) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_expirememberat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, field) + ZEND_ARG_INFO(0, timestamp) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_hrandfield arginfo_class_RedisCluster_delex + #define arginfo_class_RedisCluster_hset arginfo_class_RedisCluster_hincrby #define arginfo_class_RedisCluster_hsetnx arginfo_class_RedisCluster_hincrby @@ -370,6 +423,39 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hstrlen, 0, 0, 2) ZEND_ARG_INFO(0, field) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexpire, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, ttl) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_hpexpire arginfo_class_RedisCluster_hexpire + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hexpireat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, time) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_hpexpireat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, mstime) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_httl arginfo_class_RedisCluster_hgetdel + +#define arginfo_class_RedisCluster_hpttl arginfo_class_RedisCluster_hgetdel + +#define arginfo_class_RedisCluster_hexpiretime arginfo_class_RedisCluster_hgetdel + +#define arginfo_class_RedisCluster_hpexpiretime arginfo_class_RedisCluster_hgetdel + +#define arginfo_class_RedisCluster_hpersist arginfo_class_RedisCluster_hgetdel + #define arginfo_class_RedisCluster_hvals arginfo_class_RedisCluster__prefix #define arginfo_class_RedisCluster_incr arginfo_class_RedisCluster_decr @@ -450,6 +536,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_msetnx arginfo_class_RedisCluster_mset +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_msetex, 0, 0, 1) + ZEND_ARG_INFO(0, key_vals) + ZEND_ARG_INFO(0, expiry) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_multi, 0, 0, 0) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() @@ -629,9 +720,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_smove, 0, 0, 3) ZEND_ARG_INFO(0, member) ZEND_END_ARG_INFO() -#define arginfo_class_RedisCluster_sort arginfo_class_RedisCluster_hrandfield +#define arginfo_class_RedisCluster_sort arginfo_class_RedisCluster_delex -#define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_hrandfield +#define arginfo_class_RedisCluster_sort_ro arginfo_class_RedisCluster_delex #define arginfo_class_RedisCluster_spop arginfo_class_RedisCluster_lpop @@ -668,6 +759,70 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisCluster_watch arginfo_class_RedisCluster_del +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_vadd, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, values) + ZEND_ARG_INFO(0, element) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_vsim, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_vcard arginfo_class_RedisCluster__prefix + +#define arginfo_class_RedisCluster_vdim arginfo_class_RedisCluster__prefix + +#define arginfo_class_RedisCluster_vinfo arginfo_class_RedisCluster__prefix + +#define arginfo_class_RedisCluster_vismember arginfo_class_RedisCluster_hexists + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_vemb, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, raw) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_vrandmember arginfo_class_RedisCluster_lpop + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_vrange, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, min) + ZEND_ARG_INFO(0, max) + ZEND_ARG_INFO(0, count) +ZEND_END_ARG_INFO() + +#define arginfo_class_RedisCluster_vrem arginfo_class_RedisCluster_hexists + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_vlinks, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, withscores) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_vgetattr, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, decode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_vsetattr, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, attributes) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_gcra, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, maxBurst) + ZEND_ARG_INFO(0, requestsPerPeriod) + ZEND_ARG_INFO(0, period) + ZEND_ARG_INFO(0, tokens) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xack, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, group) @@ -696,6 +851,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xdel, 0, 0, 2) ZEND_ARG_INFO(0, ids) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xdelex, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, ids) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_xgroup, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, key) @@ -817,7 +978,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangestore, 0, 0, 4) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() -#define arginfo_class_RedisCluster_zrandmember arginfo_class_RedisCluster_hrandfield +#define arginfo_class_RedisCluster_zrandmember arginfo_class_RedisCluster_delex ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebylex, 0, 0, 3) ZEND_ARG_INFO(0, key) @@ -878,6 +1039,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zdiff, 0, 0, 1) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_digest arginfo_class_RedisCluster__prefix ZEND_METHOD(RedisCluster, __construct); ZEND_METHOD(RedisCluster, _compress); @@ -885,6 +1047,7 @@ ZEND_METHOD(RedisCluster, _uncompress); ZEND_METHOD(RedisCluster, _serialize); ZEND_METHOD(RedisCluster, _unserialize); ZEND_METHOD(RedisCluster, _pack); +ZEND_METHOD(RedisCluster, _digest); ZEND_METHOD(RedisCluster, _unpack); ZEND_METHOD(RedisCluster, _prefix); ZEND_METHOD(RedisCluster, _masters); @@ -892,6 +1055,8 @@ ZEND_METHOD(RedisCluster, _redir); ZEND_METHOD(RedisCluster, acl); ZEND_METHOD(RedisCluster, append); ZEND_METHOD(RedisCluster, bgrewriteaof); +ZEND_METHOD(RedisCluster, wait); +ZEND_METHOD(RedisCluster, waitaof); ZEND_METHOD(RedisCluster, bgsave); ZEND_METHOD(RedisCluster, bitcount); ZEND_METHOD(RedisCluster, bitop); @@ -919,6 +1084,8 @@ ZEND_METHOD(RedisCluster, decr); ZEND_METHOD(RedisCluster, decrby); ZEND_METHOD(RedisCluster, decrbyfloat); ZEND_METHOD(RedisCluster, del); +ZEND_METHOD(RedisCluster, delex); +ZEND_METHOD(RedisCluster, delifeq); ZEND_METHOD(RedisCluster, discard); ZEND_METHOD(RedisCluster, dump); ZEND_METHOD(RedisCluster, echo); @@ -946,6 +1113,9 @@ ZEND_METHOD(RedisCluster, georadiusbymember_ro); ZEND_METHOD(RedisCluster, geosearch); ZEND_METHOD(RedisCluster, geosearchstore); ZEND_METHOD(RedisCluster, get); +ZEND_METHOD(RedisCluster, getdel); +ZEND_METHOD(RedisCluster, getWithMeta); +ZEND_METHOD(RedisCluster, getex); ZEND_METHOD(RedisCluster, getbit); ZEND_METHOD(RedisCluster, getlasterror); ZEND_METHOD(RedisCluster, getmode); @@ -959,17 +1129,32 @@ ZEND_METHOD(RedisCluster, hdel); ZEND_METHOD(RedisCluster, hexists); ZEND_METHOD(RedisCluster, hget); ZEND_METHOD(RedisCluster, hgetall); +ZEND_METHOD(RedisCluster, hgetWithMeta); ZEND_METHOD(RedisCluster, hincrby); ZEND_METHOD(RedisCluster, hincrbyfloat); ZEND_METHOD(RedisCluster, hkeys); ZEND_METHOD(RedisCluster, hlen); ZEND_METHOD(RedisCluster, hmget); +ZEND_METHOD(RedisCluster, hgetex); +ZEND_METHOD(RedisCluster, hsetex); +ZEND_METHOD(RedisCluster, hgetdel); ZEND_METHOD(RedisCluster, hmset); ZEND_METHOD(RedisCluster, hscan); +ZEND_METHOD(RedisCluster, expiremember); +ZEND_METHOD(RedisCluster, expirememberat); ZEND_METHOD(RedisCluster, hrandfield); ZEND_METHOD(RedisCluster, hset); ZEND_METHOD(RedisCluster, hsetnx); ZEND_METHOD(RedisCluster, hstrlen); +ZEND_METHOD(RedisCluster, hexpire); +ZEND_METHOD(RedisCluster, hpexpire); +ZEND_METHOD(RedisCluster, hexpireat); +ZEND_METHOD(RedisCluster, hpexpireat); +ZEND_METHOD(RedisCluster, httl); +ZEND_METHOD(RedisCluster, hpttl); +ZEND_METHOD(RedisCluster, hexpiretime); +ZEND_METHOD(RedisCluster, hpexpiretime); +ZEND_METHOD(RedisCluster, hpersist); ZEND_METHOD(RedisCluster, hvals); ZEND_METHOD(RedisCluster, incr); ZEND_METHOD(RedisCluster, incrby); @@ -992,6 +1177,7 @@ ZEND_METHOD(RedisCluster, ltrim); ZEND_METHOD(RedisCluster, mget); ZEND_METHOD(RedisCluster, mset); ZEND_METHOD(RedisCluster, msetnx); +ZEND_METHOD(RedisCluster, msetex); ZEND_METHOD(RedisCluster, multi); ZEND_METHOD(RedisCluster, object); ZEND_METHOD(RedisCluster, persist); @@ -1056,10 +1242,25 @@ ZEND_METHOD(RedisCluster, unsubscribe); ZEND_METHOD(RedisCluster, unlink); ZEND_METHOD(RedisCluster, unwatch); ZEND_METHOD(RedisCluster, watch); +ZEND_METHOD(RedisCluster, vadd); +ZEND_METHOD(RedisCluster, vsim); +ZEND_METHOD(RedisCluster, vcard); +ZEND_METHOD(RedisCluster, vdim); +ZEND_METHOD(RedisCluster, vinfo); +ZEND_METHOD(RedisCluster, vismember); +ZEND_METHOD(RedisCluster, vemb); +ZEND_METHOD(RedisCluster, vrandmember); +ZEND_METHOD(RedisCluster, vrange); +ZEND_METHOD(RedisCluster, vrem); +ZEND_METHOD(RedisCluster, vlinks); +ZEND_METHOD(RedisCluster, vgetattr); +ZEND_METHOD(RedisCluster, vsetattr); +ZEND_METHOD(RedisCluster, gcra); ZEND_METHOD(RedisCluster, xack); ZEND_METHOD(RedisCluster, xadd); ZEND_METHOD(RedisCluster, xclaim); ZEND_METHOD(RedisCluster, xdel); +ZEND_METHOD(RedisCluster, xdelex); ZEND_METHOD(RedisCluster, xgroup); ZEND_METHOD(RedisCluster, xautoclaim); ZEND_METHOD(RedisCluster, xinfo); @@ -1101,7 +1302,7 @@ ZEND_METHOD(RedisCluster, zinter); ZEND_METHOD(RedisCluster, zdiffstore); ZEND_METHOD(RedisCluster, zunion); ZEND_METHOD(RedisCluster, zdiff); - +ZEND_METHOD(RedisCluster, digest); static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, __construct, arginfo_class_RedisCluster___construct, ZEND_ACC_PUBLIC) @@ -1110,6 +1311,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, _serialize, arginfo_class_RedisCluster__serialize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _unserialize, arginfo_class_RedisCluster__unserialize, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _pack, arginfo_class_RedisCluster__pack, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, _digest, arginfo_class_RedisCluster__digest, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _unpack, arginfo_class_RedisCluster__unpack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _prefix, arginfo_class_RedisCluster__prefix, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, _masters, arginfo_class_RedisCluster__masters, ZEND_ACC_PUBLIC) @@ -1117,6 +1319,8 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, acl, arginfo_class_RedisCluster_acl, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, append, arginfo_class_RedisCluster_append, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgrewriteaof, arginfo_class_RedisCluster_bgrewriteaof, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, wait, arginfo_class_RedisCluster_wait, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, waitaof, arginfo_class_RedisCluster_waitaof, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bgsave, arginfo_class_RedisCluster_bgsave, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitcount, arginfo_class_RedisCluster_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, bitop, arginfo_class_RedisCluster_bitop, ZEND_ACC_PUBLIC) @@ -1144,6 +1348,8 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, del, arginfo_class_RedisCluster_del, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, delex, arginfo_class_RedisCluster_delex, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, delifeq, arginfo_class_RedisCluster_delifeq, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, discard, arginfo_class_RedisCluster_discard, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, dump, arginfo_class_RedisCluster_dump, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, echo, arginfo_class_RedisCluster_echo, ZEND_ACC_PUBLIC) @@ -1171,6 +1377,9 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, geosearch, arginfo_class_RedisCluster_geosearch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, geosearchstore, arginfo_class_RedisCluster_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, get, arginfo_class_RedisCluster_get, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, getdel, arginfo_class_RedisCluster_getdel, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, getWithMeta, arginfo_class_RedisCluster_getWithMeta, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, getex, arginfo_class_RedisCluster_getex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getbit, arginfo_class_RedisCluster_getbit, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getlasterror, arginfo_class_RedisCluster_getlasterror, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, getmode, arginfo_class_RedisCluster_getmode, ZEND_ACC_PUBLIC) @@ -1184,17 +1393,32 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, hexists, arginfo_class_RedisCluster_hexists, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hget, arginfo_class_RedisCluster_hget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hgetall, arginfo_class_RedisCluster_hgetall, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hgetWithMeta, arginfo_class_RedisCluster_hgetWithMeta, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hincrby, arginfo_class_RedisCluster_hincrby, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hincrbyfloat, arginfo_class_RedisCluster_hincrbyfloat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hkeys, arginfo_class_RedisCluster_hkeys, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hlen, arginfo_class_RedisCluster_hlen, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmget, arginfo_class_RedisCluster_hmget, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hgetex, arginfo_class_RedisCluster_hgetex, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hsetex, arginfo_class_RedisCluster_hsetex, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hgetdel, arginfo_class_RedisCluster_hgetdel, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hmset, arginfo_class_RedisCluster_hmset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hscan, arginfo_class_RedisCluster_hscan, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, expiremember, arginfo_class_RedisCluster_expiremember, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, expirememberat, arginfo_class_RedisCluster_expirememberat, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hrandfield, arginfo_class_RedisCluster_hrandfield, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hset, arginfo_class_RedisCluster_hset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hsetnx, arginfo_class_RedisCluster_hsetnx, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hstrlen, arginfo_class_RedisCluster_hstrlen, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpire, arginfo_class_RedisCluster_hexpire, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpire, arginfo_class_RedisCluster_hpexpire, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpireat, arginfo_class_RedisCluster_hexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpireat, arginfo_class_RedisCluster_hpexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, httl, arginfo_class_RedisCluster_httl, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpttl, arginfo_class_RedisCluster_hpttl, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hexpiretime, arginfo_class_RedisCluster_hexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpexpiretime, arginfo_class_RedisCluster_hpexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, hpersist, arginfo_class_RedisCluster_hpersist, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, hvals, arginfo_class_RedisCluster_hvals, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incr, arginfo_class_RedisCluster_incr, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, incrby, arginfo_class_RedisCluster_incrby, ZEND_ACC_PUBLIC) @@ -1217,6 +1441,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, mget, arginfo_class_RedisCluster_mget, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, mset, arginfo_class_RedisCluster_mset, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, msetnx, arginfo_class_RedisCluster_msetnx, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, msetex, arginfo_class_RedisCluster_msetex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, multi, arginfo_class_RedisCluster_multi, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, object, arginfo_class_RedisCluster_object, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, persist, arginfo_class_RedisCluster_persist, ZEND_ACC_PUBLIC) @@ -1281,10 +1506,25 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, unlink, arginfo_class_RedisCluster_unlink, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, unwatch, arginfo_class_RedisCluster_unwatch, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, watch, arginfo_class_RedisCluster_watch, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vadd, arginfo_class_RedisCluster_vadd, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vsim, arginfo_class_RedisCluster_vsim, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vcard, arginfo_class_RedisCluster_vcard, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vdim, arginfo_class_RedisCluster_vdim, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vinfo, arginfo_class_RedisCluster_vinfo, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vismember, arginfo_class_RedisCluster_vismember, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vemb, arginfo_class_RedisCluster_vemb, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vrandmember, arginfo_class_RedisCluster_vrandmember, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vrange, arginfo_class_RedisCluster_vrange, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vrem, arginfo_class_RedisCluster_vrem, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vlinks, arginfo_class_RedisCluster_vlinks, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vgetattr, arginfo_class_RedisCluster_vgetattr, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, vsetattr, arginfo_class_RedisCluster_vsetattr, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, gcra, arginfo_class_RedisCluster_gcra, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xack, arginfo_class_RedisCluster_xack, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xadd, arginfo_class_RedisCluster_xadd, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xclaim, arginfo_class_RedisCluster_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xdel, arginfo_class_RedisCluster_xdel, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, xdelex, arginfo_class_RedisCluster_xdelex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xgroup, arginfo_class_RedisCluster_xgroup, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xautoclaim, arginfo_class_RedisCluster_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, xinfo, arginfo_class_RedisCluster_xinfo, ZEND_ACC_PUBLIC) @@ -1326,11 +1566,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, zdiffstore, arginfo_class_RedisCluster_zdiffstore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zunion, arginfo_class_RedisCluster_zunion, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zdiff, arginfo_class_RedisCluster_zdiff, ZEND_ACC_PUBLIC) - ZEND_FE_END -}; - - -static const zend_function_entry class_RedisClusterException_methods[] = { + ZEND_ME(RedisCluster, digest, arginfo_class_RedisCluster_digest, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -1339,7 +1575,11 @@ static zend_class_entry *register_class_RedisCluster(void) zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisCluster", class_RedisCluster_methods); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif zval const_OPT_SLAVE_FAILOVER_value; ZVAL_LONG(&const_OPT_SLAVE_FAILOVER_value, REDIS_OPT_FAILOVER); @@ -1378,8 +1618,12 @@ static zend_class_entry *register_class_RedisClusterException(zend_class_entry * { zend_class_entry ce, *class_entry; - INIT_CLASS_ENTRY(ce, "RedisClusterException", class_RedisClusterException_methods); + INIT_CLASS_ENTRY(ce, "RedisClusterException", NULL); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_RuntimeException, 0); +#else class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException); +#endif return class_entry; } diff --git a/redis_commands.c b/redis_commands.c index 94129331c9..8c8518649a 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -16,12 +16,14 @@ +----------------------------------------------------------------------+ */ +#include "hash/php_hash.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "redis_commands.h" + #include "php_network.h" #ifndef PHP_WIN32 @@ -30,6 +32,10 @@ #include #endif +#ifdef HAVE_REDIS_JSON +#include +#endif + #include /* Georadius sort type */ @@ -72,6 +78,45 @@ typedef struct redisRestoreOptions { zend_long freq; } redisRestoreOptions; +typedef enum redisSetType { + REDIS_SET_NONE, + REDIS_SET_NX, + REDIS_SET_XX +} redisSetType; + +typedef enum redisExpiryType { + REDIS_EXPIRY_NONE, + REDIS_EXPIRY_EX, + REDIS_EXPIRY_PX, + REDIS_EXPIRY_EXAT, + REDIS_EXPIRY_PXAT, +} redisExpiryType; + +typedef enum redisEqType { + REDIS_IF_NONE, + REDIS_IFEQ, + REDIS_IFNE, + REDIS_IFDEQ, + REDIS_IFDNE +} redisEqType; + +typedef struct redisExpiryOptions { + redisExpiryType type; + zend_long ttl; + zend_bool keepttl; +} redisExpiryOptions; + +typedef struct redisSetOptions { + redisSetType type; + zend_bool get; + redisExpiryOptions expiry; + struct { + redisEqType type; + zval *zval; + } eq; +} redisSetOptions; + + #define REDIS_ZCMD_HAS_DST_KEY (1 << 0) #define REDIS_ZCMD_HAS_WITHSCORES (1 << 1) #define REDIS_ZCMD_HAS_BY_LEX_SCORE (1 << 2) @@ -168,16 +213,17 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args) return NULL; } // Branch based on the directive - if (!strcasecmp(Z_STRVAL(z_args[0]), "kill")) { + if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "kill")) { // Simple SCRIPT_KILL command REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); redis_cmd_append_sstr(cmd, ZEND_STRL("KILL")); - } else if (!strcasecmp(Z_STRVAL(z_args[0]), "flush")) { + } else if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "flush")) { // Simple SCRIPT FLUSH [ASYNC | SYNC] if (argc > 1 && ( - Z_TYPE(z_args[1]) != IS_STRING || - strcasecmp(Z_STRVAL(z_args[1]), "sync") || - strcasecmp(Z_STRVAL(z_args[1]), "async") + Z_TYPE(z_args[1]) != IS_STRING || ( + !zend_string_equals_literal_ci(Z_STR(z_args[1]), "sync") && + !zend_string_equals_literal_ci(Z_STR(z_args[1]), "async") + ) )) { return NULL; } @@ -186,7 +232,7 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args) if (argc > 1) { redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); } - } else if (!strcasecmp(Z_STRVAL(z_args[0]), "load")) { + } else if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "load")) { // Make sure we have a second argument, and it's not empty. If it is // empty, we can just return an empty array (which is what Redis does) if (argc < 2 || Z_TYPE(z_args[1]) != IS_STRING || Z_STRLEN(z_args[1]) < 1) { @@ -196,7 +242,7 @@ redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args) REDIS_CMD_INIT_SSTR_STATIC(cmd, argc, "SCRIPT"); redis_cmd_append_sstr(cmd, ZEND_STRL("LOAD")); redis_cmd_append_sstr(cmd, Z_STRVAL(z_args[1]), Z_STRLEN(z_args[1])); - } else if (!strcasecmp(Z_STRVAL(z_args[0]), "exists")) { + } else if (zend_string_equals_literal_ci(Z_STR(z_args[0]), "exists")) { // Make sure we have a second argument if (argc < 2) { return NULL; @@ -267,11 +313,11 @@ int redis_key_long_val_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zend_long expire; zval *z_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "slz", &key, &key_len, - &expire, &z_val) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STRING(key, key_len) + Z_PARAM_LONG(expire) + Z_PARAM_ZVAL(z_val) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "klv", key, key_len, expire, z_val); @@ -450,11 +496,9 @@ int redis_key_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *key; size_t key_len; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &key_len) - ==FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(key, key_len); + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "k", key, key_len); @@ -487,7 +531,7 @@ redis_failover_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } else if (zend_string_equals_literal_ci(zkey, "port")) { port = zval_get_long(z_ele); } else if (zend_string_equals_literal_ci(zkey, "force")) { - force = zval_is_true(z_ele); + force = zend_is_true(z_ele); } } } ZEND_HASH_FOREACH_END(); @@ -575,7 +619,7 @@ int redis_key_dbl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Generic to construct SCAN and variant commands */ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, - long it, char *pat, int pat_len, long count) + uint64_t it, char *pat, int pat_len, long count) { static char *kw[] = {"SCAN","SSCAN","HSCAN","ZSCAN"}; int argc; @@ -592,7 +636,7 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, } // Append cursor - redis_cmd_append_sstr_long(&cmdstr, it); + redis_cmd_append_sstr_u64(&cmdstr, it); // Append count if we've got one if (count) { @@ -633,7 +677,7 @@ void redis_get_zcmd_options(redisZcmdOptions *dst, zval *src, int flags) { if (key) { if ((flags & REDIS_ZCMD_HAS_WITHSCORES) && zend_string_equals_literal_ci(key, "WITHSCORES")) - dst->withscores = zval_is_true(zv); + dst->withscores = zend_is_true(zv); else if ((flags & REDIS_ZCMD_HAS_LIMIT) && zend_string_equals_literal_ci(key, "LIMIT") && Z_TYPE_P(zv) == IS_ARRAY) { @@ -673,6 +717,34 @@ void redis_get_zcmd_options(redisZcmdOptions *dst, zval *src, int flags) { } ZEND_HASH_FOREACH_END(); } +#if defined(__clang__) || \ + (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) +#define PHPREDIS_HAVE_BSWAP32 1 +#endif + +static void redis_copy_fp32(char *dst, float src) { +#ifdef PHPREDIS_BIG_ENDIAN +#ifdef PHPREDIS_HAVE_BSWAP32 + uint32_t val; + memcpy(&val, &src, sizeof(val)); + val = __builtin_bswap32(val); + memcpy(dst, &val, sizeof(val)); +#else + union { + float f; + unsigned char b[4]; + } u; + u.f = src; + dst[0] = u.b[3]; + dst[1] = u.b[2]; + dst[2] = u.b[1]; + dst[3] = u.b[0]; +#endif +#else + memcpy(dst, &src, sizeof(src)); +#endif +} + // + ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count] [WITHSCORES] // + ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset count] // + ZREVRANGE key start stop [WITHSCORES] @@ -1045,7 +1117,7 @@ redis_fcall_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_ARRAY_HT(args) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - redis_cmd_init_sstr(&cmdstr, 2 + (keys ? zend_hash_num_elements(keys) : 0) + + redis_cmd_init_sstr(&cmdstr, 2 + (keys ? zend_hash_num_elements(keys) : 0) + (args ? zend_hash_num_elements(args) : 0), kw, strlen(kw)); redis_cmd_append_sstr_zstr(&cmdstr, fn); redis_cmd_append_sstr_long(&cmdstr, keys ? zend_hash_num_elements(keys) : 0); @@ -1093,7 +1165,7 @@ redis_zrandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (zend_string_equals_literal_ci(zkey, "count")) { count = zval_get_long(z_ele); } else if (zend_string_equals_literal_ci(zkey, "withscores")) { - withscores = zval_is_true(z_ele); + withscores = zend_is_true(z_ele); } } } ZEND_HASH_FOREACH_END(); @@ -1177,7 +1249,7 @@ static int redis_cmd_append_sstr_score(smart_string *dst, zval *score) { cmdlen = dst->len; if (Z_TYPE_P(score) == IS_LONG) { - redis_cmd_append_sstr_long(dst, Z_LVAL_P(score)); + redis_cmd_append_sstr_zend_long(dst, Z_LVAL_P(score)); } else if (Z_TYPE_P(score) == IS_DOUBLE) { redis_cmd_append_sstr_dbl(dst, Z_DVAL_P(score)); } else if (Z_TYPE_P(score) == IS_STRING) { @@ -1264,6 +1336,7 @@ int redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { + smart_string cmdstr = {0}; zend_string *host = NULL; zend_long port = 6379; @@ -1278,12 +1351,19 @@ int redis_replicaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } + redis_cmd_init_sstr(&cmdstr, 2, kw, strlen(kw)); + if (ZEND_NUM_ARGS() == 2) { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "Sd", host, (int)port); + redis_cmd_append_sstr_zstr(&cmdstr, host); + redis_cmd_append_sstr_long(&cmdstr, port); } else { - *cmd_len = REDIS_SPPRINTF(cmd, kw, "ss", ZEND_STRL("NO"), ZEND_STRL("ONE")); + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO"); + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ONE"); } + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; } @@ -1495,7 +1575,7 @@ int redis_pubsub_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) { if (arg != NULL) { if (Z_TYPE_P(arg) != IS_STRING) { - php_error_docref(NULL, E_WARNING, "Invalid patern value"); + php_error_docref(NULL, E_WARNING, "Invalid pattern value"); return FAILURE; } pattern = zval_get_string(arg); @@ -1580,7 +1660,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZEND_HASH_FOREACH_VAL(ht_chan, z_chan) { redis_cmd_append_sstr_key_zval(&cmdstr, z_chan, redis_sock, slot ? &s2 : NULL); - if (shardslot != REDIS_CLUSTER_SLOTS && s2 != shardslot) { + if (slot && (shardslot != REDIS_CLUSTER_SLOTS && s2 != shardslot)) { php_error_docref(NULL, E_WARNING, "All shard channels needs to belong to a single slot"); smart_string_free(&cmdstr); efree(sctx); @@ -1591,7 +1671,7 @@ int redis_subscribe_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // Push values out *cmd_len = cmdstr.len; *cmd = cmdstr.c; - *ctx = (void*)sctx; + *ctx = sctx; if (shardslot != REDIS_CLUSTER_SLOTS) { if (slot) *slot = shardslot; @@ -1711,62 +1791,58 @@ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_eval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - char *lua; int argc = 0; - zval *z_arr = NULL, *z_ele; - HashTable *ht_arr; + zval *zv; + HashTable *ht = NULL; zend_long num_keys = 0; smart_string cmdstr = {0}; - size_t lua_len; - zend_string *zstr; + zend_string *arg, *lua, *tmp; short prevslot = -1; - /* Parse args */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|al", &lua, &lua_len, - &z_arr, &num_keys) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(1, 3) + Z_PARAM_STR(lua) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(ht) + Z_PARAM_LONG(num_keys) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - /* Grab arg count */ - if (z_arr != NULL) { - ht_arr = Z_ARRVAL_P(z_arr); - argc = zend_hash_num_elements(ht_arr); - } + if (ht == NULL) + ht = (HashTable*)&zend_empty_array; + + argc = zend_hash_num_elements(ht); /* EVAL[SHA] {script || sha1} {num keys} */ redis_cmd_init_sstr(&cmdstr, 2 + argc, kw, strlen(kw)); - redis_cmd_append_sstr(&cmdstr, lua, lua_len); + redis_cmd_append_sstr_zstr(&cmdstr, lua); redis_cmd_append_sstr_long(&cmdstr, num_keys); + /* Pick a random slot up front. Any provided key(s) will override this */ + CMD_RAND_SLOT(slot); + // Iterate over our args if we have any - if (argc > 0) { - ZEND_HASH_FOREACH_VAL(ht_arr, z_ele) { - zstr = zval_get_string(z_ele); - - /* If we're still on a key, prefix it check slot */ - if (num_keys-- > 0) { - redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot); - - /* If we have been passed a slot, all keys must match */ - if (slot) { - if (prevslot != -1 && prevslot != *slot) { - zend_string_release(zstr); - php_error_docref(0, E_WARNING, "All keys do not map to the same slot"); - return FAILURE; - } - prevslot = *slot; + ZEND_HASH_FOREACH_VAL(ht, zv) { + arg = zval_get_tmp_string(zv, &tmp); + + /* If we're still on a key, prefix it check slot */ + if (num_keys-- > 0) { + redis_cmd_append_sstr_key_zstr(&cmdstr, arg, redis_sock, slot); + + /* If we have been passed a slot, all keys must match */ + if (slot) { + if (prevslot != -1 && prevslot != *slot) { + zend_tmp_string_release(tmp); + php_error_docref(0, E_WARNING, + "All keys do not map to the same slot"); + return FAILURE; } - } else { - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); + prevslot = *slot; } + } else { + redis_cmd_append_sstr_zstr(&cmdstr, arg); + } - zend_string_release(zstr); - } ZEND_HASH_FOREACH_END(); - } else { - /* Any slot will do */ - CMD_RAND_SLOT(slot); - } + zend_tmp_string_release(tmp); + } ZEND_HASH_FOREACH_END(); *cmd = cmdstr.c; *cmd_len = cmdstr.len; @@ -1779,44 +1855,32 @@ int redis_key_varval_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_args; + zval *args = NULL; + zend_string *key = NULL; smart_string cmdstr = {0}; size_t i; - int argc = ZEND_NUM_ARGS(); - - // We at least need a key and one value - if (argc < 2) { - zend_wrong_param_count(); - return FAILURE; - } + int argc = 0; - // Make sure we at least have a key, and we can get other args - z_args = emalloc(argc * sizeof(zval)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, -1) + Z_PARAM_STR(key) + Z_PARAM_VARIADIC('*', args, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); /* Initialize our command */ - redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + redis_cmd_init_sstr(&cmdstr, argc + 1, kw, strlen(kw)); /* Append key */ - zend_string *zstr = zval_get_string(&z_args[0]); - redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr), redis_sock, slot); - zend_string_release(zstr); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); /* Add members */ - for (i = 1; i < argc; i++ ){ - redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock); + for (i = 0; i < argc; i++) { + redis_cmd_append_sstr_zval(&cmdstr, &args[i], redis_sock); } // Push out values *cmd = cmdstr.c; *cmd_len = cmdstr.len; - // Cleanup arg array - efree(z_args); - // Success! return SUCCESS; } @@ -1877,6 +1941,7 @@ gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, smart_string cmdstr = {0}; zval *argv = NULL; int argc = 0; + uint32_t i; ZEND_PARSE_PARAMETERS_START(min_argc, -1) Z_PARAM_VARIADIC('*', argv, argc) @@ -1884,7 +1949,7 @@ gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); - for (uint32_t i = 0; i < argc; i++) { + for (i = 0; i < argc; i++) { redis_cmd_append_sstr_zval(&cmdstr, &argv[i], NULL); } @@ -1894,15 +1959,31 @@ gen_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +static void +redis_cmd_append_sstr_mset_kvals(smart_string *cmdstr, RedisSock *redis_sock, + HashTable *kvals, short *slot) +{ + zend_string *key; + zend_ulong idx; + zval *zv; + + ZEND_HASH_FOREACH_KEY_VAL(kvals, idx, key, zv) { + ZVAL_DEREF(zv); + if (key) { + redis_cmd_append_sstr_key_zstr(cmdstr, key, redis_sock, slot); + } else { + redis_cmd_append_sstr_key_long(cmdstr, idx, redis_sock, slot); + } + redis_cmd_append_sstr_zval(cmdstr, zv, redis_sock); + } ZEND_HASH_FOREACH_END(); +} + int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; HashTable *kvals = NULL; - zend_string *key; - zend_ulong idx; - zval *zv; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY_HT(kvals) @@ -1911,17 +1992,9 @@ int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (zend_hash_num_elements(kvals) == 0) return FAILURE; - redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(kvals) * 2, kw, strlen(kw)); - - ZEND_HASH_FOREACH_KEY_VAL(kvals, idx, key, zv) { - ZVAL_DEREF(zv); - if (key) { - redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, NULL); - } else { - redis_cmd_append_sstr_key_long(&cmdstr, idx, redis_sock, NULL); - } - redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); - } ZEND_HASH_FOREACH_END(); + redis_cmd_init_sstr(&cmdstr, zend_hash_num_elements(kvals) * 2, kw, + strlen(kw)); + redis_cmd_append_sstr_mset_kvals(&cmdstr, redis_sock, kvals, slot); *cmd = cmdstr.c; *cmd_len = cmdstr.len; @@ -1986,7 +2059,8 @@ static int gen_varkey_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } ZEND_HASH_FOREACH_END(); } else { - for(uint32_t i = 0; i < argc - !!has_timeout; i++) { + uint32_t i; + for(i = 0; i < argc - !!has_timeout; i++) { redis_cmd_append_sstr_key_zval(&cmdstr, &argv[i], redis_sock, slot); if (slot) { if (kslot != -1 && *slot != kslot) @@ -2104,8 +2178,22 @@ int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_script_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - return gen_vararg_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, 1, - "SCRIPT", cmd, cmd_len, slot, ctx); + int argc = 0; + smart_string cmdstr = {0}; + zval *argv = NULL; + + ZEND_PARSE_PARAMETERS_START(1, -1) + Z_PARAM_VARIADIC('*', argv, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (redis_build_script_cmd(&cmdstr, argc, argv) == NULL) { + return FAILURE; + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; } /* Generic handling of every blocking pop command (BLPOP, BZPOP[MIN/MAX], etc */ @@ -2229,6 +2317,34 @@ redis_acl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int redis_waitaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zend_long numlocal, numreplicas, timeout; + smart_string cmdstr = {0}; + + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_LONG(numlocal) + Z_PARAM_LONG(numreplicas) + Z_PARAM_LONG(timeout) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (numlocal < 0 || numreplicas < 0 || timeout < 0) { + php_error_docref(NULL, E_WARNING, "No arguments can be negative"); + return FAILURE; + } + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 3, "WAITAOF"); + redis_cmd_append_sstr_long(&cmdstr, numlocal); + redis_cmd_append_sstr_long(&cmdstr, numreplicas); + redis_cmd_append_sstr_long(&cmdstr, timeout); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* Attempt to pull a long expiry from a zval. We're more restrictave than zval_get_long * because that function will return integers from things like open file descriptors * which should simply fail as a TTL */ @@ -2257,141 +2373,188 @@ static int redis_try_get_expiry(zval *zv, zend_long *lval) { } } -/* SET */ -int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) -{ - smart_string cmdstr = {0}; - zval *z_value, *z_opts=NULL; - char *key = NULL, *exp_type = NULL, *set_type = NULL; - long exp_set = 0, keep_ttl = 0; - zend_long expire = -1; - zend_bool get = 0; - size_t key_len; +static zend_bool zstr_to_expiry_type(redisExpiryType *dst, zend_string *src) { + redisExpiryType tmp = REDIS_EXPIRY_NONE; - // Make sure the function is being called correctly - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz|z", &key, &key_len, - &z_value, &z_opts) == FAILURE) - { - return FAILURE; + if (zend_string_equals_literal_ci(src, "EX")) { + tmp = REDIS_EXPIRY_EX; + } else if (zend_string_equals_literal_ci(src, "PX")) { + tmp = REDIS_EXPIRY_PX; + } else if (zend_string_equals_literal_ci(src, "EXAT")) { + tmp = REDIS_EXPIRY_EXAT; + } else if (zend_string_equals_literal_ci(src, "PXAT")) { + tmp = REDIS_EXPIRY_PXAT; + } else { + return 0; } - // Check for an options array - if (z_opts && Z_TYPE_P(z_opts) == IS_ARRAY) { - HashTable *kt = Z_ARRVAL_P(z_opts); - zend_string *zkey; - zval *v; + *dst = tmp; + return 1; +} - /* Iterate our option array */ - ZEND_HASH_FOREACH_STR_KEY_VAL(kt, zkey, v) { - ZVAL_DEREF(v); - /* Detect PX or EX argument and validate timeout */ - if (zkey && (zend_string_equals_literal_ci(zkey, "EX") || - zend_string_equals_literal_ci(zkey, "PX") || - zend_string_equals_literal_ci(zkey, "EXAT") || - zend_string_equals_literal_ci(zkey, "PXAT")) - ) { - exp_set = 1; +static zend_bool zstr_to_eq_type(redisEqType *dst, zend_string *src) { + *dst = REDIS_IF_NONE; - /* Set expire type */ - exp_type = ZSTR_VAL(zkey); + if (zend_string_equals_literal_ci(src, "IFEQ")) { + *dst = REDIS_IFEQ; + } else if (zend_string_equals_literal_ci(src, "IFNE")) { + *dst = REDIS_IFNE; + } else if (zend_string_equals_literal_ci(src, "IFDEQ")) { + *dst = REDIS_IFDEQ; + } else if (zend_string_equals_literal_ci(src, "IFDNE")) { + *dst = REDIS_IFDNE; + } else { + return 0; + } - /* Try to extract timeout */ - if (Z_TYPE_P(v) == IS_LONG) { - expire = Z_LVAL_P(v); - } else if (Z_TYPE_P(v) == IS_STRING) { - expire = atol(Z_STRVAL_P(v)); - } - } else if (Z_TYPE_P(v) == IS_STRING) { - if (zend_string_equals_literal_ci(Z_STR_P(v), "KEEPTTL")) { - keep_ttl = 1; - } else if (zend_string_equals_literal_ci(Z_STR_P(v), "GET")) { - get = 1; - } else if (zend_string_equals_literal_ci(Z_STR_P(v), "NX") || - zend_string_equals_literal_ci(Z_STR_P(v), "XX")) - { - set_type = Z_STRVAL_P(v); - } + return 1; +} + +static void fill_set_options_ht(redisSetOptions *dst, HashTable *ht) { + zend_string *key; + zval *zv; + + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) { + if (key) { + if (zstr_to_expiry_type(&dst->expiry.type, key)) { + dst->expiry.ttl = zval_get_long(zv); + } else if (zstr_to_eq_type(&dst->eq.type, key)) { + dst->eq.zval = zv; + } else if (zend_string_equals_literal_ci(key, "GET")) { + dst->get = zend_is_true(zv); + } + } else if (Z_TYPE_P(zv) == IS_STRING) { + key = Z_STR_P(zv); + if (zend_string_equals_literal_ci(key, "NX")) { + dst->type = REDIS_SET_NX; + } else if (zend_string_equals_literal_ci(key, "XX")) { + dst->type = REDIS_SET_XX; + } else if (zend_string_equals_literal_ci(key, "GET")) { + dst->get = 1; + } else if (zend_string_equals_literal_ci(key, "KEEPTTL")) { + dst->expiry.type = REDIS_EXPIRY_NONE; + dst->expiry.keepttl = 1; } - } ZEND_HASH_FOREACH_END(); - } else if (z_opts && Z_TYPE_P(z_opts) != IS_NULL) { - if (redis_try_get_expiry(z_opts, &expire) == FAILURE) { - php_error_docref(NULL, E_WARNING, "Expire must be a long, double, or a numeric string"); - return FAILURE; } + } ZEND_HASH_FOREACH_END(); +} - exp_set = 1; - } +static int +fill_set_options_zval(redisSetOptions *dst, zval *zv, zend_bool legacy_set) { + zend_long lval; - /* Protect the user from syntax errors but give them some info about what's wrong */ - if (exp_set && expire < 1) { - php_error_docref(NULL, E_WARNING, "EXPIRE can't be < 1"); - return FAILURE; - } else if (exp_type && keep_ttl) { - php_error_docref(NULL, E_WARNING, "KEEPTTL can't be combined with EX or PX option"); - return FAILURE; - } + memset(dst, 0, sizeof(*dst)); - /* Backward compatibility: If we are passed no options except an EXPIRE ttl, we - * actually execute a SETEX command */ - if (expire > 0 && !exp_type && !set_type && !keep_ttl) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "klv", key, key_len, expire, z_value); + if (zv == NULL) + return SUCCESS; + + if (Z_TYPE_P(zv) == IS_ARRAY) { + fill_set_options_ht(dst, Z_ARRVAL_P(zv)); + return SUCCESS; + } else if (redis_try_get_expiry(zv, &lval) == SUCCESS && lval > 0) { + if (!legacy_set) + dst->expiry.type = REDIS_EXPIRY_EX; + dst->expiry.ttl = lval; return SUCCESS; } - /* Calculate argc based on options set */ - int argc = 2 + (exp_type ? 2 : 0) + (set_type != NULL) + (keep_ttl != 0) + get; + php_error_docref(NULL, E_WARNING, + "EXPIRY is invalid (must be an int, float, or numeric string >= 1)"); - /* Initial SET */ - redis_cmd_init_sstr(&cmdstr, argc, "SET", 3); - redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); - redis_cmd_append_sstr_zval(&cmdstr, z_value, redis_sock); + return FAILURE; +} - if (exp_type) { - redis_cmd_append_sstr(&cmdstr, exp_type, strlen(exp_type)); - redis_cmd_append_sstr_long(&cmdstr, (long)expire); +static void +redis_cmd_append_sstr_expiry(smart_string *cmdstr, redisExpiryOptions *e) +{ + if (e->type == REDIS_EXPIRY_NONE && !e->keepttl) + return; + + if (e->keepttl) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "KEEPTTL"); + return; + } else if (e->type == REDIS_EXPIRY_EX) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "EX"); + } else if (e->type == REDIS_EXPIRY_PX) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PX"); + } else if (e->type == REDIS_EXPIRY_EXAT) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "EXAT"); + } else if (e->type == REDIS_EXPIRY_PXAT) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "PXAT"); } - if (set_type) - redis_cmd_append_sstr(&cmdstr, set_type, strlen(set_type)); - if (keep_ttl) - redis_cmd_append_sstr(&cmdstr, "KEEPTTL", 7); - if (get) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GET"); - *ctx = PHPREDIS_CTX_PTR; + redis_cmd_append_sstr_zend_long(cmdstr, e->ttl); +} + +static void +redis_cmd_append_eq_clause(smart_string *cmdstr, RedisSock *redis_sock, + redisEqType type, zval *zv) +{ + zend_bool pack; + + if (type == REDIS_IF_NONE) + return; + + if (type == REDIS_IFEQ) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "IFEQ"); + } else if (type == REDIS_IFNE) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "IFNE"); + } else if (type == REDIS_IFDEQ) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "IFDEQ"); + } else if (type == REDIS_IFDNE) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "IFDNE"); } - /* Push command and length to the caller */ - *cmd = cmdstr.c; - *cmd_len = cmdstr.len; + pack = type == REDIS_IFEQ || type == REDIS_IFNE; + redis_cmd_append_sstr_zval(cmdstr, zv, pack ? redis_sock : NULL); +} - return SUCCESS; +static void +redis_cmd_append_sstr_set_type(smart_string *cmdstr, redisSetType type) { + if (type == REDIS_SET_NX) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "NX"); + } else if (type == REDIS_SET_XX) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "XX"); + } } -/* MGET */ -int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +/* MSETEX */ +int redis_msetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) { + redisSetOptions opt = {0}; smart_string cmdstr = {0}; - HashTable *keys = NULL; - zval *zkey; - - /* RedisCluster has a custom MGET implementation */ - ZEND_ASSERT(slot == NULL); + zval *zexp = NULL; + HashTable *kvals; + int argc; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_ARRAY_HT(keys) + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ARRAY_HT(kvals) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_OR_NULL(zexp) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - if (zend_hash_num_elements(keys) == 0) + if (zend_hash_num_elements(kvals) == 0) { + php_error_docref(NULL, E_WARNING, "No key/value pairs provided"); return FAILURE; + } - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, zend_hash_num_elements(keys), "MGET"); + if (fill_set_options_zval(&opt, zexp, 0) != SUCCESS) + return FAILURE; - ZEND_HASH_FOREACH_VAL(keys, zkey) { - ZVAL_DEREF(zkey); - redis_cmd_append_sstr_key_zval(&cmdstr, zkey, redis_sock, slot); - } ZEND_HASH_FOREACH_END(); + argc = 1 + (2 * zend_hash_num_elements(kvals)) + !!opt.type; + if (opt.expiry.keepttl) { + argc += 1; + } else if (opt.expiry.type != REDIS_EXPIRY_NONE) { + argc += 2; + } + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "MSETEX"); + + redis_cmd_append_sstr_u64(&cmdstr, zend_hash_num_elements(kvals)); + redis_cmd_append_sstr_mset_kvals(&cmdstr, redis_sock, kvals, slot); + redis_cmd_append_sstr_set_type(&cmdstr, opt.type); + redis_cmd_append_sstr_expiry(&cmdstr, &opt.expiry); *cmd = cmdstr.c; *cmd_len = cmdstr.len; @@ -2399,15 +2562,115 @@ int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -int -redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +/* SET */ +int redis_set_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) { + zval *z_value = NULL, *z_opts = NULL; smart_string cmdstr = {0}; - char *key, *exp_type = NULL; - zval *z_opts = NULL, *z_ele; - zend_long expire = -1; - zend_bool persist = 0; + redisSetOptions opt = {0}; + zend_string *key; + int argc = 2; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(key) + Z_PARAM_ZVAL(z_value) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_OR_NULL(z_opts) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (fill_set_options_zval(&opt, z_opts, 1) != SUCCESS) + return FAILURE; + + /* You can't use IFEQ with NX or XX */ + if (opt.type && opt.eq.type != REDIS_IF_NONE) { + php_error_docref(NULL, E_WARNING, + "IF clauses can't be combined with NX/XX"); + return FAILURE; + } + + /* Backward compatibility: If we are passed no options except an EXPIRE ttl, we + * actually execute the SETEX command */ + if (opt.expiry.ttl > 0 && opt.expiry.type == REDIS_EXPIRY_NONE && !opt.type) + { + *cmd_len = REDIS_CMD_SPPRINTF(cmd, "SETEX", "Klv", key, opt.expiry.ttl, + z_value); + return SUCCESS; + } + + /* Add additional argc depending on options */ + argc += (opt.eq.type != REDIS_IF_NONE ? 2 : 0) + !!opt.type + !!opt.get; + if (opt.expiry.keepttl) { + argc += 1; + } else if (opt.expiry.type != REDIS_EXPIRY_NONE) { + argc += 2; + } + + /* Initial SET */ + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "SET"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_zval(&cmdstr, z_value, redis_sock); + + if (opt.eq.type != REDIS_IF_NONE) { + redis_cmd_append_eq_clause(&cmdstr, redis_sock, opt.eq.type, opt.eq.zval); + } else if (opt.type != REDIS_SET_NONE) { + redis_cmd_append_sstr_set_type(&cmdstr, opt.type); + } + + if (opt.get) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "GET"); + *ctx = PHPREDIS_CTX_PTR; + } + + redis_cmd_append_sstr_expiry(&cmdstr, &opt.expiry); + + /* Push command and length to the caller */ + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +/* MGET */ +int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + HashTable *keys = NULL; + zval *zkey; + + /* RedisCluster has a custom MGET implementation */ + ZEND_ASSERT(slot == NULL); + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ARRAY_HT(keys) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(keys) == 0) + return FAILURE; + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, zend_hash_num_elements(keys), "MGET"); + + ZEND_HASH_FOREACH_VAL(keys, zkey) { + ZVAL_DEREF(zkey); + redis_cmd_append_sstr_key_zval(&cmdstr, zkey, redis_sock, slot); + } ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +int +redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + char *key, *exp_type = NULL; + zval *z_opts = NULL, *z_ele; + zend_long expire = -1; + zend_bool persist = 0; zend_string *zkey; size_t key_len; @@ -2430,9 +2693,14 @@ redis_getex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, expire = zval_get_long(z_ele); persist = 0; } else if (ZSTR_STRICMP_STATIC(zkey, "PERSIST")) { - persist = zval_is_true(z_ele); + persist = zend_is_true(z_ele); exp_type = NULL; } + } else if (Z_TYPE_P(z_ele) == IS_STRING && + zend_string_equals_literal_ci(Z_STR_P(z_ele), "PERSIST")) + { + persist = zend_is_true(z_ele); + exp_type = NULL; } } ZEND_HASH_FOREACH_END(); } @@ -2554,6 +2822,53 @@ int redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, TYPE_DECR, redis_sock, cmd, cmd_len, slot, ctx); } +typedef enum xdelExMode { + REDIS_XDELEX_NONE, + REDIS_XDELEX_KEEPREF, + REDIS_XDELEX_DELREF, + REDIS_XDELEX_ACKED, +} xdelExMode; + +int redis_delex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + redisEqType type = REDIS_IF_NONE; + smart_string cmdstr = {0}; + zend_string *key, *ztype; + HashTable *ht = NULL; + zval *zv = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(key) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(ht) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (ht != NULL) { + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, ztype, zv) { + if (ztype == NULL) + continue; + + ZVAL_DEREF(zv); + + if (zstr_to_eq_type(&type, ztype)) + break; + + php_error_docref(NULL, E_WARNING, "Unknown option '%s'", ZSTR_VAL(ztype)); + } ZEND_HASH_FOREACH_END(); + } + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, type != REDIS_IF_NONE ? 3 : 1, "DELEX"); + + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_eq_clause(&cmdstr, redis_sock, type, zv); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* HINCRBY */ int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -2597,15 +2912,93 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +static inline zval *coerce_hash_field(zval *zv, zval *aux) { + char buf[32]; + zend_long lv; + size_t len; + + if (UNEXPECTED(Z_TYPE_P(zv) == IS_STRING && + is_numeric_string(Z_STRVAL_P(zv), + Z_STRLEN_P(zv), &lv, NULL, 0) == IS_LONG)) + { + len = snprintf(buf, sizeof(buf), ZEND_LONG_FMT, lv); + if (len == Z_STRLEN_P(zv) && redis_strncmp(Z_STRVAL_P(zv), buf, len) == 0) { + ZVAL_LONG(aux, lv); + return aux; + } + } + + return zv; +} + +/* A helper function to build HMGET, HGETEX, HGETDEL context arrays we curry + * to the reply side to return to the user fields and values */ +static HashTable * +build_hash_context_ht(HashTable *htsrc, zend_bool (*cb)(zval*)) { + zend_string *key, *tmp; + HashTable *ht; + zval *zv, aux; + + ALLOC_HASHTABLE(ht); + zend_hash_init(ht, zend_hash_num_elements(htsrc), + NULL, ZVAL_PTR_DTOR, 0); + + ZEND_HASH_FOREACH_VAL(htsrc, zv) + ZVAL_DEREF(zv); + if (cb && !cb(zv)) + continue; + + /* Legacy behavior: Integer strings become integer keys */ + zv = coerce_hash_field(zv, &aux); + + if (Z_TYPE_P(zv) == IS_LONG) { + zend_hash_index_add_empty_element(ht, Z_LVAL_P(zv)); + } else { + key = zval_get_tmp_string(zv, &tmp); + zend_hash_add_empty_element(ht, key); + zend_tmp_string_release(tmp); + } + ZEND_HASH_FOREACH_END(); + + /* Sanity check that we have at least one value */ + if (zend_hash_num_elements(ht) == 0) { + zend_hash_destroy(ht); + FREE_HASHTABLE(ht); + return NULL; + } + + return ht; +} + +/* Legacy HMGET behavior predicate */ +static zend_bool hmget_filter(zval *zv) { + return (Z_TYPE_P(zv) == IS_STRING && Z_STRLEN_P(zv) > 0) || + (Z_TYPE_P(zv) == IS_LONG); +} + +static void +redis_cmd_append_sstr_hash_fields(smart_string *cmdstr, HashTable *ht) { + zend_string *key; + zend_ulong idx; + + ZEND_HASH_FOREACH_KEY(ht, idx, key) { + if (key) { + redis_cmd_append_sstr_zstr(cmdstr, key); + } else { + redis_cmd_append_sstr_long(cmdstr, idx); + } + } ZEND_HASH_FOREACH_END(); + +} + /* HMGET */ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *field = NULL, *zctx = NULL; + HashTable *fields = NULL, *htctx = NULL; smart_string cmdstr = {0}; - HashTable *fields = NULL; zend_string *key = NULL; - zend_ulong valid = 0; + int argc; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_STR(key) @@ -2615,34 +3008,22 @@ int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (zend_hash_num_elements(fields) == 0) return FAILURE; - zctx = ecalloc(1 + zend_hash_num_elements(fields), sizeof(*zctx)); - - ZEND_HASH_FOREACH_VAL(fields, field) { - ZVAL_DEREF(field); - if (!((Z_TYPE_P(field) == IS_STRING && Z_STRLEN_P(field) > 0) || Z_TYPE_P(field) == IS_LONG)) - continue; - - ZVAL_COPY(&zctx[valid++], field); - } ZEND_HASH_FOREACH_END(); - - if (valid == 0) { - efree(zctx); + htctx = build_hash_context_ht(fields, hmget_filter); + if (htctx == NULL) { + php_error_docref(NULL, E_WARNING, "No valid fields provided"); return FAILURE; } - ZVAL_NULL(&zctx[valid]); - - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + valid, "HMGET"); + argc = 1 + zend_hash_num_elements(htctx); + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "HMGET"); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - for (zend_ulong i = 0; i < valid; i++) { - redis_cmd_append_sstr_zval(&cmdstr, &zctx[i], NULL); - } + redis_cmd_append_sstr_hash_fields(&cmdstr, htctx); // Push out command, length, and key context *cmd = cmdstr.c; *cmd_len = cmdstr.len; - *ctx = zctx; + *ctx = htctx; return SUCCESS; } @@ -2719,14 +3100,14 @@ static void redis_get_lcs_options(redisLcsOptions *dst, HashTable *ht) { if (key) { if (zend_string_equals_literal_ci(key, "LEN")) { dst->idx = 0; - dst->len = zval_is_true(zv); + dst->len = zend_is_true(zv); } else if (zend_string_equals_literal_ci(key, "IDX")) { dst->len = 0; - dst->idx = zval_is_true(zv); + dst->idx = zend_is_true(zv); } else if (zend_string_equals_literal_ci(key, "MINMATCHLEN")) { dst->minmatchlen = zval_get_long(zv); } else if (zend_string_equals_literal_ci(key, "WITHMATCHLEN")) { - dst->withmatchlen = zval_is_true(zv); + dst->withmatchlen = zend_is_true(zv); } else { php_error_docref(NULL, E_WARNING, "Unknown LCS option '%s'", ZSTR_VAL(key)); } @@ -3413,60 +3794,51 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, { int i, argc; smart_string cmdstr = {0}; - zend_string *zkey; - zval *z_args, *z_ele; - - if ((argc = ZEND_NUM_ARGS()) < 2) { - return FAILURE; - } + zend_string *key, *zkey; + zval *args, *z_ele; + zend_ulong idx; - z_args = ecalloc(argc, sizeof(*z_args)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, -1) + Z_PARAM_STR(key) + Z_PARAM_VARIADIC('*', args, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - if (argc == 2) { - if (Z_TYPE(z_args[1]) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL(z_args[1])) == 0) { - efree(z_args); + if (argc == 1) { + if (Z_TYPE_P(args) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_P(args)) == 0) { return FAILURE; } /* Initialize our command */ - redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL(z_args[1])), ZEND_STRL("HSET")); + redis_cmd_init_sstr(&cmdstr, 1 + zend_hash_num_elements(Z_ARRVAL_P(args)) * 2, ZEND_STRL("HSET")); /* Append key */ - zkey = zval_get_string(&z_args[0]); - redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, slot); - zend_string_release(zkey); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL(z_args[1]), zkey, z_ele) { - if (zkey != NULL) { - ZVAL_DEREF(z_ele); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); - redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(args), idx, zkey, z_ele) { + ZVAL_DEREF(z_ele); + if (zkey == NULL) { + redis_cmd_append_sstr_long(&cmdstr, idx); + } else { + redis_cmd_append_sstr_zstr(&cmdstr, zkey); } + redis_cmd_append_sstr_zval(&cmdstr, z_ele, redis_sock); } ZEND_HASH_FOREACH_END(); } else { - if (argc % 2 == 0) { - efree(z_args); + if (argc % 2 != 0) { return FAILURE; } + /* Initialize our command */ - redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HSET")); + redis_cmd_init_sstr(&cmdstr, argc + 1, ZEND_STRL("HSET")); /* Append key */ - zkey = zval_get_string(&z_args[0]); - redis_cmd_append_sstr_key(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey), redis_sock, slot); - zend_string_release(zkey); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - for (i = 1; i < argc; ++i) { + for (i = 0; i < argc; ++i) { if (i % 2) { - zkey = zval_get_string(&z_args[i]); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zkey), ZSTR_LEN(zkey)); - zend_string_release(zkey); + redis_cmd_append_sstr_zval(&cmdstr, &args[i], redis_sock); } else { - redis_cmd_append_sstr_zval(&cmdstr, &z_args[i], redis_sock); + redis_cmd_append_sstr_zval(&cmdstr, &args[i], NULL); } } } @@ -3475,9 +3847,6 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, *cmd = cmdstr.c; *cmd_len = cmdstr.len; - // Cleanup arg array - efree(z_args); - return SUCCESS; } @@ -3527,12 +3896,20 @@ redis_hrandfield_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (zend_string_equals_literal_ci(zkey, "count")) { count = zval_get_long(z_ele); } else if (zend_string_equals_literal_ci(zkey, "withvalues")) { - withvalues = zval_is_true(z_ele); + withvalues = zend_is_true(z_ele); + } + } else if (Z_TYPE_P(z_ele) == IS_STRING) { + if (zend_string_equals_literal_ci(Z_STR_P(z_ele), "WITHVALUES")) { + withvalues = 1; } } } ZEND_HASH_FOREACH_END(); } + /* If we're sending WITHVALUES we must also send a count */ + if (count == 0 && withvalues) + count = 1; + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + (count != 0) + withvalues, "HRANDFIELD"); redis_cmd_append_sstr_key(&cmdstr, key, key_len, redis_sock, slot); @@ -3571,8 +3948,9 @@ int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* SRANDMEMBER */ -int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +int redis_randmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { uint32_t argc = ZEND_NUM_ARGS(); smart_string cmdstr = {0}; @@ -3585,7 +3963,7 @@ int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_LONG(count) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, ZEND_NUM_ARGS(), "SRANDMEMBER"); + redis_cmd_init_sstr(&cmdstr, 1 + (argc == 2), kw, strlen(kw)); redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); if (argc == 2) redis_cmd_append_sstr_long(&cmdstr, count); @@ -3665,7 +4043,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (slot) { php_error_docref(NULL, E_WARNING, "SORT BY option is not allowed in Redis Cluster"); - zval_dtor(&z_argv); + zval_ptr_dtor_nogc(&z_argv); return FAILURE; } @@ -3695,7 +4073,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (cross_slot) { php_error_docref(0, E_WARNING, "Error, SORT key and STORE key have different slots!"); - zval_dtor(&z_argv); + zval_ptr_dtor_nogc(&z_argv); return FAILURE; } @@ -3716,7 +4094,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (slot) { php_error_docref(NULL, E_WARNING, "GET option for SORT disabled in Redis Cluster"); - zval_dtor(&z_argv); + zval_ptr_dtor_nogc(&z_argv); return FAILURE; } @@ -3745,7 +4123,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (added == 0) { php_error_docref(NULL, E_WARNING, "Array of GET values requested, but none are valid"); - zval_dtor(&z_argv); + zval_ptr_dtor_nogc(&z_argv); return FAILURE; } } @@ -3754,7 +4132,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, // ALPHA if (((z_ele = zend_hash_str_find(ht_opts, "alpha", sizeof("alpha") - 1)) != NULL || (z_ele = zend_hash_str_find(ht_opts, "ALPHA", sizeof("ALPHA") - 1)) != NULL) && - zval_is_true(z_ele) + zend_is_true(z_ele) ) { add_next_index_stringl(&z_argv, "ALPHA", sizeof("ALPHA") - 1); } @@ -3775,7 +4153,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ) { php_error_docref(NULL, E_WARNING, "LIMIT options on SORT command must be longs or strings"); - zval_dtor(&z_argv); + zval_ptr_dtor_nogc(&z_argv); return FAILURE; } @@ -3817,7 +4195,7 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, /* Clean up our arguments array. Note we don't have to free any prefixed * key as that we didn't duplicate the pointer if we prefixed */ - zval_dtor(&z_argv); + zval_ptr_dtor_nogc(&z_argv); // Push our length and command *cmd_len = cmdstr.len; @@ -3831,57 +4209,32 @@ int redis_sort_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - zval *z_args; smart_string cmdstr = {0}; - char *arg; - int arg_free, i; - size_t arg_len; - int argc = ZEND_NUM_ARGS(); - zend_string *zstr; - - // We need at least KEY and one member - if (argc < 2) { - return FAILURE; - } - - // Grab arguments as an array - z_args = emalloc(argc * sizeof(zval)); - if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { - efree(z_args); - return FAILURE; - } - - // Get first argument (the key) as a string - zstr = zval_get_string(&z_args[0]); - arg = ZSTR_VAL(zstr); - arg_len = ZSTR_LEN(zstr); + zend_string *key = NULL; + int i; + int argc = 0; + zval *args; - // Prefix - arg_free = redis_key_prefix(redis_sock, &arg, &arg_len); + ZEND_PARSE_PARAMETERS_START(2, -1) + Z_PARAM_STR(key) + Z_PARAM_VARIADIC('*', args, argc) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); // Start command construction - redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HDEL")); - redis_cmd_append_sstr(&cmdstr, arg, arg_len); + redis_cmd_init_sstr(&cmdstr, argc + 1, ZEND_STRL("HDEL")); - // Set our slot, free key if we prefixed it - CMD_SET_SLOT(slot,arg,arg_len); - zend_string_release(zstr); - if (arg_free) efree(arg); + // Append key + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); // Iterate through the members we're removing - for (i = 1; i < argc; i++) { - zstr = zval_get_string(&z_args[i]); - redis_cmd_append_sstr(&cmdstr, ZSTR_VAL(zstr), ZSTR_LEN(zstr)); - zend_string_release(zstr); + for (i = 0; i < argc; i++) { + redis_cmd_append_sstr_zval(&cmdstr, &args[i], NULL); } // Push out values *cmd = cmdstr.c; *cmd_len = cmdstr.len; - // Cleanup - efree(z_args); - // Success! return SUCCESS; } @@ -4120,7 +4473,7 @@ static int get_georadius_count_options(zval *optval, geoOptions *opts) { z_tmp = zend_hash_index_find(Z_ARRVAL_P(optval), 1); if (z_tmp) { - opts->any = zval_is_true(z_tmp); + opts->any = zend_is_true(z_tmp); } } else { if (Z_LVAL_P(optval) <= 0) @@ -4365,38 +4718,56 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { char *key, *unit; - int argc = 2; + int argc = 0; size_t keylen, unitlen; geoOptions gopts = {0}; smart_string cmdstr = {0}; zval *position, *shape, *opts = NULL, *z_ele; zend_string *zkey, *zstr; + zend_bool bypolygon = 0; + HashTable *ht; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a", - &key, &keylen, &position, &shape, - &unit, &unitlen, &opts) == FAILURE) + if (zend_parse_parameters(ZEND_NUM_ARGS(), "szzs|a", &key, &keylen, + &position, &shape, &unit, &unitlen, + &opts) == FAILURE) { return FAILURE; } - if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { - argc += 2; - } else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) { - argc += 3; - } else { - php_error_docref(NULL, E_WARNING, "Invalid position"); - return FAILURE; - } - if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { argc += 2; - } else if (Z_TYPE_P(shape) == IS_ARRAY) { + } else if (Z_TYPE_P(shape) == IS_ARRAY && + zend_hash_num_elements(Z_ARRVAL_P(shape)) == 2) + { + // BYBOX argc += 3; + } else if (Z_TYPE_P(shape) == IS_ARRAY && + zend_hash_num_elements(Z_ARRVAL_P(shape)) >= 6 && + zend_hash_num_elements(Z_ARRVAL_P(shape)) % 2 == 0) + { + // BYPOLYGON N verticies + argc += 2 + zend_hash_num_elements(Z_ARRVAL_P(shape)); + bypolygon = 1; } else { php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); return FAILURE; } + if (!bypolygon) { + if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { + argc += 2; + } else if (Z_TYPE_P(position) == IS_ARRAY && + zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) + { + argc += 3; + } else { + php_error_docref(NULL, E_WARNING, "Invalid position"); + return FAILURE; + } + } + + argc += bypolygon ? 1 : 2; + /* Attempt to parse our options array */ if (opts != NULL) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) { @@ -4429,20 +4800,29 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "GEOSEARCH"); redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); - if (Z_TYPE_P(position) == IS_ARRAY) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { - ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); - } ZEND_HASH_FOREACH_END(); - } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); + if (!bypolygon) { + if (Z_TYPE_P(position) == IS_ARRAY) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); + } ZEND_HASH_FOREACH_END(); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); + } } if (Z_TYPE_P(shape) == IS_ARRAY) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) { + ht = Z_ARRVAL_P(shape); + if (bypolygon) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYPOLYGON"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ht) / 2); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); + } + + ZEND_HASH_FOREACH_VAL(ht, z_ele) { ZVAL_DEREF(z_ele); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); } ZEND_HASH_FOREACH_END(); @@ -4450,7 +4830,10 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS"); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape)); } - redis_cmd_append_sstr(&cmdstr, unit, unitlen); + + if (!bypolygon) { + redis_cmd_append_sstr(&cmdstr, unit, unitlen); + } /* Append optional arguments */ if (gopts.withcoord) REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHCOORD"); @@ -4473,7 +4856,7 @@ redis_geosearch_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } } - if ((argc = gopts.withcoord + gopts.withdist + gopts.withhash) > 0) { + if (gopts.withcoord + gopts.withdist + gopts.withhash > 0) { *ctx = PHPREDIS_CTX_PTR; } @@ -4487,14 +4870,16 @@ int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) { - int argc = 3; char *dest, *src, *unit; size_t destlen, srclen, unitlen; geoOptions gopts = {0}; smart_string cmdstr = {0}; zval *position, *shape, *opts = NULL, *z_ele; + zend_bool bypolygon = 0; zend_string *zkey; + HashTable *ht; short s2 = 0; + int argc = 0; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sszzs|a", &dest, &destlen, &src, &srclen, &position, &shape, @@ -4503,22 +4888,36 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { + if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { argc += 2; - } else if (Z_TYPE_P(position) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) { + } else if (Z_TYPE_P(shape) == IS_ARRAY && + zend_hash_num_elements(Z_ARRVAL_P(shape)) == 2) + { argc += 3; + } else if (Z_TYPE_P(shape) == IS_ARRAY && + zend_hash_num_elements(Z_ARRVAL_P(shape)) >= 6 && + zend_hash_num_elements(Z_ARRVAL_P(shape)) % 2 == 0) + { + argc += 2 + zend_hash_num_elements(Z_ARRVAL_P(shape)); + bypolygon = 1; } else { - php_error_docref(NULL, E_WARNING, "Invalid position"); + php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); return FAILURE; } - if (Z_TYPE_P(shape) == IS_LONG || Z_TYPE_P(shape) == IS_DOUBLE) { - argc += 2; - } else if (Z_TYPE_P(shape) == IS_ARRAY) { - argc += 3; - } else { - php_error_docref(NULL, E_WARNING, "Invalid shape dimensions"); - return FAILURE; + argc += bypolygon ? 2 : 3; + + if (!bypolygon) { + if (Z_TYPE_P(position) == IS_STRING && Z_STRLEN_P(position) > 0) { + argc += 2; + } else if (Z_TYPE_P(position) == IS_ARRAY && + zend_hash_num_elements(Z_ARRVAL_P(position)) == 2) + { + argc += 3; + } else { + php_error_docref(NULL, E_WARNING, "Invalid position"); + return FAILURE; + } } /* Attempt to parse our options array */ @@ -4561,20 +4960,28 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - if (Z_TYPE_P(position) == IS_ARRAY) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { - ZVAL_DEREF(z_ele); - redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); - } ZEND_HASH_FOREACH_END(); - } else { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); - redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); + if (!bypolygon) { + if (Z_TYPE_P(position) == IS_ARRAY) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMLONLAT"); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(position), z_ele) { + ZVAL_DEREF(z_ele); + redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); + } ZEND_HASH_FOREACH_END(); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FROMMEMBER"); + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(position), Z_STRLEN_P(position)); + } } if (Z_TYPE_P(shape) == IS_ARRAY) { - REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(shape), z_ele) { + ht = Z_ARRVAL_P(shape); + if (bypolygon) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYPOLYGON"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ht) / 2); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYBOX"); + } + ZEND_HASH_FOREACH_VAL(ht, z_ele) { ZVAL_DEREF(z_ele); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(z_ele)); } ZEND_HASH_FOREACH_END(); @@ -4582,7 +4989,10 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BYRADIUS"); redis_cmd_append_sstr_dbl(&cmdstr, zval_get_double(shape)); } - redis_cmd_append_sstr(&cmdstr, unit, unitlen); + + if (!bypolygon) { + redis_cmd_append_sstr(&cmdstr, unit, unitlen); + } /* Append sort if it's not GEO_NONE */ if (gopts.sort == SORT_ASC) { @@ -4616,6 +5026,338 @@ redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Starting with Redis version 6.0.0: Added the AUTH2 option. */ +int redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_string *key, *field, *tmp; + HashTable *fields; + int argc; + zval *zv; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(fields) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(fields) < 1) { + php_error_docref(NULL, E_WARNING, "Must pass at least one field"); + return FAILURE; + } + + // 3 because FIELDS + argc = 3 + zend_hash_num_elements(fields); + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields)); + + ZEND_HASH_FOREACH_VAL(fields, zv) + field = zval_get_tmp_string(zv, &tmp); + redis_cmd_append_sstr_zstr(&cmdstr, field); + zend_tmp_string_release(tmp); + ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +typedef struct redisHGetExOptions { + zend_string *exp_type; + zend_long exp_arg; +} redisHGetExOptions; + +static int get_hgetex_expiry_opts(redisHGetExOptions *dst, zval *zv) { + zend_string *zkey; + HashTable *ht; + zval *zexp; + + *dst = (redisHGetExOptions) { + .exp_type = NULL, + .exp_arg = -1 + }; + + if (zv == NULL) + return SUCCESS; + + if (Z_TYPE_P(zv) != IS_STRING && Z_TYPE_P(zv) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, + "Expiry value must be a string or an array of strings"); + return FAILURE; + } + + if (Z_TYPE_P(zv) == IS_STRING) { + dst->exp_type = Z_STR_P(zv); + dst->exp_arg = -1; + return SUCCESS; + } + + ht = Z_ARRVAL_P(zv); + + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, zkey, zexp) + if (zkey == NULL) + continue; + + if (Z_TYPE_P(zexp) != IS_LONG || Z_LVAL_P(zexp) < 0) { + php_error_docref(NULL, E_WARNING, + "Expiry must be an integer >= 0"); + return FAILURE; + } + + dst->exp_type = zkey; + dst->exp_arg = Z_LVAL_P(zexp); + ZEND_HASH_FOREACH_END(); + + return SUCCESS; +} + +int redis_hgetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + redisHGetExOptions opts = {0}; + smart_string cmdstr = {0}; + HashTable *fields, *htctx; + zval *zexpiry = NULL; + zend_string *key; + int argc; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(fields) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_OR_NULL(zexpiry); + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(fields) == 0) { + php_error_docref(NULL, E_WARNING, "Must pass at least one field"); + return FAILURE; + } else if (get_hgetex_expiry_opts(&opts, zexpiry) == FAILURE) { + return FAILURE; + } + + htctx = build_hash_context_ht(fields, hmget_filter); + if (htctx == NULL) { + php_error_docref(NULL, E_WARNING, + "Failed to build context hash table"); + return FAILURE; + } + + argc = 3 + (opts.exp_type != NULL) + (opts.exp_arg >= 0) + + zend_hash_num_elements(fields); + + redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HGETEX")); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + if (opts.exp_type) { + redis_cmd_append_sstr_zstr(&cmdstr, opts.exp_type); + if (opts.exp_arg >= 0) + redis_cmd_append_sstr_long(&cmdstr, opts.exp_arg); + } + + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(htctx)); + redis_cmd_append_sstr_hash_fields(&cmdstr, htctx); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + *ctx = htctx; + + return SUCCESS; +} + +typedef struct redisHSetExOptions { + zend_string *set_mode; + zend_string *exp_type; + zend_long exp_arg; +} redisHSetExOptions; + +void get_hsetex_expiry_options(redisHSetExOptions *dst, HashTable *src) { + zend_string *key; + zend_long lval; + zval *zv; + + *dst = (redisHSetExOptions) { + .exp_arg = -1, + }; + + if (src == NULL) + return; + + ZEND_HASH_FOREACH_STR_KEY_VAL(src, key, zv) { + if (key == NULL) { + if (Z_TYPE_P(zv) == IS_STRING) { + key = Z_STR_P(zv); + if (zend_string_equals_literal_ci(key, "FNX") || + zend_string_equals_literal_ci(key, "FXX")) + { + dst->set_mode = Z_STR_P(zv); + } else if (zend_string_equals_literal_ci(key, "KEEPTTL")) { + dst->exp_type = key; + dst->exp_arg = -1; + } + } + } else if (zend_string_equals_literal_ci(key, "EX") || + zend_string_equals_literal_ci(key, "PX") || + zend_string_equals_literal_ci(key, "EXAT") || + zend_string_equals_literal_ci(key, "PXAT")) + { + lval = zval_get_long(zv); + if (lval >= 0) { + dst->exp_type = key; + dst->exp_arg = lval; + } else { + php_error_docref(NULL, E_WARNING, "Timeouts must be >= 0"); + } + } + } ZEND_HASH_FOREACH_END(); +} + +int redis_hsetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + redisHSetExOptions opts = {0}; + smart_string cmdstr = {0}; + HashTable *fields, *expiry = NULL; + zend_string *key; + zend_ulong idx; + zval *zv; + int argc; + + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(fields) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(expiry); + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(fields) == 0) { + php_error_docref(NULL, E_WARNING, "Must pass at least one field"); + return FAILURE; + } + + get_hsetex_expiry_options(&opts, expiry); + + argc = 3 + !!opts.set_mode + !!opts.exp_type + (opts.exp_arg >= 0) + + zend_hash_num_elements(fields) * 2; + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "HSETEX"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + if (opts.set_mode) + redis_cmd_append_sstr_zstr(&cmdstr, opts.set_mode); + if (opts.exp_type) { + redis_cmd_append_sstr_zstr(&cmdstr, opts.exp_type); + if (opts.exp_arg >= 0) + redis_cmd_append_sstr_long(&cmdstr, opts.exp_arg); + } + + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields)); + + ZEND_HASH_FOREACH_KEY_VAL(fields, idx, key, zv) + if (key) { + redis_cmd_append_sstr_zstr(&cmdstr, key); + } else { + redis_cmd_append_sstr_long(&cmdstr, idx); + } + redis_cmd_append_sstr_zval(&cmdstr, zv, redis_sock); + ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +int redis_hgetdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + HashTable *fields, *htctx; + smart_string cmdstr = {0}; + zend_string *key; + int argc; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(key); + Z_PARAM_ARRAY_HT(fields); + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(fields) == 0) { + php_error_docref(NULL, E_WARNING, "Must pass at least one field"); + return FAILURE; + } + + htctx = build_hash_context_ht(fields, hmget_filter); + if (htctx == NULL) { + php_error_docref(NULL, E_WARNING, + "Failed to build context hash table"); + return FAILURE; + } + + argc = 3 + zend_hash_num_elements(htctx); + + redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("HGETDEL")); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(htctx)); + redis_cmd_append_sstr_hash_fields(&cmdstr, htctx); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + *ctx = htctx; + + return SUCCESS; +} + +int redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + zend_string *key, *option = NULL, *tmp, *field; + smart_string cmdstr = {0}; + HashTable *fields; + zend_long ttl; + zval *zv; + int argc; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STR(key) + Z_PARAM_LONG(ttl) + Z_PARAM_ARRAY_HT(fields) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(option) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(fields) < 1) { + php_error_docref(NULL, E_WARNING, "Must pass at least one field"); + return FAILURE; + } + + // 4 because FIELDS + argc = 4 + zend_hash_num_elements(fields) + (option ? 1 : 0); + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_long(&cmdstr, ttl); + if (option) redis_cmd_append_sstr_zstr(&cmdstr, option); + + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FIELDS"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(fields)); + + ZEND_HASH_FOREACH_VAL(fields, zv) + field = zval_get_tmp_string(zv, &tmp); + redis_cmd_append_sstr_zstr(&cmdstr, field); + zend_tmp_string_release(tmp); + ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* MIGRATE */ int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -4897,13 +5639,13 @@ redis_build_client_tracking_command(smart_string *cmdstr, int argc, zval *z_args } prefix = z_ele; } else if (zend_string_equals_literal_ci(zkey, "bcast")) { - bcast = zval_is_true(z_ele); + bcast = zend_is_true(z_ele); } else if (zend_string_equals_literal_ci(zkey, "optin")) { - optin = zval_is_true(z_ele); + optin = zend_is_true(z_ele); } else if (zend_string_equals_literal_ci(zkey, "optout")) { - optout = zval_is_true(z_ele); + optout = zend_is_true(z_ele); } else if (zend_string_equals_literal_ci(zkey, "noloop")) { - noloop = zval_is_true(z_ele); + noloop = zend_is_true(z_ele); } } } ZEND_HASH_FOREACH_END(); @@ -4917,7 +5659,7 @@ redis_build_client_tracking_command(smart_string *cmdstr, int argc, zval *z_args ZVAL_STRICMP_STATIC(&z_args[0], "off") )) { redis_cmd_append_sstr(cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); - } else if (zval_is_true(&z_args[0])) { + } else if (zend_is_true(&z_args[0])) { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ON"); } else { REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "OFF"); @@ -4992,7 +5734,7 @@ redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZVAL_STRICMP_STATIC(&z_args[0], "no") )) { redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); - } else if (zval_is_true(&z_args[0])) { + } else if (zend_is_true(&z_args[0])) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "YES"); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NO"); @@ -5022,7 +5764,7 @@ redis_client_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, ZVAL_STRICMP_STATIC(&z_args[0], "off") )) { redis_cmd_append_sstr(&cmdstr, Z_STRVAL(z_args[0]), Z_STRLEN(z_args[0])); - } else if (zval_is_true(&z_args[0])) { + } else if (zend_is_true(&z_args[0])) { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ON"); } else { REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "OFF"); @@ -5196,13 +5938,13 @@ redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, if (zend_string_equals_literal_ci(zkey, "db")) { db = zval_get_long(zv); } else if (zend_string_equals_literal_ci(zkey, "replace")) { - replace = zval_is_true(zv); + replace = zend_is_true(zv); } } ZEND_HASH_FOREACH_END(); } if (slot && db != -1) { - php_error_docref(NULL, E_WARNING, "Cant copy to a specific DB in cluster mode"); + php_error_docref(NULL, E_WARNING, "Can't copy to a specific DB in cluster mode"); return FAILURE; } @@ -5227,42 +5969,156 @@ redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -/* XADD */ -int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx) +static xdelExMode zstr_to_xdelex_mode(zend_string *s) { + if (s == NULL) + return REDIS_XDELEX_NONE; + else if (zend_string_equals_literal_ci(s, "KEEPREF")) + return REDIS_XDELEX_KEEPREF; + else if (zend_string_equals_literal_ci(s, "DELREF")) + return REDIS_XDELEX_DELREF; + else if (zend_string_equals_literal_ci(s, "ACKED")) + return REDIS_XDELEX_ACKED; + + php_error_docref(NULL, E_WARNING, "Unknown mode '%s'", ZSTR_VAL(s)); + + return REDIS_XDELEX_NONE; +} + +void redis_cmd_append_delex_mode(smart_string *cmdstr, xdelExMode mode) { + if (mode == REDIS_XDELEX_KEEPREF) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "KEEPREF"); + } else if (mode == REDIS_XDELEX_DELREF) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "DELREF"); + } else if (mode == REDIS_XDELEX_ACKED) { + REDIS_CMD_APPEND_SSTR_STATIC(cmdstr, "ACKED"); + } +} + +int redis_xdelex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) { + xdelExMode mode = REDIS_XDELEX_NONE; + zend_string *key, *mstr = NULL; smart_string cmdstr = {0}; - zend_string *arrkey; - zval *z_fields, *value; - zend_long maxlen = 0; - zend_bool approx = 0, nomkstream = 0; - zend_ulong idx; - HashTable *ht_fields; - int fcount, argc; - char *key, *id; - size_t keylen, idlen; + HashTable *ids; + int argc = 3; + zval *id; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|lbb", &key, &keylen, - &id, &idlen, &z_fields, &maxlen, &approx, - &nomkstream) == FAILURE) - { - return FAILURE; - } + ZEND_PARSE_PARAMETERS_START(2, 3) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(ids) + Z_PARAM_OPTIONAL + Z_PARAM_STR(mstr) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); - /* At least one field and string are required */ - ht_fields = Z_ARRVAL_P(z_fields); - if ((fcount = zend_hash_num_elements(ht_fields)) == 0) { + if (zend_hash_num_elements(ids) == 0) { + php_error_docref(NULL, E_WARNING, "At least one ID must be specified"); return FAILURE; } - if (maxlen < 0 || (maxlen == 0 && approx != 0)) { - php_error_docref(NULL, E_WARNING, - "Warning: Invalid MAXLEN argument or approximate flag"); - } + mode = zstr_to_xdelex_mode(mstr); + argc += (mode != REDIS_XDELEX_NONE) + zend_hash_num_elements(ids); + redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("XDELEX")); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); - /* Calculate argc for XADD. It's a bit complex because we've got - * an optional MAXLEN argument which can either take the form MAXLEN N + redis_cmd_append_delex_mode(&cmdstr, mode); + + redis_cmd_append_sstr(&cmdstr, ZEND_STRL("IDS")); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ids)); + + ZEND_HASH_FOREACH_VAL(ids, id) { + redis_cmd_append_sstr_zval(&cmdstr, id, NULL); + } ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; +} + +// XACKDEL key group [KEEPREF | DELREF | ACKED] IDS numids id [id ...] +int redis_xackdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zend_string *key, *group, *mode = NULL; + xdelExMode dtype = REDIS_XDELEX_NONE; + smart_string cmdstr = {0}; + HashTable *ids; + int argc; + zval *id; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STR(key) + Z_PARAM_STR(group) + Z_PARAM_ARRAY_HT(ids) + Z_PARAM_OPTIONAL + Z_PARAM_STR(mode) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(ids) == 0) { + php_error_docref(NULL, E_WARNING, "At least one ID must be specified"); + return FAILURE; + } + + dtype = zstr_to_xdelex_mode(mode); + + argc = 4 + (dtype != REDIS_XDELEX_NONE) + zend_hash_num_elements(ids); + redis_cmd_init_sstr(&cmdstr, argc, ZEND_STRL("XACKDEL")); + + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_zstr(&cmdstr, group); + redis_cmd_append_delex_mode(&cmdstr, dtype); + + redis_cmd_append_sstr(&cmdstr, ZEND_STRL("IDS")); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(ids)); + + ZEND_HASH_FOREACH_VAL(ids, id) { + redis_cmd_append_sstr_zval(&cmdstr, id, NULL); + } ZEND_HASH_FOREACH_END(); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + + +/* XADD */ +int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_string *arrkey; + zval *z_fields, *value; + zend_long maxlen = 0; + zend_bool approx = 0, nomkstream = 0; + zend_ulong idx; + HashTable *ht_fields; + int fcount, argc; + char *key, *id; + size_t keylen, idlen; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssa|lbb", &key, &keylen, + &id, &idlen, &z_fields, &maxlen, &approx, + &nomkstream) == FAILURE) + { + return FAILURE; + } + + /* At least one field and string are required */ + ht_fields = Z_ARRVAL_P(z_fields); + if ((fcount = zend_hash_num_elements(ht_fields)) == 0) { + return FAILURE; + } + + if (maxlen < 0 || (maxlen == 0 && approx != 0)) { + php_error_docref(NULL, E_WARNING, + "Warning: Invalid MAXLEN argument or approximate flag"); + } + + + /* Calculate argc for XADD. It's a bit complex because we've got + * an optional MAXLEN argument which can either take the form MAXLEN N * or MAXLEN ~ N */ argc = 2 + nomkstream + (fcount * 2) + (maxlen > 0 ? (approx ? 3 : 2) : 0); @@ -5348,14 +6204,16 @@ int redis_xpending_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* X[REV]RANGE key start end [COUNT count] */ -int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, short *slot, - void **ctx) +static int +redis_xrange_generic_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, zend_bool have_count_literal, char **cmd, + int *cmd_len, short *slot, void **ctx) { smart_string cmdstr = {0}; char *key, *start, *end; size_t keylen, startlen, endlen; zend_long count = -1; + int argc; if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|l", &key, &keylen, &start, &startlen, &end, &endlen, &count) @@ -5364,13 +6222,16 @@ int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - redis_cmd_init_sstr(&cmdstr, 3 + (2 * (count > -1)), kw, strlen(kw)); + argc = 3 + ((have_count_literal ? 2 : 1) * (count > -1)); + redis_cmd_init_sstr(&cmdstr, argc, kw, strlen(kw)); + redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); redis_cmd_append_sstr(&cmdstr, start, startlen); redis_cmd_append_sstr(&cmdstr, end, endlen); if (count > -1) { - redis_cmd_append_sstr(&cmdstr, ZEND_STRL("COUNT")); + if (have_count_literal) + redis_cmd_append_sstr(&cmdstr, ZEND_STRL("COUNT")); redis_cmd_append_sstr_long(&cmdstr, count); } @@ -5379,6 +6240,22 @@ int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + return redis_xrange_generic_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, kw, 1, cmd, cmd_len, slot, ctx); +} + +int redis_vrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) +{ + return redis_xrange_generic_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, kw, 0, cmd, cmd_len, slot, ctx); +} + /* Helper function to take an associative array and append the Redis * STREAMS stream [stream...] id [id ...] arguments to a command string. */ static int @@ -5739,7 +6616,7 @@ redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return FAILURE; } - argc = 5 + (count > 0 ? 1 + count : 0) + justid; + argc = 5 + (count > 0 ? 2 : 0) + justid; REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "XAUTOCLAIM"); redis_cmd_append_sstr_key(&cmdstr, key, keylen, redis_sock, slot); @@ -6000,7 +6877,7 @@ int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, Z_PARAM_STR(key) Z_PARAM_LONG(timeout) Z_PARAM_OPTIONAL - Z_PARAM_STR(mode) + Z_PARAM_STR_OR_NULL(mode) ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); if (mode != NULL && !(zend_string_equals_literal_ci(mode, "NX") || @@ -6023,6 +6900,57 @@ int redis_expire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +static int +generic_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, size_t kw_len, int has_unit, char **cmd, + int *cmd_len, short *slot) +{ + zend_string *key, *mem, *unit = NULL; + smart_string cmdstr = {0}; + zend_long expiry; + + ZEND_PARSE_PARAMETERS_START(3, has_unit ? 4 : 3) + Z_PARAM_STR(key) + Z_PARAM_STR(mem) + Z_PARAM_LONG(expiry) + if (has_unit) { + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(unit) + } + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + redis_cmd_init_sstr(&cmdstr, 3 + (unit != NULL), kw, kw_len); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_zstr(&cmdstr, mem); + redis_cmd_append_sstr_long(&cmdstr, expiry); + + if (unit != NULL) { + redis_cmd_append_sstr_zstr(&cmdstr, unit); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + + +int redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return generic_expiremember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, ZEND_STRL("EXPIREMEMBER"), 1, + cmd, cmd_len, slot); +} + +int redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + return generic_expiremember_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, + redis_sock, ZEND_STRL("EXPIREMEMBERAT"), 0, + cmd, cmd_len, slot); +} + int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -6048,6 +6976,560 @@ redis_sentinel_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +typedef enum redisVAddQuant { + REDIS_QUANT_NONE, + REDIS_QUANT_NOQUANT, + REDIS_QUANT_Q8, + REDIS_QUANT_BIN, +} redisVAddQuant; + +typedef struct redisVAddOptions { + enum redisVAddQuant quant; + zend_long reduce; + zend_bool values; + zend_bool cas; + zend_long ef; + zend_string *attributes; + zend_long numlinks; +} redisVAddOptions; + +static zend_bool validate_vadd_integer(zend_string *name, zval *zv, zend_long min) { + if (Z_TYPE_P(zv) != IS_LONG || Z_LVAL_P(zv) < min) { + php_error_docref(NULL, E_WARNING, "%s must be >= " + ZEND_LONG_FMT, ZSTR_VAL(name), min); + return 0; + } + + return 1; +} + +static zend_string *zval_to_vattr(zval *zv) { + if (Z_TYPE_P(zv) == IS_STRING) { + return zval_get_string(zv); +#ifdef HAVE_REDIS_JSON + } else if (Z_TYPE_P(zv) == IS_ARRAY) { + smart_str aux = {0}; + php_json_encode(&aux, zv, PHP_JSON_OBJECT_AS_ARRAY); + return aux.s; + } else { + php_error_docref(NULL, E_WARNING, "SETATTR must be a string or array"); + } +#else + } else { + php_error_docref(NULL, E_WARNING, "SETATTR must be a string"); + } +#endif + + return NULL; +} + +static void parse_vadd_options(redisVAddOptions *dst, HashTable *ht) { + zend_string *key; + zval *zv; + + *dst = (redisVAddOptions){ + .ef = -1, + .numlinks = -1, + .reduce = -1, + }; + + if (ht == NULL) + return; + + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) { + if (key != NULL) { + if (zend_string_equals_literal_ci(key, "REDUCE")) { + if (validate_vadd_integer(key, zv, 1)) + dst->reduce = Z_LVAL_P(zv); + } else if (zend_string_equals_literal_ci(key, "M")) { + if (validate_vadd_integer(key, zv, 1)) + dst->numlinks = Z_LVAL_P(zv); + } else if (zend_string_equals_literal_ci(key, "EF")) { + if (validate_vadd_integer(key, zv, 1)) + dst->ef = Z_LVAL_P(zv); + } else if (zend_string_equals_literal_ci(key, "CAS")) { + dst->cas = zend_is_true(zv); + } else if (zend_string_equals_literal_ci(key, "VALUES")) { + dst->values = zend_is_true(zv); + } else if (zend_string_equals_literal_ci(key, "SETATTR")) { + dst->attributes = zval_to_vattr(zv); + } + } else if (Z_TYPE_P(zv) == IS_STRING) { + if (zend_string_equals_literal_ci(Z_STR_P(zv), "VALUES")) { + dst->values = 1; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "FP32")) { + dst->values = 0; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "NOQUANT")) { + dst->quant = REDIS_QUANT_NOQUANT; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "Q8")) { + dst->quant = REDIS_QUANT_Q8; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "BIN")) { + dst->quant = REDIS_QUANT_BIN; + } + } + } ZEND_HASH_FOREACH_END(); +} + +static void free_vadd_options(redisVAddOptions *o) { + if (o->attributes) + zend_string_release(o->attributes); +} + +static void +redis_cmd_append_sstr_fp32(smart_string *dst, HashTable *ht) { + uint32_t i = 0; + char *aux; + zval *zv; + + aux = ecalloc(1, zend_hash_num_elements(ht) * sizeof(float)); + + ZEND_HASH_FOREACH_VAL(ht, zv) { + redis_copy_fp32(&aux[4 * i++], zval_get_double(zv)); + } ZEND_HASH_FOREACH_END(); + + redis_cmd_append_sstr(dst, aux, 4 * i); + + efree(aux); +} + +static void redis_cmd_append_sstr_fp32_values(smart_string *dst, HashTable *ht) { + zval *zv; + + ZEND_HASH_FOREACH_VAL(ht, zv) { + redis_cmd_append_sstr_dbl(dst, zval_get_double(zv)); + } ZEND_HASH_FOREACH_END(); +} + +int +redis_vadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + HashTable *options = NULL, *vector; + smart_string cmdstr = {0}; + redisVAddOptions opts; + zend_string *key; + zval *element; + int argc; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STR(key) + Z_PARAM_ARRAY_HT(vector) + Z_PARAM_ZVAL(element) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(options) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + if (zend_hash_num_elements(vector) < 1) { + php_error_docref(NULL, E_WARNING, + "At least one vector element is required"); + return FAILURE; + } + + parse_vadd_options(&opts, options); + + // Two formulations: + // VADD FP32 [vector] + // VADD VALUES .. + // So and are always present and we need to calculate the + // other arguments dynamically. + argc = 2 + + (opts.reduce > -1 ? 2 : 0) + + (opts.values ? 2 + zend_hash_num_elements(vector) : 2) + + !!opts.cas + + (opts.attributes != NULL ? 2 : 0) + + (opts.numlinks > 0 ? 2 : 0) + + (opts.ef > 0 ? 2 : 0) + + !!(opts.quant != REDIS_QUANT_NONE); + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "VADD"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + + if (opts.reduce > -1) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "REDUCE"); + redis_cmd_append_sstr_long(&cmdstr, opts.reduce); + } + + if (opts.values) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "VALUES"); + redis_cmd_append_sstr_long(&cmdstr, zend_hash_num_elements(vector)); + redis_cmd_append_sstr_fp32_values(&cmdstr, vector); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FP32"); + redis_cmd_append_sstr_fp32(&cmdstr, vector); + } + + redis_cmd_append_sstr_zval(&cmdstr, element, redis_sock); + + if (opts.cas) + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "CAS"); + + if (opts.quant != REDIS_QUANT_NONE) { + if (opts.quant == REDIS_QUANT_Q8) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "Q8"); + } else if (opts.quant == REDIS_QUANT_BIN) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "BIN"); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NOQUANT"); + } + } + + if (opts.ef > -1) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "EF"); + redis_cmd_append_sstr_long(&cmdstr, opts.ef); + } + + if (opts.attributes != NULL) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "SETATTR"); + redis_cmd_append_sstr_zstr(&cmdstr, opts.attributes); + } + + if (opts.numlinks > -1) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "M"); + redis_cmd_append_sstr_long(&cmdstr, opts.numlinks); + } + + free_vadd_options(&opts); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +typedef enum redisVSimMemberType { + REDIS_VSIM_NONE, + REDIS_VSIM_FP32, + REDIS_VSIM_VALUES, + REDIS_VSIM_ELE, +} redisVSimMemberType; + +typedef struct redisVSimOptions { + redisVSimMemberType type; + zend_long count; + zend_long ef; + zend_string *filter; + zend_long filter_ef; + zend_bool truth; + zend_bool nothread; + zend_bool withscores; +} redisVSimOptions; + +static void free_vsim_options(redisVSimOptions *opts) { + if (opts->filter != NULL) + zend_string_release(opts->filter); +} + +static void parse_vsim_options(redisVSimOptions *dst, HashTable *ht) { + zend_string *key; + zval *zv; + + *dst = (redisVSimOptions){ + .filter_ef = -1, + .ef = -1, + }; + + if (ht == NULL) + return; + + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, zv) { + if (key != NULL) { + if (zend_string_equals_literal_ci(key, "EF")) { + if (validate_vadd_integer(key, zv, 1)) + dst->ef = Z_LVAL_P(zv); + } else if (zend_string_equals_literal_ci(key, "FILTER")) { + dst->filter = zval_get_string(zv); + } else if (zend_string_equals_literal_ci(key, "FILTER-EF")) { + if (validate_vadd_integer(key, zv, 1)) + dst->filter_ef = Z_LVAL_P(zv); + } else if (zend_string_equals_literal_ci(key, "COUNT")) { + if (validate_vadd_integer(key, zv, 1)) + dst->count = Z_LVAL_P(zv); + } else if (zend_string_equals_literal_ci(key, "WITHSCORES")) { + dst->withscores = zend_is_true(zv); + } + } else if (Z_TYPE_P(zv) == IS_STRING) { + if (zend_string_equals_literal_ci(Z_STR_P(zv), "FP32")) { + dst->type = REDIS_VSIM_FP32; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "VALUES")) { + dst->type = REDIS_VSIM_VALUES; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "ELE")) { + dst->type = REDIS_VSIM_ELE; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "NOTHREAD")) { + dst->nothread = 1; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "TRUTH")) { + dst->truth = 1; + } else if (zend_string_equals_literal_ci(Z_STR_P(zv), "WITHSCORES")) { + dst->withscores = 1; + } + } + } ZEND_HASH_FOREACH_END(); +} + +static redisVSimMemberType detect_vsim_type(zval *zv) { + zval *ele; + + if (Z_TYPE_P(zv) != IS_ARRAY) + return REDIS_VSIM_ELE; + + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), ele) { + if (Z_TYPE_P(ele) != IS_LONG && Z_TYPE_P(ele) != IS_DOUBLE) + return REDIS_VSIM_ELE; + } ZEND_HASH_FOREACH_END(); + + return REDIS_VSIM_FP32; +} + +int +redis_vsim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + HashTable *options = NULL; + smart_string cmdstr = {0}; + redisVSimOptions opts; + zend_string *key; + zval *member; + int argc; + + ZEND_PARSE_PARAMETERS_START(2, 3) { + Z_PARAM_STR(key) + Z_PARAM_ZVAL(member) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT_OR_NULL(options) + } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + parse_vsim_options(&opts, options); + + if (opts.type == REDIS_VSIM_NONE) + opts.type = detect_vsim_type(member); + + /* Sanity check on the member type */ + if (opts.type != REDIS_VSIM_ELE && Z_TYPE_P(member) != IS_ARRAY) { + php_error_docref(NULL, E_WARNING, + "member must be an array when not querying in ELE mode"); + return FAILURE; + } + + argc = 1 + (opts.count > 0 ? 2 : 0) + (opts.ef > 0 ? 2 : 0) + + (opts.filter != NULL ? 2 : 0) + (opts.filter_ef > 0 ? 2 : 0) + + !!opts.truth + !!opts.nothread + !!opts.withscores; + + if (opts.type == REDIS_VSIM_ELE || opts.type == REDIS_VSIM_FP32) { + // ELE or FP32 + argc += 2; + } else if (opts.type == REDIS_VSIM_VALUES) { + // VALUES + argc += 2 + zend_hash_num_elements(Z_ARRVAL_P(member)); + } else { + zend_error_noreturn(E_ERROR, + "Internal error. type shouldn't be REDIS_VSIM_NONE!"); + } + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, argc, "VSIM"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + + if (opts.type == REDIS_VSIM_FP32) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FP32"); + redis_cmd_append_sstr_fp32(&cmdstr, Z_ARRVAL_P(member)); + } else if (opts.type == REDIS_VSIM_VALUES) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "VALUES"); + redis_cmd_append_sstr_long(&cmdstr, + zend_hash_num_elements(Z_ARRVAL_P(member))); + redis_cmd_append_sstr_fp32_values(&cmdstr, Z_ARRVAL_P(member)); + } else { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ELE"); + redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); + } + + if (opts.withscores) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); + *ctx = PHPREDIS_CTX_PTR; + } + + if (opts.count > 0) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "COUNT"); + redis_cmd_append_sstr_long(&cmdstr, opts.count); + } + + if (opts.ef > 0) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "EF"); + redis_cmd_append_sstr_long(&cmdstr, opts.ef); + } + + if (opts.filter != NULL) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FILTER"); + redis_cmd_append_sstr_zstr(&cmdstr, opts.filter); + } + + if (opts.filter_ef > 0) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "FILTER-EF"); + redis_cmd_append_sstr_long(&cmdstr, opts.filter_ef); + } + + if (opts.truth) + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TRUTH"); + if (opts.nothread) + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "NOTHREAD"); + + free_vsim_options(&opts); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +int +redis_vemb_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_bool raw = 0; + zend_string *key; + zval *member; + + ZEND_PARSE_PARAMETERS_START(2, 3) { + Z_PARAM_STR(key) + Z_PARAM_ZVAL(member); + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(raw) + } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + !!raw, "VEMB"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); + + if (raw) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "RAW"); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +int +redis_vlinks_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_bool withscores = 0; + zend_string *key; + zval *member; + + ZEND_PARSE_PARAMETERS_START(2, 3) { + Z_PARAM_STR(key) + Z_PARAM_ZVAL(member) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(withscores) + } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + !!withscores, "VLINKS"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); + if (withscores) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "WITHSCORES"); + *ctx = PHPREDIS_CTX_PTR; + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +int redis_vgetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_bool raw = 0; + zend_string *key; + zval *member; + + ZEND_PARSE_PARAMETERS_START(2, 3) { + Z_PARAM_STR(key) + Z_PARAM_ZVAL(member) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(raw) + } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2, "VGETATTR"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); + + *ctx = raw ? NULL : PHPREDIS_CTX_PTR; + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +int +redis_vsetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + smart_string cmdstr = {0}; + zend_string *key, *attr; + zval *member, *zattr; + + ZEND_PARSE_PARAMETERS_START(3, 3) { + Z_PARAM_STR(key) + Z_PARAM_ZVAL(member) + Z_PARAM_ZVAL(zattr) + } ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + attr = zval_to_vattr(zattr); + if (zattr == NULL) + return FAILURE; + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 3, "VSETATTR"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_zval(&cmdstr, member, redis_sock); + redis_cmd_append_sstr_zstr(&cmdstr, attr); + + zend_string_release(attr); + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + +// GCRA key max-burst requests-per-period period [NUM_REQUESTS count] +int redis_gcra_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx) +{ + zend_long max_burst, req_per_period, period, tokens = 0; + smart_string cmdstr = {0}; + zend_string *key; + + ZEND_PARSE_PARAMETERS_START(4, 5) + Z_PARAM_STR(key) + Z_PARAM_LONG(max_burst) + Z_PARAM_LONG(req_per_period) + Z_PARAM_LONG(period) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(tokens) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 4 + (tokens > 0 ? 2 : 0), "GCRA"); + redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot); + redis_cmd_append_sstr_long(&cmdstr, max_burst); + redis_cmd_append_sstr_long(&cmdstr, req_per_period); + redis_cmd_append_sstr_long(&cmdstr, period); + + if (tokens > 0) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "TOKENS"); + redis_cmd_append_sstr_long(&cmdstr, tokens); + } + + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* * Redis commands that don't deal with the server at all. The RedisSock* * pointer is the only thing retrieved differently, so we just take that @@ -6073,6 +7555,8 @@ void redis_getoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_LONG(redis_sock->compression); case REDIS_OPT_COMPRESSION_LEVEL: RETURN_LONG(redis_sock->compression_level); + case REDIS_OPT_PACK_IGNORE_NUMBERS: + RETURN_BOOL(redis_sock->pack_ignore_numbers); case REDIS_OPT_PREFIX: if (redis_sock->prefix) { RETURN_STRINGL(ZSTR_VAL(redis_sock->prefix), ZSTR_LEN(redis_sock->prefix)); @@ -6161,6 +7645,9 @@ void redis_setoption_handler(INTERNAL_FUNCTION_PARAMETERS, RETURN_TRUE; } break; + case REDIS_OPT_PACK_IGNORE_NUMBERS: + redis_sock->pack_ignore_numbers = zend_is_true(val); + RETURN_TRUE; case REDIS_OPT_COMPRESSION_LEVEL: val_long = zval_get_long(val); redis_sock->compression_level = val_long; @@ -6384,6 +7871,70 @@ void redis_pack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { if (valfree) efree(val); } +#if PHP_VERSION_ID >= 80100 +static zend_string *redis_xxh3_digest(RedisSock *redis_sock, zval *zv) { + zend_string *algo, *hex; + const php_hash_ops *ops; + unsigned char *digest; + int valfree; + size_t len; + char *val; + void *ctx; + + algo = zend_string_init(ZEND_STRL("XXH3"), 0); + ops = php_hash_fetch_ops(algo); + if (ops == NULL) { + zend_string_release(algo); + return NULL; + } + + valfree = redis_pack(redis_sock, zv, &val, &len); + + ctx = emalloc(ops->context_size); + ops->hash_init(ctx, NULL); + ops->hash_update(ctx, (const unsigned char *)val, len); + + digest = emalloc(ops->digest_size); + ops->hash_final(digest, ctx); + + hex = zend_string_safe_alloc(ops->digest_size, 2, 0, 0); + php_hash_bin2hex(ZSTR_VAL(hex), digest, ops->digest_size); + ZSTR_VAL(hex)[ZSTR_LEN(hex)] = '\0'; + + efree(ctx); + efree(digest); + if (valfree) efree(val); + zend_string_release(algo); + + return hex; +} +#endif + +void redis_digest_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zend_class_entry *exception_ce) +{ + zend_string *digest; + zval *zv; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zv) + ZEND_PARSE_PARAMETERS_END_EX(return ); + + #if PHP_VERSION_ID >= 80100 + digest = redis_xxh3_digest(redis_sock, zv); + + if (digest == NULL) { + zend_throw_exception(exception_ce, "XXH3 hashing not available?", 0); + RETURN_THROWS(); + } + + RETURN_STR(digest); + #else + zend_throw_exception(exception_ce, "Method requires PHP >= 8.1", 0); + RETURN_FALSE; + #endif +} + void redis_unpack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { zend_string *str; @@ -6391,8 +7942,6 @@ void redis_unpack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock) { RETURN_FALSE; } - if (redis_unpack(redis_sock, ZSTR_VAL(str), ZSTR_LEN(str), return_value) == 0) { - RETURN_STR_COPY(str); - } + redis_unpack(redis_sock, ZSTR_VAL(str), ZSTR_LEN(str), return_value); } /* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */ diff --git a/redis_commands.h b/redis_commands.h index 3165ba0eab..fee9f431ba 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -31,6 +31,23 @@ int redis_build_raw_cmd(zval *z_args, int argc, char **cmd, int *cmd_len); /* Construct a script command */ smart_string *redis_build_script_cmd(smart_string *cmd, int argc, zval *z_args); +typedef int (*redis_cmd_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +typedef int (*redis_kw_cmd_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx); + +/* Process a command but with a specific command building function + * and keyword which is passed to us*/ +void redis_process_kw_cmd(INTERNAL_FUNCTION_PARAMETERS, const char *kw, + redis_kw_cmd_cb cmd_cb, FailableResultCallback resp_cb, + void *ctx); + +#define REDIS_PROCESS_KW_CMD(kw, cmdfunc, resp_func) \ + redis_process_kw_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, kw, \ + cmdfunc, resp_func, NULL) + /* Redis command generics. Many commands share common prototypes meaning that * we can write one function to handle all of them. For example, there are * many COMMAND key value commands, or COMMAND key commands. */ @@ -174,6 +191,9 @@ int redis_flush_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_xrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_vrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_georadius_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -191,6 +211,9 @@ int redis_geosearchstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock * specific processing we do (e.g. verifying subarguments) that make them * unique */ +int redis_waitaof_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_info_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -215,6 +238,9 @@ int redis_incr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_decr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_delex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_hincrby_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -224,6 +250,15 @@ int redis_hincrbyfloat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hmget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_hgetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_hsetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_hgetdel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_mget_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -278,9 +313,6 @@ int redis_hset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_hsetnx_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); -int redis_srandmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char **cmd, int *cmd_len, short *slot, void **ctx); - int redis_select_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -306,7 +338,7 @@ int redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, - long it, char *pat, int pat_len, long count); + uint64_t it, char *pat, int pat_len, long count); int redis_geoadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -317,9 +349,33 @@ int redis_geodist_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_migrate_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_vadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_vsim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_vemb_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_vgetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_vsetattr_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_gcra_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_vlinks_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_xadd_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_xdelex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_xautoclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -347,6 +403,21 @@ int redis_xreadgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_xtrim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_expiremember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_expirememberat_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_randmember_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_hexpire_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); + +int redis_httl_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_lmove_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -362,6 +433,9 @@ int redis_vararg_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, int redis_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_msetex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_sentinel_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); @@ -393,6 +467,8 @@ void redis_uncompress_handler(INTERNAL_FUNCTION_PARAMETERS, void redis_pack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); void redis_unpack_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock); +void redis_digest_handler(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + zend_class_entry *exception_ce); #endif diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 0837a1f01f..e74e2d6c4a 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8cf0ecc2f5a43c6ede68d537a76faa23cb912d96 */ + * Stub hash: 3ee3118802fef67d6bd7176f2a72f0568d962c8b */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -24,6 +24,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis__pack arginfo_class_Redis__compress +#define arginfo_class_Redis__digest arginfo_class_Redis__compress + #define arginfo_class_Redis__unpack arginfo_class_Redis__compress ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_acl, 0, 0, 1) @@ -44,6 +46,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_bgrewriteaof arginfo_class_Redis___destruct +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_waitaof, 0, 0, 3) + ZEND_ARG_INFO(0, numlocal) + ZEND_ARG_INFO(0, numreplicas) + ZEND_ARG_INFO(0, timeout) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_bitcount, 0, 0, 1) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) @@ -157,6 +165,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_del, 0, 0, 1) ZEND_ARG_VARIADIC_INFO(0, other_keys) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_delex, 0, 0, 1) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_delifeq arginfo_class_Redis_append + #define arginfo_class_Redis_delete arginfo_class_Redis_del #define arginfo_class_Redis_discard arginfo_class_Redis___destruct @@ -295,6 +310,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_get arginfo_class_Redis__prefix +#define arginfo_class_Redis_getWithMeta arginfo_class_Redis__prefix + #define arginfo_class_Redis_getAuth arginfo_class_Redis___destruct ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2) @@ -302,10 +319,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getBit, 0, 0, 2) ZEND_ARG_INFO(0, idx) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getEx, 0, 0, 1) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() +#define arginfo_class_Redis_getEx arginfo_class_Redis_delex #define arginfo_class_Redis_getDBNum arginfo_class_Redis___destruct @@ -325,6 +339,10 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_getPort arginfo_class_Redis___destruct +#define arginfo_class_Redis_serverName arginfo_class_Redis___destruct + +#define arginfo_class_Redis_serverVersion arginfo_class_Redis___destruct + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_getRange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) @@ -365,6 +383,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_hGetAll arginfo_class_Redis__prefix +#define arginfo_class_Redis_hGetWithMeta arginfo_class_Redis_hGet + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hIncrBy, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, field) @@ -382,17 +402,26 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMget, 0, 0, 2) ZEND_ARG_INFO(0, fields) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hgetex, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, expiry) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_hsetex arginfo_class_Redis_hgetex + +#define arginfo_class_Redis_hgetdel arginfo_class_Redis_hMget + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hMset, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, fieldvals) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_hRandField arginfo_class_Redis_getEx +#define arginfo_class_Redis_hRandField arginfo_class_Redis_delex -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hSet, 0, 0, 3) +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hSet, 0, 0, 1) ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, member) - ZEND_ARG_INFO(0, value) + ZEND_ARG_VARIADIC_INFO(0, fields_and_vals) ZEND_END_ARG_INFO() #define arginfo_class_Redis_hSetNx arginfo_class_Redis_hIncrBy @@ -401,6 +430,39 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_hVals arginfo_class_Redis__prefix +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hexpire, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, ttl) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_hpexpire arginfo_class_Redis_hexpire + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hexpireat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, time) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hpexpireat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, mstime) + ZEND_ARG_INFO(0, fields) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_httl arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpttl arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hexpiretime arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpexpiretime arginfo_class_Redis_hMget + +#define arginfo_class_Redis_hpersist arginfo_class_Redis_hMget + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hscan, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(1, iterator) @@ -408,6 +470,19 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_hscan, 0, 0, 2) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expiremember, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, field) + ZEND_ARG_INFO(0, ttl) + ZEND_ARG_INFO(0, unit) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_expirememberat, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, field) + ZEND_ARG_INFO(0, timestamp) +ZEND_END_ARG_INFO() + #define arginfo_class_Redis_incr arginfo_class_Redis_decr #define arginfo_class_Redis_incrBy arginfo_class_Redis_append @@ -514,6 +589,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_mset, 0, 0, 1) ZEND_ARG_INFO(0, key_values) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_msetex, 0, 0, 1) + ZEND_ARG_INFO(0, key_vals) + ZEND_ARG_INFO(0, expiry) +ZEND_END_ARG_INFO() + #define arginfo_class_Redis_msetnx arginfo_class_Redis_mset ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_multi, 0, 0, 0) @@ -720,9 +800,9 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_slowlog, 0, 0, 1) ZEND_ARG_INFO(0, length) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_sort arginfo_class_Redis_getEx +#define arginfo_class_Redis_sort arginfo_class_Redis_delex -#define arginfo_class_Redis_sort_ro arginfo_class_Redis_getEx +#define arginfo_class_Redis_sort_ro arginfo_class_Redis_delex ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_sortAsc, 0, 0, 1) ZEND_ARG_INFO(0, key) @@ -819,6 +899,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xdel, 0, 0, 2) ZEND_ARG_INFO(0, ids) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xdelex, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, ids) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xgroup, 0, 0, 1) ZEND_ARG_INFO(0, operation) ZEND_ARG_INFO(0, key) @@ -874,6 +960,70 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xrevrange, 0, 0, 3) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_vadd, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, values) + ZEND_ARG_INFO(0, element) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_vsim, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_vcard arginfo_class_Redis__prefix + +#define arginfo_class_Redis_vdim arginfo_class_Redis__prefix + +#define arginfo_class_Redis_vinfo arginfo_class_Redis__prefix + +#define arginfo_class_Redis_vismember arginfo_class_Redis_hGet + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_vemb, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, raw) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_vrandmember arginfo_class_Redis_lPop + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_vrange, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, min) + ZEND_ARG_INFO(0, max) + ZEND_ARG_INFO(0, count) +ZEND_END_ARG_INFO() + +#define arginfo_class_Redis_vrem arginfo_class_Redis_hGet + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_vsetattr, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, attributes) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_vgetattr, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, decode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_vlinks, 0, 0, 2) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, member) + ZEND_ARG_INFO(0, withscores) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_gcra, 0, 0, 4) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, maxBurst) + ZEND_ARG_INFO(0, requestsPerPeriod) + ZEND_ARG_INFO(0, period) + ZEND_ARG_INFO(0, tokens) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xtrim, 0, 0, 2) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, threshold) @@ -935,7 +1085,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zrangestore, 0, 0, 4) ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() -#define arginfo_class_Redis_zRandMember arginfo_class_Redis_getEx +#define arginfo_class_Redis_zRandMember arginfo_class_Redis_delex #define arginfo_class_Redis_zRank arginfo_class_Redis_hGet @@ -1004,6 +1154,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_zunionstore arginfo_class_Redis_zinterstore +#define arginfo_class_Redis_digest arginfo_class_Redis__prefix ZEND_METHOD(Redis, __construct); ZEND_METHOD(Redis, __destruct); @@ -1013,12 +1164,14 @@ ZEND_METHOD(Redis, _prefix); ZEND_METHOD(Redis, _serialize); ZEND_METHOD(Redis, _unserialize); ZEND_METHOD(Redis, _pack); +ZEND_METHOD(Redis, _digest); ZEND_METHOD(Redis, _unpack); ZEND_METHOD(Redis, acl); ZEND_METHOD(Redis, append); ZEND_METHOD(Redis, auth); ZEND_METHOD(Redis, bgSave); ZEND_METHOD(Redis, bgrewriteaof); +ZEND_METHOD(Redis, waitaof); ZEND_METHOD(Redis, bitcount); ZEND_METHOD(Redis, bitop); ZEND_METHOD(Redis, bitpos); @@ -1043,6 +1196,8 @@ ZEND_METHOD(Redis, debug); ZEND_METHOD(Redis, decr); ZEND_METHOD(Redis, decrBy); ZEND_METHOD(Redis, del); +ZEND_METHOD(Redis, delex); +ZEND_METHOD(Redis, delifeq); ZEND_METHOD(Redis, discard); ZEND_METHOD(Redis, dump); ZEND_METHOD(Redis, echo); @@ -1073,6 +1228,7 @@ ZEND_METHOD(Redis, georadiusbymember_ro); ZEND_METHOD(Redis, geosearch); ZEND_METHOD(Redis, geosearchstore); ZEND_METHOD(Redis, get); +ZEND_METHOD(Redis, getWithMeta); ZEND_METHOD(Redis, getAuth); ZEND_METHOD(Redis, getBit); ZEND_METHOD(Redis, getEx); @@ -1084,6 +1240,8 @@ ZEND_METHOD(Redis, getMode); ZEND_METHOD(Redis, getOption); ZEND_METHOD(Redis, getPersistentID); ZEND_METHOD(Redis, getPort); +ZEND_METHOD(Redis, serverName); +ZEND_METHOD(Redis, serverVersion); ZEND_METHOD(Redis, getRange); ZEND_METHOD(Redis, lcs); ZEND_METHOD(Redis, getReadTimeout); @@ -1095,18 +1253,33 @@ ZEND_METHOD(Redis, hDel); ZEND_METHOD(Redis, hExists); ZEND_METHOD(Redis, hGet); ZEND_METHOD(Redis, hGetAll); +ZEND_METHOD(Redis, hGetWithMeta); ZEND_METHOD(Redis, hIncrBy); ZEND_METHOD(Redis, hIncrByFloat); ZEND_METHOD(Redis, hKeys); ZEND_METHOD(Redis, hLen); ZEND_METHOD(Redis, hMget); +ZEND_METHOD(Redis, hgetex); +ZEND_METHOD(Redis, hsetex); +ZEND_METHOD(Redis, hgetdel); ZEND_METHOD(Redis, hMset); ZEND_METHOD(Redis, hRandField); ZEND_METHOD(Redis, hSet); ZEND_METHOD(Redis, hSetNx); ZEND_METHOD(Redis, hStrLen); ZEND_METHOD(Redis, hVals); +ZEND_METHOD(Redis, hexpire); +ZEND_METHOD(Redis, hpexpire); +ZEND_METHOD(Redis, hexpireat); +ZEND_METHOD(Redis, hpexpireat); +ZEND_METHOD(Redis, httl); +ZEND_METHOD(Redis, hpttl); +ZEND_METHOD(Redis, hexpiretime); +ZEND_METHOD(Redis, hpexpiretime); +ZEND_METHOD(Redis, hpersist); ZEND_METHOD(Redis, hscan); +ZEND_METHOD(Redis, expiremember); +ZEND_METHOD(Redis, expirememberat); ZEND_METHOD(Redis, incr); ZEND_METHOD(Redis, incrBy); ZEND_METHOD(Redis, incrByFloat); @@ -1133,6 +1306,7 @@ ZEND_METHOD(Redis, mget); ZEND_METHOD(Redis, migrate); ZEND_METHOD(Redis, move); ZEND_METHOD(Redis, mset); +ZEND_METHOD(Redis, msetex); ZEND_METHOD(Redis, msetnx); ZEND_METHOD(Redis, multi); ZEND_METHOD(Redis, object); @@ -1216,6 +1390,7 @@ ZEND_METHOD(Redis, xadd); ZEND_METHOD(Redis, xautoclaim); ZEND_METHOD(Redis, xclaim); ZEND_METHOD(Redis, xdel); +ZEND_METHOD(Redis, xdelex); ZEND_METHOD(Redis, xgroup); ZEND_METHOD(Redis, xinfo); ZEND_METHOD(Redis, xlen); @@ -1224,6 +1399,20 @@ ZEND_METHOD(Redis, xrange); ZEND_METHOD(Redis, xread); ZEND_METHOD(Redis, xreadgroup); ZEND_METHOD(Redis, xrevrange); +ZEND_METHOD(Redis, vadd); +ZEND_METHOD(Redis, vsim); +ZEND_METHOD(Redis, vcard); +ZEND_METHOD(Redis, vdim); +ZEND_METHOD(Redis, vinfo); +ZEND_METHOD(Redis, vismember); +ZEND_METHOD(Redis, vemb); +ZEND_METHOD(Redis, vrandmember); +ZEND_METHOD(Redis, vrange); +ZEND_METHOD(Redis, vrem); +ZEND_METHOD(Redis, vsetattr); +ZEND_METHOD(Redis, vgetattr); +ZEND_METHOD(Redis, vlinks); +ZEND_METHOD(Redis, gcra); ZEND_METHOD(Redis, xtrim); ZEND_METHOD(Redis, zAdd); ZEND_METHOD(Redis, zCard); @@ -1256,7 +1445,7 @@ ZEND_METHOD(Redis, zinterstore); ZEND_METHOD(Redis, zscan); ZEND_METHOD(Redis, zunion); ZEND_METHOD(Redis, zunionstore); - +ZEND_METHOD(Redis, digest); static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, __construct, arginfo_class_Redis___construct, ZEND_ACC_PUBLIC) @@ -1267,12 +1456,14 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, _serialize, arginfo_class_Redis__serialize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _unserialize, arginfo_class_Redis__unserialize, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _pack, arginfo_class_Redis__pack, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, _digest, arginfo_class_Redis__digest, ZEND_ACC_PUBLIC) ZEND_ME(Redis, _unpack, arginfo_class_Redis__unpack, ZEND_ACC_PUBLIC) ZEND_ME(Redis, acl, arginfo_class_Redis_acl, ZEND_ACC_PUBLIC) ZEND_ME(Redis, append, arginfo_class_Redis_append, ZEND_ACC_PUBLIC) ZEND_ME(Redis, auth, arginfo_class_Redis_auth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgSave, arginfo_class_Redis_bgSave, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bgrewriteaof, arginfo_class_Redis_bgrewriteaof, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, waitaof, arginfo_class_Redis_waitaof, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitcount, arginfo_class_Redis_bitcount, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitop, arginfo_class_Redis_bitop, ZEND_ACC_PUBLIC) ZEND_ME(Redis, bitpos, arginfo_class_Redis_bitpos, ZEND_ACC_PUBLIC) @@ -1297,7 +1488,13 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, decr, arginfo_class_Redis_decr, ZEND_ACC_PUBLIC) ZEND_ME(Redis, decrBy, arginfo_class_Redis_decrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, del, arginfo_class_Redis_del, ZEND_ACC_PUBLIC) - ZEND_MALIAS(Redis, delete, del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) + ZEND_ME(Redis, delex, arginfo_class_Redis_delex, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, delifeq, arginfo_class_Redis_delifeq, ZEND_ACC_PUBLIC) +#if (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("delete", zim_Redis_del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, NULL) +#else + ZEND_RAW_FENTRY("delete", zim_Redis_del, arginfo_class_Redis_delete, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#endif ZEND_ME(Redis, discard, arginfo_class_Redis_discard, ZEND_ACC_PUBLIC) ZEND_ME(Redis, dump, arginfo_class_Redis_dump, ZEND_ACC_PUBLIC) ZEND_ME(Redis, echo, arginfo_class_Redis_echo, ZEND_ACC_PUBLIC) @@ -1328,6 +1525,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, geosearch, arginfo_class_Redis_geosearch, ZEND_ACC_PUBLIC) ZEND_ME(Redis, geosearchstore, arginfo_class_Redis_geosearchstore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, get, arginfo_class_Redis_get, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, getWithMeta, arginfo_class_Redis_getWithMeta, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getAuth, arginfo_class_Redis_getAuth, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getBit, arginfo_class_Redis_getBit, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getEx, arginfo_class_Redis_getEx, ZEND_ACC_PUBLIC) @@ -1339,6 +1537,8 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, getOption, arginfo_class_Redis_getOption, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getPersistentID, arginfo_class_Redis_getPersistentID, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getPort, arginfo_class_Redis_getPort, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, serverName, arginfo_class_Redis_serverName, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, serverVersion, arginfo_class_Redis_serverVersion, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getRange, arginfo_class_Redis_getRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, lcs, arginfo_class_Redis_lcs, ZEND_ACC_PUBLIC) ZEND_ME(Redis, getReadTimeout, arginfo_class_Redis_getReadTimeout, ZEND_ACC_PUBLIC) @@ -1350,18 +1550,33 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, hExists, arginfo_class_Redis_hExists, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGet, arginfo_class_Redis_hGet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hGetAll, arginfo_class_Redis_hGetAll, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hGetWithMeta, arginfo_class_Redis_hGetWithMeta, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hIncrBy, arginfo_class_Redis_hIncrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hIncrByFloat, arginfo_class_Redis_hIncrByFloat, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hKeys, arginfo_class_Redis_hKeys, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hLen, arginfo_class_Redis_hLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hMget, arginfo_class_Redis_hMget, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hgetex, arginfo_class_Redis_hgetex, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hsetex, arginfo_class_Redis_hsetex, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hgetdel, arginfo_class_Redis_hgetdel, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hMset, arginfo_class_Redis_hMset, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hRandField, arginfo_class_Redis_hRandField, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hSet, arginfo_class_Redis_hSet, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hSetNx, arginfo_class_Redis_hSetNx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hStrLen, arginfo_class_Redis_hStrLen, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hVals, arginfo_class_Redis_hVals, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpire, arginfo_class_Redis_hexpire, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpire, arginfo_class_Redis_hpexpire, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpireat, arginfo_class_Redis_hexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpireat, arginfo_class_Redis_hpexpireat, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, httl, arginfo_class_Redis_httl, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpttl, arginfo_class_Redis_hpttl, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hexpiretime, arginfo_class_Redis_hexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpexpiretime, arginfo_class_Redis_hpexpiretime, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, hpersist, arginfo_class_Redis_hpersist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, hscan, arginfo_class_Redis_hscan, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, expiremember, arginfo_class_Redis_expiremember, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, expirememberat, arginfo_class_Redis_expirememberat, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incr, arginfo_class_Redis_incr, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incrBy, arginfo_class_Redis_incrBy, ZEND_ACC_PUBLIC) ZEND_ME(Redis, incrByFloat, arginfo_class_Redis_incrByFloat, ZEND_ACC_PUBLIC) @@ -1388,10 +1603,15 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, migrate, arginfo_class_Redis_migrate, ZEND_ACC_PUBLIC) ZEND_ME(Redis, move, arginfo_class_Redis_move, ZEND_ACC_PUBLIC) ZEND_ME(Redis, mset, arginfo_class_Redis_mset, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, msetex, arginfo_class_Redis_msetex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, msetnx, arginfo_class_Redis_msetnx, ZEND_ACC_PUBLIC) ZEND_ME(Redis, multi, arginfo_class_Redis_multi, ZEND_ACC_PUBLIC) ZEND_ME(Redis, object, arginfo_class_Redis_object, ZEND_ACC_PUBLIC) - ZEND_MALIAS(Redis, open, connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#if (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("open", zim_Redis_connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, NULL) +#else + ZEND_RAW_FENTRY("open", zim_Redis_connect, arginfo_class_Redis_open, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#endif ZEND_ME(Redis, pconnect, arginfo_class_Redis_pconnect, ZEND_ACC_PUBLIC) ZEND_ME(Redis, persist, arginfo_class_Redis_persist, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pexpire, arginfo_class_Redis_pexpire, ZEND_ACC_PUBLIC) @@ -1401,7 +1621,11 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, pfmerge, arginfo_class_Redis_pfmerge, ZEND_ACC_PUBLIC) ZEND_ME(Redis, ping, arginfo_class_Redis_ping, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pipeline, arginfo_class_Redis_pipeline, ZEND_ACC_PUBLIC) - ZEND_MALIAS(Redis, popen, pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#if (PHP_VERSION_ID >= 80400) + ZEND_RAW_FENTRY("popen", zim_Redis_pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED, NULL, NULL) +#else + ZEND_RAW_FENTRY("popen", zim_Redis_pconnect, arginfo_class_Redis_popen, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) +#endif ZEND_ME(Redis, psetex, arginfo_class_Redis_psetex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, psubscribe, arginfo_class_Redis_psubscribe, ZEND_ACC_PUBLIC) ZEND_ME(Redis, pttl, arginfo_class_Redis_pttl, ZEND_ACC_PUBLIC) @@ -1473,6 +1697,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, xautoclaim, arginfo_class_Redis_xautoclaim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xclaim, arginfo_class_Redis_xclaim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xdel, arginfo_class_Redis_xdel, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, xdelex, arginfo_class_Redis_xdelex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xgroup, arginfo_class_Redis_xgroup, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xinfo, arginfo_class_Redis_xinfo, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xlen, arginfo_class_Redis_xlen, ZEND_ACC_PUBLIC) @@ -1481,6 +1706,20 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, xread, arginfo_class_Redis_xread, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xreadgroup, arginfo_class_Redis_xreadgroup, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xrevrange, arginfo_class_Redis_xrevrange, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vadd, arginfo_class_Redis_vadd, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vsim, arginfo_class_Redis_vsim, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vcard, arginfo_class_Redis_vcard, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vdim, arginfo_class_Redis_vdim, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vinfo, arginfo_class_Redis_vinfo, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vismember, arginfo_class_Redis_vismember, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vemb, arginfo_class_Redis_vemb, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vrandmember, arginfo_class_Redis_vrandmember, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vrange, arginfo_class_Redis_vrange, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vrem, arginfo_class_Redis_vrem, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vsetattr, arginfo_class_Redis_vsetattr, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vgetattr, arginfo_class_Redis_vgetattr, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, vlinks, arginfo_class_Redis_vlinks, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, gcra, arginfo_class_Redis_gcra, ZEND_ACC_PUBLIC) ZEND_ME(Redis, xtrim, arginfo_class_Redis_xtrim, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zAdd, arginfo_class_Redis_zAdd, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zCard, arginfo_class_Redis_zCard, ZEND_ACC_PUBLIC) @@ -1513,11 +1752,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, zscan, arginfo_class_Redis_zscan, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zunion, arginfo_class_Redis_zunion, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zunionstore, arginfo_class_Redis_zunionstore, ZEND_ACC_PUBLIC) - ZEND_FE_END -}; - - -static const zend_function_entry class_RedisException_methods[] = { + ZEND_ME(Redis, digest, arginfo_class_Redis_digest, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -1526,7 +1761,11 @@ static zend_class_entry *register_class_Redis(void) zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "Redis", class_Redis_methods); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif zval const_REDIS_NOT_FOUND_value; ZVAL_LONG(&const_REDIS_NOT_FOUND_value, REDIS_NOT_FOUND); @@ -1570,6 +1809,12 @@ static zend_class_entry *register_class_Redis(void) zend_declare_class_constant_ex(class_entry, const_REDIS_STREAM_name, &const_REDIS_STREAM_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_REDIS_STREAM_name); + zval const_REDIS_VECTORSET_value; + ZVAL_LONG(&const_REDIS_VECTORSET_value, REDIS_VECTORSET); + zend_string *const_REDIS_VECTORSET_name = zend_string_init_interned("REDIS_VECTORSET", sizeof("REDIS_VECTORSET") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_REDIS_VECTORSET_name, &const_REDIS_VECTORSET_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_REDIS_VECTORSET_name); + zval const_ATOMIC_value; ZVAL_LONG(&const_ATOMIC_value, ATOMIC); zend_string *const_ATOMIC_name = zend_string_init_interned("ATOMIC", sizeof("ATOMIC") - 1, 1); @@ -1636,6 +1881,12 @@ static zend_class_entry *register_class_Redis(void) zend_declare_class_constant_ex(class_entry, const_OPT_NULL_MULTIBULK_AS_NULL_name, &const_OPT_NULL_MULTIBULK_AS_NULL_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_OPT_NULL_MULTIBULK_AS_NULL_name); + zval const_OPT_PACK_IGNORE_NUMBERS_value; + ZVAL_LONG(&const_OPT_PACK_IGNORE_NUMBERS_value, REDIS_OPT_PACK_IGNORE_NUMBERS); + zend_string *const_OPT_PACK_IGNORE_NUMBERS_name = zend_string_init_interned("OPT_PACK_IGNORE_NUMBERS", sizeof("OPT_PACK_IGNORE_NUMBERS") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_OPT_PACK_IGNORE_NUMBERS_name, &const_OPT_PACK_IGNORE_NUMBERS_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_OPT_PACK_IGNORE_NUMBERS_name); + zval const_SERIALIZER_NONE_value; ZVAL_LONG(&const_SERIALIZER_NONE_value, REDIS_SERIALIZER_NONE); zend_string *const_SERIALIZER_NONE_name = zend_string_init_interned("SERIALIZER_NONE", sizeof("SERIALIZER_NONE") - 1, 1); @@ -1707,13 +1958,21 @@ static zend_class_entry *register_class_Redis(void) zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_DEFAULT_name, &const_COMPRESSION_ZSTD_DEFAULT_value, ZEND_ACC_PUBLIC, NULL); zend_string_release(const_COMPRESSION_ZSTD_DEFAULT_name); #endif -#if defined(HAVE_REDIS_ZSTD) && defined(ZSTD_CLEVEL_MAX) +#if defined(HAVE_REDIS_ZSTD) && ZSTD_VERSION_NUMBER >= 10400 - zval const_COMPRESSION_ZSTD_MAX_value; - ZVAL_LONG(&const_COMPRESSION_ZSTD_MAX_value, ZSTD_CLEVEL_MAX); - zend_string *const_COMPRESSION_ZSTD_MAX_name = zend_string_init_interned("COMPRESSION_ZSTD_MAX", sizeof("COMPRESSION_ZSTD_MAX") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MAX_name, &const_COMPRESSION_ZSTD_MAX_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release(const_COMPRESSION_ZSTD_MAX_name); + zval const_COMPRESSION_ZSTD_MIN_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MIN_value, ZSTD_minCLevel()); + zend_string *const_COMPRESSION_ZSTD_MIN_name = zend_string_init_interned("COMPRESSION_ZSTD_MIN", sizeof("COMPRESSION_ZSTD_MIN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MIN_name, &const_COMPRESSION_ZSTD_MIN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MIN_name); +#endif +#if defined(HAVE_REDIS_ZSTD) && !(ZSTD_VERSION_NUMBER >= 10400) + + zval const_COMPRESSION_ZSTD_MIN_value; + ZVAL_LONG(&const_COMPRESSION_ZSTD_MIN_value, 1); + zend_string *const_COMPRESSION_ZSTD_MIN_name = zend_string_init_interned("COMPRESSION_ZSTD_MIN", sizeof("COMPRESSION_ZSTD_MIN") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_COMPRESSION_ZSTD_MIN_name, &const_COMPRESSION_ZSTD_MIN_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_COMPRESSION_ZSTD_MIN_name); #endif #if defined(HAVE_REDIS_ZSTD) @@ -1863,8 +2122,12 @@ static zend_class_entry *register_class_RedisException(zend_class_entry *class_e { zend_class_entry ce, *class_entry; - INIT_CLASS_ENTRY(ce, "RedisException", class_RedisException_methods); + INIT_CLASS_ENTRY(ce, "RedisException", NULL); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, class_entry_RuntimeException, 0); +#else class_entry = zend_register_internal_class_ex(&ce, class_entry_RuntimeException); +#endif return class_entry; } diff --git a/redis_sentinel.stub.php b/redis_sentinel.stub.php index 486ac438ac..32551e1dd5 100644 --- a/redis_sentinel.stub.php +++ b/redis_sentinel.stub.php @@ -8,7 +8,7 @@ class RedisSentinel { - public function __construct(array $options = null); + public function __construct(?array $options = null); /** @return bool|RedisSentinel */ public function ckquorum(string $master); diff --git a/redis_sentinel_arginfo.h b/redis_sentinel_arginfo.h index e917b7d5ba..c813d2397b 100644 --- a/redis_sentinel_arginfo.h +++ b/redis_sentinel_arginfo.h @@ -1,8 +1,8 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f1f746cc848b1debcdf88eae015732720ba206c8 */ + * Stub hash: ca40579af888c5bb0661cd0201d840297474479a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel_ckquorum, 0, 0, 1) @@ -33,7 +33,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisSentinel_slaves arginfo_class_RedisSentinel_ckquorum - ZEND_METHOD(RedisSentinel, __construct); ZEND_METHOD(RedisSentinel, ckquorum); ZEND_METHOD(RedisSentinel, failover); @@ -47,7 +46,6 @@ ZEND_METHOD(RedisSentinel, reset); ZEND_METHOD(RedisSentinel, sentinels); ZEND_METHOD(RedisSentinel, slaves); - static const zend_function_entry class_RedisSentinel_methods[] = { ZEND_ME(RedisSentinel, __construct, arginfo_class_RedisSentinel___construct, ZEND_ACC_PUBLIC) ZEND_ME(RedisSentinel, ckquorum, arginfo_class_RedisSentinel_ckquorum, ZEND_ACC_PUBLIC) @@ -69,7 +67,11 @@ static zend_class_entry *register_class_RedisSentinel(void) zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisSentinel", class_RedisSentinel_methods); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif return class_entry; } diff --git a/redis_sentinel_legacy_arginfo.h b/redis_sentinel_legacy_arginfo.h index 5f7a70d26d..f6a641ef91 100644 --- a/redis_sentinel_legacy_arginfo.h +++ b/redis_sentinel_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f1f746cc848b1debcdf88eae015732720ba206c8 */ + * Stub hash: ca40579af888c5bb0661cd0201d840297474479a */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisSentinel___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -32,7 +32,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_RedisSentinel_slaves arginfo_class_RedisSentinel_ckquorum - ZEND_METHOD(RedisSentinel, __construct); ZEND_METHOD(RedisSentinel, ckquorum); ZEND_METHOD(RedisSentinel, failover); @@ -46,7 +45,6 @@ ZEND_METHOD(RedisSentinel, reset); ZEND_METHOD(RedisSentinel, sentinels); ZEND_METHOD(RedisSentinel, slaves); - static const zend_function_entry class_RedisSentinel_methods[] = { ZEND_ME(RedisSentinel, __construct, arginfo_class_RedisSentinel___construct, ZEND_ACC_PUBLIC) ZEND_ME(RedisSentinel, ckquorum, arginfo_class_RedisSentinel_ckquorum, ZEND_ACC_PUBLIC) @@ -68,7 +66,11 @@ static zend_class_entry *register_class_RedisSentinel(void) zend_class_entry ce, *class_entry; INIT_CLASS_ENTRY(ce, "RedisSentinel", class_RedisSentinel_methods); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif return class_entry; } diff --git a/redis_session.c b/redis_session.c index 2bdace7b8d..e9cbc7f202 100644 --- a/redis_session.c +++ b/redis_session.c @@ -43,13 +43,37 @@ #define CLUSTER_SESSION_PREFIX "PHPREDIS_CLUSTER_SESSION:" /* Session lock LUA as well as its SHA1 hash */ -#define LOCK_RELEASE_LUA_STR "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end" -#define LOCK_RELEASE_LUA_LEN (sizeof(LOCK_RELEASE_LUA_STR) - 1) -#define LOCK_RELEASE_SHA_STR "b70c2384248f88e6b75b9f89241a180f856ad852" -#define LOCK_RELEASE_SHA_LEN (sizeof(LOCK_RELEASE_SHA_STR) - 1) +#define LOCK_DEL_LUA_STR "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end" +#define LOCK_DEL_SHA_STR "b70c2384248f88e6b75b9f89241a180f856ad852" + +typedef struct evalCmd { + char *kw; + char *str; + size_t len; +} evalCmd; + +static evalCmd lua_cmd[2] = { + {"EVALSHA", ZEND_STRL(LOCK_DEL_SHA_STR)}, + {"EVAL", ZEND_STRL(LOCK_DEL_LUA_STR)} +}; + +typedef enum lockDelCmd { + LOCK_DEL_EVAL, + LOCK_DEL_DELEX, + LOCK_DEL_DELIFEQ, +} lockDelCmd; + +typedef enum releaseResult { + DEL_SUCCESS, + DEL_FAILURE, + DEL_NO_CMD, +} delResult; + + +static inline zend_bool is_redis_ok(const char *str, size_t len) { + return len == 3 && !memcmp(str, "+OK", 3); +} -/* Check if a response is the Redis +OK status response */ -#define IS_REDIS_OK(r, len) (r != NULL && len == 3 && !memcmp(r, "+OK", 3)) #define NEGATIVE_LOCK_RESPONSE 1 #define CLUSTER_DEFAULT_PREFIX() \ @@ -79,22 +103,18 @@ typedef struct redis_pool_member_ { } redis_pool_member; typedef struct { - int totalWeight; int count; redis_pool_member *head; redis_session_lock_status lock_status; - + int buster; } redis_pool; -// static char *session_conf_string(HashTable *ht, const char *key, size_t keylen) { -// } - -PHP_REDIS_API void +static void redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight) { - redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member)); + redis_pool_member *rpm = ecalloc(1, sizeof(*rpm)); rpm->redis_sock = redis_sock; rpm->weight = weight; @@ -108,10 +128,14 @@ PHP_REDIS_API void redis_pool_free(redis_pool *pool) { redis_pool_member *rpm, *next; + + if (pool == NULL) + return; + rpm = pool->head; while (rpm) { next = rpm->next; - redis_sock_disconnect(rpm->redis_sock, 0); + redis_sock_disconnect(rpm->redis_sock, 0, 1); redis_free_socket(rpm->redis_sock); efree(rpm); rpm = next; @@ -126,7 +150,7 @@ redis_pool_free(redis_pool *pool) { efree(pool); } -/* Retreive session.gc_maxlifetime from php.ini protecting against an integer overflow */ +/* Retrieve session.gc_maxlifetime from php.ini protecting against an integer overflow */ static int session_gc_maxlifetime(void) { zend_long value = INI_INT("session.gc_maxlifetime"); if (value > INT_MAX) { @@ -140,9 +164,83 @@ static int session_gc_maxlifetime(void) { return value; } +/* Retrieve redis.session.compression from php.ini */ +static int session_compression_type(void) { + const char *compression = INI_STR("redis.session.compression"); + + if(compression == NULL || *compression == '\0' || + redis_strncasecmp(compression, ZEND_STRL("none")) == 0) + { + return REDIS_COMPRESSION_NONE; + } + +#ifdef HAVE_REDIS_LZF + if(redis_strncasecmp(compression, ZEND_STRL("lzf")) == 0) { + return REDIS_COMPRESSION_LZF; + } +#endif +#ifdef HAVE_REDIS_ZSTD + if(redis_strncasecmp(compression, ZEND_STRL("zstd")) == 0) { + return REDIS_COMPRESSION_ZSTD; + } +#endif +#ifdef HAVE_REDIS_LZ4 + if(redis_strncasecmp(compression, ZEND_STRL("lz4")) == 0) { + return REDIS_COMPRESSION_LZ4; + } +#endif + + if (strcasecmp(compression, "lzf") || + strcasecmp(compression, "zstd") || + strcasecmp(compression, "lz4")) + { + php_error_docref(NULL, E_NOTICE, + "redis.session.compression: '%s' compression is not available. " + "Rebuild phpredis with %s support.", compression, compression); + } else { + php_error_docref(NULL, E_NOTICE, + "redis.session.compression: '%s' is invalid", compression); + } + + return REDIS_COMPRESSION_NONE; +} + +/* Helper to compress session data */ +static int +session_compress_data(RedisSock *redis_sock, char *data, size_t len, + char **compressed_data, size_t *compressed_len) +{ + if (redis_sock->compression) { + if(redis_compress(redis_sock, compressed_data, compressed_len, data, len)) { + return 1; + } + } + + *compressed_data = data; + *compressed_len = len; + + return 0; +} + +/* Helper to uncompress session data */ +static int +session_uncompress_data(RedisSock *redis_sock, char *data, size_t len, + char **decompressed_data, size_t *decompressed_len) { + if (redis_sock->compression) { + if(redis_uncompress(redis_sock, decompressed_data, decompressed_len, data, len)) { + return 1; + } + } + + *decompressed_data = data; + *decompressed_len = len; + + return 0; +} + /* Send a command to Redis. Returns byte count written to socket (-1 on failure) */ static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, - char **reply, int *replylen) + char **reply, int *replylen) { *reply = NULL; int len_written = redis_sock_write(redis_sock, cmd, cmdlen); @@ -154,14 +252,38 @@ static int redis_simple_cmd(RedisSock *redis_sock, char *cmd, int cmdlen, return len_written; } +static int +redis_simple_cmd_sstr(RedisSock *redis_sock, smart_string *cmd, + char **reply, int *replylen) +{ + return redis_simple_cmd(redis_sock, cmd->c, cmd->len, reply, replylen); +} + +static inline int weighted_seed(zend_string *key) { + /* In GCC the fast-path is one 32-bit load with a union */ + union { int pos; } u; + + if (EXPECTED(ZSTR_LEN(key) >= sizeof(u.pos))) { + memcpy(&u.pos, ZSTR_VAL(key), sizeof(u.pos)); + return u.pos; + } + + u.pos = 0; + + memcpy(&u.pos, ZSTR_VAL(key), ZSTR_LEN(key)); + + return u.pos; +} + PHP_REDIS_API redis_pool_member * -redis_pool_get_sock(redis_pool *pool, const char *key) { +redis_pool_get_sock(redis_pool *pool, zend_string *key) { + redis_pool_member *rpm; + int pos, i; - unsigned int pos, i; - memcpy(&pos, key, sizeof(pos)); - pos %= pool->totalWeight; + /* In the next release, ensure pool->totalWeight > 0 */ + pos = weighted_seed(key) % (pool->totalWeight ? pool->totalWeight : 1); - redis_pool_member *rpm = pool->head; + rpm = pool->head; for(i = 0; i < pool->totalWeight;) { if (pos >= i && pos < i + rpm->weight) { @@ -185,7 +307,7 @@ static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len sent_len = redis_simple_cmd(redis_sock, cmd, cmd_len, &reply, &reply_len); if (reply) { - if (IS_REDIS_OK(reply, reply_len)) { + if (is_redis_ok(reply, reply_len)) { efree(reply); return SUCCESS; } @@ -197,20 +319,41 @@ static int set_session_lock_key(RedisSock *redis_sock, char *cmd, int cmd_len return sent_len >= 0 ? NEGATIVE_LOCK_RESPONSE : FAILURE; } -static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status - ) -{ - char *cmd, hostname[HOST_NAME_MAX] = {0}, suffix[] = "_LOCK"; - int cmd_len, lock_wait_time, retries, i, set_lock_key_result, expiry; +static void generate_lock_key(redis_session_lock_status *status) { + static const char suffix[] = "_LOCK"; + + if (status->lock_key) + zend_string_release(status->lock_key); + + status->lock_key = zend_string_concat2(ZSTR_VAL(status->session_key), + ZSTR_LEN(status->session_key), + ZEND_STRL(suffix)); +} + +static void generate_lock_secret(redis_session_lock_status *status) { + char hostname[HOST_NAME_MAX] = {0}; + + gethostname(hostname, HOST_NAME_MAX); + if (status->lock_secret) + zend_string_release(status->lock_secret); + + status->lock_secret = strpprintf(0, "%s|%ld", hostname, (long)getpid()); +} + +static int +lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_status) { + zend_long wait_time, expiry, retries, attempt = 0; + int cmd_len, result; + char *cmd; /* Short circuit if we are already locked or not using session locks */ if (lock_status->is_locked || !INI_INT("redis.session.locking_enabled")) return SUCCESS; /* How long to wait between attempts to acquire lock */ - lock_wait_time = INI_INT("redis.session.lock_wait_time"); - if (lock_wait_time == 0) { - lock_wait_time = 20000; + wait_time = INI_INT("redis.session.lock_wait_time"); + if (wait_time == 0) { + wait_time = 20000; } /* Maximum number of times to retry (-1 means infinite) */ @@ -225,16 +368,8 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s expiry = INI_INT("max_execution_time"); } - /* Generate our qualified lock key */ - if (lock_status->lock_key) zend_string_release(lock_status->lock_key); - lock_status->lock_key = zend_string_alloc(ZSTR_LEN(lock_status->session_key) + sizeof(suffix) - 1, 0); - memcpy(ZSTR_VAL(lock_status->lock_key), ZSTR_VAL(lock_status->session_key), ZSTR_LEN(lock_status->session_key)); - memcpy(ZSTR_VAL(lock_status->lock_key) + ZSTR_LEN(lock_status->session_key), suffix, sizeof(suffix) - 1); - - /* Calculate lock secret */ - gethostname(hostname, HOST_NAME_MAX); - if (lock_status->lock_secret) zend_string_release(lock_status->lock_secret); - lock_status->lock_secret = strpprintf(0, "%s|%ld", hostname, (long)getpid()); + generate_lock_key(lock_status); + generate_lock_secret(lock_status); if (expiry > 0) { cmd_len = REDIS_SPPRINTF(&cmd, "SET", "SSssd", lock_status->lock_key, @@ -246,22 +381,24 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s } /* Attempt to get our lock */ - for (i = 0; retries == -1 || i <= retries; i++) { - set_lock_key_result = set_session_lock_key(redis_sock, cmd, cmd_len); + for (;;) { + result = set_session_lock_key(redis_sock, cmd, cmd_len); - if (set_lock_key_result == SUCCESS) { + if (result == SUCCESS) { lock_status->is_locked = 1; break; - } else if (set_lock_key_result == FAILURE) { - /* In case of network problems, break the loop and report to userland */ - lock_status->is_locked = 0; + } else if (result == FAILURE) { + /* Network failure */ break; } - /* Sleep unless we're done making attempts */ - if (retries == -1 || i < retries) { - usleep(lock_wait_time); + /* Lock is busy */ + + if (retries >= 0 && attempt++ >= retries) { + break; } + + usleep(wait_time); } /* Cleanup SET command */ @@ -271,7 +408,7 @@ static int lock_acquire(RedisSock *redis_sock, redis_session_lock_status *lock_s return lock_status->is_locked ? SUCCESS : FAILURE; } -#define IS_LOCK_SECRET(reply, len, secret) (len == ZSTR_LEN(secret) && !strncmp(reply, ZSTR_VAL(secret), len)) +#define IS_LOCK_SECRET(reply, len, secret) (len == ZSTR_LEN(secret) && !redis_strncmp(reply, ZSTR_VAL(secret), len)) static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_status) { if (!INI_INT("redis.session.locking_enabled")) { @@ -309,32 +446,96 @@ static int write_allowed(RedisSock *redis_sock, redis_session_lock_status *lock_ return lock_status->is_locked; } -/* Release any session lock we hold and cleanup allocated lock data. This function - * first attempts to use EVALSHA and then falls back to EVAL if EVALSHA fails. This - * will cause Redis to cache the script, so subsequent calls should then succeed - * using EVALSHA. */ -static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_status) +static delResult +get_del_result(RedisSock *redis_sock, const char *reply, int len) { - char *cmd, *reply; - int i, cmdlen, replylen; + zend_bool nocmd = 0; + + #define NOCMD_PFX "ERR unknown command" + + if (reply == NULL) { + if (redis_sock->err) { + php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(redis_sock->err)); + nocmd = zend_string_starts_with_cstr(redis_sock->err, + ZEND_STRL(NOCMD_PFX)); + redis_sock_clear_err(redis_sock); + } + return nocmd ? DEL_NO_CMD : DEL_FAILURE; + } else if (len == 4 && !redis_strncmp(reply, ZEND_STRL(":1"))) { + return DEL_SUCCESS; + } else { + return DEL_FAILURE; + } + + #undef NOCMD_PFX +} + +static delResult +lock_release_delex(RedisSock *redis_sock, redis_session_lock_status *status) { + smart_string cmd = {0}; + delResult result; + char *reply; + int len; + + REDIS_CMD_INIT_SSTR_STATIC(&cmd, 3, "DELEX"); + redis_cmd_append_sstr_zstr(&cmd, status->lock_key); + REDIS_CMD_APPEND_SSTR_STATIC(&cmd, "IFEQ"); + redis_cmd_append_sstr_zstr(&cmd, status->lock_secret); + + redis_simple_cmd_sstr(redis_sock, &cmd, &reply, &len); + + result = get_del_result(redis_sock, reply, len); + + if (reply) efree(reply); + smart_string_free(&cmd); + + return result; +} + +static delResult +lock_release_delifeq(RedisSock *redis_sock, redis_session_lock_status *status) { + smart_string cmd = {0}; + delResult result; + char *reply; + int len; + + REDIS_CMD_INIT_SSTR_STATIC(&cmd, 2, "DELIFEQ"); - /* Keywords, command, and length fallbacks */ - const char *kwd[] = {"EVALSHA", "EVAL"}; - const char *lua[] = {LOCK_RELEASE_SHA_STR, LOCK_RELEASE_LUA_STR}; - int len[] = {LOCK_RELEASE_SHA_LEN, LOCK_RELEASE_LUA_LEN}; + redis_cmd_append_sstr_zstr(&cmd, status->lock_key); + redis_cmd_append_sstr_zstr(&cmd, status->lock_secret); + + redis_simple_cmd_sstr(redis_sock, &cmd, &reply, &len); + + result = get_del_result(redis_sock, reply, len); + + if (reply) efree(reply); + smart_string_free(&cmd); + + return result; +} + +/* Release any session lock we hold and cleanup allocated lock data. This + * function first attempts to use EVALSHA and then falls back to EVAL if + * EVALSHA fails. This will cause Redis to cache the script, so subsequent + * calls should then succeed using EVALSHA. */ +static void +lock_release_lua(RedisSock *redis_sock, redis_session_lock_status *status) { + int i, cmdlen, replylen; + char *cmd, *reply; /* We first want to try EVALSHA and then fall back to EVAL */ - for (i = 0; lock_status->is_locked && i < sizeof(kwd)/sizeof(*kwd); i++) { - /* Construct our command */ - cmdlen = REDIS_SPPRINTF(&cmd, (char*)kwd[i], "sdSS", lua[i], len[i], 1, - lock_status->lock_key, lock_status->lock_secret); + for (i = 0; status->is_locked && i < sizeof(lua_cmd)/sizeof(*lua_cmd); i++) + { + cmdlen = REDIS_SPPRINTF(&cmd, lua_cmd[i].kw, "sdSS", lua_cmd[i].str, + lua_cmd[i].len, 1, status->lock_key, + status->lock_secret); /* Send it off */ redis_simple_cmd(redis_sock, cmd, cmdlen, &reply, &replylen); /* Release lock and cleanup reply if we got one */ if (reply != NULL) { - lock_status->is_locked = 0; + status->is_locked = 0; efree(reply); } @@ -343,35 +544,75 @@ static void lock_release(RedisSock *redis_sock, redis_session_lock_status *lock_ } /* Something has failed if we are still locked */ - if (lock_status->is_locked) { + if (status->is_locked) { php_error_docref(NULL, E_WARNING, "Failed to release session lock"); } } -#if PHP_VERSION_ID < 70300 -#define REDIS_URL_STR(umem) umem -#else -#define REDIS_URL_STR(umem) ZSTR_VAL(umem) -#endif +static lockDelCmd lock_release_cmd(void) { + char *cmd; -/* {{{ PS_OPEN_FUNC - */ + cmd = INI_STR("redis.session.lock_release_cmd"); + + if (cmd == NULL) { + return LOCK_DEL_EVAL; + } else if (redis_strncasecmp(cmd, ZEND_STRL("DELEX")) == 0) { + return LOCK_DEL_DELEX; + } else if (redis_strncasecmp(cmd, ZEND_STRL("DELIFEQ")) == 0) { + return LOCK_DEL_DELIFEQ; + } + + return LOCK_DEL_EVAL; +} + +static void +lock_release(RedisSock *redis_sock, redis_session_lock_status *status) { + delResult res = DEL_NO_CMD; + + if (status->lock_key == NULL) + return; + + switch (lock_release_cmd()) { + case LOCK_DEL_DELEX: + res = lock_release_delex(redis_sock, status); + break; + case LOCK_DEL_DELIFEQ: + res = lock_release_delifeq(redis_sock, status); + break; + case LOCK_DEL_EVAL: + break; /* fallthrough */ + } + + /* If res == DEL_NO_CMD LUA is selected or the new command didn't exist */ + if (res == DEL_NO_CMD) + lock_release_lua(redis_sock, status); +} + +/* {{{ PS_OPEN_FUNC */ PS_OPEN_FUNC(redis) { php_url *url; zval params, context, *zv; int i, j, path_len; +#if PHP_VERSION_ID >= 80600 + const char *save_path_str = ZSTR_VAL(save_path); + size_t save_path_len = ZSTR_LEN(save_path); +#else + const char *save_path_str = save_path; + size_t save_path_len = strlen(save_path); +#endif + redis_pool *pool = ecalloc(1, sizeof(*pool)); - for (i = 0, j = 0, path_len = strlen(save_path); i < path_len; i = j + 1) { + for (i = 0, j = 0, path_len = save_path_len; i < path_len; i = j + 1) { /* find beginning of url */ - while ( i< path_len && (isspace(save_path[i]) || save_path[i] == ',')) + while ( i< path_len && (isspace(save_path_str[i]) || save_path_str[i] == ',')) i++; /* find end of url */ j = i; - while (jfragment) { - spprintf(&query, 0, "%s#%s", REDIS_URL_STR(url->query), REDIS_URL_STR(url->fragment)); + spprintf(&query, 0, "%s#%s", ZSTR_VAL(url->query), ZSTR_VAL(url->fragment)); } else { - query = estrdup(REDIS_URL_STR(url->query)); + query = estrdup(ZSTR_VAL(url->query)); } sapi_module.treat_data(PARSE_STRING, query, ¶ms); @@ -434,11 +673,11 @@ PS_OPEN_FUNC(redis) ZVAL_ZVAL(&context, zv, 1, 0); } - zval_dtor(¶ms); + zval_ptr_dtor_nogc(¶ms); } if ((url->path == NULL && url->host == NULL) || weight <= 0 || timeout <= 0) { - char *path = estrndup(save_path+i, j-i); + char *path = estrndup(save_path_str+i, j-i); php_error_docref(NULL, E_WARNING, "Failed to parse session.save_path (error at offset %d, url was '%s')", i, path); efree(path); @@ -448,10 +687,8 @@ PS_OPEN_FUNC(redis) if (prefix) zend_string_release(prefix); if (user) zend_string_release(user); if (pass) zend_string_release(pass); - redis_pool_free(pool); - PS_SET_MOD_DATA(NULL); - return FAILURE; + goto fail; } RedisSock *redis_sock; @@ -459,14 +696,14 @@ PS_OPEN_FUNC(redis) size_t addrlen; int port, addr_free = 0; - scheme = url->scheme ? REDIS_URL_STR(url->scheme) : "tcp"; + scheme = url->scheme ? ZSTR_VAL(url->scheme) : "tcp"; if (url->host) { port = url->port; - addrlen = spprintf(&addr, 0, "%s://%s", scheme, REDIS_URL_STR(url->host)); + addrlen = spprintf(&addr, 0, "%s://%s", scheme, ZSTR_VAL(url->host)); addr_free = 1; } else { /* unix */ port = 0; - addr = REDIS_URL_STR(url->path); + addr = ZSTR_VAL(url->path); addrlen = strlen(addr); } @@ -478,9 +715,10 @@ PS_OPEN_FUNC(redis) redis_sock->dbNumber = db; } - if (Z_TYPE(context) == IS_ARRAY) { - redis_sock_set_stream_context(redis_sock, &context); - } + redis_sock->compression = session_compression_type(); + redis_sock->compression_level = INI_INT("redis.session.compression_level"); + + redis_sock_set_context_zval(redis_sock, &context); redis_pool_add(pool, redis_sock, weight); redis_sock->prefix = prefix; @@ -497,8 +735,14 @@ PS_OPEN_FUNC(redis) if (pool->head) { PS_SET_MOD_DATA(pool); return SUCCESS; + } else { + php_error_docref(NULL, E_WARNING, + "Unable to extract any servers from session.save_path"); } +fail: + redis_pool_free(pool); + PS_SET_MOD_DATA(NULL); return FAILURE; } /* }}} */ @@ -511,7 +755,7 @@ PS_CLOSE_FUNC(redis) if (pool) { if (pool->lock_status.session_key) { - redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(pool->lock_status.session_key)); + redis_pool_member *rpm = redis_pool_get_sock(pool, pool->lock_status.session_key); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (redis_sock) { @@ -530,22 +774,13 @@ PS_CLOSE_FUNC(redis) static zend_string * redis_session_key(RedisSock *redis_sock, const char *key, int key_len) { - zend_string *session; - char default_prefix[] = REDIS_SESSION_PREFIX; - char *prefix = default_prefix; - size_t prefix_len = sizeof(default_prefix)-1; - - if (redis_sock->prefix) { - prefix = ZSTR_VAL(redis_sock->prefix); - prefix_len = ZSTR_LEN(redis_sock->prefix); + if (redis_sock->prefix == NULL) { + return zend_string_concat2(ZEND_STRL(REDIS_SESSION_PREFIX), + key, key_len); } - /* build session key */ - session = zend_string_alloc(key_len + prefix_len, 0); - memcpy(ZSTR_VAL(session), prefix, prefix_len); - memcpy(ZSTR_VAL(session) + prefix_len, key, key_len); - - return session; + return zend_string_concat2(ZSTR_VAL(redis_sock->prefix), + ZSTR_LEN(redis_sock->prefix), key, key_len); } /* {{{ PS_CREATE_SID_FUNC @@ -561,7 +796,7 @@ PS_CREATE_SID_FUNC(redis) while (retries-- > 0) { zend_string* sid = php_session_create_id((void **) &pool); - redis_pool_member *rpm = redis_pool_get_sock(pool, ZSTR_VAL(sid)); + redis_pool_member *rpm = redis_pool_get_sock(pool, sid); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; @@ -571,7 +806,8 @@ PS_CREATE_SID_FUNC(redis) return php_session_create_id(NULL); } - if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); + if (pool->lock_status.session_key) + zend_string_release(pool->lock_status.session_key); pool->lock_status.session_key = redis_session_key(redis_sock, ZSTR_VAL(sid), ZSTR_LEN(sid)); if (lock_acquire(redis_sock, &pool->lock_status) == SUCCESS) { @@ -604,7 +840,7 @@ PS_VALIDATE_SID_FUNC(redis) if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey); + redis_pool_member *rpm = redis_pool_get_sock(pool, key); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); @@ -645,8 +881,13 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) if (!skeylen) return FAILURE; + /* No need to update the session timestamp if we've already done so */ + if (INI_INT("redis.session.early_refresh")) { + return SUCCESS; + } + redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey); + redis_pool_member *rpm = redis_pool_get_sock(pool, key); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); @@ -680,15 +921,15 @@ PS_UPDATE_TIMESTAMP_FUNC(redis) */ PS_READ_FUNC(redis) { - char *resp, *cmd; - int resp_len, cmd_len; + char *resp, *cmd, *compressed_buf; + int resp_len, cmd_len, compressed_free; const char *skey = ZSTR_VAL(key); - size_t skeylen = ZSTR_LEN(key); + size_t skeylen = ZSTR_LEN(key), compressed_len; if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey); + redis_pool_member *rpm = redis_pool_get_sock(pool, key); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); @@ -698,12 +939,24 @@ PS_READ_FUNC(redis) /* send GET command */ if (pool->lock_status.session_key) zend_string_release(pool->lock_status.session_key); pool->lock_status.session_key = redis_session_key(redis_sock, skey, skeylen); - cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); + + /* Update the session ttl if early refresh is enabled */ + if (INI_INT("redis.session.early_refresh")) { + cmd_len = REDIS_SPPRINTF(&cmd, "GETEX", "Ssd", pool->lock_status.session_key, + "EX", 2, session_gc_maxlifetime()); + } else { + cmd_len = REDIS_SPPRINTF(&cmd, "GET", "S", pool->lock_status.session_key); + } if (lock_acquire(redis_sock, &pool->lock_status) != SUCCESS) { - php_error_docref(NULL, E_WARNING, "Failed to acquire session lock"); - efree(cmd); - return FAILURE; + if (INI_INT("redis.session.lock_failure_readonly")) { + // opt-in legacy behavior: readonly session + php_error_docref(NULL, E_WARNING, "Failed to acquire session lock, session will be read only"); + } else { + php_error_docref(NULL, E_WARNING, "Failed to acquire session lock"); + efree(cmd); + return FAILURE; + } } if (redis_sock_write(redis_sock, cmd, cmd_len) < 0) { @@ -725,8 +978,13 @@ PS_READ_FUNC(redis) if (resp_len < 0) { *val = ZSTR_EMPTY_ALLOC(); } else { - *val = zend_string_init(resp, resp_len, 0); + compressed_free = session_uncompress_data(redis_sock, resp, resp_len, &compressed_buf, &compressed_len); + *val = zend_string_init(compressed_buf, compressed_len, 0); + if (compressed_free) { + efree(compressed_buf); // Free the buffer allocated by redis_uncompress + } } + efree(resp); return SUCCESS; @@ -738,14 +996,15 @@ PS_READ_FUNC(redis) PS_WRITE_FUNC(redis) { char *cmd, *response; - int cmd_len, response_len; - const char *skey = ZSTR_VAL(key), *sval = ZSTR_VAL(val); + int cmd_len, response_len, compressed_free; + const char *skey = ZSTR_VAL(key); size_t skeylen = ZSTR_LEN(key), svallen = ZSTR_LEN(val); + char *sval; if (!skeylen) return FAILURE; redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey); + redis_pool_member *rpm = redis_pool_get_sock(pool, key); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); @@ -755,8 +1014,14 @@ PS_WRITE_FUNC(redis) /* send SET command */ zend_string *session = redis_session_key(redis_sock, skey, skeylen); + compressed_free = session_compress_data(redis_sock, ZSTR_VAL(val), ZSTR_LEN(val), + &sval, &svallen); + cmd_len = REDIS_SPPRINTF(&cmd, "SETEX", "Sds", session, session_gc_maxlifetime(), sval, svallen); zend_string_release(session); + if (compressed_free) { + efree(sval); + } if (!write_allowed(redis_sock, &pool->lock_status)) { php_error_docref(NULL, E_WARNING, "Unable to write session: session lock not held"); @@ -772,7 +1037,7 @@ PS_WRITE_FUNC(redis) efree(cmd); - if (IS_REDIS_OK(response, response_len)) { + if (is_redis_ok(response, response_len)) { efree(response); return SUCCESS; } else { @@ -793,7 +1058,7 @@ PS_DESTROY_FUNC(redis) size_t skeylen = ZSTR_LEN(key); redis_pool *pool = PS_GET_MOD_DATA(); - redis_pool_member *rpm = redis_pool_get_sock(pool, skey); + redis_pool_member *rpm = redis_pool_get_sock(pool, key); RedisSock *redis_sock = rpm ? rpm->redis_sock : NULL; if (!redis_sock) { php_error_docref(NULL, E_WARNING, "Redis connection not available"); @@ -860,14 +1125,20 @@ PS_OPEN_FUNC(rediscluster) { int persistent = 0, failover = REDIS_FAILOVER_NONE; zend_string *prefix = NULL, *user = NULL, *pass = NULL, *failstr = NULL; +#if PHP_VERSION_ID >= 80600 + const char *save_path_str = ZSTR_VAL(save_path); +#else + const char *save_path_str = save_path; +#endif + /* Parse configuration for session handler */ array_init(&z_conf); - sapi_module.treat_data(PARSE_STRING, estrdup(save_path), &z_conf); + sapi_module.treat_data(PARSE_STRING, estrdup(save_path_str), &z_conf); /* We need seeds */ zv = REDIS_HASH_STR_FIND_TYPE_STATIC(Z_ARRVAL(z_conf), "seed", IS_ARRAY); if (zv == NULL) { - zval_dtor(&z_conf); + zval_ptr_dtor_nogc(&z_conf); return FAILURE; } @@ -884,7 +1155,7 @@ PS_OPEN_FUNC(rediscluster) { if (timeout < 0 || read_timeout < 0) { php_error_docref(NULL, E_WARNING, "Can't set negative timeout values in session configuration"); - zval_dtor(&z_conf); + zval_ptr_dtor_nogc(&z_conf); return FAILURE; } @@ -912,7 +1183,7 @@ PS_OPEN_FUNC(rediscluster) { if (user) zend_string_release(user); \ if (pass) zend_string_release(pass); \ free_seed_array(seeds, nseeds); \ - zval_dtor(&z_conf); \ + zval_ptr_dtor_nogc(&z_conf); \ /* Extract at least one valid seed or abort */ seeds = cluster_validate_args(timeout, read_timeout, ht_seeds, &nseeds, NULL); @@ -930,10 +1201,13 @@ PS_OPEN_FUNC(rediscluster) { c->flags->prefix = CLUSTER_DEFAULT_PREFIX(); } + c->flags->compression = session_compression_type(); + c->flags->compression_level = INI_INT("redis.session.compression_level"); + redis_sock_set_auth(c->flags, user, pass); if ((context = REDIS_HASH_STR_FIND_TYPE_STATIC(ht_conf, "stream", IS_ARRAY)) != NULL) { - redis_sock_set_stream_context(c->flags, context); + redis_sock_set_context_zval(c->flags, context); } /* First attempt to load from cache */ @@ -1103,7 +1377,7 @@ PS_UPDATE_TIMESTAMP_FUNC(rediscluster) { /* Attempt to send EXPIRE command */ c->readonly = 0; if (cluster_send_command(c,slot,cmd,cmdlen) < 0 || c->err) { - php_error_docref(NULL, E_NOTICE, "Redis unable to update session expiry"); + php_error_docref(NULL, E_NOTICE, "Redis unable to update session expiry"); efree(cmd); return FAILURE; } @@ -1130,8 +1404,9 @@ PS_UPDATE_TIMESTAMP_FUNC(rediscluster) { PS_READ_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey; - int cmdlen, skeylen, free_flag; + char *cmd, *skey, *compressed_buf; + int cmdlen, skeylen, free_flag, compressed_free; + size_t compressed_len; short slot; /* Set up our command and slot information */ @@ -1146,7 +1421,7 @@ PS_READ_FUNC(rediscluster) { cmdlen = redis_spprintf(NULL, NULL, &cmd, "GET", "s", skey, skeylen); c->readonly = 1; } - + efree(skey); /* Attempt to kick off our command */ @@ -1169,7 +1444,11 @@ PS_READ_FUNC(rediscluster) { if (reply->str == NULL) { *val = ZSTR_EMPTY_ALLOC(); } else { - *val = zend_string_init(reply->str, reply->len, 0); + compressed_free = session_uncompress_data(c->flags, reply->str, reply->len, &compressed_buf, &compressed_len); + *val = zend_string_init(compressed_buf, compressed_len, 0); + if (compressed_free) { + efree(compressed_buf); // Free the buffer allocated by redis_uncompress + } } free_flag = 1; @@ -1186,16 +1465,23 @@ PS_READ_FUNC(rediscluster) { PS_WRITE_FUNC(rediscluster) { redisCluster *c = PS_GET_MOD_DATA(); clusterReply *reply; - char *cmd, *skey; - int cmdlen, skeylen; + char *cmd, *skey, *sval; + int cmdlen, skeylen, compressed_free; + size_t svallen; short slot; + compressed_free = session_compress_data(c->flags, ZSTR_VAL(val), ZSTR_LEN(val), + &sval, &svallen); + /* Set up command and slot info */ skey = cluster_session_key(c, ZSTR_VAL(key), ZSTR_LEN(key), &skeylen, &slot); cmdlen = redis_spprintf(NULL, NULL, &cmd, "SETEX", "sds", skey, skeylen, session_gc_maxlifetime(), - ZSTR_VAL(val), ZSTR_LEN(val)); + sval, svallen); efree(skey); + if (compressed_free) { + efree(sval); + } /* Attempt to send command */ c->readonly = 0; diff --git a/rpm/README.md b/rpm/README.md index ac51cbe38e..30d960bb83 100644 --- a/rpm/README.md +++ b/rpm/README.md @@ -1,3 +1,3 @@ You can find and up to date version of this RPM builder here : -https://src.fedoraproject.org/rpms/php-pecl-redis5/tree/master +https://src.fedoraproject.org/rpms/php-pecl-redis6/tree/master diff --git a/sentinel.md b/sentinel.md index ec0adec52b..eac3d9e0cf 100644 --- a/sentinel.md +++ b/sentinel.md @@ -18,7 +18,48 @@ Redis Sentinel also provides other collateral tasks such as monitoring, notifica *read_timeout*: Float, value in seconds (optional, default is 0 meaning unlimited) *auth*:String, or an Array with one or two elements, used to authenticate with the redis-sentinel. (optional, default is NULL meaning NOAUTH) -##### *Example* +##### *Examples for version 6.0 or later* + +~~~php +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', +]); // default parameters +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 2.5, +]); // 2.5 sec timeout. +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 2.5, + 'persistent' => 'sentinel', +]); // persistent connection with id 'sentinel' +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 2.5, + 'persistent' => '', +]); // also persistent connection with id '' +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 1, + 'persistent' => null, + 'retryInterval' => 100, +]); // 1 sec timeout, 100ms delay between reconnection attempts. +$sentinel = new RedisSentinel([ + 'host' => '127.0.0.1', + 'port' => 26379, + 'connectTimeout' => 0, + 'persistent' => null, + 'retryInterval' => 0, + 'readTimeout' => 0, + 'auth' => 'secret', +]); // connect sentinel with password authentication +~~~ + +##### *Examples for versions older than 6.0* ~~~php $sentinel = new RedisSentinel('127.0.0.1'); // default parameters diff --git a/sentinel_library.c b/sentinel_library.c index 0fe64cc145..bed1aca385 100644 --- a/sentinel_library.c +++ b/sentinel_library.c @@ -8,7 +8,7 @@ free_redis_sentinel_object(zend_object *object) redis_sentinel_object *obj = PHPREDIS_GET_OBJECT(redis_sentinel_object, object); if (obj->sock) { - redis_sock_disconnect(obj->sock, 0); + redis_sock_disconnect(obj->sock, 0, 1); redis_free_socket(obj->sock); } zend_object_std_dtor(&obj->std); diff --git a/serialize.list b/serialize.list index d0971e287a..ecb92ac1bb 100644 --- a/serialize.list +++ b/serialize.list @@ -5,9 +5,9 @@ This file lists which methods support serialization. Only indented methods have setex setnx getSet - getMultiple + mGet append -substr +getRange strlen lPush lPushx @@ -17,19 +17,19 @@ strlen rPop blPop brPop - lRemove - lGet - lGetRange + lRange + lRem + lIndex lSet lInsert sAdd - sRemove + sRem sMove - sContains + sIsMember zAdd - zDelete + zRem zScore zRank zRevRank diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php index 5a20d271c7..82e11ddab2 100644 --- a/tests/RedisArrayTest.php +++ b/tests/RedisArrayTest.php @@ -1,12 +1,14 @@ \d+)_\w+#", $str, $out)) { + if (preg_match("#\w+_fb(?\d+)_\w+#", $str, $out)) { return $out['facebook_id']; } return $str; @@ -18,19 +20,19 @@ function parseHostPort($str, &$host, &$port) { $port = substr($str, $pos+1); } -function getRedisVersion($obj_r) { - $arr_info = $obj_r->info(); - if (!$arr_info || !isset($arr_info['redis_version'])) { - return "0.0.0"; +function getRedisVersion(object $client) { + $arr_info = $client->info(); + if ( ! $arr_info || !isset($arr_info['redis_version'])) { + return '0.0.0'; } return $arr_info['redis_version']; } /* Determine the lowest redis version attached to this RedisArray object */ -function getMinVersion($obj_ra) { - $min_version = "0.0.0"; - foreach ($obj_ra->_hosts() as $host) { - $version = getRedisVersion($obj_ra->_instance($host)); +function getMinVersion(object $ra) { + $min_version = '0.0.0'; + foreach ($ra->_hosts() as $host) { + $version = getRedisVersion($ra->_instance($host)); if (version_compare($version, $min_version) > 0) { $min_version = $version; } @@ -43,37 +45,38 @@ class Redis_Array_Test extends TestSuite { private $min_version; private $strings; - public $ra = NULL; + public $ra = NULL; private $data = NULL; public function setUp() { // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; - $this->strings = array(); - for($i = 0; $i < $n; $i++) { + $this->strings = []; + for ($i = 0; $i < $n; $i++) { $this->strings['key-'.$i] = 'val-'.$i; } - global $newRing, $oldRing, $useIndex; - $options = ['previous' => $oldRing, 'index' => $useIndex]; + global $new_ring, $old_ring, $use_index; + $options = ['previous' => $old_ring, 'index' => $use_index]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } - $this->ra = new RedisArray($newRing, $options); + + $this->ra = new RedisArray($new_ring, $options); $this->min_version = getMinVersion($this->ra); } public function testMSet() { // run mset - $this->assertTrue(TRUE === $this->ra->mset($this->strings)); + $this->assertTrue($this->ra->mset($this->strings)); // check each key individually using the array - foreach($this->strings as $k => $v) { - $this->assertTrue($v === $this->ra->get($k)); + foreach ($this->strings as $k => $v) { + $this->assertEquals($v, $this->ra->get($k)); } // check each key individually using a new connection - foreach($this->strings as $k => $v) { + foreach ($this->strings as $k => $v) { parseHostPort($this->ra->_target($k), $host, $port); $target = $this->ra->_target($k); @@ -87,17 +90,17 @@ public function testMSet() { if ($this->getAuth()) { $this->assertTrue($r->auth($this->getAuth())); } - $this->assertTrue($v === $r->get($k)); + $this->assertEquals($v, $r->get($k)); } } public function testMGet() { - $this->assertTrue(array_values($this->strings) === $this->ra->mget(array_keys($this->strings))); + $this->assertEquals(array_values($this->strings), $this->ra->mget(array_keys($this->strings))); } private function addData($commonString) { - $this->data = array(); - for($i = 0; $i < REDIS_ARRAY_DATA_SIZE; $i++) { + $this->data = []; + for ($i = 0; $i < REDIS_ARRAY_DATA_SIZE; $i++) { $k = rand().'_'.$commonString.'_'.rand(); $this->data[$k] = rand(); } @@ -107,12 +110,12 @@ private function addData($commonString) { private function checkCommonLocality() { // check that they're all on the same node. $lastNode = NULL; - foreach($this->data as $k => $v) { + foreach ($this->data as $k => $v) { $node = $this->ra->_target($k); - if($lastNode) { - $this->assertTrue($node === $lastNode); + if ($lastNode) { + $this->assertEquals($node, $lastNode); } - $this->assertTrue($this->ra->get($k) == $v); + $this->assertEqualsWeak($v, $this->ra->get($k)); $lastNode = $node; } } @@ -124,12 +127,16 @@ public function testKeyLocality() { $this->checkCommonLocality(); // with common hashing function - global $newRing, $oldRing, $useIndex; - $options = ['previous' => $oldRing, 'index' => $useIndex, 'function' => 'custom_hash']; + global $new_ring, $old_ring, $use_index; + $options = [ + 'previous' => $old_ring, + 'index' => $use_index, + 'function' => 'custom_hash' + ]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } - $this->ra = new RedisArray($newRing, $options); + $this->ra = new RedisArray($new_ring, $options); // basic key locality with custom hash $this->addData('fb'.rand()); @@ -139,30 +146,37 @@ public function testKeyLocality() { public function customDistributor($key) { $a = unpack("N*", md5($key, true)); - global $newRing; - $pos = abs($a[1]) % count($newRing); + global $new_ring; + $pos = abs($a[1]) % count($new_ring); return $pos; } public function testKeyDistributor() { - global $newRing, $useIndex; - $options = ['index' => $useIndex, 'function' => 'custom_hash', 'distributor' => [$this, "customDistributor"]]; + global $new_ring, $useIndex; + + $options = [ + 'index' => $useIndex, + 'function' => 'custom_hash', + 'distributor' => [$this, "customDistributor"] + ]; + if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } - $this->ra = new RedisArray($newRing, $options); + + $this->ra = new RedisArray($new_ring, $options); // custom key distribution function. $this->addData('fb'.rand()); // check that they're all on the expected node. $lastNode = NULL; - foreach($this->data as $k => $v) { + foreach ($this->data as $k => $v) { $node = $this->ra->_target($k); $pos = $this->customDistributor($k); - $this->assertTrue($node === $newRing[$pos]); + $this->assertEquals($node, $new_ring[$pos]); } } @@ -224,49 +238,52 @@ public function setUp() { // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; - $this->strings = array(); - for($i = 0; $i < $n; $i++) { + $this->strings = []; + for ($i = 0; $i < $n; $i++) { $this->strings['key-'.$i] = 'val-'.$i; } // initialize sets - for($i = 0; $i < $n; $i++) { + for ($i = 0; $i < $n; $i++) { // each set has 20 elements $this->sets['set-'.$i] = range($i, $i+20); } // initialize lists - for($i = 0; $i < $n; $i++) { + for ($i = 0; $i < $n; $i++) { // each list has 20 elements $this->lists['list-'.$i] = range($i, $i+20); } // initialize hashes - for($i = 0; $i < $n; $i++) { + for ($i = 0; $i < $n; $i++) { // each hash has 5 keys - $this->hashes['hash-'.$i] = array('A' => $i, 'B' => $i+1, 'C' => $i+2, 'D' => $i+3, 'E' => $i+4); + $this->hashes['hash-'.$i] = ['A' => $i, 'B' => $i+1, 'C' => $i+2, 'D' => $i+3, 'E' => $i+4]; } // initialize sorted sets - for($i = 0; $i < $n; $i++) { + for ($i = 0; $i < $n; $i++) { // each sorted sets has 5 elements - $this->zsets['zset-'.$i] = array($i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E'); + $this->zsets['zset-'.$i] = [$i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E']; } - global $newRing, $oldRing, $useIndex; - $options = ['previous' => $oldRing, 'index' => $useIndex]; + global $new_ring, $old_ring, $useIndex; + $options = [ + 'previous' => $old_ring, + 'index' => $useIndex + ]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } // create array - $this->ra = new RedisArray($newRing, $options); + $this->ra = new RedisArray($new_ring, $options); $this->min_version = getMinVersion($this->ra); } public function testFlush() { // flush all servers first. - global $serverList; - foreach($serverList as $s) { + global $server_list; + foreach ($server_list as $s) { parseHostPort($s, $host, $port); $r = new Redis(); @@ -274,7 +291,7 @@ public function testFlush() { if ($this->getAuth()) { $this->assertTrue($r->auth($this->getAuth())); } - $r->flushdb(); + $this->assertTrue($r->flushdb()); } } @@ -282,28 +299,28 @@ public function testFlush() { private function distributeKeys() { // strings - foreach($this->strings as $k => $v) { + foreach ($this->strings as $k => $v) { $this->ra->set($k, $v); } // sets - foreach($this->sets as $k => $v) { - call_user_func_array(array($this->ra, 'sadd'), array_merge(array($k), $v)); + foreach ($this->sets as $k => $v) { + call_user_func_array([$this->ra, 'sadd'], array_merge([$k], $v)); } // lists - foreach($this->lists as $k => $v) { - call_user_func_array(array($this->ra, 'rpush'), array_merge(array($k), $v)); + foreach ($this->lists as $k => $v) { + call_user_func_array([$this->ra, 'rpush'], array_merge([$k], $v)); } // hashes - foreach($this->hashes as $k => $v) { + foreach ($this->hashes as $k => $v) { $this->ra->hmset($k, $v); } // sorted sets - foreach($this->zsets as $k => $v) { - call_user_func_array(array($this->ra, 'zadd'), array_merge(array($k), $v)); + foreach ($this->zsets as $k => $v) { + call_user_func_array([$this->ra, 'zadd'], array_merge([$k], $v)); } } @@ -318,54 +335,49 @@ public function testSimpleRead() { private function readAllvalues() { // strings - foreach($this->strings as $k => $v) { - $this->assertTrue($this->ra->get($k) === $v); + foreach ($this->strings as $k => $v) { + $this->assertEquals($v, $this->ra->get($k)); } // sets - foreach($this->sets as $k => $v) { + foreach ($this->sets as $k => $v) { $ret = $this->ra->smembers($k); // get values - // sort sets - sort($v); - sort($ret); - - $this->assertTrue($ret == $v); + $this->assertEqualsWeak($v, $ret); } // lists - foreach($this->lists as $k => $v) { + foreach ($this->lists as $k => $v) { $ret = $this->ra->lrange($k, 0, -1); - $this->assertTrue($ret == $v); + $this->assertEqualsWeak($v, $ret); } // hashes - foreach($this->hashes as $k => $v) { + foreach ($this->hashes as $k => $v) { $ret = $this->ra->hgetall($k); // get values - $this->assertTrue($ret == $v); + $this->assertEqualsWeak($v, $ret); } // sorted sets - foreach($this->zsets as $k => $v) { - $ret = $this->ra->zrange($k, 0, -1, TRUE); // get values with scores + foreach ($this->zsets as $k => $v) { + $ret = $this->ra->zrange($k, 0, -1, true); // create assoc array from local dataset - $tmp = array(); - for($i = 0; $i < count($v); $i += 2) { + $tmp = []; + for ($i = 0; $i < count($v); $i += 2) { $tmp[$v[$i+1]] = $v[$i]; } // compare to RA value - $this->assertTrue($ret == $tmp); + $this->assertEqualsWeak($tmp, $ret); } } // add a new node. public function testCreateSecondRing() { - - global $newRing, $oldRing, $serverList; - $oldRing = $newRing; // back up the original. - $newRing = $serverList; // add a new node to the main ring. + global $new_ring, $old_ring, $server_list; + $old_ring = $new_ring; // back up the original. + $new_ring = $server_list; // add a new node to the main ring. } public function testReadUsingFallbackMechanism() { @@ -381,7 +393,7 @@ public function testRehashWithCallback() { $this->ra->_rehash(function ($host, $count) use (&$total) { $total += $count; }); - $this->assertTrue($total > 0); + $this->assertGT(0, $total); } public function testReadRedistributedKeys() { @@ -401,31 +413,35 @@ class Redis_Auto_Rehashing_Test extends TestSuite { public function setUp() { // initialize strings. $n = REDIS_ARRAY_DATA_SIZE; - $this->strings = array(); - for($i = 0; $i < $n; $i++) { + $this->strings = []; + for ($i = 0; $i < $n; $i++) { $this->strings['key-'.$i] = 'val-'.$i; } - global $newRing, $oldRing, $useIndex; - $options = ['previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE]; + global $new_ring, $old_ring, $useIndex; + $options = [ + 'previous' => $old_ring, + 'index' => $useIndex, + 'autorehash' => true + ]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } // create array - $this->ra = new RedisArray($newRing, $options); + $this->ra = new RedisArray($new_ring, $options); $this->min_version = getMinVersion($this->ra); } public function testDistribute() { // strings - foreach($this->strings as $k => $v) { + foreach ($this->strings as $k => $v) { $this->ra->set($k, $v); } } private function readAllvalues() { - foreach($this->strings as $k => $v) { - $this->assertTrue($this->ra->get($k) === $v); + foreach ($this->strings as $k => $v) { + $this->assertEquals($v, $this->ra->get($k)); } } @@ -436,9 +452,9 @@ public function testReadAll() { // add a new node. public function testCreateSecondRing() { - global $newRing, $oldRing, $serverList; - $oldRing = $newRing; // back up the original. - $newRing = $serverList; // add a new node to the main ring. + global $new_ring, $old_ring, $server_list; + $old_ring = $new_ring; // back up the original. + $new_ring = $server_list; // add a new node to the main ring. } // Read and migrate keys on fallback, causing the whole ring to be rehashed. @@ -448,7 +464,7 @@ public function testReadAndMigrateAll() { // Read and migrate keys on fallback, causing the whole ring to be rehashed. public function testAllKeysHaveBeenMigrated() { - foreach($this->strings as $k => $v) { + foreach ($this->strings as $k => $v) { parseHostPort($this->ra->_target($k), $host, $port); $r = new Redis; @@ -457,24 +473,29 @@ public function testAllKeysHaveBeenMigrated() { $this->assertTrue($r->auth($this->getAuth())); } - $this->assertTrue($v === $r->get($k)); // check that the key has actually been migrated to the new node. + // check that the key has actually been migrated to the new node. + $this->assertEquals($v, $r->get($k)); } } } // Test node-specific multi/exec class Redis_Multi_Exec_Test extends TestSuite { - public $ra = NULL; private $min_version; + public $ra = NULL; + + private static $new_group = NULL; + private static $new_salary = NULL; + public function setUp() { - global $newRing, $oldRing, $useIndex; - $options = ['previous' => $oldRing, 'index' => $useIndex]; + global $new_ring, $old_ring, $useIndex; + $options = ['previous' => $old_ring, 'index' => $useIndex]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } // create array - $this->ra = new RedisArray($newRing, $options); + $this->ra = new RedisArray($new_ring, $options); $this->min_version = getMinVersion($this->ra); } @@ -490,74 +511,69 @@ public function testInit() { public function testKeyDistribution() { // check that all of joe's keys are on the same instance $lastNode = NULL; - foreach(array('name', 'group', 'salary') as $field) { + foreach (['name', 'group', 'salary'] as $field) { $node = $this->ra->_target('1_{employee:joe}_'.$field); - if($lastNode) { - $this->assertTrue($node === $lastNode); + if ($lastNode) { + $this->assertEquals($node, $lastNode); } $lastNode = $node; } } public function testMultiExec() { - // Joe gets a promotion - $newGroup = $this->ra->get('{groups}:executives'); - $newSalary = 4000; + self::$new_group = $this->ra->get('{groups}:executives'); + self::$new_salary = 4000; // change both in a transaction. - $host = $this->ra->_target('{employee:joe}'); // transactions are per-node, so we need a reference to it. - $tr = $this->ra->multi($host) - ->set('1_{employee:joe}_group', $newGroup) - ->set('1_{employee:joe}_salary', $newSalary) + // transactions are per-node, so we need a reference to it. + $host = $this->ra->_target('{employee:joe}'); + $this->ra->multi($host) + ->set('1_{employee:joe}_group', self::$new_group) + ->set('1_{employee:joe}_salary', self::$new_salary) ->exec(); // check that the group and salary have been changed - $this->assertTrue($this->ra->get('1_{employee:joe}_group') === $newGroup); - $this->assertTrue($this->ra->get('1_{employee:joe}_salary') == $newSalary); + $this->assertEquals(self::$new_group, $this->ra->get('1_{employee:joe}_group')); + $this->assertEqualsWeak(self::$new_salary, $this->ra->get('1_{employee:joe}_salary')); } public function testMultiExecMSet() { - - global $newGroup, $newSalary; - $newGroup = 1; - $newSalary = 10000; + self::$new_group = 1; + self::$new_salary = 10000; // test MSET, making Joe a top-level executive $out = $this->ra->multi($this->ra->_target('{employee:joe}')) - ->mset(array('1_{employee:joe}_group' => $newGroup, '1_{employee:joe}_salary' => $newSalary)) + ->mset([ + '1_{employee:joe}_group' => self::$new_group, + '1_{employee:joe}_salary' => self::$new_salary + ]) ->exec(); - $this->assertTrue($out[0] === TRUE); + $this->assertTrue($out[0]); } public function testMultiExecMGet() { - - global $newGroup, $newSalary; - - // test MGET $out = $this->ra->multi($this->ra->_target('{employee:joe}')) - ->mget(array('1_{employee:joe}_group', '1_{employee:joe}_salary')) + ->mget(['1_{employee:joe}_group', '1_{employee:joe}_salary']) ->exec(); - $this->assertTrue($out[0][0] == $newGroup); - $this->assertTrue($out[0][1] == $newSalary); + $this->assertEqualsWeak(self::$new_group, $out[0][0]); + $this->assertEqualsWeak(self::$new_salary, $out[0][1]); } public function testMultiExecDel() { - - // test DEL $out = $this->ra->multi($this->ra->_target('{employee:joe}')) ->del('1_{employee:joe}_group', '1_{employee:joe}_salary') ->exec(); - $this->assertTrue($out[0] === 2); + $this->assertEquals(2, $out[0]); $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_group')); $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_salary')); } - public function testMutliExecUnlink() { + public function testMultiExecUnlink() { if (version_compare($this->min_version, "4.0.0", "lt")) { $this->markTestSkipped(); } @@ -569,7 +585,7 @@ public function testMutliExecUnlink() { ->del('{unlink}:key1', '{unlink}:key2') ->exec(); - $this->assertTrue($out[0] === 2); + $this->assertEquals(2, $out[0]); } public function testDiscard() { @@ -577,33 +593,32 @@ public function testDiscard() { $key = 'test_err'; $this->assertTrue($this->ra->set($key, 'test')); - $this->assertTrue('test' === $this->ra->get($key)); + $this->assertEquals('test', $this->ra->get($key)); $this->ra->watch($key); // After watch, same - $this->assertTrue('test' === $this->ra->get($key)); + $this->assertEquals('test', $this->ra->get($key)); // change in a multi/exec block. $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test1')->exec(); - $this->assertTrue($ret === array(true)); + $this->assertEquals([true], $ret); // Get after exec, 'test1': - $this->assertTrue($this->ra->get($key) === 'test1'); + $this->assertEquals('test1', $this->ra->get($key)); $this->ra->watch($key); // After second watch, still test1. - $this->assertTrue($this->ra->get($key) === 'test1'); + $this->assertEquals('test1', $this->ra->get($key)); $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test2')->discard(); // Ret after discard: NULL"; - $this->assertTrue($ret === NULL); + $this->assertNull($ret); // Get after discard, unchanged: - $this->assertTrue($this->ra->get($key) === 'test1'); + $this->assertEquals('test1', $this->ra->get($key)); } - } // Test custom distribution function @@ -613,13 +628,17 @@ class Redis_Distributor_Test extends TestSuite { private $min_version; public function setUp() { - global $newRing, $oldRing, $useIndex; - $options = ['previous' => $oldRing, 'index' => $useIndex, 'distributor' => [$this, 'distribute']]; + global $new_ring, $old_ring, $useIndex; + $options = [ + 'previous' => $old_ring, + 'index' => $useIndex, + 'distributor' => [$this, 'distribute'] + ]; if ($this->getAuth()) { $options['auth'] = $this->getAuth(); } // create array - $this->ra = new RedisArray($newRing, $options); + $this->ra = new RedisArray($new_ring, $options); $this->min_version = getMinVersion($this->ra); } @@ -629,9 +648,9 @@ public function testInit() { } public function distribute($key) { - $matches = array(); + $matches = []; if (preg_match('/{([^}]+)}.*/', $key, $matches) == 1) { - $countries = array('uk' => 0, 'us' => 1); + $countries = ['uk' => 0, 'us' => 1]; if (array_key_exists($matches[1], $countries)) { return $countries[$matches[1]]; } @@ -640,29 +659,30 @@ public function distribute($key) { } public function testDistribution() { - $ukServer = $this->ra->_target('{uk}test'); - $usServer = $this->ra->_target('{us}test'); - $deServer = $this->ra->_target('{de}test'); - $defaultServer = $this->ra->_target('unknown'); + $UK_server = $this->ra->_target('{uk}test'); + $US_server = $this->ra->_target('{us}test'); + $DE_server = $this->ra->_target('{de}test'); + $XX_server = $this->ra->_target('{xx}test'); $nodes = $this->ra->_hosts(); - $this->assertTrue($ukServer === $nodes[0]); - $this->assertTrue($usServer === $nodes[1]); - $this->assertTrue($deServer === $nodes[2]); - $this->assertTrue($defaultServer === $nodes[2]); + + $this->assertEquals($UK_server, $nodes[0]); + $this->assertEquals($US_server, $nodes[1]); + $this->assertEquals($DE_server, $nodes[2]); + $this->assertEquals($XX_server, $nodes[2]); } } -function run_tests($className, $str_filter, $str_host, $auth) { - // reset rings - global $newRing, $oldRing, $serverList; +function run_ra_tests($test_class, $filter, $host, array $full_ring, + array $sub_ring, $auth) +{ + global $new_ring, $old_ring, $server_list; - $newRing = ["$str_host:6379", "$str_host:6380", "$str_host:6381"]; - $oldRing = []; - $serverList = ["$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"]; + $server_list = $full_ring; + $new_ring = $sub_ring; + $old_ring = []; - // run - return TestSuite::run($className, $str_filter, $str_host, NULL, $auth); + return TestSuite::run($test_class, $filter, $host, NULL, $auth); } ?> diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php index 67491dde00..d271992b58 100644 --- a/tests/RedisClusterTest.php +++ b/tests/RedisClusterTest.php @@ -1,5 +1,6 @@ markTestSkipped(); } + public function testServerInfoOldRedis() { $this->markTestSkipped(); } /* Tests we'll skip all together in the context of RedisCluster. The * RedisCluster class doesn't implement specialized (non-redis) commands * such as sortAsc, or sortDesc and other commands such as SELECT are * simply invalid in Redis Cluster */ - public function testSortAsc() { return $this->markTestSkipped(); } - public function testSortDesc() { return $this->markTestSkipped(); } - public function testWait() { return $this->markTestSkipped(); } - public function testSelect() { return $this->markTestSkipped(); } - public function testReconnectSelect() { return $this->markTestSkipped(); } - public function testMultipleConnect() { return $this->markTestSkipped(); } - public function testDoublePipeNoOp() { return $this->markTestSkipped(); } - public function testSwapDB() { return $this->markTestSkipped(); } - public function testConnectException() { return $this->markTestSkipped(); } - public function testTlsConnect() { return $this->markTestSkipped(); } - public function testReset() { return $this->markTestSkipped(); } - public function testInvalidAuthArgs() { return $this->markTestSkipped(); } - public function testScanErrors() { return $this->markTestSkipped(); } + public function testPipelinePublish() { $this->markTestSkipped(); } + public function testSortAsc() { $this->markTestSkipped(); } + public function testSortDesc() { $this->markTestSkipped(); } + public function testWait() { $this->markTestSkipped(); } + public function testSelect() { $this->markTestSkipped(); } + public function testReconnectSelect() { $this->markTestSkipped(); } + public function testMultipleConnect() { $this->markTestSkipped(); } + public function testDoublePipeNoOp() { $this->markTestSkipped(); } + public function testSwapDB() { $this->markTestSkipped(); } + public function testConnectException() { $this->markTestSkipped(); } + public function testTlsConnect() { $this->markTestSkipped(); } + public function testTlsReconnect() { $this->markTestSkipped(); } + public function testReset() { $this->markTestSkipped(); } + public function testInvalidAuthArgs() { $this->markTestSkipped(); } + public function testScanErrors() { $this->markTestSkipped(); } + public function testConnectDatabaseSelect() { $this->markTestSkipped(); } /* These 'directed node' commands work differently in RedisCluster */ - public function testConfig() { return $this->markTestSkipped(); } - public function testFlushDB() { return $this->markTestSkipped(); } - public function testFunction() { return $this->markTestSkipped(); } + public function testConfig() { $this->markTestSkipped(); } + public function testFlushDB() { $this->markTestSkipped(); } + public function testFunction() { $this->markTestSkipped(); } /* Session locking feature is currently not supported in in context of Redis Cluster. The biggest issue for this is the distribution nature of Redis cluster */ - public function testSession_lockKeyCorrect() { return $this->markTestSkipped(); } - public function testSession_lockingDisabledByDefault() { return $this->markTestSkipped(); } - public function testSession_lockReleasedOnClose() { return $this->markTestSkipped(); } - public function testSession_ttlMaxExecutionTime() { return $this->markTestSkipped(); } - public function testSession_ttlLockExpire() { return $this->markTestSkipped(); } - public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { return $this->markTestSkipped(); } - public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { return $this->markTestSkipped(); } - public function testSession_correctLockRetryCount() { return $this->markTestSkipped(); } - public function testSession_defaultLockRetryCount() { return $this->markTestSkipped(); } - public function testSession_noUnlockOfOtherProcess() { return $this->markTestSkipped(); } - public function testSession_lockWaitTime() { return $this->markTestSkipped(); } + public function testSession_lockKeyCorrect() { $this->markTestSkipped(); } + public function testSession_lockingDisabledByDefault() { $this->markTestSkipped(); } + public function testSession_lockReleasedOnClose() { $this->markTestSkipped(); } + public function testSession_ttlMaxExecutionTime() { $this->markTestSkipped(); } + public function testSession_ttlLockExpire() { $this->markTestSkipped(); } + public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { $this->markTestSkipped(); } + public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { $this->markTestSkipped(); } + public function testSession_correctLockRetryCount() { $this->markTestSkipped(); } + public function testSession_defaultLockRetryCount() { $this->markTestSkipped(); } + public function testSession_noUnlockOfOtherProcess() { $this->markTestSkipped(); } + public function testSession_lockWaitTime() { $this->markTestSkipped(); } + + /* Regression test for GH #2810 */ + public function testConstructNullSeeds() { + /* new RedisCluster(null, null) must not throw TypeError. + * $seeds is declared ?array so null is a valid argument. */ + $thrown = false; + try { + new RedisCluster(null, null); + } catch (\Throwable $e) { + $thrown = true; + $this->assertFalse($e instanceof \TypeError); + } + $this->assertTrue($thrown); + + /* Passing an empty array must also not throw TypeError (control). */ + $thrown = false; + try { + new RedisCluster(null, []); + } catch (\Throwable $e) { + $thrown = true; + $this->assertFalse($e instanceof \TypeError); + } + $this->assertTrue($thrown); + + /* Both (null) and (null, null) mean "no name, no seeds" and must + * produce the same exception type. */ + $ex1 = $ex2 = null; + try { new RedisCluster(null); } + catch (\Throwable $e) { $ex1 = get_class($e); } + try { new RedisCluster(null, null); } + catch (\Throwable $e) { $ex2 = get_class($e); } + $this->assertTrue($ex1 !== null); + $this->assertEquals($ex1, $ex2); + } + + private function loadSeedsFromHostPort($host, $port) { + try { + $rc = new RedisCluster(NULL, ["$host:$port"], 1, 1, true, $this->getAuth()); + self::$seed_source = "Host: $host, Port: $port"; + return array_map(function($master) { + return sprintf('%s:%s', $master[0], $master[1]); + }, $rc->_masters()); + } catch (Exception $ex) { + /* fallthrough */ + } - /* Load our seeds on construction */ - public function __construct($str_host, $i_port, $str_auth) { - parent::__construct($str_host, $i_port, $str_auth); + self::$seed_messages[] = "--host=$host, --port=$port"; - $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; + return false; + } - if (!file_exists($str_nodemap_file)) { - fprintf(STDERR, "Error: Can't find nodemap file for seeds!\n"); - exit(1); + private function loadSeedsFromEnv() { + $seeds = getenv('REDIS_CLUSTER_NODES'); + if ( ! $seeds) { + self::$seed_messages[] = "environment variable REDIS_CLUSTER_NODES ($seeds)"; + return false; } - /* Store our node map */ - if (!self::$_arr_node_map) { - self::$_arr_node_map = array_filter( - explode("\n", file_get_contents($str_nodemap_file) - )); + self::$seed_source = 'Environment variable REDIS_CLUSTER_NODES'; + return array_filter(explode(' ', $seeds)); + } + + private function loadSeedsFromNodeMap() { + $nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap'; + if ( ! file_exists($nodemap_file)) { + self::$seed_messages[] = "nodemap file '$nodemap_file'"; + return false; } + + self::$seed_source = "Nodemap file '$nodemap_file'"; + return array_filter(explode("\n", file_get_contents($nodemap_file))); + } + + private function loadSeeds($host, $port) { + if (($seeds = $this->loadSeedsFromNodeMap())) + return $seeds; + if (($seeds = $this->loadSeedsFromEnv())) + return $seeds; + if (($seeds = $this->loadSeedsFromHostPort($host, $port))) + return $seeds; + + TestSuite::errorMessage("Error: Unable to load seeds for RedisCluster tests"); + foreach (self::$seed_messages as $msg) { + TestSuite::errorMessage(" Tried: %s", $msg); + } + + exit(1); + } + + /* Load our seeds on construction */ + public function __construct($host, $port, $auth, $tls_port = 6378) { + parent::__construct($host, $port, $auth, $tls_port); + + self::$seeds = $this->loadSeeds($host, $port); } /* Override setUp to get info from a specific node */ public function setUp() { - $this->redis = $this->newInstance(); - $info = $this->redis->info(uniqid()); - $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); + $this->redis = $this->newInstance(); + $info = $this->redis->info(uniqid()); + $this->version = $info['redis_version'] ?? '0.0.0'; + $this->is_keydb = $this->detectKeyDB($info); + $this->is_valkey = $this->detectValkey($info); + $this->valkey_version = $info['valkey_version'] ?? '0.0.0'; + } + + private function findCliExe() { + foreach (['redis-cli', 'valkey-cli'] as $candidate) { + $path = trim(shell_exec("command -v $candidate 2>/dev/null")); + if (is_executable($path)) { + return $path; + } + } + + return NULL; + } + + private function getServerReply($host, $port, $cmd) { + $cli = $this->findCliExe(); + if ( ! $cli) { + return '(no redis-cli or valkey-cli found)'; + } + + $args = [$cli, '-h', $host, '-p', $port]; + + $this->getAuthParts($user, $pass); + + if ($user) $args = array_merge($args, ['--user', $user]); + if ($pass) $args = array_merge($args, ['-a', $pass]); + + $resp = shell_exec(implode(' ', $args) . ' ' . $cmd . ' 2>/dev/null'); + + return is_string($resp) ? trim($resp) : $resp; + } + + /* Try to gat a new RedisCluster instance. The strange logic is an attempt + to solve a problem where this sometimes fails but only ever on GitHub + runners. If we're not on a runner we just get a new instance. Otherwise + we allow for two tries to get the instance. */ + private function getNewInstance() { + if (getenv('GITHUB_ACTIONS') === 'true') { + try { + return new RedisCluster(NULL, self::$seeds, 30, 30, true, + $this->getAuth()); + } catch (Exception $ex) { + TestSuite::errorMessage("Failed to connect: %s", $ex->getMessage()); + } + } + + return new RedisCluster(NULL, self::$seeds, 30, 30, true, $this->getAuth()); } /* Override newInstance as we want a RedisCluster object */ protected function newInstance() { - return new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth()); + try { + return $this->getNewInstance(); + } catch (Exception $ex) { + TestSuite::errorMessage(""); + TestSuite::errorMessage("Fatal error: %s", $ex->getMessage()); + TestSuite::errorMessage("Seeds: %s", implode(' ', self::$seeds)); + TestSuite::errorMessage("Seed source: %s", self::$seed_source); + TestSuite::errorMessage(""); + + TestSuite::errorMessage("Backtrace:"); + foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $i => $frame) { + $file = isset($frame['file']) ? basename($frame['file']) : '[internal]'; + $line = $frame['line'] ?? '?'; + $func = $frame['function'] ?? 'unknown'; + TestSuite::errorMessage(" %s:%d [%s]", $file, $line, $func); + } + + TestSuite::errorMessage("\nServer responses:"); + + /* See if we can shed some light on whether Redis is available */ + foreach (self::$seeds as $seed) { + list($host, $port) = explode(':', $seed); + + $st = microtime(true); + $reply = $this->getServerReply($host, $port, 'PING'); + $et = microtime(true); + + TestSuite::errorMessage(" [%s:%d] PING -> %s (%.4f)", $host, + $port, var_export($reply, true), + $et - $st); + } + + exit(1); + } } /* Overrides for RedisTest where the function signature is different. This @@ -114,7 +272,7 @@ public function testPing() { /* Make sure both variations work in MULTI mode */ $this->redis->multi(); $this->redis->ping('{ping-test}'); - $this->redis->ping('{ping-test}','BEEP'); + $this->redis->ping('{ping-test}', 'BEEP'); $this->assertEquals([true, 'BEEP'], $this->redis->exec()); } @@ -128,14 +286,14 @@ public function testRandomKey() { for ($i = 0; $i < 1000; $i++) { $k = $this->redis->randomKey("key:$i"); - $this->assertTrue($this->redis->exists($k)); + $this->assertEquals(1, $this->redis->exists($k)); } } public function testEcho() { - $this->assertEquals($this->redis->echo('k1', 'hello'), 'hello'); - $this->assertEquals($this->redis->echo('k2', 'world'), 'world'); - $this->assertEquals($this->redis->echo('k3', " 0123 "), " 0123 "); + $this->assertEquals('hello', $this->redis->echo('echo1', 'hello')); + $this->assertEquals('world', $this->redis->echo('echo2', 'world')); + $this->assertEquals(' 0123 ', $this->redis->echo('echo3', " 0123 ")); } public function testSortPrefix() { @@ -145,7 +303,7 @@ public function testSortPrefix() { $this->redis->sadd('some-item', 2); $this->redis->sadd('some-item', 3); - $this->assertEquals(array('1','2','3'), $this->redis->sort('some-item')); + $this->assertEquals(['1', '2', '3'], $this->redis->sort('some-item')); // Kill our set/prefix $this->redis->del('some-item'); @@ -154,15 +312,15 @@ public function testSortPrefix() { public function testDBSize() { for ($i = 0; $i < 10; $i++) { - $str_key = "key:$i"; - $this->assertTrue($this->redis->flushdb($str_key)); - $this->redis->set($str_key, "val:$i"); - $this->assertEquals(1, $this->redis->dbsize($str_key)); + $key = "key:$i"; + $this->assertTrue($this->redis->flushdb($key)); + $this->redis->set($key, "val:$i"); + $this->assertEquals(1, $this->redis->dbsize($key)); } } public function testInfo() { - $arr_check_keys = [ + $fields = [ "redis_version", "arch_bits", "uptime_in_seconds", "uptime_in_days", "connected_clients", "connected_slaves", "used_memory", "total_connections_received", "total_commands_processed", @@ -170,113 +328,113 @@ public function testInfo() { ]; for ($i = 0; $i < 3; $i++) { - $arr_info = $this->redis->info("k:$i"); - foreach ($arr_check_keys as $str_check_key) { - $this->assertTrue(isset($arr_info[$str_check_key])); + $info = $this->redis->info($i); + foreach ($fields as $field) { + $this->assertArrayKey($info, $field); } } } public function testClient() { - $str_key = 'key-' . rand(1,100); + $key = 'key-' . rand(1, 100); - $this->assertTrue($this->redis->client($str_key, 'setname', 'cluster_tests')); + $this->assertTrue($this->redis->client($key, 'setname', 'cluster_tests')); - $arr_clients = $this->redis->client($str_key, 'list'); - $this->assertTrue(is_array($arr_clients)); + $clients = $this->redis->client($key, 'list'); + $this->assertIsArray($clients); /* Find us in the list */ - $str_addr = NULL; - foreach ($arr_clients as $arr_client) { - if ($arr_client['name'] == 'cluster_tests') { - $str_addr = $arr_client['addr']; + $addr = NULL; + foreach ($clients as $client) { + if ($client['name'] == 'cluster_tests') { + $addr = $client['addr']; break; } } /* We should be in there */ - $this->assertFalse(empty($str_addr)); + $this->assertIsString($addr); /* Kill our own client! */ - $this->assertTrue($this->redis->client($str_key, 'kill', $str_addr)); + $this->assertTrue($this->redis->client($key, 'kill', $addr)); } public function testTime() { - $time_arr = $this->redis->time("k:" . rand(1,100)); - $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && - strval(intval($time_arr[0])) === strval($time_arr[0]) && - strval(intval($time_arr[1])) === strval($time_arr[1])); + [$sec, $usec] = $this->redis->time(uniqid()); + $this->assertEquals(strval(intval($sec)), strval($sec)); + $this->assertEquals(strval(intval($usec)), strval($usec)); } public function testScan() { - $i_key_count = 0; - $i_scan_count = 0; + $key_count = 0; + $scan_count = 0; /* Have scan retry for us */ $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); /* Iterate over our masters, scanning each one */ - foreach ($this->redis->_masters() as $arr_master) { + foreach ($this->redis->_masters() as $master) { /* Grab the number of keys we have */ - $i_key_count += $this->redis->dbsize($arr_master); + $key_count += $this->redis->dbsize($master); /* Scan the keys here */ $it = NULL; - while ($arr_keys = $this->redis->scan($it, $arr_master)) { - $i_scan_count += count($arr_keys); + while ($keys = $this->redis->scan($it, $master)) { + $scan_count += count($keys); } } /* Our total key count should match */ - $this->assertEquals($i_scan_count, $i_key_count); + $this->assertEquals($scan_count, $key_count); } public function testScanPrefix() { - $arr_prefixes = ['prefix-a:', 'prefix-b:']; - $str_id = uniqid(); + $prefixes = ['prefix-a:', 'prefix-b:']; + $id = uniqid(); $arr_keys = []; - foreach ($arr_prefixes as $str_prefix) { - $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix); - $this->redis->set($str_id, "LOLWUT"); - $arr_keys[$str_prefix] = $str_id; + foreach ($prefixes as $prefix) { + $this->redis->setOption(Redis::OPT_PREFIX, $prefix); + $this->redis->set($id, "LOLWUT"); + $arr_keys[$prefix] = $id; } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX); - foreach ($arr_prefixes as $str_prefix) { - $arr_prefix_keys = []; - $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix); + foreach ($prefixes as $prefix) { + $prefix_keys = []; + $this->redis->setOption(Redis::OPT_PREFIX, $prefix); - foreach ($this->redis->_masters() as $arr_master) { + foreach ($this->redis->_masters() as $master) { $it = NULL; - while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) { - foreach ($arr_iter as $str_key) { - $arr_prefix_keys[$str_prefix] = $str_key; + while ($keys = $this->redis->scan($it, $master, "*$id*")) { + foreach ($keys as $key) { + $prefix_keys[$prefix] = $key; } } } - $this->assertTrue(count($arr_prefix_keys) == 1 && isset($arr_prefix_keys[$str_prefix])); + $this->assertIsArray($prefix_keys, 1); + $this->assertArrayKey($prefix_keys, $prefix); } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX); - $arr_scan_keys = []; + $scan_keys = []; - foreach ($this->redis->_masters() as $arr_master) { + foreach ($this->redis->_masters() as $master) { $it = NULL; - while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) { - foreach ($arr_iter as $str_key) { - $arr_scan_keys[] = $str_key; + while ($keys = $this->redis->scan($it, $master, "*$id*")) { + foreach ($keys as $key) { + $scan_keys[] = $key; } } } /* We should now have both prefixs' keys */ - foreach ($arr_keys as $str_prefix => $str_id) { - $this->assertTrue(in_array("{$str_prefix}{$str_id}", $arr_scan_keys)); + foreach ($arr_keys as $prefix => $id) { + $this->assertInArray("{$prefix}{$id}", $scan_keys); } } @@ -285,36 +443,36 @@ public function testScanPrefix() { public function testPubSub() { // PUBSUB CHANNELS ... $result = $this->redis->pubsub("somekey", "channels", "*"); - $this->assertTrue(is_array($result)); + $this->assertIsArray($result); $result = $this->redis->pubsub("somekey", "channels"); - $this->assertTrue(is_array($result)); + $this->assertIsArray($result); // PUBSUB NUMSUB - $c1 = '{pubsub}-' . rand(1,100); - $c2 = '{pubsub}-' . rand(1,100); + $c1 = '{pubsub}-' . rand(1, 100); + $c2 = '{pubsub}-' . rand(1, 100); $result = $this->redis->pubsub("{pubsub}", "numsub", $c1, $c2); // Should get an array back, with two elements - $this->assertTrue(is_array($result)); - $this->assertEquals(count($result), 4); + $this->assertIsArray($result); + $this->assertEquals(4, count($result)); - $arr_zipped = []; - for ($i = 0; $i <= count($result) / 2; $i+=2) { - $arr_zipped[$result[$i]] = $result[$i+1]; + $zipped = []; + for ($i = 0; $i <= count($result) / 2; $i += 2) { + $zipped[$result[$i]] = $result[$i+1]; } - $result = $arr_zipped; + $result = $zipped; // Make sure the elements are correct, and have zero counts foreach([$c1,$c2] as $channel) { - $this->assertTrue(isset($result[$channel])); - $this->assertEquals($result[$channel], 0); + $this->assertArrayKey($result, $channel); + $this->assertEquals(0, $result[$channel]); } // PUBSUB NUMPAT $result = $this->redis->pubsub("somekey", "numpat"); - $this->assertTrue(is_int($result)); + $this->assertIsInt($result); // Invalid call $this->assertFalse($this->redis->pubsub("somekey", "notacommand")); @@ -324,40 +482,39 @@ public function testPubSub() { * be set, but rather will only fail per-node when that is the case */ public function testMSetNX() { /* All of these keys should get set */ - $this->redis->del('x','y','z'); - $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']); - $this->assertTrue(is_array($ret)); + $this->redis->del('x', 'y', 'z'); + $ret = $this->redis->msetnx(['x'=>'a', 'y'=>'b', 'z'=>'c']); + $this->assertIsArray($ret); $this->assertEquals(array_sum($ret),count($ret)); /* Delete one key */ $this->redis->del('x'); - $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']); - $this->assertTrue(is_array($ret)); - $this->assertEquals(array_sum($ret),1); + $ret = $this->redis->msetnx(['x'=>'a', 'y'=>'b', 'z'=>'c']); + $this->assertIsArray($ret); + $this->assertEquals(1, array_sum($ret)); - $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE + $this->assertFalse($this->redis->msetnx([])); // set ø → FALSE } /* Slowlog needs to take a key or [ip, port], to direct it to a node */ public function testSlowlog() { - $str_key = uniqid() . '-' . rand(1, 1000); + $key = uniqid() . '-' . rand(1, 1000); - $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get'))); - $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get', 10))); - $this->assertTrue(is_int($this->redis->slowlog($str_key, 'len'))); - $this->assertTrue($this->redis->slowlog($str_key, 'reset')); - $this->assertFalse($this->redis->slowlog($str_key, 'notvalid')); + $this->assertIsArray($this->redis->slowlog($key, 'get')); + $this->assertIsArray($this->redis->slowlog($key, 'get', 10)); + $this->assertIsInt($this->redis->slowlog($key, 'len')); + $this->assertTrue($this->redis->slowlog($key, 'reset')); + $this->assertFalse(@$this->redis->slowlog($key, 'notvalid')); } /* INFO COMMANDSTATS requires a key or ip:port for node direction */ public function testInfoCommandStats() { - $str_key = uniqid() . '-' . rand(1,1000); - $arr_info = $this->redis->info($str_key, "COMMANDSTATS"); + $info = $this->redis->info(uniqid(), "COMMANDSTATS"); - $this->assertTrue(is_array($arr_info)); - if (is_array($arr_info)) { - foreach($arr_info as $k => $str_value) { - $this->assertTrue(strpos($k, 'cmdstat_') !== false); + $this->assertIsArray($info); + if (is_array($info)) { + foreach($info as $k => $value) { + $this->assertStringContains('cmdstat_', $k); } } } @@ -375,7 +532,7 @@ public function testFailedTransactions() { // This transaction should fail because the other client changed 'x' $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === [false]); + $this->assertEquals([false], $ret); // watch and unwatch $this->redis->watch('x'); $r->incr('x'); // other instance @@ -383,16 +540,13 @@ public function testFailedTransactions() { // This should succeed as the watch has been cancelled $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === array('44')); + $this->assertEquals(['44'], $ret); } - public function testDiscard() - { - /* start transaction */ + public function testDiscard() { $this->redis->multi(); - - /* Set and get in our transaction */ - $this->redis->set('pipecount','over9000')->get('pipecount'); + $this->redis->set('pipecount', 'over9000'); + $this->redis->get('pipecount'); $this->assertTrue($this->redis->discard()); } @@ -400,10 +554,10 @@ public function testDiscard() /* RedisCluster::script() is a 'raw' command, which requires a key such that * we can direct it to a given node */ public function testScript() { - $str_key = uniqid() . '-' . rand(1,1000); + $key = uniqid() . '-' . rand(1, 1000); // Flush any scripts we have - $this->assertTrue($this->redis->script($str_key, 'flush')); + $this->assertTrue($this->redis->script($key, 'flush')); // Silly scripts to test against $s1_src = 'return 1'; @@ -414,31 +568,31 @@ public function testScript() { $s3_sha = sha1($s3_src); // None should exist - $result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha); - $this->assertTrue(is_array($result) && count($result) == 3); + $result = $this->redis->script($key, 'exists', $s1_sha, $s2_sha, $s3_sha); + $this->assertIsArray($result, 3); $this->assertTrue(is_array($result) && count(array_filter($result)) == 0); // Load them up - $this->assertTrue($this->redis->script($str_key, 'load', $s1_src) == $s1_sha); - $this->assertTrue($this->redis->script($str_key, 'load', $s2_src) == $s2_sha); - $this->assertTrue($this->redis->script($str_key, 'load', $s3_src) == $s3_sha); + $this->assertEquals($s1_sha, $this->redis->script($key, 'load', $s1_src)); + $this->assertEquals($s2_sha, $this->redis->script($key, 'load', $s2_src)); + $this->assertEquals($s3_sha, $this->redis->script($key, 'load', $s3_src)); // They should all exist - $result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha); + $result = $this->redis->script($key, 'exists', $s1_sha, $s2_sha, $s3_sha); $this->assertTrue(is_array($result) && count(array_filter($result)) == 3); } /* RedisCluster::EVALSHA needs a 'key' to let us know which node we want to * direct the command at */ public function testEvalSHA() { - $str_key = uniqid() . '-' . rand(1,1000); + $key = uniqid() . '-' . rand(1, 1000); // Flush any loaded scripts - $this->redis->script($str_key, 'flush'); + $this->redis->script($key, 'flush'); - // Non existant script (but proper sha1), and a random (not) sha1 string - $this->assertFalse($this->redis->evalsha(sha1(uniqid()),[$str_key], 1)); - $this->assertFalse($this->redis->evalsha('some-random-data'),[$str_key], 1); + // Non existent script (but proper sha1), and a random (not) sha1 string + $this->assertFalse($this->redis->evalsha(sha1(uniqid()),[$key], 1)); + $this->assertFalse($this->redis->evalsha('some-random-data'),[$key], 1); // Load a script $cb = uniqid(); // To ensure the script is new @@ -446,154 +600,163 @@ public function testEvalSHA() { $sha = sha1($scr); // Run it when it doesn't exist, run it with eval, and then run it with sha1 - $this->assertTrue(false === $this->redis->evalsha($scr,[$str_key], 1)); - $this->assertTrue(1 === $this->redis->eval($scr,[$str_key], 1)); - $this->assertTrue(1 === $this->redis->evalsha($sha,[$str_key], 1)); + $this->assertFalse($this->redis->evalsha($scr,[$key], 1)); + $this->assertEquals(1, $this->redis->eval($scr,[$key], 1)); + $this->assertEquals(1, $this->redis->evalsha($sha,[$key], 1)); } public function testEvalBulkResponse() { - $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; - $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $key1 = uniqid() . '-' . rand(1, 1000) . '{hash}'; + $key2 = uniqid() . '-' . rand(1, 1000) . '{hash}'; - $this->redis->script($str_key1, 'flush'); - $this->redis->script($str_key2, 'flush'); + $this->redis->script($key1, 'flush'); + $this->redis->script($key2, 'flush'); $scr = "return {KEYS[1],KEYS[2]}"; - $result = $this->redis->eval($scr,[$str_key1, $str_key2], 2); + $result = $this->redis->eval($scr,[$key1, $key2], 2); - $this->assertTrue($str_key1 === $result[0]); - $this->assertTrue($str_key2 === $result[1]); + $this->assertEquals($key1, $result[0]); + $this->assertEquals($key2, $result[1]); } public function testEvalBulkResponseMulti() { - $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; - $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $key1 = uniqid() . '-' . rand(1, 1000) . '{hash}'; + $key2 = uniqid() . '-' . rand(1, 1000) . '{hash}'; - $this->redis->script($str_key1, 'flush'); - $this->redis->script($str_key2, 'flush'); + $this->redis->script($key1, 'flush'); + $this->redis->script($key2, 'flush'); $scr = "return {KEYS[1],KEYS[2]}"; $this->redis->multi(); - $this->redis->eval($scr, [$str_key1, $str_key2], 2); + $this->redis->eval($scr, [$key1, $key2], 2); $result = $this->redis->exec(); - $this->assertTrue($str_key1 === $result[0][0]); - $this->assertTrue($str_key2 === $result[0][1]); + $this->assertEquals($key1, $result[0][0]); + $this->assertEquals($key2, $result[0][1]); } public function testEvalBulkEmptyResponse() { - $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; - $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $key1 = uniqid() . '-' . rand(1, 1000) . '{hash}'; + $key2 = uniqid() . '-' . rand(1, 1000) . '{hash}'; - $this->redis->script($str_key1, 'flush'); - $this->redis->script($str_key2, 'flush'); + $this->redis->script($key1, 'flush'); + $this->redis->script($key2, 'flush'); $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; - $result = $this->redis->eval($scr, [$str_key1, $str_key2], 2); + $result = $this->redis->eval($scr, [$key1, $key2], 2); - $this->assertTrue(null === $result); + $this->assertNull($result); } public function testEvalBulkEmptyResponseMulti() { - $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}'; - $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}'; + $key1 = uniqid() . '-' . rand(1, 1000) . '{hash}'; + $key2 = uniqid() . '-' . rand(1, 1000) . '{hash}'; - $this->redis->script($str_key1, 'flush'); - $this->redis->script($str_key2, 'flush'); + $this->redis->script($key1, 'flush'); + $this->redis->script($key2, 'flush'); $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end"; $this->redis->multi(); - $this->redis->eval($scr, [$str_key1, $str_key2], 2); + $this->redis->eval($scr, [$key1, $key2], 2); $result = $this->redis->exec(); - $this->assertTrue(null === $result[0]); + $this->assertNull($result[0]); } /* Cluster specific introspection stuff */ public function testIntrospection() { - $arr_masters = $this->redis->_masters(); - $this->assertTrue(is_array($arr_masters)); + $primaries = $this->redis->_masters(); + $this->assertIsArray($primaries); - foreach ($arr_masters as $arr_info) { - $this->assertTrue(is_array($arr_info)); - $this->assertTrue(is_string($arr_info[0])); - $this->assertTrue(is_long($arr_info[1])); + foreach ($primaries as [$host, $port]) { + $this->assertIsString($host); + $this->assertIsInt($port); } } - protected function genKeyName($i_key_idx, $i_type) { - switch ($i_type) { + protected function keyTypeToString($key_type) { + switch ($key_type) { case Redis::REDIS_STRING: - return "string-$i_key_idx"; + return "string"; case Redis::REDIS_SET: - return "set-$i_key_idx"; + return "set"; case Redis::REDIS_LIST: - return "list-$i_key_idx"; + return "list"; case Redis::REDIS_ZSET: - return "zset-$i_key_idx"; + return "zset"; case Redis::REDIS_HASH: - return "hash-$i_key_idx"; + return "hash"; + case Redis::REDIS_STREAM: + return "stream"; + case Redis::REDIS_VECTORSET: + return "vectorset"; default: - return "unknown-$i_key_idx"; + return "unknown($key_type)"; } + } - protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) { - $str_key = $this->genKeyName($i_key_idx, $i_type); + protected function genKeyName($key_index, $key_type) { + return sprintf('%s-%s', $this->keyTypeToString($key_type), $key_index); + } - $this->redis->del($str_key); + protected function setKeyVals($key_index, $key_type, &$arr_ref) { + $key = $this->genKeyName($key_index, $key_type); - switch ($i_type) { + $this->redis->del($key); + + switch ($key_type) { case Redis::REDIS_STRING: - $value = "$str_key-value"; - $this->redis->set($str_key, $value); + $value = "$key-value"; + $this->redis->set($key, $value); break; case Redis::REDIS_SET: $value = [ - $str_key . '-mem1', $str_key . '-mem2', $str_key . '-mem3', - $str_key . '-mem4', $str_key . '-mem5', $str_key . '-mem6' + "$key-mem1", "$key-mem2", "$key-mem3", + "$key-mem4", "$key-mem5", "$key-mem6" ]; - $arr_args = $value; - array_unshift($arr_args, $str_key); - call_user_func_array([$this->redis, 'sadd'], $arr_args); + $args = $value; + array_unshift($args, $key); + call_user_func_array([$this->redis, 'sadd'], $args); break; case Redis::REDIS_HASH: $value = [ - $str_key . '-mem1' => $str_key . '-val1', - $str_key . '-mem2' => $str_key . '-val2', - $str_key . '-mem3' => $str_key . '-val3' + "$key-mem1" => "$key-val1", + "$key-mem2" => "$key-val2", + "$key-mem3" => "$key-val3" ]; - $this->redis->hmset($str_key, $value); + $this->redis->hmset($key, $value); break; case Redis::REDIS_LIST: $value = [ - $str_key . '-ele1', $str_key . '-ele2', $str_key . '-ele3', - $str_key . '-ele4', $str_key . '-ele5', $str_key . '-ele6' + "$key-ele1", "$key-ele2", "$key-ele3", + "$key-ele4", "$key-ele5", "$key-ele6" ]; - $arr_args = $value; - array_unshift($arr_args, $str_key); - call_user_func_array([$this->redis, 'rpush'], $arr_args); + $args = $value; + array_unshift($args, $key); + call_user_func_array([$this->redis, 'rpush'], $args); break; case Redis::REDIS_ZSET: - $i_score = 1; + $score = 1; $value = [ - $str_key . '-mem1' => 1, $str_key . '-mem2' => 2, - $str_key . '-mem3' => 3, $str_key . '-mem3' => 3 + "$key-mem1" => 1, "$key-mem2" => 2, + "$key-mem3" => 3, "$key-mem3" => 3 ]; - foreach ($value as $str_mem => $i_score) { - $this->redis->zadd($str_key, $i_score, $str_mem); + foreach ($value as $mem => $score) { + $this->redis->zadd($key, $score, $mem); } break; } /* Update our reference array so we can verify values */ - $arr_ref[$str_key] = $value; - return $str_key; + $arr_ref[$key] = $value; + + return $key; } /* Verify that our ZSET values are identical */ @@ -605,56 +768,56 @@ protected function checkZSetEquality($a, $b) { array_sum($a) != array_sum($b); if ($boo_diff) { - $this->assertEquals($a,$b); + $this->assertEquals($a, $b); return; } } - protected function checkKeyValue($str_key, $i_type, $value) { - switch ($i_type) { + protected function checkKeyValue($key, $key_type, $value) { + switch ($key_type) { case Redis::REDIS_STRING: - $this->assertEquals($value, $this->redis->get($str_key)); + $this->assertEquals($value, $this->redis->get($key)); break; case Redis::REDIS_SET: - $arr_r_values = $this->redis->sMembers($str_key); + $arr_r_values = $this->redis->sMembers($key); $arr_l_values = $value; sort($arr_r_values); sort($arr_l_values); $this->assertEquals($arr_r_values, $arr_l_values); break; case Redis::REDIS_LIST: - $this->assertEquals($value, $this->redis->lrange($str_key,0,-1)); + $this->assertEquals($value, $this->redis->lrange($key, 0, -1)); break; case Redis::REDIS_HASH: - $this->assertEquals($value, $this->redis->hgetall($str_key)); + $this->assertEquals($value, $this->redis->hgetall($key)); break; case Redis::REDIS_ZSET: - $this->checkZSetEquality($value, $this->redis->zrange($str_key,0,-1,true)); + $this->checkZSetEquality($value, $this->redis->zrange($key, 0, -1, true)); break; default: - throw new Exception("Unknown type " . $i_type); + throw new Exception("Unknown type " . $key_type); } } /* Test automatic load distributor */ public function testFailOver() { - $arr_value_ref = []; - $arr_type_ref = []; + $value_ref = []; + $type_ref = []; /* Set a bunch of keys of various redis types*/ for ($i = 0; $i < 200; $i++) { - foreach ($this->_arr_redis_types as $i_type) { - $str_key = $this->setKeyVals($i, $i_type, $arr_value_ref); - $arr_type_ref[$str_key] = $i_type; + foreach ($this->redis_types as $type) { + $key = $this->setKeyVals($i, $type, $value_ref); + $type_ref[$key] = $type; } } /* Iterate over failover options */ - foreach ($this->_arr_failover_types as $i_opt) { - $this->redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $i_opt); + foreach ($this->failover_types as $failover_type) { + $this->redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $failover_type); - foreach ($arr_value_ref as $str_key => $value) { - $this->checkKeyValue($str_key, $arr_type_ref[$str_key], $value); + foreach ($value_ref as $key => $value) { + $this->checkKeyValue($key, $type_ref[$key], $value); } break; @@ -664,11 +827,11 @@ public function testFailOver() { /* Test a 'raw' command */ public function testRawCommand() { $this->redis->rawCommand('mykey', 'set', 'mykey', 'my-value'); - $this->assertEquals($this->redis->get('mykey'), 'my-value'); + $this->assertEquals('my-value', $this->redis->get('mykey')); $this->redis->del('mylist'); - $this->redis->rpush('mylist', 'A','B','C','D'); - $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']); + $this->redis->rpush('mylist', 'A', 'B', 'C', 'D'); + $this->assertEquals(['A', 'B', 'C', 'D'], $this->redis->lrange('mylist', 0, -1)); } protected function rawCommandArray($key, $args) { @@ -700,7 +863,7 @@ public function testReplyLiteral() { the command to a specific node. */ public function testAcl() { if ( ! $this->minVersionCheck("6.0")) - return $this->markTestSkipped(); + $this->markTestSkipped(); $this->assertInArray('default', $this->redis->acl('foo', 'USERS')); } @@ -708,12 +871,14 @@ public function testAcl() { public function testSession() { @ini_set('session.save_handler', 'rediscluster'); - @ini_set('session.save_path', $this->getFullHostPath() . '&failover=error'); - if (!@session_start()) { - return $this->markTestSkipped(); - } + @ini_set('session.save_path', $this->sessionSavePath() . '&failover=error'); + + if ( ! @session_start()) + $this->markTestSkipped(); + session_write_close(); - $this->assertTrue($this->redis->exists('PHPREDIS_CLUSTER_SESSION:' . session_id())); + + $this->assertKeyExists($this->sessionPrefix() . session_id()); } @@ -723,8 +888,8 @@ public function testSlotCache() { $pong = 0; for ($i = 0; $i < 10; $i++) { - $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth()); - $pong += $obj_rc->ping("key:$i"); + $new_client = $this->newInstance(); + $pong += $new_client->ping("key:$i"); } $this->assertEquals($pong, $i); @@ -739,24 +904,29 @@ public function testConnectionPool() { $pong = 0; for ($i = 0; $i < 10; $i++) { - $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth()); - $pong += $obj_rc->ping("key:$i"); + $new_client = $this->newInstance(); + $pong += $new_client->ping("key:$i"); } $this->assertEquals($pong, $i); ini_set('redis.pconnect.pooling_enabled', $prev_value); } + protected function sessionPrefix(): string { + return 'PHPREDIS_CLUSTER_SESSION:'; + } + + protected function sessionSaveHandler(): string { + return 'rediscluster'; + } + /** * @inheritdoc */ - protected function getFullHostPath() - { - $auth = $this->getAuthFragment(); - + protected function sessionSavePath(): string { return implode('&', array_map(function ($host) { return 'seed[]=' . $host; - }, self::$_arr_node_map)) . ($auth ? "&$auth" : ''); + }, self::$seeds)) . '&' . $this->getAuthFragment(); } /* Test correct handling of null multibulk replies */ @@ -778,5 +948,9 @@ public function testNullArray() { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } + + protected function execWaitAOF() { + return $this->redis->waitaof(uniqid(), 0, 0, 0); + } } ?> diff --git a/tests/RedisSentinelTest.php b/tests/RedisSentinelTest.php index 0fdc3a957e..a624971b69 100644 --- a/tests/RedisSentinelTest.php +++ b/tests/RedisSentinelTest.php @@ -1,6 +1,6 @@ checkFields($slave); } } + + protected function getClients(Redis $redis, string $cmd) + { + $result = []; + + foreach ($redis->client('list') as $client) { + if ($client['cmd'] !== $cmd) + continue; + + $result[] = $client['id']; + } + + return $result; + } + + public function testPersistent() { + /* I think the tests just use the default port */ + $redis = new Redis; + $redis->connect($this->getHost(), 26379); + + $id = null; + + for ($i = 0; $i < 3; $i++) { + $sentinel = new RedisSentinel([ + 'host' => $this->getHost(), + 'persistent' => 'sentinel', + ]); + + $this->assertTrue($sentinel->ping()); + + $clients = $this->getClients($redis, 'ping'); + + /* Capture the ping client */ + $id ??= $clients[0]; + + unset($sentinel); + } + + /* The same client should have been reused */ + $this->assertEquals($id, $clients[0]); + } } diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 20aabf8bcc..e682e23f22 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -1,9 +1,14 @@ - [-121.837478, 39.728494], @@ -18,35 +23,83 @@ class Redis_Test extends TestSuite Redis::SERIALIZER_PHP, ]; - /** - * @var Redis - */ - public $redis; + protected function getNilValue() { + return FALSE; + } - /** - * @var string - */ - protected $sessionPrefix = 'PHPREDIS_SESSION:'; + protected function getSerializers() { + $result = [Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP]; - /** - * @var string - */ - protected $sessionSaveHandler = 'redis'; + if (defined('Redis::SERIALIZER_IGBINARY')) + $result[] = Redis::SERIALIZER_IGBINARY; + if (defined('Redis::SERIALIZER_JSON')) + $result[] = Redis::SERIALIZER_JSON; + if (defined('Redis::SERIALIZER_MSGPACK')) + $result[] = Redis::SERIALIZER_MSGPACK; + + return $result; + } + + protected function getCompressors() { + $result['none'] = Redis::COMPRESSION_NONE; + if (defined('Redis::COMPRESSION_LZF')) + $result['lzf'] = Redis::COMPRESSION_LZF; + if (defined('Redis::COMPRESSION_LZ4')) + $result['lz4'] = Redis::COMPRESSION_LZ4; + if (defined('Redis::COMPRESSION_ZSTD')) + $result['zstd'] = Redis::COMPRESSION_ZSTD; + + return $result; + } + + /* Overridable left/right constants */ + protected function getLeftConstant() { + return Redis::LEFT; + } + + protected function getRightConstant() { + return Redis::RIGHT; + } + + protected function detectKeyDB($info) { + if (!is_array($info)) + return false; + + return strpos($info['executable'] ?? '', 'keydb') !== false || + isset($info['keydb']) || isset($info['mvcc_depth']); + } + + protected function detectValkey($info) { + return is_array($info) && ($info['executable'] ?? '') === 'valkey'; + } public function setUp() { $this->redis = $this->newInstance(); + $info = $this->redis->info(); - $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0'); - if (defined('Redis::SERIALIZER_IGBINARY')) { - $this->serializers[] = Redis::SERIALIZER_IGBINARY; - } + $this->version = $info['redis_version'] ?? '0.0.0'; + $this->valkey_version = $info['valkey_version'] ?? '0.0.0'; + + $this->is_keydb = $this->detectKeyDB($info); + $this->is_valkey = $this->detectValKey($info); + } + + protected function haveCommand(string $cmd): bool { + $info = $this->redis->command('info', $cmd); + $name = $info[0][0] ?? null; + + return $name && strcasecmp($cmd, $name) === 0; } protected function minVersionCheck($version) { return version_compare($this->version, $version) >= 0; } + protected function minValkeyVersionCheck($version) { + return $this->is_valkey && version_compare($this->valkey_version, $version) >= 0; + } + protected function mstime() { return round(microtime(true)*1000); } @@ -69,38 +122,29 @@ protected function getAuthParts(&$user, &$pass) { } } - protected function getAuthFragment() { - static $_authidx = 0; - $_authidx++; + protected function sessionPrefix(): string { + return 'PHPREDIS_SESSION:'; + } + + protected function sessionSaveHandler(): string { + return 'redis'; + } + protected function sessionSavePath(): string { + return sprintf('tcp://%s:%d?%s', $this->getHost(), $this->getPort(), + $this->getAuthFragment()); + } + + protected function getAuthFragment() { $this->getAuthParts($user, $pass); if ($user && $pass) { - if ($_authidx % 2 == 0) - return "auth[user]=$user&auth[pass]=$pass"; - else - return "auth[]=$user&auth[]=$pass"; + return sprintf('auth[user]=%s&auth[pass]=%s', $user, $pass); } else if ($pass) { - if ($_authidx % 3 == 0) - return "auth[pass]=$pass"; - if ($_authidx % 2 == 0) - return "auth[]=$pass"; - else - return "auth=$pass"; + return sprintf('auth[pass]=%s', $pass); } else { - return NULL; - } - } - - protected function getFullHostPath() - { - $fullHostPath = parent::getFullHostPath(); - $authFragment = $this->getAuthFragment(); - - if (isset($fullHostPath) && $authFragment) { - $fullHostPath .= "?$authFragment"; + return ''; } - return $fullHostPath; } protected function newInstance() { @@ -109,34 +153,34 @@ protected function newInstance() { 'port' => $this->getPort(), ]); - if($this->getAuth()) { + if ($this->getAuth()) { $this->assertTrue($r->auth($this->getAuth())); } return $r; } public function tearDown() { - if($this->redis) { + if ($this->redis) { $this->redis->close(); } } - public function reset() - { + public function reset() { $this->setUp(); $this->tearDown(); } - /* Helper function to determine if the clsas has pipeline support */ + /* Helper function to determine if the class has pipeline support */ protected function havePipeline() { - $str_constant = get_class($this->redis) . '::PIPELINE'; - return defined($str_constant); + return defined(get_class($this->redis) . '::PIPELINE'); } - public function testMinimumVersion() - { - // Minimum server version required for tests - $this->assertTrue(version_compare($this->version, "2.4.0") >= 0); + protected function haveMulti() { + return defined(get_class($this->redis) . '::MULTI'); + } + + public function testMinimumVersion() { + $this->assertTrue(version_compare($this->version, '2.4.0') >= 0); } public function testPing() { @@ -146,64 +190,62 @@ public function testPing() { $this->assertEquals('BEEP', $this->redis->ping('BEEP')); /* Make sure we're good in MULTI mode */ - $this->redis->multi(); - - $this->redis->ping(); - $this->redis->ping('BEEP'); - $this->assertEquals([true, 'BEEP'], $this->redis->exec()); + if ($this->haveMulti()) { + $this->assertEquals( + [true, 'BEEP'], + $this->redis->multi() + ->ping() + ->ping('BEEP') + ->exec() + ); + } } public function testPipelinePublish() { - if (!$this->havePipeline()) { - $this->markTestSkipped(); - } - $ret = $this->redis->pipeline() ->publish('chan', 'msg') ->exec(); - $this->assertTrue(is_array($ret) && count($ret) === 1 && $ret[0] >= 0); + $this->assertIsArray($ret, 1); + $this->assertGT(-1, $ret[0] ?? -1); } // Run some simple tests against the PUBSUB command. This is problematic, as we // can't be sure what's going on in the instance, but we can do some things. public function testPubSub() { // Only available since 2.8.0 - if (version_compare($this->version, "2.8.0") < 0) { + if (version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped(); - return; - } // PUBSUB CHANNELS ... - $result = $this->redis->pubsub("channels", "*"); - $this->assertTrue(is_array($result)); - $result = $this->redis->pubsub("channels"); - $this->assertTrue(is_array($result)); + $result = $this->redis->pubsub('channels', '*'); + $this->assertIsArray($result); + $result = $this->redis->pubsub('channels'); + $this->assertIsArray($result); // PUBSUB NUMSUB - $c1 = uniqid() . '-' . rand(1,100); - $c2 = uniqid() . '-' . rand(1,100); + $c1 = uniqid(); + $c2 = uniqid(); - $result = $this->redis->pubsub("numsub", [$c1, $c2]); + $result = $this->redis->pubsub('numsub', [$c1, $c2]); // Should get an array back, with two elements - $this->assertTrue(is_array($result)); - $this->assertEquals(count($result), 2); + $this->assertIsArray($result); + $this->assertEquals(2, count($result)); // Make sure the elements are correct, and have zero counts - foreach([$c1,$c2] as $channel) { - $this->assertTrue(isset($result[$channel])); - $this->assertEquals($result[$channel], 0); + foreach ([$c1,$c2] as $channel) { + $this->assertArrayKeyEquals($result, $channel, 0); } // PUBSUB NUMPAT - $result = $this->redis->pubsub("numpat"); - $this->assertTrue(is_int($result)); + $result = $this->redis->pubsub('numpat'); + $this->assertIsInt($result); // Invalid calls - $this->assertFalse(@$this->redis->pubsub("notacommand")); - $this->assertFalse(@$this->redis->pubsub("numsub", "not-an-array")); + $this->assertFalse(@$this->redis->pubsub('notacommand')); + $this->assertFalse(@$this->redis->pubsub('numsub', 'not-an-array')); } /* These test cases were generated randomly. We're just trying to test @@ -225,17 +267,19 @@ public function testBitcount() { $this->redis->set('bitcountkey', hex2bin('10eb8939e68bfdb640260f0629f3')); $this->assertEquals(1, $this->redis->bitcount('bitcountkey', 8, 8, false)); - /* key, start, end, BIT */ - $this->redis->set('bitcountkey', hex2bin('cd0e4c80f9e4590d888a10')); - $this->assertEquals(5, $this->redis->bitcount('bitcountkey', 0, 9, true)); + if ( ! $this->is_keydb && $this->minVersionCheck('7.0')) { + /* key, start, end, BIT */ + $this->redis->set('bitcountkey', hex2bin('cd0e4c80f9e4590d888a10')); + $this->assertEquals(5, $this->redis->bitcount('bitcountkey', 0, 9, true)); + } } public function testBitop() { - if (!$this->minVersionCheck('2.6.0')) + if ( ! $this->minVersionCheck('2.6.0')) $this->markTestSkipped(); - $this->redis->set("{key}1", "foobar"); - $this->redis->set("{key}2", "abcdef"); + $this->redis->set('{key}1', 'foobar'); + $this->redis->set('{key}2', 'abcdef'); // Regression test for GitHub issue #2210 $this->assertEquals(6, $this->redis->bitop('AND', '{key}1', '{key}2')); @@ -243,20 +287,19 @@ public function testBitop() { // Make sure RedisCluster doesn't even send the command. We don't care // about what Redis returns @$this->redis->bitop('AND', 'key1', 'key2', 'key3'); - $this->assertEquals(NULL, $this->redis->getLastError()); + $this->assertNull($this->redis->getLastError()); $this->redis->del('{key}1', '{key}2'); } public function testBitsets() { - $this->redis->del('key'); $this->assertEquals(0, $this->redis->getBit('key', 0)); - $this->assertEquals(FALSE, $this->redis->getBit('key', -1)); + $this->assertFalse($this->redis->getBit('key', -1)); $this->assertEquals(0, $this->redis->getBit('key', 100000)); $this->redis->set('key', "\xff"); - for($i = 0; $i < 8; $i++) { + for ($i = 0; $i < 8; $i++) { $this->assertEquals(1, $this->redis->getBit('key', $i)); } $this->assertEquals(0, $this->redis->getBit('key', 8)); @@ -265,23 +308,23 @@ public function testBitsets() { $this->assertEquals(1, $this->redis->setBit('key', 0, 0)); $this->assertEquals(0, $this->redis->setBit('key', 0, 0)); $this->assertEquals(0, $this->redis->getBit('key', 0)); - $this->assertEquals("\x7f", $this->redis->get('key')); + $this->assertKeyEquals("\x7f", 'key'); // change bit 1 $this->assertEquals(1, $this->redis->setBit('key', 1, 0)); $this->assertEquals(0, $this->redis->setBit('key', 1, 0)); $this->assertEquals(0, $this->redis->getBit('key', 1)); - $this->assertEquals("\x3f", $this->redis->get('key')); + $this->assertKeyEquals("\x3f", 'key'); // change bit > 1 $this->assertEquals(1, $this->redis->setBit('key', 2, 0)); $this->assertEquals(0, $this->redis->setBit('key', 2, 0)); $this->assertEquals(0, $this->redis->getBit('key', 2)); - $this->assertEquals("\x1f", $this->redis->get('key')); + $this->assertKeyEquals("\x1f", 'key'); // values above 1 are changed to 1 but don't overflow on bits to the right. $this->assertEquals(0, $this->redis->setBit('key', 0, 0xff)); - $this->assertEquals("\x9f", $this->redis->get('key')); + $this->assertKeyEquals("\x9f", 'key'); // Verify valid offset ranges $this->assertFalse($this->redis->getBit('key', -1)); @@ -291,6 +334,8 @@ public function testBitsets() { } public function testLcs() { + if ( ! $this->minVersionCheck('7.0.0') || $this->is_keydb) + $this->markTestSkipped(); $key1 = '{lcs}1'; $key2 = '{lcs}2'; $this->assertTrue($this->redis->set($key1, '12244447777777')); @@ -313,14 +358,13 @@ public function testLcs() { } public function testLmpop() { - if(version_compare($this->version, "7.0.0") < 0) { + if (version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $key1 = '{l}1'; $key2 = '{l}2'; - $this->assertTrue($this->redis->del($key1, $key2) !== false); + $this->redis->del($key1, $key2); $this->assertEquals(6, $this->redis->rpush($key1, 'A', 'B', 'C', 'D', 'E', 'F')); $this->assertEquals(6, $this->redis->rpush($key2, 'F', 'E', 'D', 'C', 'B', 'A')); @@ -333,14 +377,14 @@ public function testLmpop() { } public function testBLmpop() { - if(version_compare($this->version, "7.0.0") < 0) { + if (version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $key1 = '{bl}1'; $key2 = '{bl}2'; - $this->assertTrue($this->redis->del($key1, $key2) !== false); + $this->redis->del($key1, $key2); + $this->assertEquals(2, $this->redis->rpush($key1, 'A', 'B')); $this->assertEquals(2, $this->redis->rpush($key2, 'C', 'D')); @@ -351,18 +395,19 @@ public function testBLmpop() { $st = microtime(true); $this->assertFalse($this->redis->blmpop(.2, [$key1, $key2], 'LEFT')); $et = microtime(true); - $this->assertTrue($et - $st >= .2); + + // Very loose tolerance because CI is run on a potato + $this->assertBetween($et - $st, .05, .75); } function testZmpop() { - if(version_compare($this->version, "7.0.0") < 0) { + if (version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $key1 = '{z}1'; $key2 = '{z}2'; - $this->assertTrue($this->redis->del($key1, $key2) !== false); + $this->redis->del($key1, $key2); $this->assertEquals(4, $this->redis->zadd($key1, 0, 'zero', 2, 'two', 4, 'four', 6, 'six')); $this->assertEquals(4, $this->redis->zadd($key2, 1, 'one', 3, 'three', 5, 'five', 7, 'seven')); @@ -379,19 +424,18 @@ function testZmpop() { $this->assertFalse($this->redis->zmpop([$key1, $key2], 'MIN')); $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, true); - $this->assertEquals(NULL, $this->redis->zmpop([$key1, $key2], 'MIN')); + $this->assertNull($this->redis->zmpop([$key1, $key2], 'MIN')); $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } function testBZmpop() { - if(version_compare($this->version, "7.0.0") < 0) { + if (version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $key1 = '{z}1'; $key2 = '{z}2'; - $this->assertTrue($this->redis->del($key1, $key2) !== false); + $this->redis->del($key1, $key2); $this->assertEquals(2, $this->redis->zadd($key1, 0, 'zero', 2, 'two')); $this->assertEquals(2, $this->redis->zadd($key2, 1, 'one', 3, 'three')); @@ -407,11 +451,12 @@ function testBZmpop() { $st = microtime(true); $this->assertFalse($this->redis->bzmpop(.2, [$key1, $key2], 'MIN')); $et = microtime(true); - $this->assertTrue($et - $st >= .2); + + $this->assertBetween($et - $st, .05, .75); } public function testBitPos() { - if (version_compare($this->version, "2.8.7") < 0) { + if (version_compare($this->version, '2.8.7') < 0) { $this->MarkTestSkipped(); return; } @@ -419,16 +464,16 @@ public function testBitPos() { $this->redis->del('bpkey'); $this->redis->set('bpkey', "\xff\xf0\x00"); - $this->assertEquals($this->redis->bitpos('bpkey', 0), 12); + $this->assertEquals(12, $this->redis->bitpos('bpkey', 0)); $this->redis->set('bpkey', "\x00\xff\xf0"); - $this->assertEquals($this->redis->bitpos('bpkey', 1, 0), 8); - $this->assertEquals($this->redis->bitpos('bpkey', 1, 1), 8); + $this->assertEquals(8, $this->redis->bitpos('bpkey', 1, 0)); + $this->assertEquals(8, $this->redis->bitpos('bpkey', 1, 1)); $this->redis->set('bpkey', "\x00\x00\x00"); - $this->assertEquals($this->redis->bitpos('bpkey', 1), -1); + $this->assertEquals(-1, $this->redis->bitpos('bpkey', 1)); - if (!$this->minVersionCheck("7.0.0")) + if ( ! $this->minVersionCheck('7.0.0')) return; $this->redis->set('bpkey', "\xF"); @@ -437,200 +482,206 @@ public function testBitPos() { $this->assertEquals(-1, $this->redis->bitpos('bpkey', 1, 1, -1, false)); } - public function test1000() { - - $s = str_repeat('A', 1000); - $this->redis->set('x', $s); - $this->assertEquals($s, $this->redis->get('x')); - - $s = str_repeat('A', 1000000); - $this->redis->set('x', $s); - $this->assertEquals($s, $this->redis->get('x')); + public function testSetLargeKeys() { + foreach ([1000, 100000, 1000000] as $size) { + $value = str_repeat('A', $size); + $this->assertTrue($this->redis->set('x', $value)); + $this->assertKeyEquals($value, 'x'); + } } public function testEcho() { - $this->assertEquals($this->redis->echo("hello"), "hello"); - $this->assertEquals($this->redis->echo(""), ""); - $this->assertEquals($this->redis->echo(" 0123 "), " 0123 "); + $this->assertEquals('hello', $this->redis->echo('hello')); + $this->assertEquals('', $this->redis->echo('')); + $this->assertEquals(' 0123 ', $this->redis->echo(' 0123 ')); } public function testErr() { - - $this->redis->set('x', '-ERR'); - $this->assertEquals($this->redis->get('x'), '-ERR'); - + $this->redis->set('x', '-ERR'); + $this->assertKeyEquals('-ERR', 'x'); } - public function testSet() - { - $this->assertEquals(TRUE, $this->redis->set('key', 'nil')); - $this->assertEquals('nil', $this->redis->get('key')); + public function testSet() { + $this->assertTrue($this->redis->set('key', 'nil')); + $this->assertKeyEquals('nil', 'key'); - $this->assertEquals(TRUE, $this->redis->set('key', 'val')); + $this->assertTrue($this->redis->set('key', 'val')); - $this->assertEquals('val', $this->redis->get('key')); - $this->assertEquals('val', $this->redis->get('key')); + $this->assertKeyEquals('val', 'key'); + $this->assertKeyEquals('val', 'key'); $this->redis->del('keyNotExist'); - $this->assertEquals(FALSE, $this->redis->get('keyNotExist')); + $this->assertKeyMissing('keyNotExist'); $this->redis->set('key2', 'val'); - $this->assertEquals('val', $this->redis->get('key2')); + $this->assertKeyEquals('val', 'key2'); - $value = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + $value1 = bin2hex(random_bytes(rand(64, 128))); + $value2 = random_bytes(rand(65536, 65536 * 2));; - $this->redis->set('key2', $value); - $this->assertEquals($value, $this->redis->get('key2')); - $this->assertEquals($value, $this->redis->get('key2')); + $this->redis->set('key2', $value1); + $this->assertKeyEquals($value1, 'key2'); + $this->assertKeyEquals($value1, 'key2'); $this->redis->del('key'); $this->redis->del('key2'); - $i = 66000; - $value2 = 'X'; - while($i--) { - $value2 .= 'A'; - } - $value2 .= 'X'; - $this->redis->set('key', $value2); - $this->assertEquals($value2, $this->redis->get('key')); + $this->assertKeyEquals($value2, 'key'); $this->redis->del('key'); - $this->assertEquals(False, $this->redis->get('key')); + $this->assertKeyMissing('key'); $data = gzcompress('42'); - $this->assertEquals(True, $this->redis->set('key', $data)); + $this->assertTrue($this->redis->set('key', $data)); $this->assertEquals('42', gzuncompress($this->redis->get('key'))); $this->redis->del('key'); $data = gzcompress('value1'); - $this->assertEquals(True, $this->redis->set('key', $data)); + $this->assertTrue($this->redis->set('key', $data)); $this->assertEquals('value1', gzuncompress($this->redis->get('key'))); $this->redis->del('key'); - $this->assertEquals(TRUE, $this->redis->set('key', 0)); - $this->assertEquals('0', $this->redis->get('key')); - $this->assertEquals(TRUE, $this->redis->set('key', 1)); - $this->assertEquals('1', $this->redis->get('key')); - $this->assertEquals(TRUE, $this->redis->set('key', 0.1)); - $this->assertEquals('0.1', $this->redis->get('key')); - $this->assertEquals(TRUE, $this->redis->set('key', '0.1')); - $this->assertEquals('0.1', $this->redis->get('key')); - $this->assertEquals(TRUE, $this->redis->set('key', TRUE)); - $this->assertEquals('1', $this->redis->get('key')); - - $this->assertEquals(True, $this->redis->set('key', '')); - $this->assertEquals('', $this->redis->get('key')); - $this->assertEquals(True, $this->redis->set('key', NULL)); - $this->assertEquals('', $this->redis->get('key')); - - $this->assertEquals(True, $this->redis->set('key', gzcompress('42'))); + $this->assertTrue($this->redis->set('key', 0)); + $this->assertKeyEquals('0', 'key'); + $this->assertTrue($this->redis->set('key', 1)); + $this->assertKeyEquals('1', 'key'); + $this->assertTrue($this->redis->set('key', 0.1)); + $this->assertKeyEquals('0.1', 'key'); + $this->assertTrue($this->redis->set('key', '0.1')); + $this->assertKeyEquals('0.1', 'key'); + $this->assertTrue($this->redis->set('key', true)); + $this->assertKeyEquals('1', 'key'); + + $this->assertTrue($this->redis->set('key', '')); + $this->assertKeyEquals('', 'key'); + $this->assertTrue($this->redis->set('key', NULL)); + $this->assertKeyEquals('', 'key'); + + $this->assertTrue($this->redis->set('key', gzcompress('42'))); $this->assertEquals('42', gzuncompress($this->redis->get('key'))); } /* Extended SET options for Redis >= 2.6.12 */ public function testExtendedSet() { // Skip the test if we don't have a new enough version of Redis - if (version_compare($this->version, '2.6.12') < 0) { + if (version_compare($this->version, '2.6.12') < 0) $this->markTestSkipped(); - return; - } /* Legacy SETEX redirection */ $this->redis->del('foo'); - $this->assertTrue($this->redis->set('foo','bar', 20)); - $this->assertEquals($this->redis->get('foo'), 'bar'); - $this->assertEquals($this->redis->ttl('foo'), 20); + $this->assertTrue($this->redis->set('foo', 'bar', 20)); + $this->assertKeyEquals('bar', 'foo'); + $this->assertEquals(20, $this->redis->ttl('foo')); /* Should coerce doubles into long */ $this->assertTrue($this->redis->set('foo', 'bar-20.5', 20.5)); - $this->assertEquals($this->redis->ttl('foo'), 20); - $this->assertEquals($this->redis->get('foo'), 'bar-20.5'); + $this->assertEquals(20, $this->redis->ttl('foo')); + $this->assertKeyEquals('bar-20.5', 'foo'); /* Invalid third arguments */ - $this->assertFalse(@$this->redis->set('foo','bar','baz')); - $this->assertFalse(@$this->redis->set('foo','bar',new StdClass())); + $this->assertFalse(@$this->redis->set('foo', 'bar', 'baz')); + $this->assertFalse(@$this->redis->set('foo', 'bar',new StdClass())); /* Set if not exist */ $this->redis->del('foo'); - $this->assertTrue($this->redis->set('foo','bar', ['nx'])); - $this->assertEquals($this->redis->get('foo'), 'bar'); - $this->assertFalse($this->redis->set('foo','bar', ['nx'])); + $this->assertTrue($this->redis->set('foo', 'bar', ['nx'])); + $this->assertKeyEquals('bar', 'foo'); + $this->assertFalse($this->redis->set('foo', 'bar', ['nx'])); /* Set if exists */ - $this->assertTrue($this->redis->set('foo','bar', ['xx'])); - $this->assertEquals($this->redis->get('foo'), 'bar'); + $this->assertTrue($this->redis->set('foo', 'bar', ['xx'])); + $this->assertKeyEquals('bar', 'foo'); $this->redis->del('foo'); - $this->assertFalse($this->redis->set('foo','bar', ['xx'])); + $this->assertFalse($this->redis->set('foo', 'bar', ['xx'])); /* Set with a TTL */ - $this->assertTrue($this->redis->set('foo','bar', ['ex'=>100])); - $this->assertEquals($this->redis->ttl('foo'), 100); + $this->assertTrue($this->redis->set('foo', 'bar', ['ex' => 100])); + $this->assertEquals(100, $this->redis->ttl('foo')); /* Set with a PTTL */ - $this->assertTrue($this->redis->set('foo','bar',['px'=>100000])); - $this->assertTrue(100000 - $this->redis->pttl('foo') < 1000); + $this->assertTrue($this->redis->set('foo', 'bar', ['px' => 100000])); + $this->assertBetween($this->redis->pttl('foo'), 99000, 100001); /* Set if exists, with a TTL */ - $this->assertTrue($this->redis->set('foo','bar',['xx','ex'=>105])); - $this->assertEquals($this->redis->ttl('foo'), 105); - $this->assertEquals($this->redis->get('foo'), 'bar'); + $this->assertTrue($this->redis->set('foo', 'bar', ['xx', 'ex' => 105])); + $this->assertEquals(105, $this->redis->ttl('foo')); + $this->assertKeyEquals('bar', 'foo'); /* Set if not exists, with a TTL */ $this->redis->del('foo'); - $this->assertTrue($this->redis->set('foo','bar', ['nx', 'ex'=>110])); - $this->assertEquals($this->redis->ttl('foo'), 110); - $this->assertEquals($this->redis->get('foo'), 'bar'); - $this->assertFalse($this->redis->set('foo','bar', ['nx', 'ex'=>110])); + $this->assertTrue($this->redis->set('foo', 'bar', ['nx', 'ex' => 110])); + $this->assertEquals(110, $this->redis->ttl('foo')); + $this->assertKeyEquals('bar', 'foo'); + $this->assertFalse($this->redis->set('foo', 'bar', ['nx', 'ex' => 110])); /* Throw some nonsense into the array, and check that the TTL came through */ $this->redis->del('foo'); - $this->assertTrue($this->redis->set('foo','barbaz', ['not-valid','nx','invalid','ex'=>200])); - $this->assertEquals($this->redis->ttl('foo'), 200); - $this->assertEquals($this->redis->get('foo'), 'barbaz'); + $this->assertTrue($this->redis->set('foo', 'barbaz', ['not-valid', 'nx', 'invalid', 'ex' => 200])); + $this->assertEquals(200, $this->redis->ttl('foo')); + $this->assertKeyEquals('barbaz', 'foo'); /* Pass NULL as the optional arguments which should be ignored */ $this->redis->del('foo'); - $this->redis->set('foo','bar', NULL); - $this->assertEquals($this->redis->get('foo'), 'bar'); - $this->assertTrue($this->redis->ttl('foo')<0); + $this->redis->set('foo', 'bar', NULL); + $this->assertKeyEquals('bar', 'foo'); + $this->assertLT(0, $this->redis->ttl('foo')); /* Make sure we ignore bad/non-string options (regression test for #1835) */ $this->assertTrue($this->redis->set('foo', 'bar', [NULL, 'EX' => 60])); $this->assertTrue($this->redis->set('foo', 'bar', [NULL, new stdClass(), 'EX' => 60])); $this->assertFalse(@$this->redis->set('foo', 'bar', [NULL, 'EX' => []])); - if (version_compare($this->version, "6.0.0") < 0) + if (version_compare($this->version, '6.0.0') < 0) return; /* KEEPTTL works by itself */ $this->redis->set('foo', 'bar', ['EX' => 100]); $this->redis->set('foo', 'bar', ['KEEPTTL']); - $this->assertTrue($this->redis->ttl('foo') > -1); + $this->assertBetween($this->redis->ttl('foo'), 90, 100); /* Works with other options */ $this->redis->set('foo', 'bar', ['XX', 'KEEPTTL']); - $this->assertTrue($this->redis->ttl('foo') > -1); + $this->assertBetween($this->redis->ttl('foo'), 90, 100); $this->redis->set('foo', 'bar', ['XX']); - $this->assertTrue($this->redis->ttl('foo') == -1); + $this->assertEquals(-1, $this->redis->ttl('foo')); - if (version_compare($this->version, "6.2.0") < 0) + if (version_compare($this->version, '6.2.0') < 0) return; - $this->assertTrue($this->redis->set('foo', 'baz', ['GET']) === 'bar'); + $this->assertEquals('bar', $this->redis->set('foo', 'baz', ['GET'])); + } + + /* Test Valkey >= 8.1 IFEQ SET option */ + public function testValkeyIfEq() { + if ( ! $this->is_valkey || ! $this->minVersionCheck('8.1.0')) + $this->markTestSkipped(); + + $this->redis->del('foo'); + $this->assertTrue($this->redis->set('foo', 'bar')); + $this->assertTrue($this->redis->set('foo', 'bar2', ['IFEQ' => 'bar'])); + $this->assertFalse($this->redis->set('foo', 'bar4', ['IFEQ' => 'bar3'])); + + $this->assertEquals('bar2', $this->redis->set('foo', 'bar3', ['IFEQ' => 'bar2', 'GET'])); } public function testGetSet() { $this->redis->del('key'); - $this->assertTrue($this->redis->getSet('key', '42') === FALSE); - $this->assertTrue($this->redis->getSet('key', '123') === '42'); - $this->assertTrue($this->redis->getSet('key', '123') === '123'); + $this->assertFalse($this->redis->getSet('key', '42')); + $this->assertEquals('42', $this->redis->getSet('key', '123')); + $this->assertEquals('123', $this->redis->getSet('key', '123')); + } + + public function testGetDel() { + $this->redis->del('key'); + $this->assertTrue($this->redis->set('key', 'iexist')); + $this->assertEquals('iexist', $this->redis->getDel('key')); + $this->assertEquals(0, $this->redis->exists('key')); } public function testRandomKey() { - for($i = 0; $i < 1000; $i++) { + for ($i = 0; $i < 1000; $i++) { $k = $this->redis->randomKey(); - $this->assertEquals($this->redis->exists($k), 1); + $this->assertKeyExists($k); } } @@ -639,8 +690,8 @@ public function testRename() { $this->redis->del('{key}0'); $this->redis->set('{key}0', 'val0'); $this->redis->rename('{key}0', '{key}1'); - $this->assertEquals(FALSE, $this->redis->get('{key}0')); - $this->assertEquals('val0', $this->redis->get('{key}1')); + $this->assertKeyMissing('{key}0'); + $this->assertKeyEquals('val0', '{key}1'); } public function testRenameNx() { @@ -648,9 +699,9 @@ public function testRenameNx() { $this->redis->del('{key}0', '{key}1'); $this->redis->set('{key}0', 'val0'); $this->redis->set('{key}1', 'val1'); - $this->assertTrue($this->redis->renameNx('{key}0', '{key}1') === FALSE); - $this->assertTrue($this->redis->get('{key}0') === 'val0'); - $this->assertTrue($this->redis->get('{key}1') === 'val1'); + $this->assertFalse($this->redis->renameNx('{key}0', '{key}1')); + $this->assertKeyEquals('val0', '{key}0'); + $this->assertKeyEquals('val1', '{key}1'); // lists $this->redis->del('{key}0'); @@ -659,62 +710,83 @@ public function testRenameNx() { $this->redis->lPush('{key}0', 'val1'); $this->redis->lPush('{key}1', 'val1-0'); $this->redis->lPush('{key}1', 'val1-1'); - $this->assertTrue($this->redis->renameNx('{key}0', '{key}1') === FALSE); - $this->assertTrue($this->redis->lRange('{key}0', 0, -1) === ['val1', 'val0']); - $this->assertTrue($this->redis->lRange('{key}1', 0, -1) === ['val1-1', 'val1-0']); + $this->assertFalse($this->redis->renameNx('{key}0', '{key}1')); + $this->assertEquals(['val1', 'val0'], $this->redis->lRange('{key}0', 0, -1)); + $this->assertEquals(['val1-1', 'val1-0'], $this->redis->lRange('{key}1', 0, -1)); $this->redis->del('{key}2'); - $this->assertTrue($this->redis->renameNx('{key}0', '{key}2') === TRUE); - $this->assertTrue($this->redis->lRange('{key}0', 0, -1) === []); - $this->assertTrue($this->redis->lRange('{key}2', 0, -1) === ['val1', 'val0']); + $this->assertTrue($this->redis->renameNx('{key}0', '{key}2')); + $this->assertEquals([], $this->redis->lRange('{key}0', 0, -1)); + $this->assertEquals(['val1', 'val0'], $this->redis->lRange('{key}2', 0, -1)); } public function testMultiple() { - $this->redis->del('k1'); - $this->redis->del('k2'); - $this->redis->del('k3'); + $kvals = [ + 'mget1' => 'v1', + 'mget2' => 'v2', + 'mget3' => 'v3' + ]; + + $this->redis->mset($kvals); - $this->redis->set('k1', 'v1'); - $this->redis->set('k2', 'v2'); - $this->redis->set('k3', 'v3'); $this->redis->set(1, 'test'); - $this->assertEquals(['v1'], $this->redis->mget(['k1'])); - $this->assertEquals(['v1', 'v3', false], $this->redis->mget(['k1', 'k3', 'NoKey'])); - $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['k1', 'k2', 'k3'])); - $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['k1', 'k2', 'k3'])); + $this->assertEquals([$kvals['mget1']], $this->redis->mget(['mget1'])); + + $this->assertEquals(['v1', 'v2', false], $this->redis->mget(['mget1', 'mget2', 'NoKey'])); + $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['mget1', 'mget2', 'mget3'])); + $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['mget1', 'mget2', 'mget3'])); $this->redis->set('k5', '$1111111111'); - $this->assertEquals([0 => '$1111111111'], $this->redis->mget(['k5'])); + $this->assertEquals(['$1111111111'], $this->redis->mget(['k5'])); - $this->assertEquals([0 => 'test'], $this->redis->mget([1])); // non-string + $this->assertEquals(['test'], $this->redis->mget([1])); // non-string } public function testMultipleBin() { + $kvals = [ + 'binkey-1' => random_bytes(16), + 'binkey-2' => random_bytes(16), + 'binkey-3' => random_bytes(16), + ]; + + foreach ($kvals as $k => $v) { + $this->redis->set($k, $v); + } + + $this->assertEquals(array_values($kvals), + $this->redis->mget(array_keys($kvals))); + } + + public function testExpireMember() { + if ( ! $this->is_keydb) + $this->markTestSkipped(); - $this->redis->del('k1'); - $this->redis->del('k2'); - $this->redis->del('k3'); + $this->redis->del('h'); + $this->redis->hmset('h', ['f1' => 'v1', 'f2' => 'v2', 'f3' => 'v3', 'f4' => 'v4']); - $this->redis->set('k1', gzcompress('v1')); - $this->redis->set('k2', gzcompress('v2')); - $this->redis->set('k3', gzcompress('v3')); + $this->assertEquals(1, $this->redis->expiremember('h', 'f1', 1)); + $this->assertEquals(1, $this->redis->expiremember('h', 'f2', 1000, 'ms')); + $this->assertEquals(1, $this->redis->expiremember('h', 'f3', 1000, null)); + $this->assertEquals(0, $this->redis->expiremember('h', 'nk', 10)); - $this->assertEquals([gzcompress('v1'), gzcompress('v2'), gzcompress('v3')], $this->redis->mget(['k1', 'k2', 'k3'])); - $this->assertEquals([gzcompress('v1'), gzcompress('v2'), gzcompress('v3')], $this->redis->mget(['k1', 'k2', 'k3'])); + $this->assertEquals(1, $this->redis->expirememberat('h', 'f4', time() + 1)); + $this->assertEquals(0, $this->redis->expirememberat('h', 'nk', time() + 1)); } - public function testSetTimeout() { + public function testExpire() { $this->redis->del('key'); $this->redis->set('key', 'value'); - $this->assertEquals('value', $this->redis->get('key')); + + $this->assertKeyEquals('value', 'key'); $this->redis->expire('key', 1); - $this->assertEquals('value', $this->redis->get('key')); + $this->assertKeyEquals('value', 'key'); sleep(2); - $this->assertEquals(False, $this->redis->get('key')); + $this->assertKeyMissing('key'); } - /* This test is prone to failure in the Travis container, so attempt to mitigate this by running more than once */ + /* This test is prone to failure in the Travis container, so attempt to + mitigate this by running more than once */ public function testExpireAt() { $success = false; @@ -730,8 +802,8 @@ public function testExpireAt() { } function testExpireOptions() { - if (!$this->minVersionCheck('7.0.0')) - return; + if ( ! $this->minVersionCheck('7.0.0')) + $this->markTestSkipped(); $this->redis->set('eopts', 'value'); @@ -757,15 +829,14 @@ function testExpireOptions() { /* Sending a nonsensical mode fails without sending a command */ $this->redis->clearLastError(); $this->assertFalse(@$this->redis->expire('eopts', 999, 'nonsense')); - $this->assertEquals(NULL, $this->redis->getLastError()); + $this->assertNull($this->redis->getLastError()); $this->redis->del('eopts'); } public function testExpiretime() { - if(version_compare($this->version, "7.0.0") < 0) { + if (version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $now = time(); @@ -777,156 +848,176 @@ public function testExpiretime() { $this->redis->del('key1'); } - public function testSetEx() { + public function testGetEx() { + if (version_compare($this->version, '6.2.0') < 0) + $this->markTestSkipped(); + + $this->assertTrue($this->redis->set('key', 'value')); + $this->assertEquals('value', $this->redis->getEx('key', ['EX' => 100])); + $this->assertBetween($this->redis->ttl('key'), 95, 100); + + $this->assertEquals('value', $this->redis->getEx('key', ['PX' => 100000])); + $this->assertBetween($this->redis->pttl('key'), 95000, 100000); + + $this->assertEquals('value', $this->redis->getEx('key', ['EXAT' => time() + 200])); + $this->assertBetween($this->redis->ttl('key'), 195, 200); + + $this->assertEquals('value', $this->redis->getEx('key', ['PXAT' => (time()*1000) + 25000])); + $this->assertBetween($this->redis->pttl('key'), 24000, 25000); + + $this->assertEquals('value', $this->redis->getEx('key', ['PERSIST' => true])); + $this->assertEquals(-1, $this->redis->ttl('key')); + + $this->assertTrue($this->redis->expire('key', 100)); + $this->assertBetween($this->redis->ttl('key'), 95, 100); + + $this->assertEquals('value', $this->redis->getEx('key', ['PERSIST'])); + $this->assertEquals(-1, $this->redis->ttl('key')); + } + + public function testSetEx() { $this->redis->del('key'); - $this->assertTrue($this->redis->setex('key', 7, 'val') === TRUE); - $this->assertTrue($this->redis->ttl('key') ===7); - $this->assertTrue($this->redis->get('key') === 'val'); + $this->assertTrue($this->redis->setex('key', 7, 'val')); + $this->assertEquals(7, $this->redis->ttl('key')); + $this->assertKeyEquals('val', 'key'); } public function testPSetEx() { $this->redis->del('key'); - $this->assertTrue($this->redis->psetex('key', 7 * 1000, 'val') === TRUE); - $this->assertTrue($this->redis->ttl('key') ===7); - $this->assertTrue($this->redis->get('key') === 'val'); + $this->assertTrue($this->redis->psetex('key', 7 * 1000, 'val')); + $this->assertEquals(7, $this->redis->ttl('key')); + $this->assertKeyEquals('val', 'key'); } public function testSetNX() { $this->redis->set('key', 42); - $this->assertTrue($this->redis->setnx('key', 'err') === FALSE); - $this->assertTrue($this->redis->get('key') === '42'); + $this->assertFalse($this->redis->setnx('key', 'err')); + $this->assertKeyEquals('42', 'key'); $this->redis->del('key'); - $this->assertTrue($this->redis->setnx('key', '42') === TRUE); - $this->assertTrue($this->redis->get('key') === '42'); + $this->assertTrue($this->redis->setnx('key', '42')); + $this->assertKeyEquals('42', 'key'); } public function testExpireAtWithLong() { - if (PHP_INT_SIZE != 8) { + if (PHP_INT_SIZE != 8) $this->markTestSkipped('64 bits only'); - } - $longExpiryTimeExceedingInt = 3153600000; + + $large_expiry = 3153600000; $this->redis->del('key'); - $this->assertTrue($this->redis->setex('key', $longExpiryTimeExceedingInt, 'val') === TRUE); - $this->assertTrue($this->redis->ttl('key') === $longExpiryTimeExceedingInt); + $this->assertTrue($this->redis->setex('key', $large_expiry, 'val')); + $this->assertEquals($large_expiry, $this->redis->ttl('key')); } - public function testIncr() - { + public function testIncr() { $this->redis->set('key', 0); $this->redis->incr('key'); - $this->assertEquals(1, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(1, 'key'); $this->redis->incr('key'); - $this->assertEquals(2, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(2, 'key'); $this->redis->incrBy('key', 3); - $this->assertEquals(5, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(5, 'key'); $this->redis->incrBy('key', 1); - $this->assertEquals(6, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(6, 'key'); $this->redis->incrBy('key', -1); - $this->assertEquals(5, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(5, 'key'); $this->redis->incr('key', 5); - $this->assertEquals(10, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(10, 'key'); $this->redis->del('key'); $this->redis->set('key', 'abc'); $this->redis->incr('key'); - $this->assertTrue("abc" === $this->redis->get('key')); + $this->assertKeyEquals('abc', 'key'); $this->redis->incr('key'); - $this->assertTrue("abc" === $this->redis->get('key')); + $this->assertKeyEquals('abc', 'key'); $this->redis->set('key', 0); $this->assertEquals(PHP_INT_MAX, $this->redis->incrby('key', PHP_INT_MAX)); } - public function testIncrByFloat() - { + public function testIncrByFloat() { // incrbyfloat is new in 2.6.0 - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } $this->redis->del('key'); $this->redis->set('key', 0); $this->redis->incrbyfloat('key', 1.5); - $this->assertEquals('1.5', $this->redis->get('key')); + $this->assertKeyEquals('1.5', 'key'); $this->redis->incrbyfloat('key', 2.25); - $this->assertEquals('3.75', $this->redis->get('key')); + $this->assertKeyEquals('3.75', 'key'); $this->redis->incrbyfloat('key', -2.25); - $this->assertEquals('1.5', $this->redis->get('key')); + $this->assertKeyEquals('1.5', 'key'); $this->redis->set('key', 'abc'); $this->redis->incrbyfloat('key', 1.5); - $this->assertTrue("abc" === $this->redis->get('key')); + $this->assertKeyEquals('abc', 'key'); $this->redis->incrbyfloat('key', -1.5); - $this->assertTrue("abc" === $this->redis->get('key')); + $this->assertKeyEquals('abc', 'key'); // Test with prefixing $this->redis->setOption(Redis::OPT_PREFIX, 'someprefix:'); $this->redis->del('key'); $this->redis->incrbyfloat('key',1.8); - $this->assertEquals(1.8, floatval($this->redis->get('key'))); // convert to float to avoid rounding issue on arm + $this->assertKeyEqualsWeak(1.8, 'key'); $this->redis->setOption(Redis::OPT_PREFIX, ''); - $this->assertEquals(1, $this->redis->exists('someprefix:key')); + $this->assertKeyExists('someprefix:key'); $this->redis->del('someprefix:key'); - } - public function testDecr() - { + public function testDecr() { $this->redis->set('key', 5); $this->redis->decr('key'); - $this->assertEquals(4, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(4, 'key'); $this->redis->decr('key'); - $this->assertEquals(3, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(3, 'key'); $this->redis->decrBy('key', 2); - $this->assertEquals(1, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(1, 'key'); $this->redis->decrBy('key', 1); - $this->assertEquals(0, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(0, 'key'); $this->redis->decrBy('key', -10); - $this->assertEquals(10, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(10, 'key'); $this->redis->decr('key', 10); - $this->assertEquals(0, (int)$this->redis->get('key')); + $this->assertKeyEqualsWeak(0, 'key'); } public function testExists() { /* Single key */ $this->redis->del('key'); - $this->assertEquals(0, $this->redis->exists('key')); + $this->assertKeyMissing('key'); $this->redis->set('key', 'val'); - $this->assertEquals(1, $this->redis->exists('key')); + $this->assertKeyExists('key'); /* Add multiple keys */ $mkeys = []; for ($i = 0; $i < 10; $i++) { - if (rand(1, 2) == 1) { - $mkey = "{exists}key:$i"; - $this->redis->set($mkey, $i); - $mkeys[] = $mkey; - } + $mkey = "{exists}key:$i"; + $this->redis->set($mkey, $i); + $mkeys[] = $mkey; } /* Test passing an array as well as the keys variadic */ @@ -935,25 +1026,29 @@ public function testExists() { } public function testTouch() { - if (!$this->minVersionCheck('3.2.1')) + if ( ! $this->minVersionCheck('3.2.1')) $this->markTestSkipped(); $this->redis->del('notakey'); $this->assertTrue($this->redis->mset(['{idle}1' => 'beep', '{idle}2' => 'boop'])); usleep(1100000); - $this->assertTrue($this->redis->object('idletime', '{idle}1') >= 1); - $this->assertTrue($this->redis->object('idletime', '{idle}2') >= 1); + $this->assertGT(0, $this->redis->object('idletime', '{idle}1')); + $this->assertGT(0, $this->redis->object('idletime', '{idle}2')); $this->assertEquals(2, $this->redis->touch('{idle}1', '{idle}2', '{idle}notakey')); - $this->assertTrue($this->redis->object('idletime', '{idle}1') == 0); - $this->assertTrue($this->redis->object('idletime', '{idle}2') == 0); + $idle1 = $this->redis->object('idletime', '{idle}1'); + $idle2 = $this->redis->object('idletime', '{idle}2'); + + /* We're not testing if idle is 0 because CPU scheduling on GitHub CI + * potatoes can cause that to erroneously fail. */ + $this->assertLT(2, $idle1); + $this->assertLT(2, $idle2); } - public function testKeys() - { + public function testKeys() { $pattern = 'keys-test-'; - for($i = 1; $i < 10; $i++) { + for ($i = 1; $i < 10; $i++) { $this->redis->set($pattern.$i, $i); } $this->redis->del($pattern.'3'); @@ -966,35 +1061,35 @@ public function testKeys() $this->assertEquals((count($keys) + 1), count($keys2)); // empty array when no key matches - $this->assertEquals([], $this->redis->keys(rand().rand().rand().'*')); + $this->assertEquals([], $this->redis->keys(uniqid() . '*')); } protected function genericDelUnlink($cmd) { - $key = 'key' . rand(); + $key = uniqid('key:'); $this->redis->set($key, 'val'); - $this->assertEquals('val', $this->redis->get($key)); + $this->assertKeyEquals('val', $key); $this->assertEquals(1, $this->redis->$cmd($key)); - $this->assertEquals(false, $this->redis->get($key)); + $this->assertFalse($this->redis->get($key)); // multiple, all existing $this->redis->set('x', 0); $this->redis->set('y', 1); $this->redis->set('z', 2); $this->assertEquals(3, $this->redis->$cmd('x', 'y', 'z')); - $this->assertEquals(false, $this->redis->get('x')); - $this->assertEquals(false, $this->redis->get('y')); - $this->assertEquals(false, $this->redis->get('z')); + $this->assertFalse($this->redis->get('x')); + $this->assertFalse($this->redis->get('y')); + $this->assertFalse($this->redis->get('z')); // multiple, none existing $this->assertEquals(0, $this->redis->$cmd('x', 'y', 'z')); - $this->assertEquals(false, $this->redis->get('x')); - $this->assertEquals(false, $this->redis->get('y')); - $this->assertEquals(false, $this->redis->get('z')); + $this->assertFalse($this->redis->get('x')); + $this->assertFalse($this->redis->get('y')); + $this->assertFalse($this->redis->get('z')); // multiple, some existing $this->redis->set('y', 1); $this->assertEquals(1, $this->redis->$cmd('x', 'y', 'z')); - $this->assertEquals(false, $this->redis->get('y')); + $this->assertFalse($this->redis->get('y')); $this->redis->set('x', 0); $this->redis->set('y', 1); @@ -1002,28 +1097,27 @@ protected function genericDelUnlink($cmd) { } public function testDelete() { - $this->genericDelUnlink("DEL"); + $this->genericDelUnlink('DEL'); } - public function testUnlink() { - if (version_compare($this->version, "4.0.0") < 0) { + public function testDelIfEq() { + if ( ! $this->haveCommand('DELIFEQ')) $this->markTestSkipped(); - return; - } - $this->genericDelUnlink("UNLINK"); + $this->assertTrue($this->redis->set('key', 'value')); + $this->assertEquals(0, $this->redis->delifeq('key', 'notvalue')); + $this->assertEquals(1, $this->redis->delifeq('key', 'value')); + $this->assertEquals(0, $this->redis->exists('key')); } - public function testType() - { - // 0 => none, (key didn't exist) - // 1=> string, - // 2 => set, - // 3 => list, - // 4 => zset, - // 5 => hash - // 6 => stream + public function testUnlink() { + if (version_compare($this->version, '4.0.0') < 0) + $this->markTestSkipped(); + + $this->genericDelUnlink('UNLINK'); + } + public function testType() { // string $this->redis->set('key', 'val'); $this->assertEquals(Redis::REDIS_STRING, $this->redis->type('key')); @@ -1041,8 +1135,8 @@ public function testType() // sadd with numeric key $this->redis->del(123); - $this->assertTrue(1 === $this->redis->sAdd(123, 'val0')); - $this->assertTrue(['val0'] === $this->redis->sMembers(123)); + $this->assertEquals(1, $this->redis->sAdd(123, 'val0')); + $this->assertEquals(['val0'], $this->redis->sMembers(123)); // zset $this->redis->del('keyZSet'); @@ -1057,86 +1151,77 @@ public function testType() $this->assertEquals(Redis::REDIS_HASH, $this->redis->type('keyHash')); // stream - if ($this->minVersionCheck("5.0")) { + if ($this->minVersionCheck('5.0')) { $this->redis->del('stream'); $this->redis->xAdd('stream', '*', ['foo' => 'bar']); $this->assertEquals(Redis::REDIS_STREAM, $this->redis->type('stream')); } + if ($this->haveCommand('vadd')) { + $this->redis->del('vset'); + $this->assertEquals(1, $this->redis->vadd('vset', [1.5, 2.5], 'foo')); + $this->assertEquals(Redis::REDIS_VECTORSET, $this->redis->type('vset')); + } + // None $this->redis->del('keyNotExists'); $this->assertEquals(Redis::REDIS_NOT_FOUND, $this->redis->type('keyNotExists')); - } public function testStr() { - $this->redis->set('key', 'val1'); - $this->assertTrue($this->redis->append('key', 'val2') === 8); - $this->assertTrue($this->redis->get('key') === 'val1val2'); + $this->assertEquals(8, $this->redis->append('key', 'val2')); + $this->assertKeyEquals('val1val2', 'key'); $this->redis->del('keyNotExist'); - $this->assertTrue($this->redis->append('keyNotExist', 'value') === 5); - $this->assertTrue($this->redis->get('keyNotExist') === 'value'); + $this->assertEquals(5, $this->redis->append('keyNotExist', 'value')); + $this->assertKeyEquals('value', 'keyNotExist'); $this->redis->set('key', 'This is a string') ; - $this->assertTrue($this->redis->getRange('key', 0, 3) === 'This'); - $this->assertTrue($this->redis->getRange('key', -6, -1) === 'string'); - $this->assertTrue($this->redis->getRange('key', -6, 100000) === 'string'); - $this->assertTrue($this->redis->get('key') === 'This is a string'); + $this->assertEquals('This', $this->redis->getRange('key', 0, 3)); + $this->assertEquals('string', $this->redis->getRange('key', -6, -1)); + $this->assertEquals('string', $this->redis->getRange('key', -6, 100000)); + $this->assertKeyEquals('This is a string', 'key'); $this->redis->set('key', 'This is a string') ; - $this->assertTrue($this->redis->strlen('key') === 16); + $this->assertEquals(16, $this->redis->strlen('key')); $this->redis->set('key', 10) ; - $this->assertTrue($this->redis->strlen('key') === 2); + $this->assertEquals(2, $this->redis->strlen('key')); $this->redis->set('key', '') ; - $this->assertTrue($this->redis->strlen('key') === 0); + $this->assertEquals(0, $this->redis->strlen('key')); $this->redis->set('key', '000') ; - $this->assertTrue($this->redis->strlen('key') === 3); + $this->assertEquals(3, $this->redis->strlen('key')); } - // PUSH, POP : LPUSH, LPOP - public function testlPop() - { - - // rpush => tail - // lpush => head - - + public function testlPop() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); - $this->redis->rPush('list', 'val3'); - + $this->redis->rPush('list', 'val3'); - // 'list' = [ 'val2', 'val', 'val3'] - - $this->assertEquals('val2', $this->redis->lPop('list')); - if (version_compare($this->version, "6.2.0") < 0) { + $this->assertEquals('val2', $this->redis->lPop('list')); + if (version_compare($this->version, '6.2.0') < 0) { $this->assertEquals('val', $this->redis->lPop('list')); $this->assertEquals('val3', $this->redis->lPop('list')); } else { $this->assertEquals(['val', 'val3'], $this->redis->lPop('list', 2)); } - $this->assertEquals(FALSE, $this->redis->lPop('list')); - // testing binary data + $this->assertFalse($this->redis->lPop('list')); - $this->redis->del('list'); - $this->assertEquals(1, $this->redis->lPush('list', gzcompress('val1'))); - $this->assertEquals(2, $this->redis->lPush('list', gzcompress('val2'))); - $this->assertEquals(3, $this->redis->lPush('list', gzcompress('val3'))); + $this->redis->del('list'); + $this->assertEquals(1, $this->redis->lPush('list', gzcompress('val1'))); + $this->assertEquals(2, $this->redis->lPush('list', gzcompress('val2'))); + $this->assertEquals(3, $this->redis->lPush('list', gzcompress('val3'))); - $this->assertEquals('val3', gzuncompress($this->redis->lPop('list'))); - $this->assertEquals('val2', gzuncompress($this->redis->lPop('list'))); - $this->assertEquals('val1', gzuncompress($this->redis->lPop('list'))); + $this->assertEquals('val3', gzuncompress($this->redis->lPop('list'))); + $this->assertEquals('val2', gzuncompress($this->redis->lPop('list'))); + $this->assertEquals('val1', gzuncompress($this->redis->lPop('list'))); } - // PUSH, POP : RPUSH, RPOP - public function testrPop() - { + public function testrPop() { $this->redis->del('list'); $this->redis->rPush('list', 'val'); @@ -1144,14 +1229,14 @@ public function testrPop() $this->redis->lPush('list', 'val3'); $this->assertEquals('val2', $this->redis->rPop('list')); - if (version_compare($this->version, "6.2.0") < 0) { + if (version_compare($this->version, '6.2.0') < 0) { $this->assertEquals('val', $this->redis->rPop('list')); $this->assertEquals('val3', $this->redis->rPop('list')); } else { $this->assertEquals(['val', 'val3'], $this->redis->rPop('list', 2)); } - $this->assertEquals(FALSE, $this->redis->rPop('list')); + $this->assertFalse($this->redis->rPop('list')); $this->redis->del('list'); $this->assertEquals(1, $this->redis->rPush('list', gzcompress('val1'))); @@ -1179,7 +1264,7 @@ public function testrPopSerialization() { public function testblockingPop() { /* Test with a double timeout in Redis >= 6.0.0 */ - if (version_compare($this->version, "6.0.0") >= 0) { + if (version_compare($this->version, '6.0.0') >= 0) { $this->redis->del('list'); $this->redis->lpush('list', 'val1', 'val2'); $this->assertEquals(['list', 'val2'], $this->redis->blpop(['list'], .1)); @@ -1210,8 +1295,7 @@ public function testblockingPop() { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false); } - public function testllen() - { + public function testLLen() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); @@ -1226,42 +1310,39 @@ public function testllen() $this->assertEquals('val', $this->redis->lPop('list')); $this->assertEquals(0, $this->redis->llen('list')); - $this->assertEquals(FALSE, $this->redis->lPop('list')); + $this->assertFalse($this->redis->lPop('list')); $this->assertEquals(0, $this->redis->llen('list')); // empty returns 0 $this->redis->del('list'); $this->assertEquals(0, $this->redis->llen('list')); // non-existent returns 0 $this->redis->set('list', 'actually not a list'); - $this->assertEquals(FALSE, $this->redis->llen('list'));// not a list returns FALSE + $this->assertFalse($this->redis->llen('list'));// not a list returns FALSE } - //lInsert, lPopx, rPopx public function testlPopx() { - //test lPushx/rPushx $this->redis->del('keyNotExists'); - $this->assertTrue($this->redis->lPushx('keyNotExists', 'value') === 0); - $this->assertTrue($this->redis->rPushx('keyNotExists', 'value') === 0); + $this->assertEquals(0, $this->redis->lPushx('keyNotExists', 'value')); + $this->assertEquals(0, $this->redis->rPushx('keyNotExists', 'value')); $this->redis->del('key'); $this->redis->lPush('key', 'val0'); - $this->assertTrue($this->redis->lPushx('key', 'val1') === 2); - $this->assertTrue($this->redis->rPushx('key', 'val2') === 3); - $this->assertTrue($this->redis->lrange('key', 0, -1) === ['val1', 'val0', 'val2']); + $this->assertEquals(2, $this->redis->lPushx('key', 'val1')); + $this->assertEquals(3, $this->redis->rPushx('key', 'val2')); + $this->assertEquals(['val1', 'val0', 'val2'], $this->redis->lrange('key', 0, -1)); //test linsert $this->redis->del('key'); $this->redis->lPush('key', 'val0'); - $this->assertTrue($this->redis->lInsert('keyNotExists', Redis::AFTER, 'val1', 'val2') === 0); - $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, 'valX', 'val2') === -1); + $this->assertEquals(0, $this->redis->lInsert('keyNotExists', Redis::AFTER, 'val1', 'val2')); + $this->assertEquals(-1, $this->redis->lInsert('key', Redis::BEFORE, 'valX', 'val2')); - $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, 'val0', 'val1') === 2); - $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, 'val0', 'val2') === 3); - $this->assertTrue($this->redis->lrange('key', 0, -1) === ['val2', 'val0', 'val1']); + $this->assertEquals(2, $this->redis->lInsert('key', Redis::AFTER, 'val0', 'val1')); + $this->assertEquals(3, $this->redis->lInsert('key', Redis::BEFORE, 'val0', 'val2')); + $this->assertEquals(['val2', 'val0', 'val1'], $this->redis->lrange('key', 0, -1)); } - public function testlPos() - { + public function testlPos() { $this->redis->del('key'); $this->redis->lPush('key', 'val0', 'val1', 'val1'); $this->assertEquals(2, $this->redis->lPos('key', 'val0')); @@ -1278,10 +1359,8 @@ public function testlPos() } } - // ltrim, lsize, lpop - public function testltrim() - { - + // ltrim, lLen, lpop + public function testltrim() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); @@ -1289,20 +1368,19 @@ public function testltrim() $this->redis->lPush('list', 'val3'); $this->redis->lPush('list', 'val4'); - $this->assertEquals(TRUE, $this->redis->ltrim('list', 0, 2)); - $this->assertEquals(3, $this->redis->llen('list')); + $this->assertTrue($this->redis->ltrim('list', 0, 2)); + $this->assertEquals(3, $this->redis->llen('list')); $this->redis->ltrim('list', 0, 0); $this->assertEquals(1, $this->redis->llen('list')); - $this->assertEquals('val4', $this->redis->lPop('list')); + $this->assertEquals('val4', $this->redis->lPop('list')); - $this->assertEquals(TRUE, $this->redis->ltrim('list', 10, 10000)); - $this->assertEquals(TRUE, $this->redis->ltrim('list', 10000, 10)); - - // test invalid type - $this->redis->set('list', 'not a list...'); - $this->assertEquals(FALSE, $this->redis->ltrim('list', 0, 2)); + $this->assertTrue($this->redis->ltrim('list', 10, 10000)); + $this->assertTrue($this->redis->ltrim('list', 10000, 10)); + // test invalid type + $this->redis->set('list', 'not a list...'); + $this->assertFalse($this->redis->ltrim('list', 0, 2)); } public function setupSort() { @@ -1325,7 +1403,7 @@ public function setupSort() { // set-up $this->redis->del('person:id'); - foreach([1,2,3,4] as $id) { + foreach ([1, 2, 3, 4] as $id) { $this->redis->lPush('person:id', $id); } } @@ -1338,9 +1416,9 @@ public function testSortPrefix() { $this->redis->sadd('some-item', 2); $this->redis->sadd('some-item', 3); - $this->assertEquals(['1','2','3'], $this->redis->sort('some-item', ['sort' => 'asc'])); - $this->assertEquals(['3','2','1'], $this->redis->sort('some-item', ['sort' => 'desc'])); - $this->assertEquals(['1','2','3'], $this->redis->sort('some-item')); + $this->assertEquals(['1', '2', '3'], $this->redis->sort('some-item', ['sort' => 'asc'])); + $this->assertEquals(['3', '2', '1'], $this->redis->sort('some-item', ['sort' => 'desc'])); + $this->assertEquals(['1', '2', '3'], $this->redis->sort('some-item')); // Kill our set/prefix $this->redis->del('some-item'); @@ -1350,7 +1428,7 @@ public function testSortPrefix() { public function testSortAsc() { $this->setupSort(); // sort by age and get IDs - $byAgeAsc = ['3','1','2','4']; + $byAgeAsc = ['3', '1', '2', '4']; $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*'])); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'sort' => 'asc'])); $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['by' => NULL])); // check that NULL works. @@ -1358,7 +1436,7 @@ public function testSortAsc() { $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['sort' => 'asc'])); // sort by age and get names - $byAgeAsc = ['Carol','Alice','Bob','Dave']; + $byAgeAsc = ['Carol', 'Alice', 'Bob', 'Dave']; $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*'])); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc'])); @@ -1368,8 +1446,8 @@ public function testSortAsc() { $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2]])); $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'asc'])); $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 4]])); - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, "4"]])); // with strings - $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => ["0", 4]])); + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, '4']])); // with strings + $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => ['0', 4]])); // sort by salary and get ages $agesBySalaryAsc = ['34', '27', '25', '41']; @@ -1383,12 +1461,12 @@ public function testSortAsc() { // list → [ghi, def, abc] $list = ['abc', 'def', 'ghi']; $this->redis->del('list'); - foreach($list as $i) { + foreach ($list as $i) { $this->redis->lPush('list', $i); } // SORT list → [ghi, def, abc] - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) { $this->assertEquals(array_reverse($list), $this->redis->sort('list')); $this->assertEquals(array_reverse($list), $this->redis->sort('list', ['sort' => 'asc'])); } else { @@ -1398,16 +1476,15 @@ public function testSortAsc() { } // SORT list ALPHA → [abc, def, ghi] - $this->assertEquals($list, $this->redis->sort('list', ['alpha' => TRUE])); - $this->assertEquals($list, $this->redis->sort('list', ['sort' => 'asc', 'alpha' => TRUE])); + $this->assertEquals($list, $this->redis->sort('list', ['alpha' => true])); + $this->assertEquals($list, $this->redis->sort('list', ['sort' => 'asc', 'alpha' => true])); } public function testSortDesc() { - $this->setupSort(); // sort by age and get IDs - $byAgeDesc = ['4','2','1','3']; + $byAgeDesc = ['4', '2', '1', '3']; $this->assertEquals($byAgeDesc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'sort' => 'desc'])); // sort by age and get names @@ -1424,12 +1501,12 @@ public function testSortDesc() { // sort non-alpha doesn't change all-string lists $list = ['def', 'abc', 'ghi']; $this->redis->del('list'); - foreach($list as $i) { + foreach ($list as $i) { $this->redis->lPush('list', $i); } // SORT list ALPHA → [abc, def, ghi] - $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sort('list', ['sort' => 'desc', 'alpha' => TRUE])); + $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sort('list', ['sort' => 'desc', 'alpha' => true])); } /* This test is just to make sure SORT and SORT_RO are both callable */ @@ -1446,9 +1523,7 @@ public function testSortHandler() { } } - // LINDEX public function testLindex() { - $this->redis->del('list'); $this->redis->lPush('list', 'val'); @@ -1461,7 +1536,7 @@ public function testLindex() { $this->assertEquals('val', $this->redis->lIndex('list', -1)); $this->assertEquals('val2', $this->redis->lIndex('list', -2)); $this->assertEquals('val3', $this->redis->lIndex('list', -3)); - $this->assertEquals(FALSE, $this->redis->lIndex('list', -4)); + $this->assertFalse($this->redis->lIndex('list', -4)); $this->redis->rPush('list', 'val4'); $this->assertEquals('val4', $this->redis->lIndex('list', 3)); @@ -1472,19 +1547,23 @@ public function testlMove() { if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - $this->redis->del('{list}0', '{list}1'); - $this->redis->lPush('{list}0', 'a'); - $this->redis->lPush('{list}0', 'b'); - $this->redis->lPush('{list}0', 'c'); + [$list1, $list2] = ['{l}0', '{l}1']; + $left = $this->getLeftConstant(); + $right = $this->getRightConstant(); + + $this->redis->del($list1, $list2); + $this->redis->lPush($list1, 'a'); + $this->redis->lPush($list1, 'b'); + $this->redis->lPush($list1, 'c'); - $return = $this->redis->lMove('{list}0', '{list}1', Redis::LEFT, Redis::RIGHT); + $return = $this->redis->lMove($list1, $list2, $left, $right); $this->assertEquals('c', $return); - $return = $this->redis->lMove('{list}0', '{list}1', Redis::RIGHT, Redis::LEFT); + $return = $this->redis->lMove($list1, $list2, $right, $left); $this->assertEquals('a', $return); - $this->assertEquals(['b'], $this->redis->lRange('{list}0', 0, -1)); - $this->assertEquals(['a', 'c'], $this->redis->lRange('{list}1', 0, -1)); + $this->assertEquals(['b'], $this->redis->lRange($list1, 0, -1)); + $this->assertEquals(['a', 'c'], $this->redis->lRange($list2, 0, -1)); } @@ -1492,21 +1571,25 @@ public function testBlmove() { if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - $this->redis->del('{list}0', '{list}1'); - $this->redis->rpush('{list}0', 'a'); + [$list1, $list2] = ['{l}0', '{l}1']; + $left = $this->getLeftConstant(); - $this->assertEquals('a', $this->redis->blmove('{list}0', '{list}1', Redis::LEFT, Redis::LEFT, 1.0)); + $this->redis->del($list1, $list2); + $this->redis->rpush($list1, 'a'); + + + $this->assertEquals('a', $this->redis->blmove($list1, $list2, $left, $left, 1.)); $st = microtime(true); - $ret = $this->redis->blmove('{list}0', '{list}1', Redis::LEFT, Redis::LEFT, .1); + $ret = $this->redis->blmove($list1, $list2, $left, $left, .1); $et = microtime(true); - $this->assertEquals(false, $ret); - $this->assertTrue($et - $st >= .1); + $this->assertFalse($ret); + $this->assertGT(.09, $et - $st); } // lRem testing - public function testlrem() { + public function testLRem() { $this->redis->del('list'); $this->redis->lPush('list', 'a'); $this->redis->lPush('list', 'b'); @@ -1546,13 +1629,13 @@ public function testlrem() { $this->assertEquals(0, $this->redis->lrem('list', 'x', 0)); $this->assertEquals(2, $this->redis->lrem('list', 'b', 0)); $this->assertEquals(1, $this->redis->lrem('list', 'c', 0)); - $this->assertEquals(FALSE, $this->redis->get('list')); + $this->assertFalse($this->redis->get('list')); $this->redis->set('list', 'actually not a list'); - $this->assertEquals(FALSE, $this->redis->lrem('list', 'x')); + $this->assertFalse($this->redis->lrem('list', 'x')); } - public function testsAdd() { + public function testSAdd() { $this->redis->del('set'); $this->assertEquals(1, $this->redis->sAdd('set', 'val')); @@ -1566,7 +1649,7 @@ public function testsAdd() { $this->assertTrue($this->redis->sismember('set', 'val2')); } - public function testscard() { + public function testSCard() { $this->redis->del('set'); $this->assertEquals(1, $this->redis->sAdd('set', 'val')); $this->assertEquals(1, $this->redis->scard('set')); @@ -1574,7 +1657,7 @@ public function testscard() { $this->assertEquals(2, $this->redis->scard('set')); } - public function testsrem() { + public function testSRem() { $this->redis->del('set'); $this->redis->sAdd('set', 'val'); $this->redis->sAdd('set', 'val2'); @@ -1604,25 +1687,24 @@ public function testsMove() { public function testsPop() { $this->redis->del('set0'); - $this->assertTrue($this->redis->sPop('set0') === FALSE); + $this->assertFalse($this->redis->sPop('set0')); $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); $v0 = $this->redis->sPop('set0'); - $this->assertTrue(1 === $this->redis->scard('set0')); - $this->assertTrue($v0 === 'val' || $v0 === 'val2'); + $this->assertEquals(1, $this->redis->scard('set0')); + $this->assertInArray($v0, ['val', 'val2']); $v1 = $this->redis->sPop('set0'); - $this->assertTrue(0 === $this->redis->scard('set0')); - $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2')); + $this->assertEquals(0, $this->redis->scard('set0')); + $this->assertEqualsCanonicalizing(['val', 'val2'], [$v0, $v1]); - $this->assertTrue($this->redis->sPop('set0') === FALSE); + $this->assertFalse($this->redis->sPop('set0')); } public function testsPopWithCount() { - if (!$this->minVersionCheck("3.2")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('3.2')) + $this->markTestSkipped(); $set = 'set0'; $prefix = 'member'; @@ -1638,29 +1720,29 @@ public function testsPopWithCount() { $ret = $this->redis->sPop($set, $i); /* Make sure we got an arary and the count is right */ - if ($this->assertTrue(is_array($ret)) && $this->assertTrue(count($ret) == $count)) { + if ($this->assertIsArray($ret, $count)) { /* Probably overkill but validate the actual returned members */ for ($i = 0; $i < $count; $i++) { - $this->assertTrue(in_array($prefix.$i, $ret)); + $this->assertInArray($prefix.$i, $ret); } } } public function testsRandMember() { $this->redis->del('set0'); - $this->assertTrue($this->redis->sRandMember('set0') === FALSE); + $this->assertFalse($this->redis->sRandMember('set0')); $this->redis->sAdd('set0', 'val'); $this->redis->sAdd('set0', 'val2'); $got = []; - while(true) { + while (true) { $v = $this->redis->sRandMember('set0'); - $this->assertTrue(2 === $this->redis->scard('set0')); // no change. - $this->assertTrue($v === 'val' || $v === 'val2'); + $this->assertEquals(2, $this->redis->scard('set0')); // no change. + $this->assertInArray($v, ['val', 'val2']); $got[$v] = $v; - if(count($got) == 2) { + if (count($got) == 2) { break; } } @@ -1671,18 +1753,25 @@ public function testsRandMember() { $this->redis->del('set0'); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); - for($i=0;$i<5;$i++) { + for ($i = 0; $i < 5; $i++) { $member = "member:$i"; $this->redis->sAdd('set0', $member); $mems[] = $member; } $member = $this->redis->srandmember('set0'); - $this->assertTrue(in_array($member, $mems)); + $this->assertInArray($member, $mems); $rmembers = $this->redis->srandmember('set0', $i); - foreach($rmembers as $reply_mem) { - $this->assertTrue(in_array($reply_mem, $mems)); + foreach ($rmembers as $reply_mem) { + $this->assertInArray($reply_mem, $mems); + } + + /* Ensure we can handle basically any return type */ + foreach ([3.1415, new stdClass(), 42, 'hello', NULL] as $val) { + $this->assertEquals(1, $this->redis->del('set0')); + $this->assertEquals(1, $this->redis->sadd('set0', $val)); + $this->assertSameType($val, $this->redis->srandmember('set0')); } $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); @@ -1697,11 +1786,11 @@ public function testSRandMemberWithCount() { $ret_neg = $this->redis->sRandMember('set0', -10); // Should both be empty arrays - $this->assertTrue(is_array($ret_pos) && empty($ret_pos)); - $this->assertTrue(is_array($ret_neg) && empty($ret_neg)); + $this->assertEquals([], $ret_pos); + $this->assertEquals([], $ret_neg); // Add a few items to the set - for($i=0;$i<100;$i++) { + for ($i = 0; $i < 100; $i++) { $this->redis->sadd('set0', "member$i"); } @@ -1709,19 +1798,19 @@ public function testSRandMemberWithCount() { $ret_slice = $this->redis->srandmember('set0', 20); // Should be an array with 20 items - $this->assertTrue(is_array($ret_slice) && count($ret_slice) == 20); + $this->assertIsArray($ret_slice, 20); // Ask for more items than are in the list (but with a positive count) $ret_slice = $this->redis->srandmember('set0', 200); // Should be an array, should be however big the set is, exactly - $this->assertTrue(is_array($ret_slice) && count($ret_slice) == $i); + $this->assertIsArray($ret_slice, $i); // Now ask for too many items but negative $ret_slice = $this->redis->srandmember('set0', -200); // Should be an array, should have exactly the # of items we asked for (will be dups) - $this->assertTrue(is_array($ret_slice) && count($ret_slice) == 200); + $this->assertIsArray($ret_slice, 200); // // Test in a pipeline @@ -1736,17 +1825,16 @@ public function testSRandMemberWithCount() { $ret = $this->redis->exec(); - $this->assertTrue(is_array($ret[0]) && count($ret[0]) == 20); - $this->assertTrue(is_array($ret[1]) && count($ret[1]) == $i); - $this->assertTrue(is_array($ret[2]) && count($ret[2]) == 200); + $this->assertIsArray($ret[0], 20); + $this->assertIsArray($ret[1], $i); + $this->assertIsArray($ret[2], 200); // Kill the set $this->redis->del('set0'); } } - public function testsismember() - { + public function testSIsMember() { $this->redis->del('set'); $this->redis->sAdd('set', 'val'); @@ -1755,32 +1843,20 @@ public function testsismember() $this->assertFalse($this->redis->sismember('set', 'val2')); } - public function testsmembers() - { + public function testSMembers() { $this->redis->del('set'); - $this->redis->sAdd('set', 'val'); - $this->redis->sAdd('set', 'val2'); - $this->redis->sAdd('set', 'val3'); - - $array = ['val', 'val2', 'val3']; - - $smembers = $this->redis->smembers('set'); - sort($smembers); - $this->assertEquals($array, $smembers); + $data = ['val', 'val2', 'val3']; + foreach ($data as $member) { + $this->redis->sAdd('set', $member); + } - $sMembers = $this->redis->sMembers('set'); - sort($sMembers); - $this->assertEquals($array, $sMembers); // test alias + $this->assertEqualsCanonicalizing($data, $this->redis->smembers('set')); } - public function testsMisMember() - { - // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + public function testsMisMember() { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('set'); @@ -1796,22 +1872,20 @@ public function testsMisMember() } public function testlSet() { - $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); - $this->redis->lPush('list', 'val3'); - - $this->assertEquals($this->redis->lIndex('list', 0), 'val3'); - $this->assertEquals($this->redis->lIndex('list', 1), 'val2'); - $this->assertEquals($this->redis->lIndex('list', 2), 'val'); + $this->redis->lPush('list', 'val3'); - $this->assertEquals(TRUE, $this->redis->lSet('list', 1, 'valx')); + $this->assertEquals('val3', $this->redis->lIndex('list', 0)); + $this->assertEquals('val2', $this->redis->lIndex('list', 1)); + $this->assertEquals('val', $this->redis->lIndex('list', 2)); - $this->assertEquals($this->redis->lIndex('list', 0), 'val3'); - $this->assertEquals($this->redis->lIndex('list', 1), 'valx'); - $this->assertEquals($this->redis->lIndex('list', 2), 'val'); + $this->assertTrue($this->redis->lSet('list', 1, 'valx')); + $this->assertEquals('val3', $this->redis->lIndex('list', 0)); + $this->assertEquals('valx', $this->redis->lIndex('list', 1)); + $this->assertEquals('val', $this->redis->lIndex('list', 2)); } public function testsInter() { @@ -1820,103 +1894,103 @@ public function testsInter() { $this->redis->del('{set}square'); // set of squares $this->redis->del('{set}seq'); // set of numbers of the form n^2 - 1 - $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; - foreach($x as $i) { + $x = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25]; + foreach ($x as $i) { $this->redis->sAdd('{set}odd', $i); } - $y = [1,2,3,5,7,11,13,17,19,23]; - foreach($y as $i) { + $y = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23]; + foreach ($y as $i) { $this->redis->sAdd('{set}prime', $i); } - $z = [1,4,9,16,25]; - foreach($z as $i) { + $z = [1, 4, 9, 16, 25]; + foreach ($z as $i) { $this->redis->sAdd('{set}square', $i); } - $t = [2,5,10,17,26]; - foreach($t as $i) { + $t = [2, 5, 10, 17, 26]; + foreach ($t as $i) { $this->redis->sAdd('{set}seq', $i); } $xy = $this->redis->sInter('{set}odd', '{set}prime'); // odd prime numbers - foreach($xy as $i) { + foreach ($xy as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_intersect($x, $y))); + $this->assertInArray($i, array_intersect($x, $y)); } $xy = $this->redis->sInter(['{set}odd', '{set}prime']); // odd prime numbers, as array. - foreach($xy as $i) { + foreach ($xy as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_intersect($x, $y))); + $this->assertInArray($i, array_intersect($x, $y)); } $yz = $this->redis->sInter('{set}prime', '{set}square'); // set of prime squares - foreach($yz as $i) { + foreach ($yz as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_intersect($y, $z))); + $this->assertInArray($i, array_intersect($y, $z)); } $yz = $this->redis->sInter(['{set}prime', '{set}square']); // set of odd squares, as array - foreach($yz as $i) { + foreach ($yz as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_intersect($y, $z))); + $this->assertInArray($i, array_intersect($y, $z)); } $zt = $this->redis->sInter('{set}square', '{set}seq'); // prime squares - $this->assertTrue($zt === []); + $this->assertEquals([], $zt); $zt = $this->redis->sInter(['{set}square', '{set}seq']); // prime squares, as array - $this->assertTrue($zt === []); + $this->assertEquals([], $zt); $xyz = $this->redis->sInter('{set}odd', '{set}prime', '{set}square');// odd prime squares - $this->assertTrue($xyz === ['1']); + $this->assertEquals(['1'], $xyz); $xyz = $this->redis->sInter(['{set}odd', '{set}prime', '{set}square']);// odd prime squares, with an array as a parameter - $this->assertTrue($xyz === ['1']); + $this->assertEquals(['1'], $xyz); $nil = $this->redis->sInter([]); - $this->assertTrue($nil === FALSE); + $this->assertFalse($nil); } public function testsInterStore() { - $this->redis->del('{set}x'); // set of odd numbers - $this->redis->del('{set}y'); // set of prime numbers - $this->redis->del('{set}z'); // set of squares - $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 + $this->redis->del('{set}x', '{set}y', '{set}z', '{set}t'); - $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; - foreach($x as $i) { + $x = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25]; + foreach ($x as $i) { $this->redis->sAdd('{set}x', $i); } - $y = [1,2,3,5,7,11,13,17,19,23]; - foreach($y as $i) { + $y = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23]; + foreach ($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = [1,4,9,16,25]; - foreach($z as $i) { + $z = [1, 4, 9, 16, 25]; + foreach ($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = [2,5,10,17,26]; - foreach($t as $i) { + $t = [2, 5, 10, 17, 26]; + foreach ($t as $i) { $this->redis->sAdd('{set}t', $i); } /* Regression test for passing a single array */ - $this->assertEquals($this->redis->sInterStore(['{set}k', '{set}x', '{set}y']), count(array_intersect($x,$y))); + $this->assertEquals( + count(array_intersect($x,$y)), + $this->redis->sInterStore(['{set}k', '{set}x', '{set}y']) + ); $count = $this->redis->sInterStore('{set}k', '{set}x', '{set}y'); // odd prime numbers $this->assertEquals($count, $this->redis->scard('{set}k')); - foreach(array_intersect($x, $y) as $i) { + foreach (array_intersect($x, $y) as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sInterStore('{set}k', '{set}y', '{set}z'); // set of odd squares $this->assertEquals($count, $this->redis->scard('{set}k')); - foreach(array_intersect($y, $z) as $i) { + foreach (array_intersect($y, $z) as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } @@ -1926,15 +2000,15 @@ public function testsInterStore() { $this->redis->del('{set}z'); $xyz = $this->redis->sInterStore('{set}k', '{set}x', '{set}y', '{set}z'); // only z missing, expect 0. - $this->assertTrue($xyz === 0); + $this->assertEquals(0, $xyz); $this->redis->del('{set}y'); $xyz = $this->redis->sInterStore('{set}k', '{set}x', '{set}y', '{set}z'); // y and z missing, expect 0. - $this->assertTrue($xyz === 0); + $this->assertEquals(0, $xyz); $this->redis->del('{set}x'); $xyz = $this->redis->sInterStore('{set}k', '{set}x', '{set}y', '{set}z'); // x y and z ALL missing, expect 0. - $this->assertTrue($xyz === 0); + $this->assertEquals(0, $xyz); } public function testsUnion() { @@ -1943,81 +2017,76 @@ public function testsUnion() { $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 - $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; - foreach($x as $i) { + $x = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25]; + foreach ($x as $i) { $this->redis->sAdd('{set}x', $i); } - $y = [1,2,3,5,7,11,13,17,19,23]; - foreach($y as $i) { + $y = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23]; + foreach ($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = [1,4,9,16,25]; - foreach($z as $i) { + $z = [1, 4, 9, 16, 25]; + foreach ($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = [2,5,10,17,26]; - foreach($t as $i) { + $t = [2, 5, 10, 17, 26]; + foreach ($t as $i) { $this->redis->sAdd('{set}t', $i); } $xy = $this->redis->sUnion('{set}x', '{set}y'); // x U y - foreach($xy as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_merge($x, $y))); + foreach ($xy as $i) { + $this->assertInArray($i, array_merge($x, $y)); } $yz = $this->redis->sUnion('{set}y', '{set}z'); // y U Z - foreach($yz as $i) { + foreach ($yz as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_merge($y, $z))); + $this->assertInArray($i, array_merge($y, $z)); } $zt = $this->redis->sUnion('{set}z', '{set}t'); // z U t - foreach($zt as $i) { + foreach ($zt as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_merge($z, $t))); + $this->assertInArray($i, array_merge($z, $t)); } $xyz = $this->redis->sUnion('{set}x', '{set}y', '{set}z'); // x U y U z - foreach($xyz as $i) { - $i = (int)$i; - $this->assertTrue(in_array($i, array_merge($x, $y, $z))); + foreach ($xyz as $i) { + $this->assertInArray($i, array_merge($x, $y, $z)); } } public function testsUnionStore() { - $this->redis->del('{set}x'); // set of odd numbers - $this->redis->del('{set}y'); // set of prime numbers - $this->redis->del('{set}z'); // set of squares - $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 + $this->redis->del('{set}x', '{set}y', '{set}z', '{set}t'); - $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; - foreach($x as $i) { + $x = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25]; + foreach ($x as $i) { $this->redis->sAdd('{set}x', $i); } - $y = [1,2,3,5,7,11,13,17,19,23]; - foreach($y as $i) { + $y = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23]; + foreach ($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = [1,4,9,16,25]; - foreach($z as $i) { + $z = [1, 4, 9, 16, 25]; + foreach ($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = [2,5,10,17,26]; - foreach($t as $i) { + $t = [2, 5, 10, 17, 26]; + foreach ($t as $i) { $this->redis->sAdd('{set}t', $i); } $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y'); // x U y $xy = array_unique(array_merge($x, $y)); $this->assertEquals($count, count($xy)); - foreach($xy as $i) { + foreach ($xy as $i) { $i = (int)$i; $this->assertTrue($this->redis->sismember('{set}k', $i)); } @@ -2025,38 +2094,35 @@ public function testsUnionStore() { $count = $this->redis->sUnionStore('{set}k', '{set}y', '{set}z'); // y U z $yz = array_unique(array_merge($y, $z)); $this->assertEquals($count, count($yz)); - foreach($yz as $i) { - $i = (int)$i; + foreach ($yz as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sUnionStore('{set}k', '{set}z', '{set}t'); // z U t $zt = array_unique(array_merge($z, $t)); $this->assertEquals($count, count($zt)); - foreach($zt as $i) { - $i = (int)$i; + foreach ($zt as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z $xyz = array_unique(array_merge($x, $y, $z)); $this->assertEquals($count, count($xyz)); - foreach($xyz as $i) { - $i = (int)$i; + foreach ($xyz as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $this->redis->del('{set}x'); // x missing now $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z - $this->assertTrue($count === count(array_unique(array_merge($y, $z)))); + $this->assertEquals($count, count(array_unique(array_merge($y, $z)))); $this->redis->del('{set}y'); // x and y missing $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z - $this->assertTrue($count === count(array_unique($z))); + $this->assertEquals($count, count(array_unique($z))); $this->redis->del('{set}z'); // x, y, and z ALL missing $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z - $this->assertTrue($count === 0); + $this->assertEquals(0, $count); } public function testsDiff() { @@ -2065,126 +2131,118 @@ public function testsDiff() { $this->redis->del('{set}z'); // set of squares $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 - $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; - foreach($x as $i) { + $x = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25]; + foreach ($x as $i) { $this->redis->sAdd('{set}x', $i); } - $y = [1,2,3,5,7,11,13,17,19,23]; - foreach($y as $i) { + $y = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23]; + foreach ($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = [1,4,9,16,25]; - foreach($z as $i) { + $z = [1, 4, 9, 16, 25]; + foreach ($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = [2,5,10,17,26]; - foreach($t as $i) { + $t = [2, 5, 10, 17, 26]; + foreach ($t as $i) { $this->redis->sAdd('{set}t', $i); } $xy = $this->redis->sDiff('{set}x', '{set}y'); // x U y - foreach($xy as $i) { + foreach ($xy as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_diff($x, $y))); + $this->assertInArray($i, array_diff($x, $y)); } $yz = $this->redis->sDiff('{set}y', '{set}z'); // y U Z - foreach($yz as $i) { + foreach ($yz as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_diff($y, $z))); + $this->assertInArray($i, array_diff($y, $z)); } $zt = $this->redis->sDiff('{set}z', '{set}t'); // z U t - foreach($zt as $i) { + foreach ($zt as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_diff($z, $t))); + $this->assertInArray($i, array_diff($z, $t)); } $xyz = $this->redis->sDiff('{set}x', '{set}y', '{set}z'); // x U y U z - foreach($xyz as $i) { + foreach ($xyz as $i) { $i = (int)$i; - $this->assertTrue(in_array($i, array_diff($x, $y, $z))); + $this->assertInArray($i, array_diff($x, $y, $z)); } } public function testsDiffStore() { - $this->redis->del('{set}x'); // set of odd numbers - $this->redis->del('{set}y'); // set of prime numbers - $this->redis->del('{set}z'); // set of squares - $this->redis->del('{set}t'); // set of numbers of the form n^2 - 1 + $this->redis->del('{set}x', '{set}y', '{set}z', '{set}t'); - $x = [1,3,5,7,9,11,13,15,17,19,21,23,25]; - foreach($x as $i) { + $x = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25]; + foreach ($x as $i) { $this->redis->sAdd('{set}x', $i); } - $y = [1,2,3,5,7,11,13,17,19,23]; - foreach($y as $i) { + $y = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23]; + foreach ($y as $i) { $this->redis->sAdd('{set}y', $i); } - $z = [1,4,9,16,25]; - foreach($z as $i) { + $z = [1, 4, 9, 16, 25]; + foreach ($z as $i) { $this->redis->sAdd('{set}z', $i); } - $t = [2,5,10,17,26]; - foreach($t as $i) { + $t = [2, 5, 10, 17, 26]; + foreach ($t as $i) { $this->redis->sAdd('{set}t', $i); } $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y'); // x - y $xy = array_unique(array_diff($x, $y)); $this->assertEquals($count, count($xy)); - foreach($xy as $i) { - $i = (int)$i; + foreach ($xy as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sDiffStore('{set}k', '{set}y', '{set}z'); // y - z $yz = array_unique(array_diff($y, $z)); $this->assertEquals($count, count($yz)); - foreach($yz as $i) { - $i = (int)$i; + foreach ($yz as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sDiffStore('{set}k', '{set}z', '{set}t'); // z - t $zt = array_unique(array_diff($z, $t)); $this->assertEquals($count, count($zt)); - foreach($zt as $i) { - $i = (int)$i; + foreach ($zt as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z'); // x - y - z $xyz = array_unique(array_diff($x, $y, $z)); $this->assertEquals($count, count($xyz)); - foreach($xyz as $i) { - $i = (int)$i; + foreach ($xyz as $i) { $this->assertTrue($this->redis->sismember('{set}k', $i)); } $this->redis->del('{set}x'); // x missing now $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z'); // x - y - z - $this->assertTrue($count === 0); + $this->assertEquals(0, $count); $this->redis->del('{set}y'); // x and y missing $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z'); // x - y - z - $this->assertTrue($count === 0); + $this->assertEquals(0, $count); $this->redis->del('{set}z'); // x, y, and z ALL missing $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z'); // x - y - z - $this->assertTrue($count === 0); + $this->assertEquals(0, $count); } public function testInterCard() { - if(version_compare($this->version, "7.0.0") < 0) { + if (version_compare($this->version, '7.0.0') < 0) $this->markTestSkipped(); - } $set_data = [ ['aardvark', 'dog', 'fish', 'squirrel', 'tiger'], @@ -2230,33 +2288,29 @@ public function testInterCard() { $this->redis->del(array_merge($ssets, $zsets)); } - public function testlrange() { + public function testLRange() { $this->redis->del('list'); $this->redis->lPush('list', 'val'); $this->redis->lPush('list', 'val2'); $this->redis->lPush('list', 'val3'); - // pos : 0 1 2 - // pos : -3 -2 -1 - // list: [val3, val2, val] + $this->assertEquals(['val3'], $this->redis->lrange('list', 0, 0)); + $this->assertEquals(['val3', 'val2'], $this->redis->lrange('list', 0, 1)); + $this->assertEquals(['val3', 'val2', 'val'], $this->redis->lrange('list', 0, 2)); + $this->assertEquals(['val3', 'val2', 'val'], $this->redis->lrange('list', 0, 3)); - $this->assertEquals($this->redis->lrange('list', 0, 0), ['val3']); - $this->assertEquals($this->redis->lrange('list', 0, 1), ['val3', 'val2']); - $this->assertEquals($this->redis->lrange('list', 0, 2), ['val3', 'val2', 'val']); - $this->assertEquals($this->redis->lrange('list', 0, 3), ['val3', 'val2', 'val']); - - $this->assertEquals($this->redis->lrange('list', 0, -1), ['val3', 'val2', 'val']); - $this->assertEquals($this->redis->lrange('list', 0, -2), ['val3', 'val2']); - $this->assertEquals($this->redis->lrange('list', -2, -1), ['val2', 'val']); + $this->assertEquals(['val3', 'val2', 'val'], $this->redis->lrange('list', 0, -1)); + $this->assertEquals(['val3', 'val2'], $this->redis->lrange('list', 0, -2)); + $this->assertEquals(['val2', 'val'], $this->redis->lrange('list', -2, -1)); $this->redis->del('list'); - $this->assertEquals($this->redis->lrange('list', 0, -1), []); + $this->assertEquals([], $this->redis->lrange('list', 0, -1)); } public function testdbSize() { $this->assertTrue($this->redis->flushDB()); $this->redis->set('x', 'y'); - $this->assertTrue($this->redis->dbSize() === 1); + $this->assertEquals(1, $this->redis->dbSize()); } public function testFlushDB() { @@ -2266,31 +2320,31 @@ public function testFlushDB() { $this->assertTrue($this->redis->flushdb(true)); } - public function testttl() { + public function testTTL() { $this->redis->set('x', 'y'); $this->redis->expire('x', 5); $ttl = $this->redis->ttl('x'); - $this->assertTrue($ttl > 0 && $ttl <= 5); + $this->assertBetween($ttl, 1, 5); // A key with no TTL $this->redis->del('x'); $this->redis->set('x', 'bar'); - $this->assertEquals($this->redis->ttl('x'), -1); + $this->assertEquals(-1, $this->redis->ttl('x')); // A key that doesn't exist (> 2.8 will return -2) - if(version_compare($this->version, "2.8.0") >= 0) { + if (version_compare($this->version, '2.8.0') >= 0) { $this->redis->del('x'); - $this->assertEquals($this->redis->ttl('x'), -2); + $this->assertEquals(-2, $this->redis->ttl('x')); } } public function testPersist() { $this->redis->set('x', 'y'); $this->redis->expire('x', 100); - $this->assertTrue(TRUE === $this->redis->persist('x')); // true if there is a timeout - $this->assertTrue(-1 === $this->redis->ttl('x')); // -1: timeout has been removed. - $this->assertTrue(FALSE === $this->redis->persist('x')); // false if there is no timeout + $this->assertTrue($this->redis->persist('x')); // true if there is a timeout + $this->assertEquals(-1, $this->redis->ttl('x')); // -1: timeout has been removed. + $this->assertFalse($this->redis->persist('x')); // false if there is no timeout $this->redis->del('x'); - $this->assertTrue(FALSE === $this->redis->persist('x')); // false if the key doesn’t exist. + $this->assertFalse($this->redis->persist('x')); // false if the key doesn’t exist. } public function testClient() { @@ -2298,38 +2352,39 @@ public function testClient() { $this->assertTrue($this->redis->client('setname', 'phpredis_unit_tests')); /* CLIENT LIST */ - $arr_clients = $this->redis->client('list'); - $this->assertTrue(is_array($arr_clients)); + $clients = $this->redis->client('list'); + $this->assertIsArray($clients); // Figure out which ip:port is us! - $str_addr = NULL; - foreach($arr_clients as $arr_client) { - if($arr_client['name'] == 'phpredis_unit_tests') { - $str_addr = $arr_client['addr']; + $address = NULL; + foreach ($clients as $client) { + if ($client['name'] == 'phpredis_unit_tests') { + $address = $client['addr']; } } // We should have found our connection - $this->assertFalse(empty($str_addr)); + $this->assertIsString($address); /* CLIENT GETNAME */ - $this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests'); + $this->assertEquals('phpredis_unit_tests', $this->redis->client('getname')); if (version_compare($this->version, '5.0.0') >= 0) { - $this->assertLess(0, $this->redis->client('id')); + $this->assertGT(0, $this->redis->client('id')); if (version_compare($this->version, '6.0.0') >= 0) { - $this->assertEquals($this->redis->client('getredir'), -1); + $this->assertEquals(-1, $this->redis->client('getredir')); $this->assertTrue($this->redis->client('tracking', 'on', ['optin' => true])); - $this->assertEquals($this->redis->client('getredir'), 0); + $this->assertEquals(0, $this->redis->client('getredir')); $this->assertTrue($this->redis->client('caching', 'yes')); $this->assertTrue($this->redis->client('tracking', 'off')); if (version_compare($this->version, '6.2.0') >= 0) { $this->assertFalse(empty($this->redis->client('info'))); - $this->assertEquals($this->redis->client('trackinginfo'), [ + $this->assertEquals([ 'flags' => ['off'], 'redirect' => -1, 'prefixes' => [], - ]); + ], $this->redis->client('trackinginfo')); + if (version_compare($this->version, '7.0.0') >= 0) { $this->assertTrue($this->redis->client('no-evict', 'on')); } @@ -2338,50 +2393,51 @@ public function testClient() { } /* CLIENT KILL -- phpredis will reconnect, so we can do this */ - $this->assertTrue($this->redis->client('kill', $str_addr)); + $this->assertTrue($this->redis->client('kill', $address)); } public function testSlowlog() { // We don't really know what's going to be in the slowlog, but make sure // the command returns proper types when called in various ways - $this->assertTrue(is_array($this->redis->slowlog('get'))); - $this->assertTrue(is_array($this->redis->slowlog('get', 10))); - $this->assertTrue(is_int($this->redis->slowlog('len'))); + $this->assertIsArray($this->redis->slowlog('get')); + $this->assertIsArray($this->redis->slowlog('get', 10)); + $this->assertIsInt($this->redis->slowlog('len')); $this->assertTrue($this->redis->slowlog('reset')); $this->assertFalse(@$this->redis->slowlog('notvalid')); } public function testWait() { - // Closest we can check based on redis commmit history - if(version_compare($this->version, '2.9.11') < 0) { + // Closest we can check based on redis commit history + if (version_compare($this->version, '2.9.11') < 0) $this->markTestSkipped(); - return; - } // We could have slaves here, so determine that - $arr_slaves = $this->redis->info(); - $i_slaves = $arr_slaves['connected_slaves']; + $info = $this->redis->info(); + $replicas = $info['connected_slaves']; // Send a couple commands $this->redis->set('wait-foo', 'over9000'); $this->redis->set('wait-bar', 'revo9000'); // Make sure we get the right replication count - $this->assertEquals($this->redis->wait($i_slaves, 100), $i_slaves); + $this->assertEquals($replicas, $this->redis->wait($replicas, 100)); // Pass more slaves than are connected - $this->redis->set('wait-foo','over9000'); - $this->redis->set('wait-bar','revo9000'); - $this->assertTrue($this->redis->wait($i_slaves+1, 100) < $i_slaves+1); + $this->redis->set('wait-foo', 'over9000'); + $this->redis->set('wait-bar', 'revo9000'); + $this->assertLT($replicas + 1, $this->redis->wait($replicas + 1, 100)); // Make sure when we pass with bad arguments we just get back false $this->assertFalse($this->redis->wait(-1, -1)); - $this->assertFalse($this->redis->wait(-1, 20)); } public function testInfo() { - foreach ([false, true] as $boo_multi) { + $sequence = [false]; + if ($this->haveMulti()) + $sequence[] = true; + + foreach ($sequence as $boo_multi) { if ($boo_multi) { $this->redis->multi(); $this->redis->info(); @@ -2392,116 +2448,217 @@ public function testInfo() { } $keys = [ - "redis_version", - "arch_bits", - "uptime_in_seconds", - "uptime_in_days", - "connected_clients", - "connected_slaves", - "used_memory", - "total_connections_received", - "total_commands_processed", - "role" + 'redis_version', + 'arch_bits', + 'uptime_in_seconds', + 'uptime_in_days', + 'connected_clients', + 'connected_slaves', + 'used_memory', + 'total_connections_received', + 'total_commands_processed', + 'role' ]; - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) { array_push($keys, - "changes_since_last_save", - "bgsave_in_progress", - "last_save_time" + 'changes_since_last_save', + 'bgsave_in_progress', + 'last_save_time' ); } else { array_push($keys, - "rdb_changes_since_last_save", - "rdb_bgsave_in_progress", - "rdb_last_save_time" + 'rdb_changes_since_last_save', + 'rdb_bgsave_in_progress', + 'rdb_last_save_time' ); } - foreach($keys as $k) { - $this->assertTrue(in_array($k, array_keys($info))); + foreach ($keys as $k) { + $this->assertInArray($k, array_keys($info)); } } - if (!$this->minVersionCheck("7.0.0")) + if ( ! $this->minVersionCheck('7.0.0')) return; $res = $this->redis->info('server', 'memory'); $this->assertTrue(is_array($res) && isset($res['redis_version']) && isset($res['used_memory'])); } - public function testInfoCommandStats() { + protected function execHello() { + $zipped = []; + + $result = $this->redis->rawCommand('HELLO'); + if ( ! is_array($result) || count($result) % 2 != 0) + return false; + + for ($i = 0; $i < count($result); $i += 2) { + $zipped[$result[$i]] = $result[$i + 1]; + } - // INFO COMMANDSTATS is new in 2.6.0 - if (version_compare($this->version, "2.5.0") < 0) { - $this->markTestSkipped(); + return $zipped; } - $info = $this->redis->info("COMMANDSTATS"); + public function testServerInfo() { + if ( ! $this->minVersionCheck('6.0.0')) + $this->markTestSkipped(); + + $hello = $this->execHello(); - $this->assertTrue(is_array($info)); - if (is_array($info)) { - foreach($info as $k => $value) { - $this->assertTrue(strpos($k, 'cmdstat_') !== false); + if ( ! $this->assertArrayKey($hello, 'server') || + ! $this->assertArrayKey($hello, 'version')) + { + return false; } + + $this->assertEquals($hello['server'], $this->redis->serverName()); + $this->assertEquals($hello['version'], $this->redis->serverVersion()); + + $info = $this->redis->info(); + + $cmd1 = $info['total_commands_processed']; + + /* Shouldn't hit the server */ + $this->assertEquals($hello['server'], $this->redis->serverName()); + $this->assertEquals($hello['version'], $this->redis->serverVersion()); + + $info = $this->redis->info(); + $cmd2 = $info['total_commands_processed']; + + $this->assertEquals(1 + $cmd1, $cmd2); + } + + public function testServerInfoOldRedis() { + if ($this->minVersionCheck('6.0.0')) + $this->markTestSkipped(); + + $this->assertFalse($this->redis->serverName()); + $this->assertFalse($this->redis->serverVersion()); } + + public function testInfoCommandStats() { + // INFO COMMANDSTATS is new in 2.6.0 + if (version_compare($this->version, '2.5.0') < 0) + $this->markTestSkipped(); + + $info = $this->redis->info('COMMANDSTATS'); + if ( ! $this->assertIsArray($info)) + return; + + foreach ($info as $k => $value) { + $this->assertStringContains('cmdstat_', $k); + } } public function testSelect() { - $this->assertFalse($this->redis->select(-1)); + $this->assertFalse(@$this->redis->select(-1)); $this->assertTrue($this->redis->select(0)); } public function testSwapDB() { - if (version_compare($this->version, "4.0.0") < 0) { + if (version_compare($this->version, '4.0.0') < 0) $this->markTestSkipped(); - } $this->assertTrue($this->redis->swapdb(0, 1)); $this->assertTrue($this->redis->swapdb(0, 1)); } public function testMset() { - $this->redis->del('x', 'y', 'z'); // remove x y z - $this->assertTrue($this->redis->mset(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z + $this->redis->del('x', 'y', 'z'); // remove x y z + $this->assertTrue($this->redis->mset(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z - $this->assertEquals($this->redis->mget(['x', 'y', 'z']), ['a', 'b', 'c']); // check x y z + $this->assertEquals(['a', 'b', 'c'], $this->redis->mget(['x', 'y', 'z'])); // check x y z - $this->redis->del('x'); // delete just x - $this->assertTrue($this->redis->mset(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z - $this->assertEquals($this->redis->mget(['x', 'y', 'z']), ['a', 'b', 'c']); // check x y z + $this->redis->del('x'); // delete just x + $this->assertTrue($this->redis->mset(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z + $this->assertEquals(['a', 'b', 'c'], $this->redis->mget(['x', 'y', 'z'])); // check x y z - $this->assertFalse($this->redis->mset([])); // set ø → FALSE + $this->assertFalse($this->redis->mset([])); // set ø → FALSE + /* + * Integer keys + */ - /* - * Integer keys - */ + // No prefix + $set_array = [-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three']; + $this->redis->del(array_keys($set_array)); + $this->assertTrue($this->redis->mset($set_array)); + $this->assertEquals(array_values($set_array), $this->redis->mget(array_keys($set_array))); + $this->redis->del(array_keys($set_array)); + + // With a prefix + $this->redis->setOption(Redis::OPT_PREFIX, 'pfx:'); + $this->redis->del(array_keys($set_array)); + $this->assertTrue($this->redis->mset($set_array)); + $this->assertEquals(array_values($set_array), $this->redis->mget(array_keys($set_array))); + $this->redis->del(array_keys($set_array)); + $this->redis->setOption(Redis::OPT_PREFIX, ''); + } + + public function testMSetEx() { + if ( ! $this->haveCommand('MSETEX')) + $this->markTestSkipped(); + + $kvals = ['{msetex}:1' => 'v1', '{msetex}:2' => 'v2']; + $keys = array_keys($kvals); + + // No options, same as MSET + $this->redis->del($keys); + $ret = $this->redis->msetex($kvals); + $this->assertEquals(1, $ret); + $this->assertGT(0, $ret); + $this->assertEquals(array_values($kvals), $this->redis->mget($keys)); + foreach ($keys as $key) { + $this->assertEquals(-1, $this->redis->ttl($key)); + } + + $options = [ + ['EX', 100, 'TTL'], + ['PX', 100000, 'PTTL'], + ['EXAT', time() + 100, 'EXPIRETIME'], + ['PXAT', $this->mstime() + 100000, 'PEXPIRETIME'] + ]; + + foreach ($options as [$opt, $val, $desc]) { + $this->assertIsInt($this->redis->del($keys)); + $this->redis->msetex($kvals, [$opt => $val]); + foreach ($keys as $key) { + /* Timiing tests on GitHub CI are atrocious so we just + want to verify that >= 1 and <= $val */ + $ttl = $this->redis->$desc($key); + $this->assertBetween($val, 1, $ttl + 1); + } + } + + /* KEEPTTL */ + $this->redis->set('{kt}:1', 'v1', 100); + $this->assertEquals(1, $this->redis->msetex(['{kt}:1' => 'v2'], + ['KEEPTTL'])); + $this->assertBetween($this->redis->ttl('{kt}:1'), 1, 100); - // No prefix - $set_array = [-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three']; - $this->redis->del(array_keys($set_array)); - $this->assertTrue($this->redis->mset($set_array)); - $this->assertEquals($this->redis->mget(array_keys($set_array)), array_values($set_array)); - $this->redis->del(array_keys($set_array)); + $keys = ['{xxnx}:1' => '1', '{xxnx}:2' => '2']; + $this->assertTrue($this->redis->mset($keys)); - // With a prefix - $this->redis->setOption(Redis::OPT_PREFIX, 'pfx:'); - $this->redis->del(array_keys($set_array)); - $this->assertTrue($this->redis->mset($set_array)); - $this->assertEquals($this->redis->mget(array_keys($set_array)), array_values($set_array)); - $this->redis->del(array_keys($set_array)); - $this->redis->setOption(Redis::OPT_PREFIX, ''); + /* XX: Only set if they all already exist */ + $this->assertEquals(1, $this->redis->msetex($keys, ['XX'])); + $this->assertEquals(1, $this->redis->del(array_rand($keys))); + $this->assertEquals(0, $this->redis->msetex($keys, ['XX'])); + + /* NX: Only if none exist. Right now one does */ + $this->assertEquals(0, $this->redis->msetex($keys, ['NX'])); + $this->assertGT(0, $this->redis->del(array_keys($keys))); + $this->assertEquals(1, $this->redis->msetex($keys, ['NX'])); } public function testMsetNX() { $this->redis->del('x', 'y', 'z'); // remove x y z - $this->assertTrue(TRUE === $this->redis->msetnx(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z + $this->assertTrue($this->redis->msetnx(['x' => 'a', 'y' => 'b', 'z' => 'c'])); // set x y z - $this->assertEquals($this->redis->mget(['x', 'y', 'z']), ['a', 'b', 'c']); // check x y z + $this->assertEquals(['a', 'b', 'c'], $this->redis->mget(['x', 'y', 'z'])); // check x y z $this->redis->del('x'); // delete just x - $this->assertTrue(FALSE === $this->redis->msetnx(['x' => 'A', 'y' => 'B', 'z' => 'C'])); // set x y z - $this->assertEquals($this->redis->mget(['x', 'y', 'z']), [FALSE, 'b', 'c']); // check x y z + $this->assertFalse($this->redis->msetnx(['x' => 'A', 'y' => 'B', 'z' => 'C'])); // set x y z + $this->assertEquals([FALSE, 'b', 'c'], $this->redis->mget(['x', 'y', 'z'])); // check x y z $this->assertFalse($this->redis->msetnx([])); // set ø → FALSE } @@ -2515,15 +2672,15 @@ public function testRpopLpush() { $this->redis->lpush('{list}y', '123'); $this->redis->lpush('{list}y', '456'); // y = [456, 123] - $this->assertEquals($this->redis->rpoplpush('{list}x', '{list}y'), 'abc'); // we RPOP x, yielding abc. - $this->assertEquals($this->redis->lrange('{list}x', 0, -1), ['def']); // only def remains in x. - $this->assertEquals($this->redis->lrange('{list}y', 0, -1), ['abc', '456', '123']); // abc has been lpushed to y. + $this->assertEquals('abc', $this->redis->rpoplpush('{list}x', '{list}y')); // we RPOP x, yielding abc. + $this->assertEquals(['def'], $this->redis->lrange('{list}x', 0, -1)); // only def remains in x. + $this->assertEquals(['abc', '456', '123'], $this->redis->lrange('{list}y', 0, -1)); // abc has been lpushed to y. // with an empty source, expecting no change. $this->redis->del('{list}x', '{list}y'); - $this->assertTrue(FALSE === $this->redis->rpoplpush('{list}x', '{list}y')); - $this->assertTrue([] === $this->redis->lrange('{list}x', 0, -1)); - $this->assertTrue([] === $this->redis->lrange('{list}y', 0, -1)); + $this->assertFalse($this->redis->rpoplpush('{list}x', '{list}y')); + $this->assertEquals([], $this->redis->lrange('{list}x', 0, -1)); + $this->assertEquals([], $this->redis->lrange('{list}y', 0, -1)); } public function testBRpopLpush() { @@ -2535,36 +2692,36 @@ public function testBRpopLpush() { $this->redis->lpush('{list}y', '123'); $this->redis->lpush('{list}y', '456'); // y = [456, 123] - $this->assertEquals($this->redis->brpoplpush('{list}x', '{list}y', 1), 'abc'); // we RPOP x, yielding abc. + $this->assertEquals('abc', $this->redis->brpoplpush('{list}x', '{list}y', 1)); // we RPOP x, yielding abc. - $this->assertEquals($this->redis->lrange('{list}x', 0, -1), ['def']); // only def remains in x. - $this->assertEquals($this->redis->lrange('{list}y', 0, -1), ['abc', '456', '123']); // abc has been lpushed to y. + $this->assertEquals(['def'], $this->redis->lrange('{list}x', 0, -1)); // only def remains in x. + $this->assertEquals(['abc', '456', '123'], $this->redis->lrange('{list}y', 0, -1)); // abc has been lpushed to y. // with an empty source, expecting no change. $this->redis->del('{list}x', '{list}y'); - $this->assertTrue(FALSE === $this->redis->brpoplpush('{list}x', '{list}y', 1)); - $this->assertTrue([] === $this->redis->lrange('{list}x', 0, -1)); - $this->assertTrue([] === $this->redis->lrange('{list}y', 0, -1)); + $this->assertFalse($this->redis->brpoplpush('{list}x', '{list}y', 1)); + $this->assertEquals([], $this->redis->lrange('{list}x', 0, -1)); + $this->assertEquals([], $this->redis->lrange('{list}y', 0, -1)); - if (!$this->minVersionCheck('6.0.0')) + if ( ! $this->minVersionCheck('6.0.0')) return; // Redis >= 6.0.0 allows floating point timeouts $st = microtime(true); - $this->assertEquals(FALSE, $this->redis->brpoplpush('{list}x', '{list}y', .1)); + $this->assertFalse($this->redis->brpoplpush('{list}x', '{list}y', .1)); $et = microtime(true); - $this->assertTrue($et - $st < 1.0); + $this->assertLT(1.0, $et - $st); } public function testZAddFirstArg() { + $zsetName = 100; // not a string! - $this->redis->del('key'); + $this->redis->del($zsetName); - $zsetName = 100; // not a string! - $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0')); - $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1')); + $this->assertEquals(1, $this->redis->zAdd($zsetName, 0, 'val0')); + $this->assertEquals(1, $this->redis->zAdd($zsetName, 1, 'val1')); - $this->assertTrue(['val0', 'val1'] === $this->redis->zRange($zsetName, 0, -1)); + $this->assertEquals(['val0', 'val1'], $this->redis->zRange($zsetName, 0, -1)); } public function testZaddIncr() { @@ -2576,72 +2733,86 @@ public function testZaddIncr() { $this->assertFalse($this->redis->zAdd('zset', ['incr'], 10, 'value', 20, 'value2')); } + /* Regression test for GitHub issue #2697 */ + public function testZAddLargeLong() { + $this->redis->del('key'); + + $val = 5000000000; + + $this->assertEquals(1, $this->redis->zAdd('key', $val, 'val0')); + + $this->assertEquals($this->redis->zscore('key', 'val0'), (float)$val); + } + public function testZX() { $this->redis->del('key'); - $this->assertTrue([] === $this->redis->zRange('key', 0, -1)); - $this->assertTrue([] === $this->redis->zRange('key', 0, -1, true)); + $this->assertEquals([], $this->redis->zRange('key', 0, -1)); + $this->assertEquals([], $this->redis->zRange('key', 0, -1, true)); - $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0')); - $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2')); - $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters - if (version_compare($this->version, "3.0.2") < 0) { - $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); + $this->assertEquals(1, $this->redis->zAdd('key', 0, 'val0')); + $this->assertEquals(1, $this->redis->zAdd('key', 2, 'val2')); + $this->assertEquals(2, $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters + if (version_compare($this->version, '3.0.2') < 0) { + $this->assertEquals(1, $this->redis->zAdd('key', 1, 'val1')); + $this->assertEquals(1, $this->redis->zAdd('key', 3, 'val3')); } else { - $this->assertTrue(1 === $this->redis->zAdd('key', [], 1, 'val1')); // empty options - $this->assertTrue(1 === $this->redis->zAdd('key', ['nx'], 3, 'val3')); // nx option - $this->assertTrue(0 === $this->redis->zAdd('key', ['xx'], 3, 'val3')); // xx option + $this->assertEquals(1, $this->redis->zAdd('key', [], 1, 'val1')); // empty options + $this->assertEquals(1, $this->redis->zAdd('key', ['nx'], 3, 'val3')); // nx option + $this->assertEquals(0, $this->redis->zAdd('key', ['xx'], 3, 'val3')); // xx option - if (version_compare($this->version, "6.2.0") >= 0) { - $this->assertTrue(0 === $this->redis->zAdd('key', ['lt'], 4, 'val3')); // lt option - $this->assertTrue(0 === $this->redis->zAdd('key', ['gt'], 2, 'val3')); // gt option + if (version_compare($this->version, '6.2.0') >= 0) { + $this->assertEquals(0, $this->redis->zAdd('key', ['lt'], 4, 'val3')); // lt option + $this->assertEquals(0, $this->redis->zAdd('key', ['gt'], 2, 'val3')); // gt option } } - $this->assertTrue(['val0', 'val1', 'val2', 'val3', 'val4', 'val5'] === $this->redis->zRange('key', 0, -1)); + $this->assertEquals(['val0', 'val1', 'val2', 'val3', 'val4', 'val5'], $this->redis->zRange('key', 0, -1)); // withscores $ret = $this->redis->zRange('key', 0, -1, true); - $this->assertTrue(count($ret) == 6); - $this->assertTrue($ret['val0'] == 0); - $this->assertTrue($ret['val1'] == 1); - $this->assertTrue($ret['val2'] == 2); - $this->assertTrue($ret['val3'] == 3); - $this->assertTrue($ret['val4'] == 4); - $this->assertTrue($ret['val5'] == 5); + $this->assertEquals(6, count($ret)); + $this->assertEquals(0.0, $ret['val0']); + $this->assertEquals(1.0, $ret['val1']); + $this->assertEquals(2.0, $ret['val2']); + $this->assertEquals(3.0, $ret['val3']); + $this->assertEquals(4.0, $ret['val4']); + $this->assertEquals(5.0, $ret['val5']); - $this->assertTrue(0 === $this->redis->zRem('key', 'valX')); - $this->assertTrue(1 === $this->redis->zRem('key', 'val3')); - $this->assertTrue(1 === $this->redis->zRem('key', 'val4')); - $this->assertTrue(1 === $this->redis->zRem('key', 'val5')); + $this->assertEquals(0, $this->redis->zRem('key', 'valX')); + $this->assertEquals(1, $this->redis->zRem('key', 'val3')); + $this->assertEquals(1, $this->redis->zRem('key', 'val4')); + $this->assertEquals(1, $this->redis->zRem('key', 'val5')); - $this->assertTrue(['val0', 'val1', 'val2'] === $this->redis->zRange('key', 0, -1)); + $this->assertEquals(['val0', 'val1', 'val2'], $this->redis->zRange('key', 0, -1)); // zGetReverseRange - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3')); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3')); + $this->assertEquals(1, $this->redis->zAdd('key', 3, 'val3')); + $this->assertEquals(1, $this->redis->zAdd('key', 3, 'aal3')); $zero_to_three = $this->redis->zRangeByScore('key', 0, 3); - $this->assertTrue(['val0', 'val1', 'val2', 'aal3', 'val3'] === $zero_to_three || ['val0', 'val1', 'val2', 'val3', 'aal3'] === $zero_to_three); + $this->assertEquals(['val0', 'val1', 'val2', 'aal3', 'val3'], $zero_to_three); $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0); - $this->assertTrue(array_reverse(['val0', 'val1', 'val2', 'aal3', 'val3']) === $three_to_zero || array_reverse(['val0', 'val1', 'val2', 'val3', 'aal3']) === $three_to_zero); + $this->assertEquals(array_reverse(['val0', 'val1', 'val2', 'aal3', 'val3']), $three_to_zero); - $this->assertTrue(5 === $this->redis->zCount('key', 0, 3)); + $this->assertEquals(5, $this->redis->zCount('key', 0, 3)); // withscores $this->redis->zRem('key', 'aal3'); - $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE]); - $this->assertTrue(['val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3] == $zero_to_three); - $this->assertTrue(4 === $this->redis->zCount('key', 0, 3)); + $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, ['withscores' => true]); + $this->assertEquals(['val0' => 0.0, 'val1' => 1.0, 'val2' => 2.0, 'val3' => 3.0], $zero_to_three); + $this->assertEquals(4, $this->redis->zCount('key', 0, 3)); // limit - $this->assertTrue(['val0'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 1]])); - $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 2]])); - $this->assertTrue(['val1', 'val2'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [1, 2]])); - $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => [0, 100]])); + $this->assertEquals(['val0'], $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 1]])); + $this->assertEquals(['val0', 'val1'], + $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 2]])); + $this->assertEquals(['val1', 'val2'], + $this->redis->zRangeByScore('key', 0, 3, ['limit' => [1, 2]])); + $this->assertEquals(['val0', 'val1'], + $this->redis->zRangeByScore('key', 0, 1, ['limit' => [0, 100]])); if ($this->minVersionCheck('6.2.0')) $this->assertEquals(['val0', 'val1'], $this->redis->zrange('key', 0, 1, ['byscore', 'limit' => [0, 100]])); @@ -2649,15 +2820,24 @@ public function testZX() { // limits as references $limit = [0, 100]; foreach ($limit as &$val) {} - $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => $limit])); + $this->assertEquals(['val0', 'val1'], $this->redis->zRangeByScore('key', 0, 1, ['limit' => $limit])); - $this->assertTrue(['val3'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 1]])); - $this->assertTrue(['val3', 'val2'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 2]])); - $this->assertTrue(['val2', 'val1'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [1, 2]])); - $this->assertTrue(['val1', 'val0'] === $this->redis->zRevRangeByScore('key', 1, 0, ['limit' => [0, 100]])); + $this->assertEquals( + ['val3'], $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 1]]) + ); + $this->assertEquals( + ['val3', 'val2'], $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 2]]) + ); + $this->assertEquals( + ['val2', 'val1'], $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [1, 2]]) + ); + $this->assertEquals( + ['val1', 'val0'], $this->redis->zRevRangeByScore('key', 1, 0, ['limit' => [0, 100]]) + ); if ($this->minVersionCheck('6.2.0')) { - $this->assertEquals(['val1', 'val0'], $this->redis->zrange('key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]])); + $this->assertEquals(['val1', 'val0'], + $this->redis->zrange('key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]])); $this->assertEquals(2, $this->redis->zrangestore('dst{key}', 'key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]])); $this->assertEquals(['val0', 'val1'], $this->redis->zRange('dst{key}', 0, -1)); @@ -2667,8 +2847,8 @@ public function testZX() { $this->assertEquals(['val1'], $this->redis->zrange('dst{key}', 0, -1)); } - $this->assertTrue(4 === $this->redis->zCard('key')); - $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); + $this->assertEquals(4, $this->redis->zCard('key')); + $this->assertEquals(1.0, $this->redis->zScore('key', 'val1')); $this->assertFalse($this->redis->zScore('key', 'val')); $this->assertFalse($this->redis->zScore(3, 2)); @@ -2678,22 +2858,31 @@ public function testZX() { $this->redis->zAdd('zset', 2, 'bar'); $this->redis->zAdd('zset', 3, 'biz'); $this->redis->zAdd('zset', 4, 'foz'); - $this->assertTrue(['foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4] == $this->redis->zRangeByScore('zset', '-inf', '+inf', ['withscores' => TRUE])); - $this->assertTrue(['foo' => 1, 'bar' => 2] == $this->redis->zRangeByScore('zset', 1, 2, ['withscores' => TRUE])); - $this->assertTrue(['bar' => 2] == $this->redis->zRangeByScore('zset', '(1', 2, ['withscores' => TRUE])); - $this->assertTrue([] == $this->redis->zRangeByScore('zset', '(1', '(2', ['withscores' => TRUE])); + $this->assertEquals( + ['foo' => 1.0, 'bar' => 2.0, 'biz' => 3.0, 'foz' => 4.0], + $this->redis->zRangeByScore('zset', '-inf', '+inf', ['withscores' => true]) + ); + $this->assertEquals( + ['foo' => 1.0, 'bar' => 2.0], + $this->redis->zRangeByScore('zset', 1, 2, ['withscores' => true]) + ); + $this->assertEquals( + ['bar' => 2.0], + $this->redis->zRangeByScore('zset', '(1', 2, ['withscores' => true]) + ); + $this->assertEquals([], $this->redis->zRangeByScore('zset', '(1', '(2', ['withscores' => true])); - $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf')); - $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2)); - $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); - $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2')); + $this->assertEquals(4, $this->redis->zCount('zset', '-inf', '+inf')); + $this->assertEquals(2, $this->redis->zCount('zset', 1, 2)); + $this->assertEquals(1, $this->redis->zCount('zset', '(1', 2)); + $this->assertEquals(0, $this->redis->zCount('zset', '(1', '(2')); // zincrby $this->redis->del('key'); - $this->assertTrue(1.0 === $this->redis->zIncrBy('key', 1, 'val1')); - $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); - $this->assertTrue(2.5 === $this->redis->zIncrBy('key', 1.5, 'val1')); - $this->assertTrue(2.5 === $this->redis->zScore('key', 'val1')); + $this->assertEquals(1.0, $this->redis->zIncrBy('key', 1, 'val1')); + $this->assertEquals(1.0, $this->redis->zScore('key', 'val1')); + $this->assertEquals(2.5, $this->redis->zIncrBy('key', 1.5, 'val1')); + $this->assertEquals(2.5, $this->redis->zScore('key', 'val1')); // zUnionStore $this->redis->del('{zset}1'); @@ -2710,26 +2899,26 @@ public function testZX() { $this->redis->zAdd('{zset}3', 4, 'val4'); $this->redis->zAdd('{zset}3', 5, 'val5'); - $this->assertTrue(4 === $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}3'])); - $this->assertTrue(['val0', 'val1', 'val4', 'val5'] === $this->redis->zRange('{zset}U', 0, -1)); + $this->assertEquals(4, $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}3'])); + $this->assertEquals(['val0', 'val1', 'val4', 'val5'], $this->redis->zRange('{zset}U', 0, -1)); // Union on non existing keys $this->redis->del('{zset}U'); - $this->assertTrue(0 === $this->redis->zUnionStore('{zset}U', ['{zset}X', '{zset}Y'])); - $this->assertTrue([] === $this->redis->zRange('{zset}U', 0, -1)); + $this->assertEquals(0, $this->redis->zUnionStore('{zset}U', ['{zset}X', '{zset}Y'])); + $this->assertEquals([],$this->redis->zRange('{zset}U', 0, -1)); // !Exist U Exist → copy of existing zset. $this->redis->del('{zset}U', 'X'); - $this->assertTrue(2 === $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}X'])); + $this->assertEquals(2, $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}X'])); // test weighted zUnion $this->redis->del('{zset}Z'); $this->assertEquals(4, $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [1, 1])); - $this->assertTrue(['val0', 'val1', 'val2', 'val3'] === $this->redis->zRange('{zset}Z', 0, -1)); + $this->assertEquals(['val0', 'val1', 'val2', 'val3'], $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->zRemRangeByScore('{zset}Z', 0, 10); - $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [5, 1])); - $this->assertTrue(['val0', 'val2', 'val3', 'val1'] === $this->redis->zRange('{zset}Z', 0, -1)); + $this->assertEquals(4, $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [5, 1])); + $this->assertEquals(['val0', 'val2', 'val3', 'val1'], $this->redis->zRange('{zset}Z', 0, -1)); $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); @@ -2738,13 +2927,13 @@ public function testZX() { //test zUnion with weights and aggegration function $this->redis->zadd('{zset}1', 1, 'duplicate'); $this->redis->zadd('{zset}2', 2, 'duplicate'); - $this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], [1,1], 'MIN'); - $this->assertTrue($this->redis->zScore('{zset}U', 'duplicate')===1.0); + $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}2'], [1, 1], 'MIN'); + $this->assertEquals(1.0, $this->redis->zScore('{zset}U', 'duplicate')); $this->redis->del('{zset}U'); - //now test zUnion *without* weights but with aggregrate function - $this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], null, 'MIN'); - $this->assertTrue($this->redis->zScore('{zset}U', 'duplicate')===1.0); + //now test zUnion *without* weights but with aggregate function + $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}2'], null, 'MIN'); + $this->assertEquals(1.0, $this->redis->zScore('{zset}U', 'duplicate')); $this->redis->del('{zset}U', '{zset}1', '{zset}2'); // test integer and float weights (GitHub issue #109). @@ -2756,7 +2945,7 @@ public function testZX() { $this->redis->zadd('{zset}2', 2, 'two'); $this->redis->zadd('{zset}2', 3, 'three'); - $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [2, 3.0]) === 3); + $this->assertEquals(3, $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [2, 3.0])); $this->redis->del('{zset}1'); $this->redis->del('{zset}2'); @@ -2767,38 +2956,38 @@ public function testZX() { $this->redis->zadd('{zset}2', 3, 'three', 4, 'four', 5, 'five'); // Make sure phpredis handles these weights - $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, 'inf']) === 5); - $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '-inf']) === 5); - $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '+inf']) === 5); + $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [1, 'inf']) ); + $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [1, '-inf'])); + $this->assertEquals(5, $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [1, '+inf'])); // Now, confirm that they're being sent, and that it works - $arr_weights = ['inf','-inf','+inf']; + $weights = ['inf', '-inf', '+inf']; - foreach($arr_weights as $str_weight) { - $r = $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1,$str_weight]); - $this->assertTrue($r===5); + foreach ($weights as $weight) { + $r = $this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [1, $weight]); + $this->assertEquals(5, $r); $r = $this->redis->zrangebyscore('{zset}3', '(-inf', '(inf',['withscores'=>true]); - $this->assertTrue(count($r)===2); - $this->assertTrue(isset($r['one'])); - $this->assertTrue(isset($r['two'])); + $this->assertEquals(2, count($r)); + $this->assertArrayKey($r, 'one'); + $this->assertArrayKey($r, 'two'); } - $this->redis->del('{zset}1','{zset}2','{zset}3'); + $this->redis->del('{zset}1', '{zset}2', '{zset}3'); $this->redis->zadd('{zset}1', 2000.1, 'one'); $this->redis->zadd('{zset}1', 3000.1, 'two'); $this->redis->zadd('{zset}1', 4000.1, 'three'); - $ret = $this->redis->zRange('{zset}1', 0, -1, TRUE); - $this->assertTrue(count($ret) === 3); + $ret = $this->redis->zRange('{zset}1', 0, -1, true); + $this->assertEquals(3, count($ret)); $retValues = array_keys($ret); - $this->assertTrue(['one', 'two', 'three'] === $retValues); + $this->assertEquals(['one', 'two', 'three'], $retValues); // + 0 converts from string to float OR integer - $this->assertTrue(is_float($ret['one'] + 0)); - $this->assertTrue(is_float($ret['two'] + 0)); - $this->assertTrue(is_float($ret['three'] + 0)); + $this->assertArrayKeyEquals($ret, 'one', 2000.1); + $this->assertArrayKeyEquals($ret, 'two', 3000.1); + $this->assertArrayKeyEquals($ret, 'three', 4000.1); $this->redis->del('{zset}1'); @@ -2806,8 +2995,8 @@ public function testZX() { $this->redis->zAdd('{zset}1', 1, 'one'); $this->redis->zAdd('{zset}1', 2, 'two'); $this->redis->zAdd('{zset}1', 3, 'three'); - $this->assertTrue(2 === $this->redis->zremrangebyrank('{zset}1', 0, 1)); - $this->assertTrue(['three' => 3] == $this->redis->zRange('{zset}1', 0, -1, TRUE)); + $this->assertEquals(2, $this->redis->zremrangebyrank('{zset}1', 0, 1)); + $this->assertEquals(['three' => 3.], $this->redis->zRange('{zset}1', 0, -1, true)); $this->redis->del('{zset}1'); @@ -2824,16 +3013,16 @@ public function testZX() { $this->redis->zAdd('{zset}3', 5, 'val5'); $this->redis->del('{zset}I'); - $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'])); - $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'])); + $this->assertEquals(['val1', 'val3'], $this->redis->zRange('{zset}I', 0, -1)); // Union on non existing keys - $this->assertTrue(0 === $this->redis->zInterStore('{zset}X', ['{zset}X', '{zset}Y'])); - $this->assertTrue([] === $this->redis->zRange('{zset}X', 0, -1)); + $this->assertEquals(0, $this->redis->zInterStore('{zset}X', ['{zset}X', '{zset}Y'])); + $this->assertEquals([], $this->redis->zRange('{zset}X', 0, -1)); // !Exist U Exist - $this->assertTrue(0 === $this->redis->zInterStore('{zset}Y', ['{zset}1', '{zset}X'])); - $this->assertTrue([] === $this->redis->zRange('keyY', 0, -1)); + $this->assertEquals(0, $this->redis->zInterStore('{zset}Y', ['{zset}1', '{zset}X'])); + $this->assertEquals([], $this->redis->zRange('keyY', 0, -1)); // test weighted zInterStore @@ -2853,19 +3042,19 @@ public function testZX() { $this->redis->zAdd('{zset}3', 3, 'val3'); $this->redis->del('{zset}I'); - $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'], [1, 1])); - $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'], [1, 1])); + $this->assertEquals(['val1', 'val3'], $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); - $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'min')); - $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'min')); + $this->assertEquals(['val1', 'val3'], $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); - $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'max')); - $this->assertTrue(['val3', 'val1'] === $this->redis->zRange('{zset}I', 0, -1)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'max')); + $this->assertEquals(['val3', 'val1'], $this->redis->zRange('{zset}I', 0, -1)); $this->redis->del('{zset}I'); - $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], null, 'max')); - $this->assertTrue($this->redis->zScore('{zset}I', 'val1') === floatval(7)); + $this->assertEquals(2, $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], null, 'max')); + $this->assertEquals(7., $this->redis->zScore('{zset}I', 'val1')); // zrank, zrevrank $this->redis->del('z'); @@ -2873,37 +3062,54 @@ public function testZX() { $this->redis->zadd('z', 2, 'two'); $this->redis->zadd('z', 5, 'five'); - $this->assertTrue(0 === $this->redis->zRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRank('z', 'two')); - $this->assertTrue(2 === $this->redis->zRank('z', 'five')); + $this->assertEquals(0, $this->redis->zRank('z', 'one')); + $this->assertEquals(1, $this->redis->zRank('z', 'two')); + $this->assertEquals(2, $this->redis->zRank('z', 'five')); + + $this->assertEquals(2, $this->redis->zRevRank('z', 'one')); + $this->assertEquals(1, $this->redis->zRevRank('z', 'two')); + $this->assertEquals(0, $this->redis->zRevRank('z', 'five')); + } + + /* Regression for GitHub issue #2791 */ + public function testZSetSerialization() { + $this->redis->del('zs_f', 'zs_a'); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); + + $this->redis->zAdd('zs_f', 3.14, 3.14); + $this->redis->zAdd('zs_a', 1, ['foo', 'bar']); - $this->assertTrue(2 === $this->redis->zRevRank('z', 'one')); - $this->assertTrue(1 === $this->redis->zRevRank('z', 'two')); - $this->assertTrue(0 === $this->redis->zRevRank('z', 'five')); + $res_float = $this->redis->zRange('zs_f', 0, -1, true); + $this->assertEquals(['3.14' => 3.14], $res_float); + + $res_array = @$this->redis->zRange('zs_a', 0, -1, true); + $this->assertEquals(['Array' => 1.0], $res_array); + + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); } public function testZRangeScoreArg() { $this->redis->del('{z}'); - $arr_mems = ['one' => 1.0, 'two' => 2.0, 'three' => 3.0]; - foreach ($arr_mems as $str_mem => $score) { - $this->redis->zAdd('{z}', $score, $str_mem); + $mems = ['one' => 1.0, 'two' => 2.0, 'three' => 3.0]; + foreach ($mems as $mem => $score) { + $this->redis->zAdd('{z}', $score, $mem); } /* Verify we can pass true and ['withscores' => true] */ - $this->assertEquals($arr_mems, $this->redis->zRange('{z}', 0, -1, true)); - $this->assertEquals($arr_mems, $this->redis->zRange('{z}', 0, -1, ['withscores' => true])); + $this->assertEquals($mems, $this->redis->zRange('{z}', 0, -1, true)); + $this->assertEquals($mems, $this->redis->zRange('{z}', 0, -1, ['withscores' => true])); } public function testZRangeByLex() { /* ZRANGEBYLEX available on versions >= 2.8.9 */ - if(version_compare($this->version, "2.8.9") < 0) { + if (version_compare($this->version, '2.8.9') < 0) { $this->MarkTestSkipped(); return; } $this->redis->del('key'); - foreach(range('a', 'g') as $c) { + foreach (range('a', 'g') as $c) { $this->redis->zAdd('key', 0, $c); } @@ -2916,8 +3122,8 @@ public function testZRangeByLex() { $this->assertEquals(['b'], $this->redis->zRangeByLex('key', '-', '(c', 1, 2)); /* Test getting the same functionality via ZRANGE and options */ - if ($this->minVersionCheck("6.2.0")) { - $this->assertEquals(['a','b','c'], $this->redis->zRange('key', '-', '[c', ['BYLEX'])); + if ($this->minVersionCheck('6.2.0')) { + $this->assertEquals(['a', 'b', 'c'], $this->redis->zRange('key', '-', '[c', ['BYLEX'])); $this->assertEquals(['b', 'c'], $this->redis->zRange('key', '-', '[c', ['BYLEX', 'LIMIT' => [1, 2]])); $this->assertEquals(['b'], $this->redis->zRange('key', '-', '(c', ['BYLEX', 'LIMIT' => [1, 2]])); @@ -2926,7 +3132,7 @@ public function testZRangeByLex() { } public function testZLexCount() { - if (version_compare($this->version, "2.8.9") < 0) { + if (version_compare($this->version, '2.8.9') < 0) { $this->MarkTestSkipped(); return; } @@ -2938,8 +3144,8 @@ public function testZLexCount() { } /* Special -/+ values */ - $this->assertEquals($this->redis->zLexCount('key', '-', '-'), 0); - $this->assertEquals($this->redis->zLexCount('key', '-', '+'), count($entries)); + $this->assertEquals(0, $this->redis->zLexCount('key', '-', '-')); + $this->assertEquals(count($entries), $this->redis->zLexCount('key', '-', '+')); /* Verify invalid arguments return FALSE */ $this->assertFalse(@$this->redis->zLexCount('key', '[a', 'bad')); @@ -2949,19 +3155,16 @@ public function testZLexCount() { $start = $entries[0]; for ($i = 1; $i < count($entries); $i++) { $end = $entries[$i]; - $this->assertEquals($this->redis->zLexCount('key', "[$start", "[$end"), $i + 1); - $this->assertEquals($this->redis->zLexCount('key', "[$start", "($end"), $i); - $this->assertEquals($this->redis->zLexCount('key', "($start", "($end"), $i - 1); + $this->assertEquals($i + 1, $this->redis->zLexCount('key', "[$start", "[$end")); + $this->assertEquals($i, $this->redis->zLexCount('key', "[$start", "($end")); + $this->assertEquals($i - 1, $this->redis->zLexCount('key', "($start", "($end")); } } - public function testzDiff() - { + public function testzDiff() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('key'); foreach (range('a', 'c') as $c) { @@ -2972,13 +3175,10 @@ public function testzDiff() $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zDiff(['key'], ['withscores' => true])); } - public function testzInter() - { + public function testzInter() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('key'); foreach (range('a', 'c') as $c) { @@ -2989,13 +3189,10 @@ public function testzInter() $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zInter(['key'], null, ['withscores' => true])); } - public function testzUnion() - { + public function testzUnion() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('key'); foreach (range('a', 'c') as $c) { @@ -3006,13 +3203,10 @@ public function testzUnion() $this->assertEquals(['a' => 1.0, 'b' => 1.0, 'c' => 1.0], $this->redis->zUnion(['key'], null, ['withscores' => true])); } - public function testzDiffStore() - { + public function testzDiffStore() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('{zkey}src'); foreach (range('a', 'c') as $c) { @@ -3022,13 +3216,10 @@ public function testzDiffStore() $this->assertEquals(['a', 'b', 'c'], $this->redis->zRange('{zkey}dst', 0, -1)); } - public function testzMscore() - { + public function testzMscore() { // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('key'); foreach (range('a', 'c') as $c) { @@ -3043,26 +3234,26 @@ public function testzMscore() } public function testZRemRangeByLex() { - if (version_compare($this->version, "2.8.9") < 0) { + if (version_compare($this->version, '2.8.9') < 0) { $this->MarkTestSkipped(); return; } $this->redis->del('key'); $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c'); - $this->assertEquals($this->redis->zRemRangeByLex('key', '-', '+'), 3); + $this->assertEquals(3, $this->redis->zRemRangeByLex('key', '-', '+')); $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c'); - $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '[c'), 3); + $this->assertEquals(3, $this->redis->zRemRangeByLex('key', '[a', '[c')); $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c'); - $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '(a'), 0); - $this->assertEquals($this->redis->zRemRangeByLex('key', '(a', '(c'), 1); - $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '[c'), 2); + $this->assertEquals(0, $this->redis->zRemRangeByLex('key', '[a', '(a')); + $this->assertEquals(1, $this->redis->zRemRangeByLex('key', '(a', '(c')); + $this->assertEquals(2, $this->redis->zRemRangeByLex('key', '[a', '[c')); } public function testBZPop() { - if (version_compare($this->version, "5.0.0") < 0) { + if (version_compare($this->version, '5.0.0') < 0) { $this->MarkTestSkipped(); return; } @@ -3071,20 +3262,20 @@ public function testBZPop() { $this->redis->zAdd('{zs}1', 0, 'a', 1, 'b', 2, 'c'); $this->redis->zAdd('{zs}2', 3, 'A', 4, 'B', 5, 'D'); - $this->assertEquals(Array('{zs}1', 'a', '0'), $this->redis->bzPopMin('{zs}1', '{zs}2', 0)); - $this->assertEquals(Array('{zs}1', 'c', '2'), $this->redis->bzPopMax(Array('{zs}1', '{zs}2'), 0)); - $this->assertEquals(Array('{zs}2', 'A', '3'), $this->redis->bzPopMin('{zs}2', '{zs}1', 0)); + $this->assertEquals(['{zs}1', 'a', '0'], $this->redis->bzPopMin('{zs}1', '{zs}2', 0)); + $this->assertEquals(['{zs}1', 'c', '2'], $this->redis->bzPopMax(['{zs}1', '{zs}2'], 0)); + $this->assertEquals(['{zs}2', 'A', '3'], $this->redis->bzPopMin('{zs}2', '{zs}1', 0)); /* Verify timeout is being sent */ $this->redis->del('{zs}1', '{zs}2'); $st = microtime(true) * 1000; $this->redis->bzPopMin('{zs}1', '{zs}2', 1); $et = microtime(true) * 1000; - $this->assertTrue($et - $st > 100); + $this->assertGT(100, $et - $st); } public function testZPop() { - if (version_compare($this->version, "5.0.0") < 0) { + if (version_compare($this->version, '5.0.0') < 0) { $this->MarkTestSkipped(); return; } @@ -3092,23 +3283,22 @@ public function testZPop() { // zPopMax and zPopMin without a COUNT argument $this->redis->del('key'); $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e'); - $this->assertTrue(array('e' => 4.0) === $this->redis->zPopMax('key')); - $this->assertTrue(array('a' => 0.0) === $this->redis->zPopMin('key')); + $this->assertEquals(['e' => 4.0], $this->redis->zPopMax('key')); + $this->assertEquals(['a' => 0.0], $this->redis->zPopMin('key')); // zPopMax with a COUNT argument $this->redis->del('key'); $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e'); - $this->assertTrue(array('e' => 4.0, 'd' => 3.0, 'c' => 2.0) === $this->redis->zPopMax('key', 3)); + $this->assertEquals(['e' => 4.0, 'd' => 3.0, 'c' => 2.0], $this->redis->zPopMax('key', 3)); // zPopMin with a COUNT argument $this->redis->del('key'); $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e'); - $this->assertTrue(array('a' => 0.0, 'b' => 1.0, 'c' => 2.0) === $this->redis->zPopMin('key', 3)); + $this->assertEquals(['a' => 0.0, 'b' => 1.0, 'c' => 2.0], $this->redis->zPopMin('key', 3)); } - public function testZRandMember() - { - if (version_compare($this->version, "6.2.0") < 0) { + public function testZRandMember() { + if (version_compare($this->version, '6.2.0') < 0) { $this->MarkTestSkipped(); return; } @@ -3127,142 +3317,211 @@ public function testZRandMember() public function testHashes() { $this->redis->del('h', 'key'); - $this->assertTrue(0 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value')); - $this->assertTrue(1 === $this->redis->hLen('h')); - $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value')); - $this->assertTrue(2 === $this->redis->hLen('h')); + $this->assertEquals(0, $this->redis->hLen('h')); + $this->assertEquals(1, $this->redis->hSet('h', 'a', 'a-value')); + $this->assertEquals(1, $this->redis->hLen('h')); + $this->assertEquals(1, $this->redis->hSet('h', 'b', 'b-value')); + $this->assertEquals(2, $this->redis->hLen('h')); - $this->assertTrue('a-value' === $this->redis->hGet('h', 'a')); // simple get - $this->assertTrue('b-value' === $this->redis->hGet('h', 'b')); // simple get + $this->assertEquals('a-value', $this->redis->hGet('h', 'a')); // simple get + $this->assertEquals('b-value', $this->redis->hGet('h', 'b')); // simple get - $this->assertTrue(0 === $this->redis->hSet('h', 'a', 'another-value')); // replacement - $this->assertTrue('another-value' === $this->redis->hGet('h', 'a')); // get the new value + $this->assertEquals(0, $this->redis->hSet('h', 'a', 'another-value')); // replacement + $this->assertEquals('another-value', $this->redis->hGet('h', 'a')); // get the new value - $this->assertTrue('b-value' === $this->redis->hGet('h', 'b')); // simple get - $this->assertTrue(FALSE === $this->redis->hGet('h', 'c')); // unknown hash member - $this->assertTrue(FALSE === $this->redis->hGet('key', 'c')); // unknownkey + $this->assertEquals('b-value', $this->redis->hGet('h', 'b')); // simple get + $this->assertFalse($this->redis->hGet('h', 'c')); // unknown hash member + $this->assertFalse($this->redis->hGet('key', 'c')); // unknownkey // hDel - $this->assertTrue(1 === $this->redis->hDel('h', 'a')); // 1 on success - $this->assertTrue(0 === $this->redis->hDel('h', 'a')); // 0 on failure + $this->assertEquals(1, $this->redis->hDel('h', 'a')); // 1 on success + $this->assertEquals(0, $this->redis->hDel('h', 'a')); // 0 on failure $this->redis->del('h'); $this->redis->hSet('h', 'x', 'a'); $this->redis->hSet('h', 'y', 'b'); - $this->assertTrue(2 === $this->redis->hDel('h', 'x', 'y')); // variadic + $this->assertEquals(2, $this->redis->hDel('h', 'x', 'y')); // variadic // hsetnx $this->redis->del('h'); - $this->assertTrue(TRUE === $this->redis->hSetNx('h', 'x', 'a')); - $this->assertTrue(TRUE === $this->redis->hSetNx('h', 'y', 'b')); - $this->assertTrue(FALSE === $this->redis->hSetNx('h', 'x', '?')); - $this->assertTrue(FALSE === $this->redis->hSetNx('h', 'y', '?')); - $this->assertTrue('a' === $this->redis->hGet('h', 'x')); - $this->assertTrue('b' === $this->redis->hGet('h', 'y')); + $this->assertTrue($this->redis->hSetNx('h', 'x', 'a')); + $this->assertTrue($this->redis->hSetNx('h', 'y', 'b')); + $this->assertFalse($this->redis->hSetNx('h', 'x', '?')); + $this->assertFalse($this->redis->hSetNx('h', 'y', '?')); + $this->assertEquals('a', $this->redis->hGet('h', 'x')); + $this->assertEquals('b', $this->redis->hGet('h', 'y')); // keys $keys = $this->redis->hKeys('h'); - $this->assertTrue($keys === ['x', 'y'] || $keys === ['y', 'x']); + $this->assertEqualsCanonicalizing(['x', 'y'], $keys); // values $values = $this->redis->hVals('h'); - $this->assertTrue($values === ['a', 'b'] || $values === ['b', 'a']); + $this->assertEqualsCanonicalizing(['a', 'b'], $values); // keys + values $all = $this->redis->hGetAll('h'); - $this->assertTrue($all === ['x' => 'a', 'y' => 'b'] || $all === ['y' => 'b', 'x' => 'a']); + $this->assertEqualsCanonicalizing(['x' => 'a', 'y' => 'b'], $all, true); // hExists - $this->assertTrue(TRUE === $this->redis->hExists('h', 'x')); - $this->assertTrue(TRUE === $this->redis->hExists('h', 'y')); - $this->assertTrue(FALSE === $this->redis->hExists('h', 'w')); + $this->assertTrue($this->redis->hExists('h', 'x')); + $this->assertTrue($this->redis->hExists('h', 'y')); + $this->assertFalse($this->redis->hExists('h', 'w')); $this->redis->del('h'); - $this->assertTrue(FALSE === $this->redis->hExists('h', 'x')); + $this->assertFalse($this->redis->hExists('h', 'x')); // hIncrBy $this->redis->del('h'); - $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2)); - $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1)); - $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1)); - $this->assertTrue("2" === $this->redis->hGet('h', 'x')); - $this->assertTrue(PHP_INT_MAX === $this->redis->hIncrBy('h', 'x', PHP_INT_MAX-2)); - $this->assertTrue("".PHP_INT_MAX === $this->redis->hGet('h', 'x')); + $this->assertEquals(2, $this->redis->hIncrBy('h', 'x', 2)); + $this->assertEquals(3, $this->redis->hIncrBy('h', 'x', 1)); + $this->assertEquals(2, $this->redis->hIncrBy('h', 'x', -1)); + $this->assertEquals('2', $this->redis->hGet('h', 'x')); + $this->assertEquals(PHP_INT_MAX, $this->redis->hIncrBy('h', 'x', PHP_INT_MAX-2)); + $this->assertEquals(''.PHP_INT_MAX, $this->redis->hGet('h', 'x')); $this->redis->hSet('h', 'y', 'not-a-number'); - $this->assertTrue(FALSE === $this->redis->hIncrBy('h', 'y', 1)); + $this->assertFalse($this->redis->hIncrBy('h', 'y', 1)); - if (version_compare($this->version, "2.5.0") >= 0) { + if (version_compare($this->version, '2.5.0') >= 0) { // hIncrByFloat $this->redis->del('h'); - $this->assertTrue(1.5 === $this->redis->hIncrByFloat('h','x', 1.5)); - $this->assertTrue(3.0 === $this->redis->hincrByFloat('h','x', 1.5)); - $this->assertTrue(1.5 === $this->redis->hincrByFloat('h','x', -1.5)); - $this->assertTrue(1000000000001.5 === $this->redis->hincrByFloat('h','x', 1000000000000)); + $this->assertEquals(1.5, $this->redis->hIncrByFloat('h', 'x', 1.5)); + $this->assertEquals(3.0, $this->redis->hincrByFloat('h', 'x', 1.5)); + $this->assertEquals(1.5, $this->redis->hincrByFloat('h', 'x', -1.5)); + $this->assertEquals(1000000000001.5, $this->redis->hincrByFloat('h', 'x', 1000000000000)); - $this->redis->hset('h','y','not-a-number'); - $this->assertTrue(FALSE === $this->redis->hIncrByFloat('h', 'y', 1.5)); + $this->redis->hset('h', 'y', 'not-a-number'); + $this->assertFalse($this->redis->hIncrByFloat('h', 'y', 1.5)); } // hmset $this->redis->del('h'); - $this->assertTrue(TRUE === $this->redis->hMset('h', ['x' => 123, 'y' => 456, 'z' => 'abc'])); - $this->assertTrue('123' === $this->redis->hGet('h', 'x')); - $this->assertTrue('456' === $this->redis->hGet('h', 'y')); - $this->assertTrue('abc' === $this->redis->hGet('h', 'z')); - $this->assertTrue(FALSE === $this->redis->hGet('h', 't')); + $this->assertTrue($this->redis->hMset('h', ['x' => 123, 'y' => 456, 'z' => 'abc'])); + $this->assertEquals('123', $this->redis->hGet('h', 'x')); + $this->assertEquals('456', $this->redis->hGet('h', 'y')); + $this->assertEquals('abc', $this->redis->hGet('h', 'z')); + $this->assertFalse($this->redis->hGet('h', 't')); // hmget - $this->assertTrue(['x' => '123', 'y' => '456'] === $this->redis->hMget('h', ['x', 'y'])); - $this->assertTrue(['z' => 'abc'] === $this->redis->hMget('h', ['z'])); - $this->assertTrue(['x' => '123', 't' => FALSE, 'y' => '456'] === $this->redis->hMget('h', ['x', 't', 'y'])); - $this->assertFalse([123 => 'x'] === $this->redis->hMget('h', [123])); - $this->assertTrue([123 => FALSE] === $this->redis->hMget('h', [123])); + $this->assertEquals(['x' => '123', 'y' => '456'], $this->redis->hMget('h', ['x', 'y'])); + $this->assertEquals(['z' => 'abc'], $this->redis->hMget('h', ['z'])); + $this->assertEquals(['x' => '123', 't' => FALSE, 'y' => '456'], $this->redis->hMget('h', ['x', 't', 'y'])); + $this->assertEquals(['x' => '123', 't' => FALSE, 'y' => '456'], $this->redis->hMget('h', ['x', 't', 'y'])); + $this->assertNotEquals([123 => 'x'], $this->redis->hMget('h', [123])); + $this->assertEquals([123 => FALSE], $this->redis->hMget('h', [123])); // Test with an array populated with things we can't use as keys - $this->assertTrue($this->redis->hmget('h', [false,NULL,false]) === FALSE); + $this->assertFalse(@$this->redis->hmget('h', [false,NULL,false])); // Test with some invalid keys mixed in (which should just be ignored) - $this->assertTrue(['x'=>'123','y'=>'456','z'=>'abc'] === $this->redis->hMget('h',['x',null,'y','','z',false])); + $this->assertEquals( + ['x' => '123', 'y' => '456', 'z' => 'abc'], + $this->redis->hMget('h', ['x', null, 'y', '', 'z', false]) + ); // hmget/hmset with numeric fields $this->redis->del('h'); - $this->assertTrue(TRUE === $this->redis->hMset('h', [123 => 'x', 'y' => 456])); - $this->assertTrue('x' === $this->redis->hGet('h', 123)); - $this->assertTrue('x' === $this->redis->hGet('h', '123')); - $this->assertTrue('456' === $this->redis->hGet('h', 'y')); - $this->assertTrue([123 => 'x', 'y' => '456'] === $this->redis->hMget('h', ['123', 'y'])); + $this->assertTrue($this->redis->hMset('h', [123 => 'x', 'y' => 456])); + $this->assertEquals('x', $this->redis->hGet('h', 123)); + $this->assertEquals('x', $this->redis->hGet('h', '123')); + $this->assertEquals('456', $this->redis->hGet('h', 'y')); + $this->assertEquals([123 => 'x', 'y' => '456'], $this->redis->hMget('h', ['123', 'y'])); // references $keys = [123, 'y']; foreach ($keys as &$key) {} - $this->assertTrue([123 => 'x', 'y' => '456'] === $this->redis->hMget('h', $keys)); + $this->assertEquals([123 => 'x', 'y' => '456'], $this->redis->hMget('h', $keys)); // check non-string types. $this->redis->del('h1'); - $this->assertTrue(TRUE === $this->redis->hMSet('h1', ['x' => 0, 'y' => [], 'z' => new stdclass(), 't' => NULL])); + $this->assertTrue($this->redis->hMSet('h1', ['x' => 0, 'y' => [], 'z' => new stdclass(), 't' => NULL])); $h1 = $this->redis->hGetAll('h1'); - $this->assertTrue('0' === $h1['x']); - $this->assertTrue('Array' === $h1['y']); - $this->assertTrue('Object' === $h1['z']); - $this->assertTrue('' === $h1['t']); + $this->assertEquals('0', $h1['x']); + $this->assertEquals('Array', $h1['y']); + $this->assertEquals('Object', $h1['z']); + $this->assertEquals('', $h1['t']); + + // hset with fields + values as an associative array + if (version_compare($this->version, '4.0.0') >= 0) { + $this->redis->del('h'); + $this->assertEquals(3, $this->redis->hSet('h', ['x' => 123, 'y' => 456, 'z' => 'abc'])); + $this->assertEquals(['x' => '123', 'y' => '456', 'z' => 'abc'], $this->redis->hGetAll('h')); + $this->assertEquals(0, $this->redis->hSet('h', ['x' => 789])); + $this->assertEquals(['x' => '789', 'y' => '456', 'z' => 'abc'], $this->redis->hGetAll('h')); + } + + // hset with variadic fields + values + if (version_compare($this->version, '4.0.0') >= 0) { + $this->redis->del('h'); + $this->assertEquals(3, $this->redis->hSet('h', 'x', 123, 'y', 456, 'z', 'abc')); + $this->assertEquals(['x' => '123', 'y' => '456', 'z' => 'abc'], $this->redis->hGetAll('h')); + $this->assertEquals(0, $this->redis->hSet('h', 'x', 789)); + $this->assertEquals(['x' => '789', 'y' => '456', 'z' => 'abc'], $this->redis->hGetAll('h')); + } // hstrlen if (version_compare($this->version, '3.2.0') >= 0) { $this->redis->del('h'); - $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // key doesn't exist + $this->assertEquals(0, $this->redis->hStrLen('h', 'x')); // key doesn't exist $this->redis->hSet('h', 'foo', 'bar'); - $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // field is not present in the hash - $this->assertTrue(3 === $this->redis->hStrLen('h', 'foo')); + $this->assertEquals(0, $this->redis->hStrLen('h', 'x')); // field is not present in the hash + $this->assertEquals(3, $this->redis->hStrLen('h', 'foo')); } } - public function testHRandField() - { - if (version_compare($this->version, "6.2.0") < 0) { - $this->MarkTestSkipped(); - return; + /* Regression test for GitHub issue 2731 */ + public function testNumericPrefixHashFields() { + $hash = [ + '86deaeb05e3f7760b67e92897a1325e0fdd8618d' => 'one', + '-86deaeb05e3f7760b67e92897a1325e0fdd8618d' => 'two', + 12345 => 'a_real_number', + -12345 => 'a_negative_real_number', + ]; + + $this->assertIsInt($this->redis->del('hash')); + $this->assertTrue($this->redis->hmset('hash', $hash)); + + $res = $this->redis->hmget('hash', array_keys($hash)); + + // The keys from our local variable and res should be equal + $this->assertEqualsCanonicalizing(array_keys($hash), array_keys($res)); + } + + /* Regression test for GitHub issue 2795 / PR 2796 */ + public function testNumericStringEncoding() { + $fields = [ + '0001', '0010', '-0005', '+0005', '1e3', '1E+3', '1e-3', '-1E-3', + '+1.5e2', '-2.25E+4', '0001e-2', + ]; + + $hashKey = 'hash:numeric-strings'; + $this->assertIsInt($this->redis->del($hashKey)); + + $payload = []; + foreach ($fields as $index => $field) { + $payload[$field] = "value-{$index}"; } + + $this->assertTrue($this->redis->hMset($hashKey, $payload)); + + $hmget = $this->redis->hMget($hashKey, $fields); + $this->assertEquals($payload, $hmget); + + foreach ($payload as $key => $value) { + $this->assertTrue($this->redis->mset([$key => $value])); + $this->assertEquals([$value], $this->redis->mget([$key])); + } + + $this->redis->del($hashKey); + if ($payload !== []) { + $this->redis->del(...array_keys($payload)); + } + } + + public function testHRandField() { + if (version_compare($this->version, '6.2.0') < 0) + $this->MarkTestSkipped(); + $this->redis->del('key'); $this->redis->hMSet('key', ['a' => 0, 'b' => 1, 'c' => 'foo', 'd' => 'bar', 'e' => null]); $this->assertInArray($this->redis->hRandField('key'), ['a', 'b', 'c', 'd', 'e']); @@ -3274,6 +3533,16 @@ public function testHRandField() $result = $this->redis->hRandField('key', ['count' => 2, 'withvalues' => true]); $this->assertEquals(2, count($result)); $this->assertEquals(array_intersect_key($result, ['a' => 0, 'b' => 1, 'c' => 'foo', 'd' => 'bar', 'e' => null]), $result); + + /* Make sure PhpRedis sends COUNt (1) when `WITHVALUES` is set */ + $result = $this->redis->hRandField('key', ['withvalues' => true]); + $this->assertNull($this->redis->getLastError()); + $this->assertIsArray($result); + $this->assertEquals(1, count($result)); + + /* We can return false if the key doesn't exist */ + $this->assertIsInt($this->redis->del('notahash')); + $this->assertFalse($this->redis->hRandField('notahash')); } public function testSetRange() { @@ -3281,83 +3550,71 @@ public function testSetRange() { $this->redis->del('key'); $this->redis->set('key', 'hello world'); $this->redis->setRange('key', 6, 'redis'); - $this->assertTrue('hello redis' === $this->redis->get('key')); + $this->assertKeyEquals('hello redis', 'key'); $this->redis->setRange('key', 6, 'you'); // don't cut off the end - $this->assertTrue('hello youis' === $this->redis->get('key')); + $this->assertKeyEquals('hello youis', 'key'); $this->redis->set('key', 'hello world'); - // $this->assertTrue(11 === $this->redis->setRange('key', -6, 'redis')); // works with negative offsets too! (disabled because not all versions support this) - // $this->assertTrue('hello redis' === $this->redis->get('key')); // fill with zeros if needed $this->redis->del('key'); $this->redis->setRange('key', 6, 'foo'); - $this->assertTrue("\x00\x00\x00\x00\x00\x00foo" === $this->redis->get('key')); + $this->assertKeyEquals("\x00\x00\x00\x00\x00\x00foo", 'key'); } public function testObject() { /* Version 3.0.0 (represented as >= 2.9.0 in redis info) and moving - * forward uses "embstr" instead of "raw" for small string values */ - if (version_compare($this->version, "2.9.0") < 0) { - $str_small_encoding = "raw"; + * forward uses 'embstr' instead of 'raw' for small string values */ + if (version_compare($this->version, '2.9.0') < 0) { + $small_encoding = 'raw'; } else { - $str_small_encoding = "embstr"; + $small_encoding = 'embstr'; } $this->redis->del('key'); - $this->assertTrue($this->redis->object('encoding', 'key') === FALSE); - $this->assertTrue($this->redis->object('refcount', 'key') === FALSE); - $this->assertTrue($this->redis->object('idletime', 'key') === FALSE); + $this->assertFalse($this->redis->object('encoding', 'key')); + $this->assertFalse($this->redis->object('refcount', 'key')); + $this->assertFalse($this->redis->object('idletime', 'key')); $this->redis->set('key', 'value'); - $this->assertTrue($this->redis->object('encoding', 'key') === $str_small_encoding); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + $this->assertEquals($small_encoding, $this->redis->object('encoding', 'key')); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); $this->redis->lpush('key', 'value'); - $str_encoding = $this->redis->object('encoding', 'key'); - if (version_compare($this->version, '7.1.240') >= 0) { - /* Since redis 7.2-rc1 */ - $valid = ['listpack']; - } else { - /* Newer versions of redis are going to encode lists as 'quicklists', - * so 'quicklist' or 'ziplist' or 'listpack' are valid here */ - $valid = ['ziplist', 'quicklist']; - } - $this->assertTrue(in_array($str_encoding, $valid), $str_encoding); + /* Redis has improved the encoding here throughout the various versions. The value + can either be 'ziplist', 'quicklist', or 'listpack' */ + $encoding = $this->redis->object('encoding', 'key'); + $this->assertInArray($encoding, ['ziplist', 'quicklist', 'listpack']); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); $this->redis->sadd('key', 'value'); - $str_encoding = $this->redis->object('encoding', 'key'); - if (version_compare($this->version, '7.1.240') >= 0) { - /* Since redis 7.2-rc1 */ - $valid = ['listpack']; - } else { - $valid = ['hashtable']; - } - $this->assertTrue(in_array($str_encoding, $valid), $str_encoding); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + + /* Redis 7.2.0 switched to 'listpack' for small sets */ + $encoding = $this->redis->object('encoding', 'key'); + $this->assertInArray($encoding, ['hashtable', 'listpack']); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); $this->redis->sadd('key', 42); $this->redis->sadd('key', 1729); - $this->assertTrue($this->redis->object('encoding', 'key') === "intset"); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + $this->assertEquals('intset', $this->redis->object('encoding', 'key')); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); $this->redis->del('key'); - $this->redis->lpush('key', str_repeat('A', pow(10,6))); // 1M elements, too big for a ziplist. + $this->redis->lpush('key', str_repeat('A', pow(10, 6))); // 1M elements, too big for a ziplist. - $str_encoding = $this->redis->object('encoding', 'key'); - $this->assertTrue($str_encoding === "linkedlist" || $str_encoding == "quicklist"); + $encoding = $this->redis->object('encoding', 'key'); + $this->assertInArray($encoding, ['linkedlist', 'quicklist']); - $this->assertTrue($this->redis->object('refcount', 'key') === 1); + $this->assertEquals(1, $this->redis->object('refcount', 'key')); $this->assertTrue(is_numeric($this->redis->object('idletime', 'key'))); } @@ -3366,18 +3623,18 @@ public function testMultiExec() { $this->differentType(Redis::MULTI); // with prefix as well - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->sequence(Redis::MULTI); $this->differentType(Redis::MULTI); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); $this->redis->set('x', '42'); - $this->assertTrue(TRUE === $this->redis->watch('x')); + $this->assertTrue($this->redis->watch('x')); $ret = $this->redis->multi()->get('x')->exec(); // successful transaction - $this->assertTrue($ret === ['42']); + $this->assertEquals(['42'], $ret); } public function testFailedTransactions() { @@ -3390,7 +3647,7 @@ public function testFailedTransactions() { $r->incr('x'); $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === FALSE); // failed because another client changed our watched key between WATCH and EXEC. + $this->assertFalse($ret); // failed because another client changed our watched key between WATCH and EXEC. // watch and unwatch $this->redis->watch('x'); @@ -3399,32 +3656,30 @@ public function testFailedTransactions() { $ret = $this->redis->multi()->get('x')->exec(); - $this->assertTrue($ret === ['44']); // succeeded since we've cancel the WATCH command. + // succeeded since we've cancel the WATCH command. + $this->assertEquals(['44'], $ret); } public function testPipeline() { - if (!$this->havePipeline()) { + if ( ! $this->havePipeline()) $this->markTestSkipped(); - } $this->sequence(Redis::PIPELINE); $this->differentType(Redis::PIPELINE); // with prefix as well - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->sequence(Redis::PIPELINE); $this->differentType(Redis::PIPELINE); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } - public function testPipelineMultiExec() - { - if (!$this->havePipeline()) { + public function testPipelineMultiExec() { + if ( ! $this->havePipeline()) $this->markTestSkipped(); - } $ret = $this->redis->pipeline()->multi()->exec()->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertEquals(1, count($ret)); // empty transaction $ret = $this->redis->pipeline() @@ -3434,11 +3689,27 @@ public function testPipelineMultiExec() ->multi()->get('x')->del('x')->exec() ->ping() ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertEquals(5, count($ret)); // should be 5 atomic operations } - /* Github issue #1211 (ignore redundant calls to pipeline or multi) */ + public function testMultiEmpty() + { + $ret = $this->redis->multi()->exec(); + $this->assertEquals([], $ret); + } + + public function testPipelineEmpty() + { + if (!$this->havePipeline()) { + $this->markTestSkipped(); + } + + $ret = $this->redis->pipeline()->exec(); + $this->assertEquals([], $ret); + } + + /* GitHub issue #1211 (ignore redundant calls to pipeline or multi) */ public function testDoublePipeNoOp() { /* Only the first pipeline should be honored */ for ($i = 0; $i < 6; $i++) { @@ -3446,7 +3717,7 @@ public function testDoublePipeNoOp() { } /* Set and get in our pipeline */ - $this->redis->set('pipecount','over9000')->get('pipecount'); + $this->redis->set('pipecount', 'over9000')->get('pipecount'); $data = $this->redis->exec(); $this->assertEquals([true,'over9000'], $data); @@ -3463,14 +3734,13 @@ public function testDoublePipeNoOp() { $this->assertEquals([true, 'over9000'], $data); } - public function testDiscard() - { + public function testDiscard() { foreach ([Redis::PIPELINE, Redis::MULTI] as $mode) { /* start transaction */ $this->redis->multi($mode); /* Set and get in our transaction */ - $this->redis->set('pipecount','over9000')->get('pipecount'); + $this->redis->set('pipecount', 'over9000')->get('pipecount'); /* first call closes transaction and clears commands queue */ $this->assertTrue($this->redis->discard()); @@ -3487,11 +3757,11 @@ protected function sequence($mode) { ->get('x') ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $i = 0; - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] === Redis::REDIS_STRING); - $this->assertTrue($ret[$i] === '42' || $ret[$i] === 42); + $this->assertTrue($ret[$i++]); + $this->assertEquals(Redis::REDIS_STRING, $ret[$i++]); + $this->assertEqualsWeak('42', $ret[$i]); $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer @@ -3517,26 +3787,26 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i++])); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 'value1'); - $this->assertTrue($ret[$i++] == 'value1'); - $this->assertTrue($ret[$i++] == 'value2'); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 5); - $this->assertTrue($ret[$i++] == 5); - $this->assertTrue($ret[$i++] == 4); - $this->assertTrue($ret[$i++] == 4); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 4); - $this->assertTrue($ret[$i++] == FALSE); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 9); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 4); - $this->assertTrue(count($ret) == $i); + $this->assertEqualsWeak(true, $ret[$i++]); + $this->assertEqualsWeak('value1', $ret[$i++]); + $this->assertEqualsWeak('value1', $ret[$i++]); + $this->assertEqualsWeak('value2', $ret[$i++]); + $this->assertEqualsWeak(true, $ret[$i++]); + $this->assertEqualsWeak(5, $ret[$i++]); + $this->assertEqualsWeak(5, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEqualsWeak(true, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEqualsWeak(FALSE, $ret[$i++]); + $this->assertEqualsWeak(true, $ret[$i++]); + $this->assertEqualsWeak(true, $ret[$i++]); + $this->assertEqualsWeak(9, $ret[$i++]); + $this->assertEqualsWeak(true, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEquals($i, count($ret)); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); @@ -3550,14 +3820,14 @@ protected function sequence($mode) { ->exists('{key}3') ->exec(); - $this->assertTrue(is_array($ret)); - $this->assertTrue($ret[0] == TRUE); - $this->assertTrue($ret[1] == TRUE); - $this->assertTrue($ret[2] == TRUE); - $this->assertTrue($ret[3] == FALSE); - $this->assertTrue($ret[4] == TRUE); - $this->assertTrue($ret[5] == TRUE); - $this->assertTrue($ret[6] == FALSE); + $this->assertIsArray($ret); + $this->assertEqualsWeak(true, $ret[0]); + $this->assertEqualsWeak(true, $ret[1]); + $this->assertEqualsWeak(true, $ret[2]); + $this->assertEqualsWeak(false, $ret[3]); + $this->assertEqualsWeak(true, $ret[4]); + $this->assertEqualsWeak(true, $ret[5]); + $this->assertEqualsWeak(false, $ret[6]); // ttl, mget, mset, msetnx, expire, expireAt $this->redis->del('key'); @@ -3571,17 +3841,17 @@ protected function sequence($mode) { ->expireAt('key', '0000') ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $i = 0; $ttl = $ret[$i++]; - $this->assertTrue($ttl === -1 || $ttl === -2); - $this->assertTrue($ret[$i++] === ['val1', 'valX', FALSE]); // mget - $this->assertTrue($ret[$i++] === TRUE); // mset - $this->assertTrue($ret[$i++] === TRUE); // set - $this->assertTrue($ret[$i++] === TRUE); // expire - $this->assertTrue($ret[$i++] === 5); // ttl - $this->assertTrue($ret[$i++] === TRUE); // expireAt - $this->assertTrue(count($ret) == $i); + $this->assertBetween($ttl, -2, -1); + $this->assertEquals(['val1', 'valX', false], $ret[$i++]); // mget + $this->assertTrue($ret[$i++]); // mset + $this->assertTrue($ret[$i++]); // set + $this->assertTrue($ret[$i++]); // expire + $this->assertEquals(5, $ret[$i++]); // ttl + $this->assertTrue($ret[$i++]); // expireAt + $this->assertEquals($i, count($ret)); $ret = $this->redis->multi($mode) ->set('{list}lkey', 'x') @@ -3601,35 +3871,34 @@ protected function sequence($mode) { ->llen('{list}lkey') ->lIndex('{list}lkey', 0) ->lrange('{list}lkey', 0, -1) - ->lSet('{list}lkey', 1, "newValue") // check errors on key not exists + ->lSet('{list}lkey', 1, 'newValue') // check errors on key not exists ->lrange('{list}lkey', 0, -1) ->llen('{list}lkey') ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $i = 0; - $this->assertTrue($ret[$i++] === TRUE); // SET - $this->assertTrue($ret[$i++] === TRUE); // SET - $this->assertTrue($ret[$i++] === 2); // deleting 2 keys - $this->assertTrue($ret[$i++] === 1); // rpush, now 1 element - $this->assertTrue($ret[$i++] === 2); // lpush, now 2 elements - $this->assertTrue($ret[$i++] === 3); // lpush, now 3 elements - $this->assertTrue($ret[$i++] === 4); // lpush, now 4 elements - $this->assertTrue($ret[$i++] === 5); // lpush, now 5 elements - $this->assertTrue($ret[$i++] === 6); // lpush, now 6 elements - $this->assertTrue($ret[$i++] === 'lvalue'); // rpoplpush returns the element: "lvalue" - $this->assertTrue($ret[$i++] === ['lvalue']); // lDest contains only that one element. - $this->assertTrue($ret[$i++] === 'lvalue'); // removing a second element from lkey, now 4 elements left ↓ - $this->assertTrue($ret[$i++] === 4); // 4 elements left, after 2 pops. - $this->assertTrue($ret[$i++] === 3); // removing 3 elements, now 1 left. - $this->assertTrue($ret[$i++] === 1); // 1 element left - $this->assertTrue($ret[$i++] === "lvalue"); // this is the current head. - $this->assertTrue($ret[$i++] === ["lvalue"]); // this is the current list. - $this->assertTrue($ret[$i++] === FALSE); // updating a non-existent element fails. - $this->assertTrue($ret[$i++] === ["lvalue"]); // this is the current list. - $this->assertTrue($ret[$i++] === 1); // 1 element left - $this->assertTrue(count($ret) == $i); - + $this->assertTrue($ret[$i++]); // SET + $this->assertTrue($ret[$i++]); // SET + $this->assertEquals(2, $ret[$i++]); // deleting 2 keys + $this->assertEquals(1, $ret[$i++]); // rpush, now 1 element + $this->assertEquals(2, $ret[$i++]); // lpush, now 2 elements + $this->assertEquals(3, $ret[$i++]); // lpush, now 3 elements + $this->assertEquals(4, $ret[$i++]); // lpush, now 4 elements + $this->assertEquals(5, $ret[$i++]); // lpush, now 5 elements + $this->assertEquals(6, $ret[$i++]); // lpush, now 6 elements + $this->assertEquals('lvalue', $ret[$i++]); // rpoplpush returns the element: 'lvalue' + $this->assertEquals(['lvalue'], $ret[$i++]); // lDest contains only that one element. + $this->assertEquals('lvalue', $ret[$i++]); // removing a second element from lkey, now 4 elements left ↓ + $this->assertEquals(4, $ret[$i++]); // 4 elements left, after 2 pops. + $this->assertEquals(3, $ret[$i++]); // removing 3 elements, now 1 left. + $this->assertEquals(1, $ret[$i++]); // 1 element left + $this->assertEquals('lvalue', $ret[$i++]); // this is the current head. + $this->assertEquals(['lvalue'], $ret[$i++]); // this is the current list. + $this->assertFalse($ret[$i++]); // updating a non-existent element fails. + $this->assertEquals(['lvalue'], $ret[$i++]); // this is the current list. + $this->assertEquals(1, $ret[$i++]); // 1 element left + $this->assertEquals($i, count($ret)); $ret = $this->redis->multi($mode) ->del('{list}lkey', '{list}lDest') @@ -3640,16 +3909,18 @@ protected function sequence($mode) { ->lrange('{list}lDest', 0, -1) ->lpop('{list}lkey') ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); + $i = 0; - $this->assertTrue($ret[$i++] <= 2); // deleted 0, 1, or 2 items - $this->assertTrue($ret[$i++] === 1); // 1 element in the list - $this->assertTrue($ret[$i++] === 2); // 2 elements in the list - $this->assertTrue($ret[$i++] === 3); // 3 elements in the list - $this->assertTrue($ret[$i++] === 'lvalue'); // rpoplpush returns the element: "lvalue" - $this->assertTrue($ret[$i++] === ['lvalue']); // rpoplpush returns the element: "lvalue" - $this->assertTrue($ret[$i++] === 'lvalue'); // pop returns the front element: "lvalue" - $this->assertTrue(count($ret) == $i); + + $this->assertLTE(2, $ret[$i++]); // deleting 2 keys + $this->assertEquals(1, $ret[$i++]); // 1 element in the list + $this->assertEquals(2, $ret[$i++]); // 2 elements in the list + $this->assertEquals(3, $ret[$i++]); // 3 elements in the list + $this->assertEquals('lvalue', $ret[$i++]); // rpoplpush returns the element: 'lvalue' + $this->assertEquals(['lvalue'], $ret[$i++]); // rpoplpush returns the element: 'lvalue' + $this->assertEquals('lvalue', $ret[$i++]); // pop returns the front element: 'lvalue' + $this->assertEquals($i, count($ret)); $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); @@ -3677,25 +3948,25 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] <= 1); $i++; - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 'value1'); - $this->assertTrue($ret[$i++] == 'value1'); - $this->assertTrue($ret[$i++] == 'value2'); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 5); - $this->assertTrue($ret[$i++] == 5); - $this->assertTrue($ret[$i++] == 4); - $this->assertTrue($ret[$i++] == 4); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 4); - $this->assertTrue($ret[$i++] == FALSE); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 9); - $this->assertTrue($ret[$i++] == TRUE); - $this->assertTrue($ret[$i++] == 4); + $this->assertIsArray($ret); + $this->assertLTE(1, $ret[$i++]); + $this->assertEqualsWeak(true, $ret[$i++]); + $this->assertEquals('value1', $ret[$i++]); + $this->assertEquals('value1', $ret[$i++]); + $this->assertEquals('value2', $ret[$i++]); + $this->assertEqualsWeak(true, $ret[$i++]); + $this->assertEqualsWeak(5, $ret[$i++]); + $this->assertEqualsWeak(5, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertTrue($ret[$i++]); + $this->assertEqualsWeak(4, $ret[$i++]); + $this->assertFalse($ret[$i++]); + $this->assertTrue($ret[$i++]); + $this->assertEquals(9, $ret[$i++]); // incrby('{key}2', 5) + $this->assertEqualsWeak(9, $ret[$i++]); // get('{key}2') + $this->assertEquals(4, $ret[$i++]); // decrby('{key}2', 5) + $this->assertEqualsWeak(4, $ret[$i++]); // get('{key}2') $this->assertTrue($ret[$i++]); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); @@ -3710,15 +3981,15 @@ protected function sequence($mode) { ->exists('{key}3') ->exec(); - $this->assertTrue(is_array($ret)); - $this->assertTrue($ret[0] == TRUE); - $this->assertTrue($ret[1] == TRUE); - $this->assertTrue($ret[2] == TRUE); - $this->assertTrue($ret[3] == TRUE); - $this->assertTrue($ret[4] == FALSE); - $this->assertTrue($ret[5] == TRUE); - $this->assertTrue($ret[6] == TRUE); - $this->assertTrue($ret[7] == FALSE); + $this->assertIsArray($ret); + $this->assertEquals(1, $ret[0]); // del('{key}1') + $this->assertEquals(1, $ret[1]); // del('{key}2') + $this->assertEquals(1, $ret[2]); // del('{key}3') + $this->assertTrue($ret[3]); // set('{key}1', 'val1') + $this->assertFalse($ret[4]); // setnx('{key}1', 'valX') + $this->assertTrue($ret[5]); // setnx('{key}2', 'valX') + $this->assertEquals(1, $ret[6]); // exists('{key}1') + $this->assertEquals(0, $ret[7]); // exists('{key}3') // ttl, mget, mset, msetnx, expire, expireAt $ret = $this->redis->multi($mode) @@ -3731,16 +4002,16 @@ protected function sequence($mode) { ->expireAt('key', '0000') ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i++])); - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 3); // mget - $i++; - $this->assertTrue($ret[$i++] === TRUE); // mset always returns TRUE - $this->assertTrue($ret[$i++] === TRUE); // set always returns TRUE - $this->assertTrue($ret[$i++] === TRUE); // expire always returns TRUE - $this->assertTrue($ret[$i++] === 5); // TTL was just set. - $this->assertTrue($ret[$i++] === TRUE); // expireAt returns TRUE for an existing key - $this->assertTrue(count($ret) === $i); + $this->assertIsArray($ret[$i++], 3); +// $i++; + $this->assertTrue($ret[$i++]); // mset always returns true + $this->assertTrue($ret[$i++]); // set always returns true + $this->assertTrue($ret[$i++]); // expire always returns true + $this->assertEquals(5, $ret[$i++]); // TTL was just set. + $this->assertTrue($ret[$i++]); // expireAt returns true for an existing key + $this->assertEquals($i, count($ret)); // lists $ret = $this->redis->multi($mode) @@ -3759,33 +4030,32 @@ protected function sequence($mode) { ->llen('{l}key') ->lIndex('{l}key', 0) ->lrange('{l}key', 0, -1) - ->lSet('{l}key', 1, "newValue") // check errors on missing key + ->lSet('{l}key', 1, 'newValue') // check errors on missing key ->lrange('{l}key', 0, -1) ->llen('{l}key') ->exec(); - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $i = 0; - $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // del - $i++; - $this->assertTrue($ret[$i++] === 1); // 1 value - $this->assertTrue($ret[$i++] === 2); // 2 values - $this->assertTrue($ret[$i++] === 3); // 3 values - $this->assertTrue($ret[$i++] === 4); // 4 values - $this->assertTrue($ret[$i++] === 5); // 5 values - $this->assertTrue($ret[$i++] === 6); // 6 values - $this->assertTrue($ret[$i++] === 'lvalue'); - $this->assertTrue($ret[$i++] === ['lvalue']); // 1 value only in lDest - $this->assertTrue($ret[$i++] === 'lvalue'); // now 4 values left - $this->assertTrue($ret[$i++] === 4); - $this->assertTrue($ret[$i++] === 3); // removing 3 elements. - $this->assertTrue($ret[$i++] === 1); // length is now 1 - $this->assertTrue($ret[$i++] === 'lvalue'); // this is the head - $this->assertTrue($ret[$i++] === ['lvalue']); // 1 value only in lkey - $this->assertTrue($ret[$i++] === FALSE); // can't set list[1] if we only have a single value in it. - $this->assertTrue($ret[$i++] === ['lvalue']); // the previous error didn't touch anything. - $this->assertTrue($ret[$i++] === 1); // the previous error didn't change the length - $this->assertTrue(count($ret) === $i); + $this->assertBetween($ret[$i++], 0, 2); // del + $this->assertEquals(1, $ret[$i++]); // 1 value + $this->assertEquals(2, $ret[$i++]); // 2 values + $this->assertEquals(3, $ret[$i++]); // 3 values + $this->assertEquals(4, $ret[$i++]); // 4 values + $this->assertEquals(5, $ret[$i++]); // 5 values + $this->assertEquals(6, $ret[$i++]); // 6 values + $this->assertEquals('lvalue', $ret[$i++]); + $this->assertEquals(['lvalue'], $ret[$i++]); // 1 value only in lDest + $this->assertEquals('lvalue', $ret[$i++]); // now 4 values left + $this->assertEquals(4, $ret[$i++]); + $this->assertEquals(3, $ret[$i++]); // removing 3 elements. + $this->assertEquals(1, $ret[$i++]); // length is now 1 + $this->assertEquals('lvalue', $ret[$i++]); // this is the head + $this->assertEquals(['lvalue'], $ret[$i++]); // 1 value only in lkey + $this->assertFalse($ret[$i++]); // can't set list[1] if we only have a single value in it. + $this->assertEquals(['lvalue'], $ret[$i++]); // the previous error didn't touch anything. + $this->assertEquals(1, $ret[$i++]); // the previous error didn't change the length + $this->assertEquals($i, count($ret)); // sets @@ -3795,10 +4065,8 @@ protected function sequence($mode) { ->sadd('{s}key1', 'sValue2') ->sadd('{s}key1', 'sValue3') ->sadd('{s}key1', 'sValue4') - ->sadd('{s}key2', 'sValue1') ->sadd('{s}key2', 'sValue2') - ->scard('{s}key1') ->srem('{s}key1', 'sValue2') ->scard('{s}key1') @@ -3821,53 +4089,52 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 5); $i++; // deleted at most 5 values. - $this->assertTrue($ret[$i++] === 1); // skey1 now has 1 element. - $this->assertTrue($ret[$i++] === 1); // skey1 now has 2 elements. - $this->assertTrue($ret[$i++] === 1); // skey1 now has 3 elements. - $this->assertTrue($ret[$i++] === 1); // skey1 now has 4 elements. - - $this->assertTrue($ret[$i++] === 1); // skey2 now has 1 element. - $this->assertTrue($ret[$i++] === 1); // skey2 now has 2 elements. - - $this->assertTrue($ret[$i++] === 4); - $this->assertTrue($ret[$i++] === 1); // we did remove that value. - $this->assertTrue($ret[$i++] === 3); // now 3 values only. - $this->assertTrue($ret[$i++] === TRUE); // the move did succeed. - $this->assertTrue($ret[$i++] === 3); // sKey2 now has 3 values. - $this->assertTrue($ret[$i++] === TRUE); // sKey2 does contain sValue4. - foreach(['sValue1', 'sValue3'] as $k) { // sKey1 contains sValue1 and sValue3. - $this->assertTrue(in_array($k, $ret[$i])); - } - $this->assertTrue(count($ret[$i++]) === 2); - foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // sKey2 contains sValue1, sValue2, and sValue4. - $this->assertTrue(in_array($k, $ret[$i])); - } - $this->assertTrue(count($ret[$i++]) === 3); - $this->assertTrue($ret[$i++] === ['sValue1']); // intersection - $this->assertTrue($ret[$i++] === 1); // intersection + store → 1 value in the destination set. - $this->assertTrue($ret[$i++] === ['sValue1']); // sinterstore destination contents - - foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeydest U sKey2) contains sValue1, sValue2, and sValue4. - $this->assertTrue(in_array($k, $ret[$i])); - } - $this->assertTrue(count($ret[$i++]) === 3); // union size + $this->assertIsArray($ret); + $this->assertBetween($ret[$i++], 0, 5); // we deleted at most 5 values. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 1 element. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 2 elements. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 3 elements. + $this->assertEquals(1, $ret[$i++]); // skey1 now has 4 elements. + $this->assertEquals(1, $ret[$i++]); // skey2 now has 1 element. + $this->assertEquals(1, $ret[$i++]); // skey2 now has 2 elements. + $this->assertEquals(4, $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); // we did remove that value. + $this->assertEquals(3, $ret[$i++]); // now 3 values only. + + $this->assertTrue($ret[$i++]); // the move did succeed. + $this->assertEquals(3, $ret[$i++]); // sKey2 now has 3 values. + $this->assertTrue($ret[$i++]); // sKey2 does contain sValue4. + foreach (['sValue1', 'sValue3'] as $k) { // sKey1 contains sValue1 and sValue3. + $this->assertInArray($k, $ret[$i]); + } + $this->assertEquals(2, count($ret[$i++])); + foreach (['sValue1', 'sValue2', 'sValue4'] as $k) { // sKey2 contains sValue1, sValue2, and sValue4. + $this->assertInArray($k, $ret[$i]); + } + $this->assertEquals(3, count($ret[$i++])); + $this->assertEquals(['sValue1'], $ret[$i++]); // intersection + $this->assertEquals(1, $ret[$i++]); // intersection + store → 1 value in the destination set. + $this->assertEquals(['sValue1'], $ret[$i++]); // sinterstore destination contents + + foreach (['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeydest U sKey2) contains sValue1, sValue2, and sValue4. + $this->assertInArray($k, $ret[$i]); + } + $this->assertEquals(3, count($ret[$i++])); // union size + + $this->assertEquals(3, $ret[$i++]); // unionstore size + foreach (['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeyUnion) contains sValue1, sValue2, and sValue4. + $this->assertInArray($k, $ret[$i]); + } + $this->assertEquals(3, count($ret[$i++])); // skeyUnion size + + $this->assertEquals(['sValue3'], $ret[$i++]); // diff skey1, skey2 : only sValue3 is not shared. + $this->assertEquals(1, $ret[$i++]); // sdiffstore size == 1 + $this->assertEquals(['sValue3'], $ret[$i++]); // contents of sDiffDest + + $this->assertInArray($ret[$i++], ['sValue1', 'sValue2', 'sValue4']); // we removed an element from sKey2 + $this->assertEquals(2, $ret[$i++]); // sKey2 now has 2 elements only. - $this->assertTrue($ret[$i++] === 3); // unionstore size - foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeyUnion) contains sValue1, sValue2, and sValue4. - $this->assertTrue(in_array($k, $ret[$i])); - } - $this->assertTrue(count($ret[$i++]) === 3); // skeyUnion size - - $this->assertTrue($ret[$i++] === ['sValue3']); // diff skey1, skey2 : only sValue3 is not shared. - $this->assertTrue($ret[$i++] === 1); // sdiffstore size == 1 - $this->assertTrue($ret[$i++] === ['sValue3']); // contents of sDiffDest - - $this->assertTrue(in_array($ret[$i++], ['sValue1', 'sValue2', 'sValue4'])); // we removed an element from sKey2 - $this->assertTrue($ret[$i++] === 2); // sKey2 now has 2 elements only. - - $this->assertTrue(count($ret) === $i); + $this->assertEquals($i, count($ret)); // sorted sets $ret = $this->redis->multi($mode) @@ -3904,39 +4171,39 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 5); $i++; // deleting at most 5 keys - $this->assertTrue($ret[$i++] === 1); - $this->assertTrue($ret[$i++] === 1); - $this->assertTrue($ret[$i++] === 1); - $this->assertTrue($ret[$i++] === ['zValue1', 'zValue2', 'zValue5']); - $this->assertTrue($ret[$i++] === 1); - $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5']); - $this->assertTrue($ret[$i++] === 1); // adding zValue11 - $this->assertTrue($ret[$i++] === 1); // adding zValue12 - $this->assertTrue($ret[$i++] === 1); // adding zValue13 - $this->assertTrue($ret[$i++] === 1); // adding zValue14 - $this->assertTrue($ret[$i++] === 1); // adding zValue15 - $this->assertTrue($ret[$i++] === 3); // deleted zValue11, zValue12, zValue13 - $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5', 'zValue14', 'zValue15']); - $this->assertTrue($ret[$i++] === ['zValue15', 'zValue14', 'zValue5', 'zValue1']); - $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5']); - $this->assertTrue($ret[$i++] === 4); // 4 elements - $this->assertTrue($ret[$i++] === 15.0); - $this->assertTrue($ret[$i++] === 1); // added value - $this->assertTrue($ret[$i++] === 1); // added value - $this->assertTrue($ret[$i++] === 1); // zinter only has 1 value - $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5', 'zValue14', 'zValue15']); // {z}key1 contents - $this->assertTrue($ret[$i++] === ['zValue2', 'zValue5']); // {z}key2 contents - $this->assertTrue($ret[$i++] === ['zValue5']); // {z}inter contents - $this->assertTrue($ret[$i++] === 5); // {z}Union has 5 values (1,2,5,14,15) - $this->assertTrue($ret[$i++] === ['zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15']); // {z}Union contents - $this->assertTrue($ret[$i++] === 1); // added value to {z}key5, with score 5 - $this->assertTrue($ret[$i++] === 8.0); // incremented score by 3 → it is now 8. - $this->assertTrue($ret[$i++] === 8.0); // current score is 8. - $this->assertTrue($ret[$i++] === FALSE); // score for unknown element. - - $this->assertTrue(count($ret) === $i); + $this->assertIsArray($ret); + $this->assertBetween($ret[$i++], 0, 5); // we deleted at most 5 values. + $this->assertEquals(1, $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); + $this->assertEquals(['zValue1', 'zValue2', 'zValue5'], $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); + $this->assertEquals(['zValue1', 'zValue5'], $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); // adding zValue11 + $this->assertEquals(1, $ret[$i++]); // adding zValue12 + $this->assertEquals(1, $ret[$i++]); // adding zValue13 + $this->assertEquals(1, $ret[$i++]); // adding zValue14 + $this->assertEquals(1, $ret[$i++]); // adding zValue15 + $this->assertEquals(3, $ret[$i++]); // deleted zValue11, zValue12, zValue13 + $this->assertEquals(['zValue1', 'zValue5', 'zValue14', 'zValue15'], $ret[$i++]); + $this->assertEquals(['zValue15', 'zValue14', 'zValue5', 'zValue1'], $ret[$i++]); + $this->assertEquals(['zValue1', 'zValue5'], $ret[$i++]); + $this->assertEquals(4, $ret[$i++]); // 4 elements + $this->assertEquals(15.0, $ret[$i++]); + $this->assertEquals(1, $ret[$i++]); // added value + $this->assertEquals(1, $ret[$i++]); // added value + $this->assertEquals(1, $ret[$i++]); // zinter only has 1 value + $this->assertEquals(['zValue1', 'zValue5', 'zValue14', 'zValue15'], $ret[$i++]); // {z}key1 contents + $this->assertEquals(['zValue2', 'zValue5'], $ret[$i++]); // {z}key2 contents + $this->assertEquals(['zValue5'], $ret[$i++]); // {z}inter contents + $this->assertEquals(5, $ret[$i++]); // {z}Union has 5 values (1, 2, 5, 14, 15) + $this->assertEquals(['zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15'], $ret[$i++]); // {z}Union contents + $this->assertEquals(1, $ret[$i++]); // added value to {z}key5, with score 5 + $this->assertEquals(8.0, $ret[$i++]); // incremented score by 3 → it is now 8. + $this->assertEquals(8.0, $ret[$i++]); // current score is 8. + $this->assertFalse($ret[$i++]); // score for unknown element. + + $this->assertEquals($i, count($ret)); // hash $ret = $this->redis->multi($mode) @@ -3959,24 +4226,24 @@ protected function sequence($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue($ret[$i++] <= 1); // delete - $this->assertTrue($ret[$i++] === 1); // added 1 element - $this->assertTrue($ret[$i++] === 1); // added 1 element - $this->assertTrue($ret[$i++] === 1); // added 1 element - $this->assertTrue($ret[$i++] === ['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3']); // hmget, 3 elements - $this->assertTrue($ret[$i++] === 'value1'); // hget - $this->assertTrue($ret[$i++] === 3); // hlen - $this->assertTrue($ret[$i++] === 1); // hdel succeeded - $this->assertTrue($ret[$i++] === 0); // hdel failed - $this->assertTrue($ret[$i++] === FALSE); // hexists didn't find the deleted key - $this->assertTrue($ret[$i] === ['key1', 'key3'] || $ret[$i] === ['key3', 'key1']); $i++; // hkeys - $this->assertTrue($ret[$i] === ['value1', 'value3'] || $ret[$i] === ['value3', 'value1']); $i++; // hvals - $this->assertTrue($ret[$i] === ['key1' => 'value1', 'key3' => 'value3'] || $ret[$i] === ['key3' => 'value3', 'key1' => 'value1']); $i++; // hgetall - $this->assertTrue($ret[$i++] === 1); // added 1 element - $this->assertTrue($ret[$i++] === 1); // added the element, so 1. - $this->assertTrue($ret[$i++] === 'non-string'); // hset succeeded - $this->assertTrue(count($ret) === $i); + $this->assertIsArray($ret); + $this->assertLT(2, $ret[$i++]); // delete + $this->assertEquals(1, $ret[$i++]); // added 1 element + $this->assertEquals(1, $ret[$i++]); // added 1 element + $this->assertEquals(1, $ret[$i++]); // added 1 element + $this->assertEquals(['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3'], $ret[$i++]); // hmget, 3 elements + $this->assertEquals('value1', $ret[$i++]); // hget + $this->assertEquals(3, $ret[$i++]); // hlen + $this->assertEquals(1, $ret[$i++]); // hdel succeeded + $this->assertEquals(0, $ret[$i++]); // hdel failed + $this->assertFalse($ret[$i++]); // hexists didn't find the deleted key + $this->assertEqualsCanonicalizing(['key1', 'key3'], $ret[$i++]); // hkeys + $this->assertEqualsCanonicalizing(['value1', 'value3'], $ret[$i++]); // hvals + $this->assertEqualsCanonicalizing(['key1' => 'value1', 'key3' => 'value3'], $ret[$i++]); // hgetall + $this->assertEquals(1, $ret[$i++]); // added 1 element + $this->assertEquals(1, $ret[$i++]); // added the element, so 1. + $this->assertEquals('non-string', $ret[$i++]); // hset succeeded + $this->assertEquals($i, count($ret)); $ret = $this->redis->multi($mode) // default to MULTI, not PIPELINE. ->del('test') @@ -3984,29 +4251,28 @@ protected function sequence($mode) { ->get('test') ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); - $this->assertTrue($ret[$i++] <= 1); // delete - $this->assertTrue($ret[$i++] === TRUE); // added 1 element - $this->assertTrue($ret[$i++] === 'xyz'); - $this->assertTrue(count($ret) === $i); + $this->assertIsArray($ret); + $this->assertLTE(1, $ret[$i++]); // delete + $this->assertTrue($ret[$i++]); // added 1 element + $this->assertEquals('xyz', $ret[$i++]); + $this->assertEquals($i, count($ret)); // GitHub issue 78 $this->redis->del('test'); - for($i = 1; $i <= 5; $i++) + for ($i = 1; $i <= 5; $i++) $this->redis->zadd('test', $i, (string)$i); $result = $this->redis->multi($mode) - ->zscore('test', "1") - ->zscore('test', "6") - ->zscore('test', "8") - ->zscore('test', "2") + ->zscore('test', '1') + ->zscore('test', '6') + ->zscore('test', '8') + ->zscore('test', '2') ->exec(); - $this->assertTrue($result === [1.0, FALSE, FALSE, 2.0]); + $this->assertEquals([1.0, FALSE, FALSE, 2.0], $result); } protected function differentType($mode) { - // string $key = '{hash}string'; $dkey = '{hash}' . __FUNCTION__; @@ -4023,7 +4289,7 @@ protected function differentType($mode) { ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) - ->lSet($key, 0, "newValue") + ->lSet($key, 0, 'newValue') ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) @@ -4075,60 +4341,60 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i++])); // delete - $this->assertTrue($ret[$i++] === TRUE); // set - - $this->assertTrue($ret[$i++] === FALSE); // rpush - $this->assertTrue($ret[$i++] === FALSE); // lpush - $this->assertTrue($ret[$i++] === FALSE); // llen - $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // lrange - $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lindex - $this->assertTrue($ret[$i++] === FALSE); // lset - $this->assertTrue($ret[$i++] === FALSE); // lremove - $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // rpop - $this->assertTrue($ret[$i++] === FALSE); // rpoplush - - $this->assertTrue($ret[$i++] === FALSE); // sadd - $this->assertTrue($ret[$i++] === FALSE); // sremove - $this->assertTrue($ret[$i++] === FALSE); // spop - $this->assertTrue($ret[$i++] === FALSE); // smove - $this->assertTrue($ret[$i++] === FALSE); // scard - $this->assertTrue($ret[$i++] === FALSE); // sismember - $this->assertTrue($ret[$i++] === FALSE); // sinter - $this->assertTrue($ret[$i++] === FALSE); // sunion - $this->assertTrue($ret[$i++] === FALSE); // sdiff - $this->assertTrue($ret[$i++] === FALSE); // smembers - $this->assertTrue($ret[$i++] === FALSE); // srandmember - - $this->assertTrue($ret[$i++] === FALSE); // zadd - $this->assertTrue($ret[$i++] === FALSE); // zrem - $this->assertTrue($ret[$i++] === FALSE); // zincrby - $this->assertTrue($ret[$i++] === FALSE); // zrank - $this->assertTrue($ret[$i++] === FALSE); // zrevrank - $this->assertTrue($ret[$i++] === FALSE); // zrange - $this->assertTrue($ret[$i++] === FALSE); // zreverserange - $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore - $this->assertTrue($ret[$i++] === FALSE); // zcount - $this->assertTrue($ret[$i++] === FALSE); // zcard - $this->assertTrue($ret[$i++] === FALSE); // zscore - $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore - - $this->assertTrue($ret[$i++] === FALSE); // hset - $this->assertTrue($ret[$i++] === FALSE); // hget - $this->assertTrue($ret[$i++] === FALSE); // hmget - $this->assertTrue($ret[$i++] === FALSE); // hmset - $this->assertTrue($ret[$i++] === FALSE); // hincrby - $this->assertTrue($ret[$i++] === FALSE); // hexists - $this->assertTrue($ret[$i++] === FALSE); // hdel - $this->assertTrue($ret[$i++] === FALSE); // hlen - $this->assertTrue($ret[$i++] === FALSE); // hkeys - $this->assertTrue($ret[$i++] === FALSE); // hvals - $this->assertTrue($ret[$i++] === FALSE); // hgetall + $this->assertTrue($ret[$i++]); // set + + $this->assertFalse($ret[$i++]); // rpush + $this->assertFalse($ret[$i++]); // lpush + $this->assertFalse($ret[$i++]); // llen + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // lrange + $this->assertFalse($ret[$i++]); // ltrim + $this->assertFalse($ret[$i++]); // lindex + $this->assertFalse($ret[$i++]); // lset + $this->assertFalse($ret[$i++]); // lrem + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // rpop + $this->assertFalse($ret[$i++]); // rpoplush + + $this->assertFalse($ret[$i++]); // sadd + $this->assertFalse($ret[$i++]); // srem + $this->assertFalse($ret[$i++]); // spop + $this->assertFalse($ret[$i++]); // smove + $this->assertFalse($ret[$i++]); // scard + $this->assertFalse($ret[$i++]); // sismember + $this->assertFalse($ret[$i++]); // sinter + $this->assertFalse($ret[$i++]); // sunion + $this->assertFalse($ret[$i++]); // sdiff + $this->assertFalse($ret[$i++]); // smembers + $this->assertFalse($ret[$i++]); // srandmember + + $this->assertFalse($ret[$i++]); // zadd + $this->assertFalse($ret[$i++]); // zrem + $this->assertFalse($ret[$i++]); // zincrby + $this->assertFalse($ret[$i++]); // zrank + $this->assertFalse($ret[$i++]); // zrevrank + $this->assertFalse($ret[$i++]); // zrange + $this->assertFalse($ret[$i++]); // zreverserange + $this->assertFalse($ret[$i++]); // zrangebyscore + $this->assertFalse($ret[$i++]); // zcount + $this->assertFalse($ret[$i++]); // zcard + $this->assertFalse($ret[$i++]); // zscore + $this->assertFalse($ret[$i++]); // zremrangebyrank + $this->assertFalse($ret[$i++]); // zremrangebyscore + + $this->assertFalse($ret[$i++]); // hset + $this->assertFalse($ret[$i++]); // hget + $this->assertFalse($ret[$i++]); // hmget + $this->assertFalse($ret[$i++]); // hmset + $this->assertFalse($ret[$i++]); // hincrby + $this->assertFalse($ret[$i++]); // hexists + $this->assertFalse($ret[$i++]); // hdel + $this->assertFalse($ret[$i++]); // hlen + $this->assertFalse($ret[$i++]); // hkeys + $this->assertFalse($ret[$i++]); // hvals + $this->assertFalse($ret[$i++]); // hgetall $this->assertEquals($i, count($ret)); @@ -4194,58 +4460,57 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i++])); // delete - $this->assertTrue($ret[$i++] === 1); // lpush - - $this->assertTrue($ret[$i++] === FALSE); // get - $this->assertTrue($ret[$i++] === FALSE); // getset - $this->assertTrue($ret[$i++] === FALSE); // append - $this->assertTrue($ret[$i++] === FALSE); // getRange - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget - $i++; - $this->assertTrue($ret[$i++] === FALSE); // incr - $this->assertTrue($ret[$i++] === FALSE); // incrBy - $this->assertTrue($ret[$i++] === FALSE); // decr - $this->assertTrue($ret[$i++] === FALSE); // decrBy - - $this->assertTrue($ret[$i++] === FALSE); // sadd - $this->assertTrue($ret[$i++] === FALSE); // sremove - $this->assertTrue($ret[$i++] === FALSE); // spop - $this->assertTrue($ret[$i++] === FALSE); // smove - $this->assertTrue($ret[$i++] === FALSE); // scard - $this->assertTrue($ret[$i++] === FALSE); // sismember - $this->assertTrue($ret[$i++] === FALSE); // sinter - $this->assertTrue($ret[$i++] === FALSE); // sunion - $this->assertTrue($ret[$i++] === FALSE); // sdiff - $this->assertTrue($ret[$i++] === FALSE); // smembers - $this->assertTrue($ret[$i++] === FALSE); // srandmember - - $this->assertTrue($ret[$i++] === FALSE); // zadd - $this->assertTrue($ret[$i++] === FALSE); // zrem - $this->assertTrue($ret[$i++] === FALSE); // zincrby - $this->assertTrue($ret[$i++] === FALSE); // zrank - $this->assertTrue($ret[$i++] === FALSE); // zrevrank - $this->assertTrue($ret[$i++] === FALSE); // zrange - $this->assertTrue($ret[$i++] === FALSE); // zreverserange - $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore - $this->assertTrue($ret[$i++] === FALSE); // zcount - $this->assertTrue($ret[$i++] === FALSE); // zcard - $this->assertTrue($ret[$i++] === FALSE); // zscore - $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore - - $this->assertTrue($ret[$i++] === FALSE); // hset - $this->assertTrue($ret[$i++] === FALSE); // hget - $this->assertTrue($ret[$i++] === FALSE); // hmget - $this->assertTrue($ret[$i++] === FALSE); // hmset - $this->assertTrue($ret[$i++] === FALSE); // hincrby - $this->assertTrue($ret[$i++] === FALSE); // hexists - $this->assertTrue($ret[$i++] === FALSE); // hdel - $this->assertTrue($ret[$i++] === FALSE); // hlen - $this->assertTrue($ret[$i++] === FALSE); // hkeys - $this->assertTrue($ret[$i++] === FALSE); // hvals - $this->assertTrue($ret[$i++] === FALSE); // hgetall + $this->assertEquals(1, $ret[$i++]); // lpush + + $this->assertFalse($ret[$i++]); // get + $this->assertFalse($ret[$i++]); // getset + $this->assertFalse($ret[$i++]); // append + $this->assertFalse($ret[$i++]); // getRange + $this->assertEquals([false], $ret[$i++]); // mget + $this->assertFalse($ret[$i++]); // incr + $this->assertFalse($ret[$i++]); // incrBy + $this->assertFalse($ret[$i++]); // decr + $this->assertFalse($ret[$i++]); // decrBy + + $this->assertFalse($ret[$i++]); // sadd + $this->assertFalse($ret[$i++]); // srem + $this->assertFalse($ret[$i++]); // spop + $this->assertFalse($ret[$i++]); // smove + $this->assertFalse($ret[$i++]); // scard + $this->assertFalse($ret[$i++]); // sismember + $this->assertFalse($ret[$i++]); // sinter + $this->assertFalse($ret[$i++]); // sunion + $this->assertFalse($ret[$i++]); // sdiff + $this->assertFalse($ret[$i++]); // smembers + $this->assertFalse($ret[$i++]); // srandmember + + $this->assertFalse($ret[$i++]); // zadd + $this->assertFalse($ret[$i++]); // zrem + $this->assertFalse($ret[$i++]); // zincrby + $this->assertFalse($ret[$i++]); // zrank + $this->assertFalse($ret[$i++]); // zrevrank + $this->assertFalse($ret[$i++]); // zrange + $this->assertFalse($ret[$i++]); // zreverserange + $this->assertFalse($ret[$i++]); // zrangebyscore + $this->assertFalse($ret[$i++]); // zcount + $this->assertFalse($ret[$i++]); // zcard + $this->assertFalse($ret[$i++]); // zscore + $this->assertFalse($ret[$i++]); // zremrangebyrank + $this->assertFalse($ret[$i++]); // zremrangebyscore + + $this->assertFalse($ret[$i++]); // hset + $this->assertFalse($ret[$i++]); // hget + $this->assertFalse($ret[$i++]); // hmget + $this->assertFalse($ret[$i++]); // hmset + $this->assertFalse($ret[$i++]); // hincrby + $this->assertFalse($ret[$i++]); // hexists + $this->assertFalse($ret[$i++]); // hdel + $this->assertFalse($ret[$i++]); // hlen + $this->assertFalse($ret[$i++]); // hkeys + $this->assertFalse($ret[$i++]); // hvals + $this->assertFalse($ret[$i++]); // hgetall $this->assertEquals($i, count($ret)); @@ -4275,7 +4540,7 @@ protected function differentType($mode) { ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) - ->lSet($key, 0, "newValue") + ->lSet($key, 0, 'newValue') ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) @@ -4312,59 +4577,58 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i++])); // delete - $this->assertTrue($ret[$i++] === 1); // zadd - - $this->assertTrue($ret[$i++] === FALSE); // get - $this->assertTrue($ret[$i++] === FALSE); // getset - $this->assertTrue($ret[$i++] === FALSE); // append - $this->assertTrue($ret[$i++] === FALSE); // getRange - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget - $i++; - $this->assertTrue($ret[$i++] === FALSE); // incr - $this->assertTrue($ret[$i++] === FALSE); // incrBy - $this->assertTrue($ret[$i++] === FALSE); // decr - $this->assertTrue($ret[$i++] === FALSE); // decrBy - - $this->assertTrue($ret[$i++] === FALSE); // rpush - $this->assertTrue($ret[$i++] === FALSE); // lpush - $this->assertTrue($ret[$i++] === FALSE); // llen - $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // lrange - $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lindex - $this->assertTrue($ret[$i++] === FALSE); // lset - $this->assertTrue($ret[$i++] === FALSE); // lremove - $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // rpop - $this->assertTrue($ret[$i++] === FALSE); // rpoplush - - $this->assertTrue($ret[$i++] === FALSE); // zadd - $this->assertTrue($ret[$i++] === FALSE); // zrem - $this->assertTrue($ret[$i++] === FALSE); // zincrby - $this->assertTrue($ret[$i++] === FALSE); // zrank - $this->assertTrue($ret[$i++] === FALSE); // zrevrank - $this->assertTrue($ret[$i++] === FALSE); // zrange - $this->assertTrue($ret[$i++] === FALSE); // zreverserange - $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore - $this->assertTrue($ret[$i++] === FALSE); // zcount - $this->assertTrue($ret[$i++] === FALSE); // zcard - $this->assertTrue($ret[$i++] === FALSE); // zscore - $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore - - $this->assertTrue($ret[$i++] === FALSE); // hset - $this->assertTrue($ret[$i++] === FALSE); // hget - $this->assertTrue($ret[$i++] === FALSE); // hmget - $this->assertTrue($ret[$i++] === FALSE); // hmset - $this->assertTrue($ret[$i++] === FALSE); // hincrby - $this->assertTrue($ret[$i++] === FALSE); // hexists - $this->assertTrue($ret[$i++] === FALSE); // hdel - $this->assertTrue($ret[$i++] === FALSE); // hlen - $this->assertTrue($ret[$i++] === FALSE); // hkeys - $this->assertTrue($ret[$i++] === FALSE); // hvals - $this->assertTrue($ret[$i++] === FALSE); // hgetall + $this->assertEquals(1, $ret[$i++]); // zadd + + $this->assertFalse($ret[$i++]); // get + $this->assertFalse($ret[$i++]); // getset + $this->assertFalse($ret[$i++]); // append + $this->assertFalse($ret[$i++]); // getRange + $this->assertEquals([false], $ret[$i++]); // mget + $this->assertFalse($ret[$i++]); // incr + $this->assertFalse($ret[$i++]); // incrBy + $this->assertFalse($ret[$i++]); // decr + $this->assertFalse($ret[$i++]); // decrBy + + $this->assertFalse($ret[$i++]); // rpush + $this->assertFalse($ret[$i++]); // lpush + $this->assertFalse($ret[$i++]); // llen + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // lrange + $this->assertFalse($ret[$i++]); // ltrim + $this->assertFalse($ret[$i++]); // lindex + $this->assertFalse($ret[$i++]); // lset + $this->assertFalse($ret[$i++]); // lrem + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // rpop + $this->assertFalse($ret[$i++]); // rpoplush + + $this->assertFalse($ret[$i++]); // zadd + $this->assertFalse($ret[$i++]); // zrem + $this->assertFalse($ret[$i++]); // zincrby + $this->assertFalse($ret[$i++]); // zrank + $this->assertFalse($ret[$i++]); // zrevrank + $this->assertFalse($ret[$i++]); // zrange + $this->assertFalse($ret[$i++]); // zreverserange + $this->assertFalse($ret[$i++]); // zrangebyscore + $this->assertFalse($ret[$i++]); // zcount + $this->assertFalse($ret[$i++]); // zcard + $this->assertFalse($ret[$i++]); // zscore + $this->assertFalse($ret[$i++]); // zremrangebyrank + $this->assertFalse($ret[$i++]); // zremrangebyscore + + $this->assertFalse($ret[$i++]); // hset + $this->assertFalse($ret[$i++]); // hget + $this->assertFalse($ret[$i++]); // hmget + $this->assertFalse($ret[$i++]); // hmset + $this->assertFalse($ret[$i++]); // hincrby + $this->assertFalse($ret[$i++]); // hexists + $this->assertFalse($ret[$i++]); // hdel + $this->assertFalse($ret[$i++]); // hlen + $this->assertFalse($ret[$i++]); // hkeys + $this->assertFalse($ret[$i++]); // hvals + $this->assertFalse($ret[$i++]); // hgetall $this->assertEquals($i, count($ret)); @@ -4394,7 +4658,7 @@ protected function differentType($mode) { ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) - ->lSet($key, 0, "newValue") + ->lSet($key, 0, 'newValue') ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) @@ -4429,57 +4693,56 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i++])); // delete - $this->assertTrue($ret[$i++] === 1); // zadd - - $this->assertTrue($ret[$i++] === FALSE); // get - $this->assertTrue($ret[$i++] === FALSE); // getset - $this->assertTrue($ret[$i++] === FALSE); // append - $this->assertTrue($ret[$i++] === FALSE); // getRange - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget - $i++; - $this->assertTrue($ret[$i++] === FALSE); // incr - $this->assertTrue($ret[$i++] === FALSE); // incrBy - $this->assertTrue($ret[$i++] === FALSE); // decr - $this->assertTrue($ret[$i++] === FALSE); // decrBy - - $this->assertTrue($ret[$i++] === FALSE); // rpush - $this->assertTrue($ret[$i++] === FALSE); // lpush - $this->assertTrue($ret[$i++] === FALSE); // llen - $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // lrange - $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lindex - $this->assertTrue($ret[$i++] === FALSE); // lset - $this->assertTrue($ret[$i++] === FALSE); // lremove - $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // rpop - $this->assertTrue($ret[$i++] === FALSE); // rpoplush - - $this->assertTrue($ret[$i++] === FALSE); // sadd - $this->assertTrue($ret[$i++] === FALSE); // sremove - $this->assertTrue($ret[$i++] === FALSE); // spop - $this->assertTrue($ret[$i++] === FALSE); // smove - $this->assertTrue($ret[$i++] === FALSE); // scard - $this->assertTrue($ret[$i++] === FALSE); // sismember - $this->assertTrue($ret[$i++] === FALSE); // sinter - $this->assertTrue($ret[$i++] === FALSE); // sunion - $this->assertTrue($ret[$i++] === FALSE); // sdiff - $this->assertTrue($ret[$i++] === FALSE); // smembers - $this->assertTrue($ret[$i++] === FALSE); // srandmember - - $this->assertTrue($ret[$i++] === FALSE); // hset - $this->assertTrue($ret[$i++] === FALSE); // hget - $this->assertTrue($ret[$i++] === FALSE); // hmget - $this->assertTrue($ret[$i++] === FALSE); // hmset - $this->assertTrue($ret[$i++] === FALSE); // hincrby - $this->assertTrue($ret[$i++] === FALSE); // hexists - $this->assertTrue($ret[$i++] === FALSE); // hdel - $this->assertTrue($ret[$i++] === FALSE); // hlen - $this->assertTrue($ret[$i++] === FALSE); // hkeys - $this->assertTrue($ret[$i++] === FALSE); // hvals - $this->assertTrue($ret[$i++] === FALSE); // hgetall + $this->assertEquals(1, $ret[$i++]); // zadd + + $this->assertFalse($ret[$i++]); // get + $this->assertFalse($ret[$i++]); // getset + $this->assertFalse($ret[$i++]); // append + $this->assertFalse($ret[$i++]); // getRange + $this->assertEquals([false], $ret[$i++]); // mget + $this->assertFalse($ret[$i++]); // incr + $this->assertFalse($ret[$i++]); // incrBy + $this->assertFalse($ret[$i++]); // decr + $this->assertFalse($ret[$i++]); // decrBy + + $this->assertFalse($ret[$i++]); // rpush + $this->assertFalse($ret[$i++]); // lpush + $this->assertFalse($ret[$i++]); // llen + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // lrange + $this->assertFalse($ret[$i++]); // ltrim + $this->assertFalse($ret[$i++]); // lindex + $this->assertFalse($ret[$i++]); // lset + $this->assertFalse($ret[$i++]); // lrem + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // rpop + $this->assertFalse($ret[$i++]); // rpoplush + + $this->assertFalse($ret[$i++]); // sadd + $this->assertFalse($ret[$i++]); // srem + $this->assertFalse($ret[$i++]); // spop + $this->assertFalse($ret[$i++]); // smove + $this->assertFalse($ret[$i++]); // scard + $this->assertFalse($ret[$i++]); // sismember + $this->assertFalse($ret[$i++]); // sinter + $this->assertFalse($ret[$i++]); // sunion + $this->assertFalse($ret[$i++]); // sdiff + $this->assertFalse($ret[$i++]); // smembers + $this->assertFalse($ret[$i++]); // srandmember + + $this->assertFalse($ret[$i++]); // hset + $this->assertFalse($ret[$i++]); // hget + $this->assertFalse($ret[$i++]); // hmget + $this->assertFalse($ret[$i++]); // hmset + $this->assertFalse($ret[$i++]); // hincrby + $this->assertFalse($ret[$i++]); // hexists + $this->assertFalse($ret[$i++]); // hdel + $this->assertFalse($ret[$i++]); // hlen + $this->assertFalse($ret[$i++]); // hkeys + $this->assertFalse($ret[$i++]); // hvals + $this->assertFalse($ret[$i++]); // hgetall $this->assertEquals($i, count($ret)); @@ -4509,7 +4772,7 @@ protected function differentType($mode) { ->lrange($key, 0, -1) ->lTrim($key, 0, 1) ->lIndex($key, 0) - ->lSet($key, 0, "newValue") + ->lSet($key, 0, 'newValue') ->lrem($key, 'lvalue', 1) ->lPop($key) ->rPop($key) @@ -4546,59 +4809,58 @@ protected function differentType($mode) { ->exec(); $i = 0; - $this->assertTrue(is_array($ret)); + $this->assertIsArray($ret); $this->assertTrue(is_long($ret[$i++])); // delete - $this->assertTrue($ret[$i++] === 1); // hset - - $this->assertTrue($ret[$i++] === FALSE); // get - $this->assertTrue($ret[$i++] === FALSE); // getset - $this->assertTrue($ret[$i++] === FALSE); // append - $this->assertTrue($ret[$i++] === FALSE); // getRange - $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget - $i++; - $this->assertTrue($ret[$i++] === FALSE); // incr - $this->assertTrue($ret[$i++] === FALSE); // incrBy - $this->assertTrue($ret[$i++] === FALSE); // decr - $this->assertTrue($ret[$i++] === FALSE); // decrBy - - $this->assertTrue($ret[$i++] === FALSE); // rpush - $this->assertTrue($ret[$i++] === FALSE); // lpush - $this->assertTrue($ret[$i++] === FALSE); // llen - $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // lrange - $this->assertTrue($ret[$i++] === FALSE); // ltrim - $this->assertTrue($ret[$i++] === FALSE); // lindex - $this->assertTrue($ret[$i++] === FALSE); // lset - $this->assertTrue($ret[$i++] === FALSE); // lremove - $this->assertTrue($ret[$i++] === FALSE); // lpop - $this->assertTrue($ret[$i++] === FALSE); // rpop - $this->assertTrue($ret[$i++] === FALSE); // rpoplush - - $this->assertTrue($ret[$i++] === FALSE); // sadd - $this->assertTrue($ret[$i++] === FALSE); // sremove - $this->assertTrue($ret[$i++] === FALSE); // spop - $this->assertTrue($ret[$i++] === FALSE); // smove - $this->assertTrue($ret[$i++] === FALSE); // scard - $this->assertTrue($ret[$i++] === FALSE); // sismember - $this->assertTrue($ret[$i++] === FALSE); // sinter - $this->assertTrue($ret[$i++] === FALSE); // sunion - $this->assertTrue($ret[$i++] === FALSE); // sdiff - $this->assertTrue($ret[$i++] === FALSE); // smembers - $this->assertTrue($ret[$i++] === FALSE); // srandmember - - $this->assertTrue($ret[$i++] === FALSE); // zadd - $this->assertTrue($ret[$i++] === FALSE); // zrem - $this->assertTrue($ret[$i++] === FALSE); // zincrby - $this->assertTrue($ret[$i++] === FALSE); // zrank - $this->assertTrue($ret[$i++] === FALSE); // zrevrank - $this->assertTrue($ret[$i++] === FALSE); // zrange - $this->assertTrue($ret[$i++] === FALSE); // zreverserange - $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore - $this->assertTrue($ret[$i++] === FALSE); // zcount - $this->assertTrue($ret[$i++] === FALSE); // zcard - $this->assertTrue($ret[$i++] === FALSE); // zscore - $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank - $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore + $this->assertEquals(1, $ret[$i++]); // hset + + $this->assertFalse($ret[$i++]); // get + $this->assertFalse($ret[$i++]); // getset + $this->assertFalse($ret[$i++]); // append + $this->assertFalse($ret[$i++]); // getRange + $this->assertEquals([false], $ret[$i++]); // mget + $this->assertFalse($ret[$i++]); // incr + $this->assertFalse($ret[$i++]); // incrBy + $this->assertFalse($ret[$i++]); // decr + $this->assertFalse($ret[$i++]); // decrBy + + $this->assertFalse($ret[$i++]); // rpush + $this->assertFalse($ret[$i++]); // lpush + $this->assertFalse($ret[$i++]); // llen + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // lrange + $this->assertFalse($ret[$i++]); // ltrim + $this->assertFalse($ret[$i++]); // lindex + $this->assertFalse($ret[$i++]); // lset + $this->assertFalse($ret[$i++]); // lrem + $this->assertFalse($ret[$i++]); // lpop + $this->assertFalse($ret[$i++]); // rpop + $this->assertFalse($ret[$i++]); // rpoplush + + $this->assertFalse($ret[$i++]); // sadd + $this->assertFalse($ret[$i++]); // srem + $this->assertFalse($ret[$i++]); // spop + $this->assertFalse($ret[$i++]); // smove + $this->assertFalse($ret[$i++]); // scard + $this->assertFalse($ret[$i++]); // sismember + $this->assertFalse($ret[$i++]); // sinter + $this->assertFalse($ret[$i++]); // sunion + $this->assertFalse($ret[$i++]); // sdiff + $this->assertFalse($ret[$i++]); // smembers + $this->assertFalse($ret[$i++]); // srandmember + + $this->assertFalse($ret[$i++]); // zadd + $this->assertFalse($ret[$i++]); // zrem + $this->assertFalse($ret[$i++]); // zincrby + $this->assertFalse($ret[$i++]); // zrank + $this->assertFalse($ret[$i++]); // zrevrank + $this->assertFalse($ret[$i++]); // zrange + $this->assertFalse($ret[$i++]); // zreverserange + $this->assertFalse($ret[$i++]); // zrangebyscore + $this->assertFalse($ret[$i++]); // zcount + $this->assertFalse($ret[$i++]); // zcard + $this->assertFalse($ret[$i++]); // zscore + $this->assertFalse($ret[$i++]); // zremrangebyrank + $this->assertFalse($ret[$i++]); // zremrangebyscore $this->assertEquals($i, count($ret)); } @@ -4608,62 +4870,62 @@ public function testDifferentTypeString() { $dkey = '{hash}' . __FUNCTION__; $this->redis->del($key); - $this->assertEquals(TRUE, $this->redis->set($key, 'value')); + $this->assertTrue($this->redis->set($key, 'value')); // lists I/F - $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue')); - $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue')); - $this->assertEquals(FALSE, $this->redis->lLen($key)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->rPop($key)); - $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1')); + $this->assertFalse($this->redis->rPush($key, 'lvalue')); + $this->assertFalse($this->redis->lPush($key, 'lvalue')); + $this->assertFalse($this->redis->lLen($key)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->lrange($key, 0, -1)); + $this->assertFalse($this->redis->lTrim($key, 0, 1)); + $this->assertFalse($this->redis->lIndex($key, 0)); + $this->assertFalse($this->redis->lSet($key, 0, 'newValue')); + $this->assertFalse($this->redis->lrem($key, 'lvalue', 1)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->rPop($key)); + $this->assertFalse($this->redis->rPoplPush($key, $dkey . 'lkey1')); // sets I/F - $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sPop($key)); - $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->scard($key)); - $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey. 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7')); - $this->assertEquals(FALSE, $this->redis->sMembers($key)); - $this->assertEquals(FALSE, $this->redis->sRandMember($key)); + $this->assertFalse($this->redis->sAdd($key, 'sValue1')); + $this->assertFalse($this->redis->srem($key, 'sValue1')); + $this->assertFalse($this->redis->sPop($key)); + $this->assertFalse($this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); + $this->assertFalse($this->redis->scard($key)); + $this->assertFalse($this->redis->sismember($key, 'sValue1')); + $this->assertFalse($this->redis->sInter($key, $dkey. 'skey2')); + $this->assertFalse($this->redis->sUnion($key, $dkey . 'skey4')); + $this->assertFalse($this->redis->sDiff($key, $dkey . 'skey7')); + $this->assertFalse($this->redis->sMembers($key)); + $this->assertFalse($this->redis->sRandMember($key)); // sorted sets I/F - $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zAdd($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRem($key, 'zValue1')); + $this->assertFalse($this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRevRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRange($key, 0, -1)); + $this->assertFalse($this->redis->zRevRange($key, 0, -1)); + $this->assertFalse($this->redis->zRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zCount($key, 0, -1)); + $this->assertFalse($this->redis->zCard($key)); + $this->assertFalse($this->redis->zScore($key, 'zValue1')); + $this->assertFalse($this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertFalse($this->redis->zRemRangeByScore($key, 1, 2)); // hash I/F - $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); - $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1')); - $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1'])); - $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1'])); - $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1)); - $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2')); - $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2')); - $this->assertEquals(FALSE, $this->redis->hLen($key)); - $this->assertEquals(FALSE, $this->redis->hKeys($key)); - $this->assertEquals(FALSE, $this->redis->hVals($key)); - $this->assertEquals(FALSE, $this->redis->hGetAll($key)); + $this->assertFalse($this->redis->hSet($key, 'key1', 'value1')); + $this->assertFalse($this->redis->hGet($key, 'key1')); + $this->assertFalse($this->redis->hMGet($key, ['key1'])); + $this->assertFalse($this->redis->hMSet($key, ['key1' => 'value1'])); + $this->assertFalse($this->redis->hIncrBy($key, 'key2', 1)); + $this->assertFalse($this->redis->hExists($key, 'key2')); + $this->assertFalse($this->redis->hDel($key, 'key2')); + $this->assertFalse($this->redis->hLen($key)); + $this->assertFalse($this->redis->hKeys($key)); + $this->assertFalse($this->redis->hVals($key)); + $this->assertFalse($this->redis->hGetAll($key)); } public function testDifferentTypeList() { @@ -4674,56 +4936,56 @@ public function testDifferentTypeList() { $this->assertEquals(1, $this->redis->lPush($key, 'value')); // string I/F - $this->assertEquals(FALSE, $this->redis->get($key)); - $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); - $this->assertEquals(FALSE, $this->redis->append($key, 'append')); - $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); + $this->assertFalse($this->redis->get($key)); + $this->assertFalse($this->redis->getset($key, 'value2')); + $this->assertFalse($this->redis->append($key, 'append')); + $this->assertFalse($this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + $this->assertFalse($this->redis->incr($key)); + $this->assertFalse($this->redis->incrBy($key, 1)); + $this->assertFalse($this->redis->decr($key)); + $this->assertFalse($this->redis->decrBy($key, 1)); // sets I/F - $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sPop($key)); - $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->scard($key)); - $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7')); - $this->assertEquals(FALSE, $this->redis->sMembers($key)); - $this->assertEquals(FALSE, $this->redis->sRandMember($key)); + $this->assertFalse($this->redis->sAdd($key, 'sValue1')); + $this->assertFalse($this->redis->srem($key, 'sValue1')); + $this->assertFalse($this->redis->sPop($key)); + $this->assertFalse($this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); + $this->assertFalse($this->redis->scard($key)); + $this->assertFalse($this->redis->sismember($key, 'sValue1')); + $this->assertFalse($this->redis->sInter($key, $dkey . 'skey2')); + $this->assertFalse($this->redis->sUnion($key, $dkey . 'skey4')); + $this->assertFalse($this->redis->sDiff($key, $dkey . 'skey7')); + $this->assertFalse($this->redis->sMembers($key)); + $this->assertFalse($this->redis->sRandMember($key)); // sorted sets I/F - $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zAdd($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRem($key, 'zValue1')); + $this->assertFalse($this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRevRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRange($key, 0, -1)); + $this->assertFalse($this->redis->zRevRange($key, 0, -1)); + $this->assertFalse($this->redis->zRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zCount($key, 0, -1)); + $this->assertFalse($this->redis->zCard($key)); + $this->assertFalse($this->redis->zScore($key, 'zValue1')); + $this->assertFalse($this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertFalse($this->redis->zRemRangeByScore($key, 1, 2)); // hash I/F - $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); - $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1')); - $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1'])); - $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1'])); - $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1)); - $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2')); - $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2')); - $this->assertEquals(FALSE, $this->redis->hLen($key)); - $this->assertEquals(FALSE, $this->redis->hKeys($key)); - $this->assertEquals(FALSE, $this->redis->hVals($key)); - $this->assertEquals(FALSE, $this->redis->hGetAll($key)); + $this->assertFalse($this->redis->hSet($key, 'key1', 'value1')); + $this->assertFalse($this->redis->hGet($key, 'key1')); + $this->assertFalse($this->redis->hMGet($key, ['key1'])); + $this->assertFalse($this->redis->hMSet($key, ['key1' => 'value1'])); + $this->assertFalse($this->redis->hIncrBy($key, 'key2', 1)); + $this->assertFalse($this->redis->hExists($key, 'key2')); + $this->assertFalse($this->redis->hDel($key, 'key2')); + $this->assertFalse($this->redis->hLen($key)); + $this->assertFalse($this->redis->hKeys($key)); + $this->assertFalse($this->redis->hVals($key)); + $this->assertFalse($this->redis->hGetAll($key)); } public function testDifferentTypeSet() { @@ -4733,57 +4995,57 @@ public function testDifferentTypeSet() { $this->assertEquals(1, $this->redis->sAdd($key, 'value')); // string I/F - $this->assertEquals(FALSE, $this->redis->get($key)); - $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); - $this->assertEquals(FALSE, $this->redis->append($key, 'append')); - $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); + $this->assertFalse($this->redis->get($key)); + $this->assertFalse($this->redis->getset($key, 'value2')); + $this->assertFalse($this->redis->append($key, 'append')); + $this->assertFalse($this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + $this->assertFalse($this->redis->incr($key)); + $this->assertFalse($this->redis->incrBy($key, 1)); + $this->assertFalse($this->redis->decr($key)); + $this->assertFalse($this->redis->decrBy($key, 1)); // lists I/F - $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue')); - $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue')); - $this->assertEquals(FALSE, $this->redis->lLen($key)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->rPop($key)); - $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1')); + $this->assertFalse($this->redis->rPush($key, 'lvalue')); + $this->assertFalse($this->redis->lPush($key, 'lvalue')); + $this->assertFalse($this->redis->lLen($key)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->lrange($key, 0, -1)); + $this->assertFalse($this->redis->lTrim($key, 0, 1)); + $this->assertFalse($this->redis->lIndex($key, 0)); + $this->assertFalse($this->redis->lSet($key, 0, 'newValue')); + $this->assertFalse($this->redis->lrem($key, 'lvalue', 1)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->rPop($key)); + $this->assertFalse($this->redis->rPoplPush($key, $dkey . 'lkey1')); // sorted sets I/F - $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zAdd($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRem($key, 'zValue1')); + $this->assertFalse($this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRevRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRange($key, 0, -1)); + $this->assertFalse($this->redis->zRevRange($key, 0, -1)); + $this->assertFalse($this->redis->zRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zCount($key, 0, -1)); + $this->assertFalse($this->redis->zCard($key)); + $this->assertFalse($this->redis->zScore($key, 'zValue1')); + $this->assertFalse($this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertFalse($this->redis->zRemRangeByScore($key, 1, 2)); // hash I/F - $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); - $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1')); - $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1'])); - $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1'])); - $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1)); - $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2')); - $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2')); - $this->assertEquals(FALSE, $this->redis->hLen($key)); - $this->assertEquals(FALSE, $this->redis->hKeys($key)); - $this->assertEquals(FALSE, $this->redis->hVals($key)); - $this->assertEquals(FALSE, $this->redis->hGetAll($key)); + $this->assertFalse($this->redis->hSet($key, 'key1', 'value1')); + $this->assertFalse($this->redis->hGet($key, 'key1')); + $this->assertFalse($this->redis->hMGet($key, ['key1'])); + $this->assertFalse($this->redis->hMSet($key, ['key1' => 'value1'])); + $this->assertFalse($this->redis->hIncrBy($key, 'key2', 1)); + $this->assertFalse($this->redis->hExists($key, 'key2')); + $this->assertFalse($this->redis->hDel($key, 'key2')); + $this->assertFalse($this->redis->hLen($key)); + $this->assertFalse($this->redis->hKeys($key)); + $this->assertFalse($this->redis->hVals($key)); + $this->assertFalse($this->redis->hGetAll($key)); } public function testDifferentTypeSortedSet() { @@ -4794,55 +5056,55 @@ public function testDifferentTypeSortedSet() { $this->assertEquals(1, $this->redis->zAdd($key, 0, 'value')); // string I/F - $this->assertEquals(FALSE, $this->redis->get($key)); - $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); - $this->assertEquals(FALSE, $this->redis->append($key, 'append')); - $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); + $this->assertFalse($this->redis->get($key)); + $this->assertFalse($this->redis->getset($key, 'value2')); + $this->assertFalse($this->redis->append($key, 'append')); + $this->assertFalse($this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + $this->assertFalse($this->redis->incr($key)); + $this->assertFalse($this->redis->incrBy($key, 1)); + $this->assertFalse($this->redis->decr($key)); + $this->assertFalse($this->redis->decrBy($key, 1)); // lists I/F - $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue')); - $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue')); - $this->assertEquals(FALSE, $this->redis->lLen($key)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->rPop($key)); - $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1')); + $this->assertFalse($this->redis->rPush($key, 'lvalue')); + $this->assertFalse($this->redis->lPush($key, 'lvalue')); + $this->assertFalse($this->redis->lLen($key)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->lrange($key, 0, -1)); + $this->assertFalse($this->redis->lTrim($key, 0, 1)); + $this->assertFalse($this->redis->lIndex($key, 0)); + $this->assertFalse($this->redis->lSet($key, 0, 'newValue')); + $this->assertFalse($this->redis->lrem($key, 'lvalue', 1)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->rPop($key)); + $this->assertFalse($this->redis->rPoplPush($key, $dkey . 'lkey1')); // sets I/F - $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sPop($key)); - $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->scard($key)); - $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7')); - $this->assertEquals(FALSE, $this->redis->sMembers($key)); - $this->assertEquals(FALSE, $this->redis->sRandMember($key)); + $this->assertFalse($this->redis->sAdd($key, 'sValue1')); + $this->assertFalse($this->redis->srem($key, 'sValue1')); + $this->assertFalse($this->redis->sPop($key)); + $this->assertFalse($this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); + $this->assertFalse($this->redis->scard($key)); + $this->assertFalse($this->redis->sismember($key, 'sValue1')); + $this->assertFalse($this->redis->sInter($key, $dkey . 'skey2')); + $this->assertFalse($this->redis->sUnion($key, $dkey . 'skey4')); + $this->assertFalse($this->redis->sDiff($key, $dkey . 'skey7')); + $this->assertFalse($this->redis->sMembers($key)); + $this->assertFalse($this->redis->sRandMember($key)); // hash I/F - $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1')); - $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1')); - $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1'])); - $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1'])); - $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1)); - $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2')); - $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2')); - $this->assertEquals(FALSE, $this->redis->hLen($key)); - $this->assertEquals(FALSE, $this->redis->hKeys($key)); - $this->assertEquals(FALSE, $this->redis->hVals($key)); - $this->assertEquals(FALSE, $this->redis->hGetAll($key)); + $this->assertFalse($this->redis->hSet($key, 'key1', 'value1')); + $this->assertFalse($this->redis->hGet($key, 'key1')); + $this->assertFalse($this->redis->hMGet($key, ['key1'])); + $this->assertFalse($this->redis->hMSet($key, ['key1' => 'value1'])); + $this->assertFalse($this->redis->hIncrBy($key, 'key2', 1)); + $this->assertFalse($this->redis->hExists($key, 'key2')); + $this->assertFalse($this->redis->hDel($key, 'key2')); + $this->assertFalse($this->redis->hLen($key)); + $this->assertFalse($this->redis->hKeys($key)); + $this->assertFalse($this->redis->hVals($key)); + $this->assertFalse($this->redis->hGetAll($key)); } public function testDifferentTypeHash() { @@ -4853,131 +5115,229 @@ public function testDifferentTypeHash() { $this->assertEquals(1, $this->redis->hSet($key, 'key', 'value')); // string I/F - $this->assertEquals(FALSE, $this->redis->get($key)); - $this->assertEquals(FALSE, $this->redis->getset($key, 'value2')); - $this->assertEquals(FALSE, $this->redis->append($key, 'append')); - $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8)); + $this->assertFalse($this->redis->get($key)); + $this->assertFalse($this->redis->getset($key, 'value2')); + $this->assertFalse($this->redis->append($key, 'append')); + $this->assertFalse($this->redis->getRange($key, 0, 8)); $this->assertEquals([FALSE], $this->redis->mget([$key])); - $this->assertEquals(FALSE, $this->redis->incr($key)); - $this->assertEquals(FALSE, $this->redis->incrBy($key, 1)); - $this->assertEquals(FALSE, $this->redis->decr($key)); - $this->assertEquals(FALSE, $this->redis->decrBy($key, 1)); + $this->assertFalse($this->redis->incr($key)); + $this->assertFalse($this->redis->incrBy($key, 1)); + $this->assertFalse($this->redis->decr($key)); + $this->assertFalse($this->redis->decrBy($key, 1)); // lists I/F - $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue')); - $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue')); - $this->assertEquals(FALSE, $this->redis->lLen($key)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1)); - $this->assertEquals(FALSE, $this->redis->lIndex($key, 0)); - $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue")); - $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1)); - $this->assertEquals(FALSE, $this->redis->lPop($key)); - $this->assertEquals(FALSE, $this->redis->rPop($key)); - $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1')); + $this->assertFalse($this->redis->rPush($key, 'lvalue')); + $this->assertFalse($this->redis->lPush($key, 'lvalue')); + $this->assertFalse($this->redis->lLen($key)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->lrange($key, 0, -1)); + $this->assertFalse($this->redis->lTrim($key, 0, 1)); + $this->assertFalse($this->redis->lIndex($key, 0)); + $this->assertFalse($this->redis->lSet($key, 0, 'newValue')); + $this->assertFalse($this->redis->lrem($key, 'lvalue', 1)); + $this->assertFalse($this->redis->lPop($key)); + $this->assertFalse($this->redis->rPop($key)); + $this->assertFalse($this->redis->rPoplPush($key, $dkey . 'lkey1')); // sets I/F - $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sPop($key)); - $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); - $this->assertEquals(FALSE, $this->redis->scard($key)); - $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1')); - $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey . 'skey2')); - $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4')); - $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7')); - $this->assertEquals(FALSE, $this->redis->sMembers($key)); - $this->assertEquals(FALSE, $this->redis->sRandMember($key)); + $this->assertFalse($this->redis->sAdd($key, 'sValue1')); + $this->assertFalse($this->redis->srem($key, 'sValue1')); + $this->assertFalse($this->redis->sPop($key)); + $this->assertFalse($this->redis->sMove($key, $dkey . 'skey1', 'sValue1')); + $this->assertFalse($this->redis->scard($key)); + $this->assertFalse($this->redis->sismember($key, 'sValue1')); + $this->assertFalse($this->redis->sInter($key, $dkey . 'skey2')); + $this->assertFalse($this->redis->sUnion($key, $dkey . 'skey4')); + $this->assertFalse($this->redis->sDiff($key, $dkey . 'skey7')); + $this->assertFalse($this->redis->sMembers($key)); + $this->assertFalse($this->redis->sRandMember($key)); // sorted sets I/F - $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1)); - $this->assertEquals(FALSE, $this->redis->zCard($key)); - $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1')); - $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2)); - $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zAdd($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRem($key, 'zValue1')); + $this->assertFalse($this->redis->zIncrBy($key, 1, 'zValue1')); + $this->assertFalse($this->redis->zRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRevRank($key, 'zValue1')); + $this->assertFalse($this->redis->zRange($key, 0, -1)); + $this->assertFalse($this->redis->zRevRange($key, 0, -1)); + $this->assertFalse($this->redis->zRangeByScore($key, 1, 2)); + $this->assertFalse($this->redis->zCount($key, 0, -1)); + $this->assertFalse($this->redis->zCard($key)); + $this->assertFalse($this->redis->zScore($key, 'zValue1')); + $this->assertFalse($this->redis->zRemRangeByRank($key, 1, 2)); + $this->assertFalse($this->redis->zRemRangeByScore($key, 1, 2)); } public function testSerializerPHP() { $this->checkSerializer(Redis::SERIALIZER_PHP); // with prefix - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->checkSerializer(Redis::SERIALIZER_PHP); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } - public function testSerializerIGBinary() { - if(defined('Redis::SERIALIZER_IGBINARY')) { - $this->checkSerializer(Redis::SERIALIZER_IGBINARY); - - // with prefix - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); - $this->checkSerializer(Redis::SERIALIZER_IGBINARY); - $this->redis->setOption(Redis::OPT_PREFIX, ""); - - /* Test our igbinary header check logic. The check allows us to do - simple INCR type operations even with the serializer enabled, and - should also protect against igbinary-like data from being erroneously - deserialized */ - $this->redis->del('incrkey'); - - $this->redis->set('spoof-1', "\x00\x00\x00\x00"); - $this->redis->set('spoof-2', "\x00\x00\x00\x00bad-version1"); - $this->redis->set('spoof-3', "\x00\x00\x00\x05bad-version2"); - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); - - $this->assertEquals(16, $this->redis->incrby('incrkey', 16)); - $this->assertEquals('16', $this->redis->get('incrkey')); - - $this->assertEquals("\x00\x00\x00\x00", $this->redis->get('spoof-1')); - $this->assertEquals("\x00\x00\x00\x00bad-version1", $this->redis->get('spoof-2')); - $this->assertEquals("\x00\x00\x00\x05bad-version2", $this->redis->get('spoof-3')); - $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); + private function cartesianProduct(array $arrays) { + $result = [[]]; + + foreach ($arrays as $array) { + $append = []; + foreach ($result as $product) { + foreach ($array as $item) { + $newProduct = $product; + $newProduct[] = $item; + $append[] = $newProduct; + } + } - $this->redis->del('incrkey', 'spoof-1', 'spoof-2', 'spoof-3'); + $result = $append; } + + return $result; } - public function testSerializerMsgPack() { - if(defined('Redis::SERIALIZER_MSGPACK')) { - $this->checkSerializer(Redis::SERIALIZER_MSGPACK); + public function testIgnoreNumbers() { + $combinations = $this->cartesianProduct([ + [false, true, false], + $this->getSerializers(), + $this->getCompressors(), + ]); + + foreach ($combinations as [$ignore, $serializer, $compression]) { + $this->redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, $ignore); + $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); + $this->redis->setOption(Redis::OPT_COMPRESSION, $compression); + + $this->assertIsInt($this->redis->del('answer')); + $this->assertIsInt($this->redis->del('hash')); + + $transparent = $compression === Redis::COMPRESSION_NONE && + ($serializer === Redis::SERIALIZER_NONE || + $serializer === Redis::SERIALIZER_JSON); + + if ($transparent || $ignore) { + $expected_answer = 42; + $expected_pi = 3.14; + } else { + $expected_answer = false; + $expected_pi = false; + } + + $this->assertTrue($this->redis->set('answer', 32)); + $this->assertEquals($expected_answer, $this->redis->incr('answer', 10)); + + $this->assertTrue($this->redis->set('pi', 3.04)); + $this->assertEquals($expected_pi, $this->redis->incrByFloat('pi', 0.1)); + + $this->assertEquals(1, $this->redis->hset('hash', 'answer', 32)); + $this->assertEquals($expected_answer, $this->redis->hIncrBy('hash', 'answer', 10)); + + $this->assertEquals(1, $this->redis->hset('hash', 'pi', 3.04)); + $this->assertEquals($expected_pi, $this->redis->hIncrByFloat('hash', 'pi', 0.1)); + } + + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); + $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); + $this->redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, false); + } + + function testIgnoreNumbersReturnTypes() { + $combinations = $this->cartesianProduct([ + [false, true], + array_filter($this->getSerializers(), function($s) { + return $s !== Redis::SERIALIZER_NONE; + }), + array_filter($this->getCompressors(), function($c) { + return $c !== Redis::COMPRESSION_NONE; + }), + ]); + + foreach ($combinations as [$ignore, $serializer, $compression]) { + $this->redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, $ignore); + $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); + $this->redis->setOption(Redis::OPT_COMPRESSION, $compression); + + foreach ([42, 3.14] as $value) { + $this->assertTrue($this->redis->set('key', $value)); - // with prefix - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); - $this->checkSerializer(Redis::SERIALIZER_MSGPACK); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + /* There's a known issue in the PHP JSON parser, which + can stringify numbers. Unclear the root cause */ + if ($serializer == Redis::SERIALIZER_JSON) { + $this->assertEqualsWeak($value, $this->redis->get('key')); + } else { + $this->assertEquals($value, $this->redis->get('key')); + } + } } + + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); + $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); + $this->redis->setOption(Redis::OPT_PACK_IGNORE_NUMBERS, false); } - public function testSerializerJSON() - { + public function testSerializerIGBinary() { + if ( ! defined('Redis::SERIALIZER_IGBINARY')) + $this->markTestSkipped('Redis::SERIALIZER_IGBINARY is not defined'); + + $this->checkSerializer(Redis::SERIALIZER_IGBINARY); + + // with prefix + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); + $this->checkSerializer(Redis::SERIALIZER_IGBINARY); + $this->redis->setOption(Redis::OPT_PREFIX, ''); + + /* Test our igbinary header check logic. The check allows us to do + simple INCR type operations even with the serializer enabled, and + should also protect against igbinary-like data from being erroneously + deserialized */ + $this->redis->del('incrkey'); + + $this->redis->set('spoof-1', "\x00\x00\x00\x00"); + $this->redis->set('spoof-2', "\x00\x00\x00\x00bad-version1"); + $this->redis->set('spoof-3', "\x00\x00\x00\x05bad-version2"); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); + + $this->assertEquals(16, $this->redis->incrby('incrkey', 16)); + $this->assertKeyEquals('16', 'incrkey'); + + $this->assertKeyEquals("\x00\x00\x00\x00", 'spoof-1'); + $this->assertKeyEquals("\x00\x00\x00\x00bad-version1", 'spoof-2'); + $this->assertKeyEquals("\x00\x00\x00\x05bad-version2", 'spoof-3'); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); + + $this->redis->del('incrkey', 'spoof-1', 'spoof-2', 'spoof-3'); + } + + public function testSerializerMsgPack() { + if ( ! defined('Redis::SERIALIZER_MSGPACK')) + $this->markTestSkipped('Redis::SERIALIZER_MSGPACK is not defined'); + + $this->checkSerializer(Redis::SERIALIZER_MSGPACK); + + // with prefix + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); + $this->checkSerializer(Redis::SERIALIZER_MSGPACK); + $this->redis->setOption(Redis::OPT_PREFIX, ''); + } + + public function testSerializerJSON() { $this->checkSerializer(Redis::SERIALIZER_JSON); // with prefix - $this->redis->setOption(Redis::OPT_PREFIX, "test:"); + $this->redis->setOption(Redis::OPT_PREFIX, 'test:'); $this->checkSerializer(Redis::SERIALIZER_JSON); - $this->redis->setOption(Redis::OPT_PREFIX, ""); + $this->redis->setOption(Redis::OPT_PREFIX, ''); } private function checkSerializer($mode) { - $this->redis->del('key'); - $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // default + $this->assertEquals(Redis::SERIALIZER_NONE, $this->redis->getOption(Redis::OPT_SERIALIZER)); // default - $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, $mode) === TRUE); // set ok - $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === $mode); // get ok + $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, $mode)); // set ok + $this->assertEquals($mode, $this->redis->getOption(Redis::OPT_SERIALIZER)); // get ok // lPush, rPush - $a = ['hello world', 42, TRUE, ['' => 1729]]; + $a = ['hello world', 42, true, ['' => 1729]]; $this->redis->del('key'); $this->redis->lPush('key', $a[0]); $this->redis->rPush('key', $a[1]); @@ -4985,67 +5345,67 @@ private function checkSerializer($mode) { $this->redis->rPush('key', $a[3]); // lrange - $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); + $this->assertEquals($a, $this->redis->lrange('key', 0, -1)); // lIndex - $this->assertTrue($a[0] === $this->redis->lIndex('key', 0)); - $this->assertTrue($a[1] === $this->redis->lIndex('key', 1)); - $this->assertTrue($a[2] === $this->redis->lIndex('key', 2)); - $this->assertTrue($a[3] === $this->redis->lIndex('key', 3)); + $this->assertEquals($a[0], $this->redis->lIndex('key', 0)); + $this->assertEquals($a[1], $this->redis->lIndex('key', 1)); + $this->assertEquals($a[2], $this->redis->lIndex('key', 2)); + $this->assertEquals($a[3], $this->redis->lIndex('key', 3)); // lrem - $this->assertTrue($this->redis->lrem('key', $a[3]) === 1); - $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lrange('key', 0, -1)); + $this->assertEquals(1, $this->redis->lrem('key', $a[3])); + $this->assertEquals(array_slice($a, 0, 3), $this->redis->lrange('key', 0, -1)); // lSet $a[0] = ['k' => 'v']; // update - $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0])); - $this->assertTrue($a[0] === $this->redis->lIndex('key', 0)); + $this->assertTrue($this->redis->lSet('key', 0, $a[0])); + $this->assertEquals($a[0], $this->redis->lIndex('key', 0)); // lInsert - $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], [1,2,3]) === 4); - $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], [4,5,6]) === 5); + $this->assertEquals(4, $this->redis->lInsert('key', Redis::BEFORE, $a[0], [1, 2, 3])); + $this->assertEquals(5, $this->redis->lInsert('key', Redis::AFTER, $a[0], [4, 5, 6])); - $a = [[1,2,3], $a[0], [4,5,6], $a[1], $a[2]]; - $this->assertTrue($a === $this->redis->lrange('key', 0, -1)); + $a = [[1, 2, 3], $a[0], [4, 5, 6], $a[1], $a[2]]; + $this->assertEquals($a, $this->redis->lrange('key', 0, -1)); // sAdd $this->redis->del('{set}key'); - $s = [1,'a', [1,2,3], ['k' => 'v']]; + $s = [1,'a', [1, 2, 3], ['k' => 'v']]; - $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[0])); - $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[1])); - $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[2])); - $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[3])); + $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[0])); + $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[1])); + $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[2])); + $this->assertEquals(1, $this->redis->sAdd('{set}key', $s[3])); // variadic sAdd $this->redis->del('k'); - $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c')); - $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); + $this->assertEquals(3, $this->redis->sAdd('k', 'a', 'b', 'c')); + $this->assertEquals(1, $this->redis->sAdd('k', 'a', 'b', 'c', 'd')); // srem - $this->assertTrue(1 === $this->redis->srem('{set}key', $s[3])); - $this->assertTrue(0 === $this->redis->srem('{set}key', $s[3])); + $this->assertEquals(1, $this->redis->srem('{set}key', $s[3])); + $this->assertEquals(0, $this->redis->srem('{set}key', $s[3])); // variadic $this->redis->del('k'); $this->redis->sAdd('k', 'a', 'b', 'c', 'd'); - $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd')); - $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e')); - $this->assertEquals(0, $this->redis->exists('k')); + $this->assertEquals(2, $this->redis->sRem('k', 'a', 'd')); + $this->assertEquals(2, $this->redis->sRem('k', 'b', 'c', 'e')); + $this->assertKeyMissing('k'); // sismember - $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[1])); - $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[2])); - $this->assertTrue(FALSE === $this->redis->sismember('{set}key', $s[3])); + $this->assertTrue($this->redis->sismember('{set}key', $s[0])); + $this->assertTrue($this->redis->sismember('{set}key', $s[1])); + $this->assertTrue($this->redis->sismember('{set}key', $s[2])); + $this->assertFalse($this->redis->sismember('{set}key', $s[3])); unset($s[3]); // sMove $this->redis->del('{set}tmp'); $this->redis->sMove('{set}key', '{set}tmp', $s[0]); - $this->assertTrue(FALSE === $this->redis->sismember('{set}key', $s[0])); - $this->assertTrue(TRUE === $this->redis->sismember('{set}tmp', $s[0])); + $this->assertFalse($this->redis->sismember('{set}key', $s[0])); + $this->assertTrue($this->redis->sismember('{set}tmp', $s[0])); unset($s[0]); // sorted sets @@ -5053,14 +5413,14 @@ private function checkSerializer($mode) { $this->redis->del('key'); // zAdd - $this->assertTrue(1 === $this->redis->zAdd('key', 0, $z[0])); - $this->assertTrue(1 === $this->redis->zAdd('key', 1, $z[1])); - $this->assertTrue(1 === $this->redis->zAdd('key', 2, $z[2])); - $this->assertTrue(1 === $this->redis->zAdd('key', 3, $z[3])); + $this->assertEquals(1, $this->redis->zAdd('key', 0, $z[0])); + $this->assertEquals(1, $this->redis->zAdd('key', 1, $z[1])); + $this->assertEquals(1, $this->redis->zAdd('key', 2, $z[2])); + $this->assertEquals(1, $this->redis->zAdd('key', 3, $z[3])); // zRem - $this->assertTrue(1 === $this->redis->zRem('key', $z[3])); - $this->assertTrue(0 === $this->redis->zRem('key', $z[3])); + $this->assertEquals(1, $this->redis->zRem('key', $z[3])); + $this->assertEquals(0, $this->redis->zRem('key', $z[3])); unset($z[3]); // variadic @@ -5068,86 +5428,86 @@ private function checkSerializer($mode) { $this->redis->zAdd('k', 0, 'a'); $this->redis->zAdd('k', 1, 'b'); $this->redis->zAdd('k', 2, 'c'); - $this->assertTrue(2 === $this->redis->zRem('k', 'a', 'c')); - $this->assertTrue(1.0 === $this->redis->zScore('k', 'b')); - $this->assertTrue($this->redis->zRange('k', 0, -1, true) == ['b' => 1.0]); + $this->assertEquals(2, $this->redis->zRem('k', 'a', 'c')); + $this->assertEquals(1.0, $this->redis->zScore('k', 'b')); + $this->assertEquals(['b' => 1.0], $this->redis->zRange('k', 0, -1, true)); // zRange - $this->assertTrue($z === $this->redis->zRange('key', 0, -1)); + $this->assertEquals($z, $this->redis->zRange('key', 0, -1)); // zScore - $this->assertTrue(0.0 === $this->redis->zScore('key', $z[0])); - $this->assertTrue(1.0 === $this->redis->zScore('key', $z[1])); - $this->assertTrue(2.0 === $this->redis->zScore('key', $z[2])); + $this->assertEquals(0.0, $this->redis->zScore('key', $z[0])); + $this->assertEquals(1.0, $this->redis->zScore('key', $z[1])); + $this->assertEquals(2.0, $this->redis->zScore('key', $z[2])); // zRank - $this->assertTrue(0 === $this->redis->zRank('key', $z[0])); - $this->assertTrue(1 === $this->redis->zRank('key', $z[1])); - $this->assertTrue(2 === $this->redis->zRank('key', $z[2])); + $this->assertEquals(0, $this->redis->zRank('key', $z[0])); + $this->assertEquals(1, $this->redis->zRank('key', $z[1])); + $this->assertEquals(2, $this->redis->zRank('key', $z[2])); // zRevRank - $this->assertTrue(2 === $this->redis->zRevRank('key', $z[0])); - $this->assertTrue(1 === $this->redis->zRevRank('key', $z[1])); - $this->assertTrue(0 === $this->redis->zRevRank('key', $z[2])); + $this->assertEquals(2, $this->redis->zRevRank('key', $z[0])); + $this->assertEquals(1, $this->redis->zRevRank('key', $z[1])); + $this->assertEquals(0, $this->redis->zRevRank('key', $z[2])); // zIncrBy - $this->assertTrue(3.0 === $this->redis->zIncrBy('key', 1.0, $z[2])); - $this->assertTrue(3.0 === $this->redis->zScore('key', $z[2])); + $this->assertEquals(3.0, $this->redis->zIncrBy('key', 1.0, $z[2])); + $this->assertEquals(3.0, $this->redis->zScore('key', $z[2])); - $this->assertTrue(5.0 === $this->redis->zIncrBy('key', 2.0, $z[2])); - $this->assertTrue(5.0 === $this->redis->zScore('key', $z[2])); + $this->assertEquals(5.0, $this->redis->zIncrBy('key', 2.0, $z[2])); + $this->assertEquals(5.0, $this->redis->zScore('key', $z[2])); - $this->assertTrue(2.0 === $this->redis->zIncrBy('key', -3.0, $z[2])); - $this->assertTrue(2.0 === $this->redis->zScore('key', $z[2])); + $this->assertEquals(2.0, $this->redis->zIncrBy('key', -3.0, $z[2])); + $this->assertEquals(2.0, $this->redis->zScore('key', $z[2])); // mset $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']]; - $this->assertTrue(TRUE === $this->redis->mset($a)); - foreach($a as $k => $v) { - $this->assertTrue($this->redis->get($k) === $v); + $this->assertTrue($this->redis->mset($a)); + foreach ($a as $k => $v) { + $this->assertKeyEquals($v, $k); } - $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']]; + $a = ['f0' => 1, 'f1' => 42, 'f2' => NULL, 'f3' => FALSE, 'f4' => ['a' => 'b']]; // hSet - $this->redis->del('key'); - foreach($a as $k => $v) { - $this->assertTrue(1 === $this->redis->hSet('key', $k, $v)); + $this->redis->del('hash'); + foreach ($a as $k => $v) { + $this->assertEquals(1, $this->redis->hSet('hash', $k, $v)); } // hGet - foreach($a as $k => $v) { - $this->assertTrue($v === $this->redis->hGet('key', $k)); + foreach ($a as $k => $v) { + $this->assertEquals($v, $this->redis->hGet('hash', $k)); } // hGetAll - $this->assertTrue($a === $this->redis->hGetAll('key')); - $this->assertTrue(TRUE === $this->redis->hExists('key', 'k0')); - $this->assertTrue(TRUE === $this->redis->hExists('key', 'k1')); - $this->assertTrue(TRUE === $this->redis->hExists('key', 'k2')); - $this->assertTrue(TRUE === $this->redis->hExists('key', 'k3')); - $this->assertTrue(TRUE === $this->redis->hExists('key', 'k4')); + $this->assertEquals($a, $this->redis->hGetAll('hash')); + $this->assertTrue($this->redis->hExists('hash', 'f0')); + $this->assertTrue($this->redis->hExists('hash', 'f1')); + $this->assertTrue($this->redis->hExists('hash', 'f2')); + $this->assertTrue($this->redis->hExists('hash', 'f3')); + $this->assertTrue($this->redis->hExists('hash', 'f4')); // hMSet - $this->redis->del('key'); - $this->redis->hMSet('key', $a); - foreach($a as $k => $v) { - $this->assertTrue($v === $this->redis->hGet('key', $k)); + $this->redis->del('hash'); + $this->redis->hMSet('hash', $a); + foreach ($a as $k => $v) { + $this->assertEquals($v, $this->redis->hGet('hash', $k)); } // hMget - $hmget = $this->redis->hMget('key', array_keys($a)); - foreach($hmget as $k => $v) { - $this->assertTrue($v === $a[$k]); + $hmget = $this->redis->hMget('hash', array_keys($a)); + foreach ($hmget as $k => $v) { + $this->assertEquals($a[$k], $v); } - // getMultiple + // mGet $this->redis->set('a', NULL); $this->redis->set('b', FALSE); $this->redis->set('c', 42); $this->redis->set('d', ['x' => 'y']); - $this->assertTrue([NULL, FALSE, 42, ['x' => 'y']] === $this->redis->mGet(['a', 'b', 'c', 'd'])); + $this->assertEquals([NULL, FALSE, 42, ['x' => 'y']], $this->redis->mGet(['a', 'b', 'c', 'd'])); // pipeline if ($this->havePipeline()) { @@ -5155,64 +5515,61 @@ private function checkSerializer($mode) { } // multi-exec - $this->sequence(Redis::MULTI); + if ($this->haveMulti()) { + $this->sequence(Redis::MULTI); + } - // keys - $this->assertTrue(is_array($this->redis->keys('*'))); + $this->assertIsArray($this->redis->keys('*')); // issue #62, hgetall $this->redis->del('hash1'); - $this->redis->hSet('hash1','data', 'test 1'); - $this->redis->hSet('hash1','session_id', 'test 2'); + $this->redis->hSet('hash1', 'data', 'test 1'); + $this->redis->hSet('hash1', 'session_id', 'test 2'); $data = $this->redis->hGetAll('hash1'); - $this->assertTrue($data['data'] === 'test 1'); - $this->assertTrue($data['session_id'] === 'test 2'); + $this->assertEquals('test 1', $data['data']); + $this->assertEquals('test 2', $data['session_id']); // issue #145, serializer with objects. $this->redis->set('x', [new stdClass, new stdClass]); $x = $this->redis->get('x'); - $this->assertTrue(is_array($x)); + $this->assertIsArray($x); if ($mode === Redis::SERIALIZER_JSON) { - $this->assertTrue(is_array($x[0])); - $this->assertTrue(is_array($x[1])); + $this->assertIsArray($x[0]); + $this->assertIsArray($x[1]); } else { - $this->assertTrue(is_object($x[0]) && get_class($x[0]) === 'stdClass'); - $this->assertTrue(is_object($x[1]) && get_class($x[1]) === 'stdClass'); + $this->assertIsObject($x[0], 'stdClass'); + $this->assertIsObject($x[1], 'stdClass'); } // revert - $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE) === TRUE); // set ok - $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE); // get ok + $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE)); // set ok + $this->assertEquals(Redis::SERIALIZER_NONE, $this->redis->getOption(Redis::OPT_SERIALIZER)); // get ok } - public function testCompressionLZF() - { - if (!defined('Redis::COMPRESSION_LZF')) { + public function testCompressionLZF() { + if ( ! defined('Redis::COMPRESSION_LZF')) $this->markTestSkipped(); - } /* Don't crash on improperly compressed LZF data */ $payload = 'not-actually-lzf-compressed'; $this->redis->set('badlzf', $payload); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_LZF); - $this->assertEquals($payload, $this->redis->get('badlzf')); + $this->assertKeyEquals($payload, 'badlzf'); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); $this->checkCompression(Redis::COMPRESSION_LZF, 0); } - public function testCompressionZSTD() - { - if (!defined('Redis::COMPRESSION_ZSTD')) { + public function testCompressionZSTD() { + if ( ! defined('Redis::COMPRESSION_ZSTD')) $this->markTestSkipped(); - } /* Issue 1936 regression. Make sure we don't overflow on bad data */ $this->redis->del('badzstd'); $this->redis->set('badzstd', '123'); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_ZSTD); - $this->assertEquals('123', $this->redis->get('badzstd')); + $this->assertKeyEquals('123', 'badzstd'); $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); $this->checkCompression(Redis::COMPRESSION_ZSTD, 0); @@ -5220,36 +5577,48 @@ public function testCompressionZSTD() } - public function testCompressionLZ4() - { - if (!defined('Redis::COMPRESSION_LZ4')) { + public function testCompressionLZ4() { + if ( ! defined('Redis::COMPRESSION_LZ4')) $this->markTestSkipped(); - } + $this->checkCompression(Redis::COMPRESSION_LZ4, 0); $this->checkCompression(Redis::COMPRESSION_LZ4, 9); } - private function checkCompression($mode, $level) - { - $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION, $mode) === TRUE); // set ok - $this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION) === $mode); // get ok + private function checkCompression($mode, $level) { + $set_cmp = $this->redis->setOption(Redis::OPT_COMPRESSION, $mode); + $this->assertTrue($set_cmp); + if ($set_cmp !== true) + return; + + $get_cmp = $this->redis->getOption(Redis::OPT_COMPRESSION); + $this->assertEquals($get_cmp, $mode); + if ($get_cmp !== $mode) + return; + + $set_lvl = $this->redis->setOption(Redis::OPT_COMPRESSION_LEVEL, $level); + $this->assertTrue($set_lvl); + if ($set_lvl !== true) + return; - $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION_LEVEL, $level) === TRUE); - $this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION_LEVEL) === $level); + $get_lvl = $this->redis->getOption(Redis::OPT_COMPRESSION_LEVEL); + $this->assertEquals($get_lvl, $level); + if ($get_lvl !== $level) + return; $val = 'xxxxxxxxxx'; $this->redis->set('key', $val); - $this->assertEquals($val, $this->redis->get('key')); + $this->assertKeyEquals($val, 'key'); /* Empty data */ $this->redis->set('key', ''); - $this->assertEquals('', $this->redis->get('key')); + $this->assertKeyEquals('', 'key'); /* Iterate through class sizes */ for ($i = 1; $i <= 65536; $i *= 2) { foreach ([str_repeat('A', $i), random_bytes($i)] as $val) { $this->redis->set('key', $val); - $this->assertEquals($val, $this->redis->get('key')); + $this->assertKeyEquals($val, 'key'); } } @@ -5260,9 +5629,8 @@ private function checkCompression($mode, $level) public function testDumpRestore() { - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } $this->redis->del('foo'); $this->redis->del('bar'); @@ -5281,8 +5649,8 @@ public function testDumpRestore() { $this->assertTrue($this->redis->restore('bar', 0, $d_foo)); // Now check that the keys have switched - $this->assertTrue($this->redis->get('foo') === 'this-is-bar'); - $this->assertTrue($this->redis->get('bar') === 'this-is-foo'); + $this->assertKeyEquals('this-is-bar', 'foo'); + $this->assertKeyEquals('this-is-foo', 'bar'); /* Test that we can REPLACE a key */ $this->assertTrue($this->redis->set('foo', 'some-value')); @@ -5290,11 +5658,11 @@ public function testDumpRestore() { /* Ensure we can set an absolute TTL */ $this->assertTrue($this->redis->restore('foo', time() + 10, $d_bar, ['REPLACE', 'ABSTTL'])); - $this->assertTrue($this->redis->ttl('foo') <= 10); + $this->assertLTE(10, $this->redis->ttl('foo')); /* Ensure we can set an IDLETIME */ $this->assertTrue($this->redis->restore('foo', 0, $d_bar, ['REPLACE', 'IDLETIME' => 200])); - $this->assertTrue($this->redis->object('idletime', 'foo') > 100); + $this->assertGT(100, $this->redis->object('idletime', 'foo')); /* We can't neccissarily check this depending on LRU policy, but at least attempt to use the FREQ option */ @@ -5306,17 +5674,17 @@ public function testDumpRestore() { public function testGetLastError() { // We shouldn't have any errors now - $this->assertTrue($this->redis->getLastError() === NULL); + $this->assertNull($this->redis->getLastError()); // test getLastError with a regular command $this->redis->set('x', 'a'); $this->assertFalse($this->redis->incr('x')); $incrError = $this->redis->getLastError(); - $this->assertTrue(strlen($incrError) > 0); + $this->assertGT(0, strlen($incrError)); // clear error $this->redis->clearLastError(); - $this->assertTrue($this->redis->getLastError() === NULL); + $this->assertNull($this->redis->getLastError()); } // Helper function to compare nested results -- from the php.net array_diff page, I believe @@ -5344,10 +5712,8 @@ private function array_diff_recursive($aArray1, $aArray2) { } public function testScript() { - - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } // Flush any scripts we have $this->assertTrue($this->redis->script('flush')); @@ -5362,13 +5728,13 @@ public function testScript() { // None should exist $result = $this->redis->script('exists', $s1_sha, $s2_sha, $s3_sha); - $this->assertTrue(is_array($result) && count($result) == 3); + $this->assertIsArray($result, 3); $this->assertTrue(is_array($result) && count(array_filter($result)) == 0); // Load them up - $this->assertTrue($this->redis->script('load', $s1_src) == $s1_sha); - $this->assertTrue($this->redis->script('load', $s2_src) == $s2_sha); - $this->assertTrue($this->redis->script('load', $s3_src) == $s3_sha); + $this->assertEquals($s1_sha, $this->redis->script('load', $s1_src)); + $this->assertEquals($s2_sha, $this->redis->script('load', $s2_src)); + $this->assertEquals($s3_sha, $this->redis->script('load', $s3_src)); // They should all exist $result = $this->redis->script('exists', $s1_sha, $s2_sha, $s3_sha); @@ -5376,10 +5742,8 @@ public function testScript() { } public function testEval() { - - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } /* The eval_ro method uses the same underlying handlers as eval so we only need to verify we can call it. */ @@ -5387,9 +5751,9 @@ public function testEval() { $this->assertEquals('1.55', $this->redis->eval_ro("return '1.55'")); // Basic single line response tests - $this->assertTrue(1 == $this->redis->eval('return 1')); - $this->assertTrue(1.55 == $this->redis->eval("return '1.55'")); - $this->assertTrue("hello, world" == $this->redis->eval("return 'hello, world'")); + $this->assertEquals(1, $this->redis->eval('return 1')); + $this->assertEqualsWeak(1.55, $this->redis->eval("return '1.55'")); + $this->assertEquals('hello, world', $this->redis->eval("return 'hello, world'")); /* * Keys to be incorporated into lua results @@ -5412,17 +5776,17 @@ public function testEval() { // Use a script to return our list, and verify its response $list = $this->redis->eval("return redis.call('lrange', KEYS[1], 0, -1)", ['{eval-key}-list'], 1); - $this->assertTrue($list === ['a','b','c']); + $this->assertEquals(['a', 'b', 'c'], $list); // Use a script to return our zset $zset = $this->redis->eval("return redis.call('zrange', KEYS[1], 0, -1)", ['{eval-key}-zset'], 1); - $this->assertTrue($zset == ['d','e','f']); + $this->assertEquals(['d', 'e', 'f'], $zset); // Test an empty MULTI BULK response $this->redis->del('{eval-key}-nolist'); $empty_resp = $this->redis->eval("return redis.call('lrange', '{eval-key}-nolist', 0, -1)", ['{eval-key}-nolist'], 1); - $this->assertTrue(is_array($empty_resp) && empty($empty_resp)); + $this->assertEquals([], $empty_resp); // Now test a nested reply $nested_script = " @@ -5432,7 +5796,7 @@ public function testEval() { redis.call('get', '{eval-key}-str2'), redis.call('lrange', 'not-any-kind-of-list', 0, -1), { - redis.call('zrange','{eval-key}-zset', 0, -1), + redis.call('zrange', '{eval-key}-zset', 0, -1), redis.call('lrange', '{eval-key}-list', 0, -1) } } @@ -5445,15 +5809,18 @@ public function testEval() { 'hello again!', [], [ - ['d','e','f'], - ['a','b','c'] + ['d', 'e', 'f'], + ['a', 'b', 'c'] ] ] ]; // Now run our script, and check our values against each other $eval_result = $this->redis->eval($nested_script, ['{eval-key}-str1', '{eval-key}-str2', '{eval-key}-zset', '{eval-key}-list'], 4); - $this->assertTrue(is_array($eval_result) && count($this->array_diff_recursive($eval_result, $expected)) == 0); + $this->assertTrue( + is_array($eval_result) && + count($this->array_diff_recursive($eval_result, $expected)) == 0 + ); /* * Nested reply wihin a multi/pipeline block @@ -5461,18 +5828,21 @@ public function testEval() { $num_scripts = 10; - $arr_modes = [Redis::MULTI]; - if ($this->havePipeline()) $arr_modes[] = Redis::PIPELINE; + $modes = [Redis::MULTI]; + if ($this->havePipeline()) $modes[] = Redis::PIPELINE; - foreach($arr_modes as $mode) { + foreach ($modes as $mode) { $this->redis->multi($mode); - for($i=0;$i<$num_scripts;$i++) { + for ($i = 0; $i < $num_scripts; $i++) { $this->redis->eval($nested_script, ['{eval-key}-dummy'], 1); } $replies = $this->redis->exec(); - foreach($replies as $reply) { - $this->assertTrue(is_array($reply) && count($this->array_diff_recursive($reply, $expected)) == 0); + foreach ($replies as $reply) { + $this->assertTrue( + is_array($reply) && + count($this->array_diff_recursive($reply, $expected)) == 0 + ); } } @@ -5480,36 +5850,33 @@ public function testEval() { * KEYS/ARGV */ - $args_script = "return {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}"; - $args_args = ['{k}1','{k}2','{k}3','v1','v2','v3']; + $args_script = 'return {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}'; + $args_args = ['{k}1', '{k}2', '{k}3', 'v1', 'v2', 'v3']; $args_result = $this->redis->eval($args_script, $args_args, 3); - $this->assertTrue($args_result === $args_args); + $this->assertEquals($args_args, $args_result); // turn on key prefixing $this->redis->setOption(Redis::OPT_PREFIX, 'prefix:'); $args_result = $this->redis->eval($args_script, $args_args, 3); // Make sure our first three are prefixed - for($i=0;$iassertTrue($args_result[$i] == 'prefix:' . $args_args[$i]); + for ($i = 0; $i < count($args_result); $i++) { + if ($i < 3) { + $this->assertEquals('prefix:' . $args_args[$i], $args_result[$i]); } else { - // Should not be prefixed - $this->assertTrue($args_result[$i] == $args_args[$i]); + $this->assertEquals($args_args[$i], $args_result[$i]); } } } public function testEvalSHA() { - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } // Flush any loaded scripts $this->redis->script('flush'); - // Non existant script (but proper sha1), and a random (not) sha1 string + // Non existent script (but proper sha1), and a random (not) sha1 string $this->assertFalse($this->redis->evalsha(sha1(uniqid()))); $this->assertFalse($this->redis->evalsha('some-random-data')); @@ -5519,9 +5886,9 @@ public function testEvalSHA() { $sha = sha1($scr); // Run it when it doesn't exist, run it with eval, and then run it with sha1 - $this->assertTrue(false === $this->redis->evalsha($scr)); - $this->assertTrue(1 === $this->redis->eval($scr)); - $this->assertTrue(1 === $this->redis->evalsha($sha)); + $this->assertFalse($this->redis->evalsha($scr)); + $this->assertEquals(1, $this->redis->eval($scr)); + $this->assertEquals(1, $this->redis->evalsha($sha)); /* Our evalsha_ro handler is the same as evalsha so just make sure we can invoke the command */ @@ -5529,61 +5896,55 @@ public function testEvalSHA() { $this->assertEquals(1, $this->redis->evalsha_ro($sha)); } + /* Regression test for #2681 where there was undefined behavior when sending + one or more arguments but no keys in cluster mode. */ + public function testEvalNoKeys() { + for ($i = 0; $i < 10; $i++) { + $this->assertEqualsWeak($i, $this->redis->eval("return $i", [42], 0)); + $this->assertEqualsWeak($i, $this->redis->eval('return ARGV[1]', [$i], 0)); + } + } + public function testSerialize() { - $vals = [1, 1.5, 'one', ['here','is','an','array']]; + $vals = [1, 1.5, 'one', ['here', 'is', 'an', 'array']]; // Test with no serialization at all - $this->assertTrue($this->redis->_serialize('test') === 'test'); - $this->assertTrue($this->redis->_serialize(1) === '1'); - $this->assertTrue($this->redis->_serialize([]) === 'Array'); - $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object'); - - $arr_serializers = [Redis::SERIALIZER_PHP]; - if(defined('Redis::SERIALIZER_IGBINARY')) { - $arr_serializers[] = Redis::SERIALIZER_IGBINARY; - } + $this->assertEquals('test', $this->redis->_serialize('test')); + $this->assertEquals('1', $this->redis->_serialize(1)); + $this->assertEquals('Array', $this->redis->_serialize([])); + $this->assertEquals('Object', $this->redis->_serialize(new stdClass)); - if(defined('Redis::SERIALIZER_MSGPACK')) { - $arr_serializers[] = Redis::SERIALIZER_MSGPACK; - } + foreach ($this->getSerializers() as $mode) { + $enc = []; + $dec = []; - foreach($arr_serializers as $mode) { - $arr_enc = []; - $arr_dec = []; - - foreach($vals as $k => $v) { + foreach ($vals as $k => $v) { $enc = $this->redis->_serialize($v); $dec = $this->redis->_unserialize($enc); // They should be the same - $this->assertTrue($enc == $dec); + $this->assertEquals($enc, $dec); } } } public function testUnserialize() { - $vals = [ - 1,1.5,'one',['this','is','an','array'] - ]; - - $serializers = Array(Redis::SERIALIZER_PHP); - - if(defined('Redis::SERIALIZER_IGBINARY')) { - $serializers[] = Redis::SERIALIZER_IGBINARY; - } + $vals = [1, 1.5,'one',['this', 'is', 'an', 'array']]; - if(defined('Redis::SERIALIZER_MSGPACK')) { - $serializers[] = Redis::SERIALIZER_MSGPACK; - } + /* We want to skip SERIALIZER_NONE because strict type checking will + fail on the assertions (which is expected). */ + $serializers = array_filter($this->getSerializers(), function($v) { + return $v != Redis::SERIALIZER_NONE; + }); - foreach($serializers as $mode) { + foreach ($serializers as $mode) { $vals_enc = []; // Pass them through redis so they're serialized - foreach($vals as $key => $val) { + foreach ($vals as $key => $val) { $this->redis->setOption(Redis::OPT_SERIALIZER, $mode); - $key = "key" . ++$key; + $key = 'key' . ++$key; $this->redis->del($key); $this->redis->set($key, $val); @@ -5593,17 +5954,17 @@ public function testUnserialize() { } // Run through our array comparing values - for($i=0;$iredis->setOption(Redis::OPT_SERIALIZER, $mode); - $this->assertTrue($vals[$i] == $this->redis->_unserialize($vals_enc[$i])); + $this->assertEquals($vals[$i], $this->redis->_unserialize($vals_enc[$i])); $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); } } } public function testCompressHelpers() { - $compressors = self::getAvailableCompression(); + $compressors = $this->getCompressors(); $vals = ['foo', 12345, random_bytes(128), '']; @@ -5635,8 +5996,8 @@ public function testPackHelpers() { $this->redis->getOption(Redis::OPT_COMPRESSION) ]; - foreach ($this->serializers as $ser) { - $compressors = self::getAvailableCompression(); + foreach ($this->getSerializers() as $ser) { + $compressors = $this->getCompressors(); foreach ($compressors as $cmp) { $this->redis->setOption(Redis::OPT_SERIALIZER, $ser); $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp); @@ -5667,14 +6028,146 @@ public function testPackHelpers() { $this->redis->setOption(Redis::OPT_COMPRESSION, $oldcmp); } + public function testGetWithMeta() { + $this->redis->del('key'); + $this->assertFalse($this->redis->get('key')); + + $result = $this->redis->getWithMeta('key'); + $this->assertIsArray($result, 2); + $this->assertArrayKeyEquals($result, 0, false); + $this->assertArrayKey($result, 1, function ($metadata) { + $this->assertIsArray($metadata); + $this->assertArrayKeyEquals($metadata, 'length', -1); + return true; + }); + + if ($this->havePipeline()) { + $batch = $this->redis->pipeline() + ->get('key') + ->getWithMeta('key') + ->exec(); + $this->assertIsArray($batch, 2); + $this->assertArrayKeyEquals($batch, 0, false); + $this->assertArrayKey($batch, 1, function ($result) { + $this->assertIsArray($result, 2); + $this->assertArrayKeyEquals($result, 0, false); + $this->assertArrayKey($result, 1, function ($metadata) { + $this->assertIsArray($metadata); + $this->assertArrayKeyEquals($metadata, 'length', -1); + return true; + }); + return true; + }); + } + + $batch = $this->redis->multi() + ->set('key', 'value') + ->getWithMeta('key') + ->exec(); + $this->assertIsArray($batch, 2); + $this->assertArrayKeyEquals($batch, 0, true); + $this->assertArrayKey($batch, 1, function ($result) { + $this->assertIsArray($result, 2); + $this->assertArrayKeyEquals($result, 0, 'value'); + $this->assertArrayKey($result, 1, function ($metadata) { + $this->assertIsArray($metadata); + $this->assertArrayKeyEquals($metadata, 'length', strlen('value')); + return true; + }); + return true; + }); + + $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); + $this->assertTrue($this->redis->set('key', false)); + + $result = $this->redis->getWithMeta('key'); + $this->assertIsArray($result, 2); + $this->assertArrayKeyEquals($result, 0, false); + $this->assertArrayKey($result, 1, function ($metadata) { + $this->assertIsArray($metadata); + $this->assertArrayKeyEquals($metadata, 'length', strlen(serialize(false))); + return true; + }); + + $this->assertFalse($this->redis->get('key')); + $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); + } + + public function testHGetWithMeta() { + $this->redis->del('hash'); + $this->assertFalse($this->redis->hget('hash', 'member')); + + $result = $this->redis->hgetWithMeta('hash', 'member'); + $this->assertIsArray($result, 2); + $this->assertArrayKeyEquals($result, 0, false); + $this->assertArrayKey($result, 1, function ($metadata) { + $this->assertIsArray($metadata); + $this->assertArrayKeyEquals($metadata, 'length', -1); + return true; + }); + + if ($this->havePipeline()) { + $batch = $this->redis->pipeline() + ->hget('hash', 'member') + ->hgetWithMeta('hash', 'member') + ->exec(); + $this->assertIsArray($batch, 2); + $this->assertArrayKeyEquals($batch, 0, false); + $this->assertArrayKey($batch, 1, function ($result) { + $this->assertIsArray($result, 2); + $this->assertArrayKeyEquals($result, 0, false); + $this->assertArrayKey($result, 1, function ($metadata) { + $this->assertIsArray($metadata); + $this->assertArrayKeyEquals($metadata, 'length', -1); + return true; + }); + return true; + }); + } + + $batch = $this->redis->multi() + ->hset('hash', 'member', 'value') + ->hgetWithMeta('hash', 'member') + ->exec(); + $this->assertIsArray($batch, 2); + $this->assertArrayKeyEquals($batch, 0, 1); + $this->assertArrayKey($batch, 1, function ($result) { + $this->assertIsArray($result, 2); + $this->assertArrayKeyEquals($result, 0, 'value'); + $this->assertArrayKey($result, 1, function ($metadata) { + $this->assertIsArray($metadata); + $this->assertArrayKeyEquals($metadata, 'length', strlen('value')); + return true; + }); + return true; + }); + + $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); + $this->assertEquals(0, $this->redis->hset('hash', 'member', false)); + + $result = $this->redis->hgetWithMeta('hash', 'member'); + $this->assertIsArray($result, 2); + $this->assertArrayKeyEquals($result, 0, false); + $this->assertArrayKey($result, 1, function ($metadata) { + $this->assertIsArray($metadata); + $this->assertArrayKeyEquals($metadata, 'length', strlen(serialize(false))); + return true; + }); + + $this->assertFalse($this->redis->hget('hash', 'member')); + $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); + } + public function testPrefix() { // no prefix $this->redis->setOption(Redis::OPT_PREFIX, ''); - $this->assertTrue('key' == $this->redis->_prefix('key')); + $this->assertEquals('key', $this->redis->_prefix('key')); // with a prefix $this->redis->setOption(Redis::OPT_PREFIX, 'some-prefix:'); - $this->assertTrue('some-prefix:key' == $this->redis->_prefix('key')); + $this->assertEquals('some-prefix:key', $this->redis->_prefix('key')); // Clear prefix $this->redis->setOption(Redis::OPT_PREFIX, ''); @@ -5702,17 +6195,17 @@ public function testReplyLiteral() { } public function testNullArray() { - $key = "key:arr"; + $key = 'key:arr'; $this->redis->del($key); foreach ([false => [], true => NULL] as $opt => $test) { $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt); - $r = $this->redis->rawCommand("BLPOP", $key, .05); + $r = $this->redis->rawCommand('BLPOP', $key, .05); $this->assertEquals($test, $r); $this->redis->multi(); - $this->redis->rawCommand("BLPOP", $key, .05); + $this->redis->rawCommand('BLPOP', $key, .05); $r = $this->redis->exec(); $this->assertEquals([$test], $r); } @@ -5739,20 +6232,22 @@ public function testNestedNullArray() { public function testConfig() { /* GET */ $cfg = $this->redis->config('GET', 'timeout'); - $this->assertTrue(is_array($cfg) && isset($cfg['timeout'])); + $this->assertArrayKey($cfg, 'timeout'); $sec = $cfg['timeout']; /* SET */ foreach ([$sec + 30, $sec] as $val) { $this->assertTrue($this->redis->config('SET', 'timeout', $val)); $cfg = $this->redis->config('GET', 'timeout'); - $this->assertTrue(isset($cfg['timeout']) && $cfg['timeout'] == $val); + $this->assertArrayKey($cfg, 'timeout', function ($v) use ($val) { + return $v == $val; + }); } /* RESETSTAT */ $c1 = count($this->redis->info('commandstats')); $this->assertTrue($this->redis->config('resetstat')); - $this->assertTrue(count($this->redis->info('commandstats')) < $c1); + $this->assertLT($c1, count($this->redis->info('commandstats'))); /* Ensure invalid calls are handled by PhpRedis */ foreach (['notacommand', 'get', 'set'] as $cmd) { @@ -5763,13 +6258,13 @@ public function testConfig() { /* REWRITE. We don't care if it actually works, just that the command be attempted */ $res = $this->redis->config('rewrite'); - $this->assertTrue(is_bool($res)); + $this->assertIsBool($res); if ($res == false) { - $this->assertPatternMatch($this->redis->getLastError(), '/.*config.*/'); + $this->assertPatternMatch('/.*config.*/', $this->redis->getLastError()); $this->redis->clearLastError(); } - if (!$this->minVersionCheck("7.0.0")) + if ( ! $this->minVersionCheck('7.0.0')) return; /* Test getting multiple values */ @@ -5784,16 +6279,14 @@ public function testConfig() { list($timeout, $max_intset) = [$settings['timeout'], $settings['set-max-intset-entries']]; $updates = [ - ['timeout' => (string)($timeout + 30), 'set-max-intset-entries' => (string)($max_intset + 128)], - ['timeout' => (string)($timeout), 'set-max-intset-entries' => (string)$max_intset], + ['timeout' => $timeout + 30, 'set-max-intset-entries' => $max_intset + 128], + ['timeout' => $timeout, 'set-max-intset-entries' => $max_intset], ]; foreach ($updates as $update) { $this->assertTrue($this->redis->config('set', $update)); $vals = $this->redis->config('get', array_keys($update)); - ksort($vals); - ksort($update); - $this->assertEquals($vals, $update); + $this->assertEqualsWeak($vals, $update, true); } /* Make sure PhpRedis catches malformed multiple get/set calls */ @@ -5825,17 +6318,15 @@ public function testReconnectSelect() { sleep($this->minVersionCheck('3.0.0') ? 2 : 11); // Make sure we're still using the same DB. - $this->assertEquals($value, $this->redis->get($key)); + $this->assertKeyEquals($value, $key); // Revert the setting. $this->redis->config('SET', 'timeout', $original_cfg['timeout']); } public function testTime() { - - if (version_compare($this->version, "2.5.0") < 0) { + if (version_compare($this->version, '2.5.0') < 0) $this->markTestSkipped(); - } $time_arr = $this->redis->time(); $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 && @@ -5844,18 +6335,17 @@ public function testTime() { } public function testReadTimeoutOption() { - $this->assertTrue(defined('Redis::OPT_READ_TIMEOUT')); - $this->redis->setOption(Redis::OPT_READ_TIMEOUT, "12.3"); + $this->redis->setOption(Redis::OPT_READ_TIMEOUT, '12.3'); $this->assertEquals(12.3, $this->redis->getOption(Redis::OPT_READ_TIMEOUT)); } public function testIntrospection() { // Simple introspection tests - $this->assertTrue($this->redis->getHost() === $this->getHost()); - $this->assertTrue($this->redis->getPort() === $this->getPort()); - $this->assertTrue($this->redis->getAuth() === $this->getAuth()); + $this->assertEquals($this->getHost(), $this->redis->getHost()); + $this->assertEquals($this->getPort(), $this->redis->getPort()); + $this->assertEquals($this->getAuth(), $this->redis->getAuth()); } public function testTransferredBytes() { @@ -5866,7 +6356,7 @@ public function testTransferredBytes() { $get_tx_resp = "*3\r\n$3\r\nGET\r\n$3\r\nkey\r\n"; $get_rx_resp = "$3\r\nval\r\n"; - $this->assertEquals('val', $this->redis->get('key')); + $this->assertKeyEquals('val', 'key'); list ($tx, $rx) = $this->redis->getTransferredBytes(); $this->assertEquals(strlen($get_tx_resp), $tx); $this->assertEquals(strlen($get_rx_resp), $rx); @@ -5887,81 +6377,79 @@ public function testTransferredBytes() { * Scan and variants */ - protected function get_keyspace_count($str_db) { - $arr_info = $this->redis->info(); - if (isset($arr_info[$str_db])) { - $arr_info = $arr_info[$str_db]; - $arr_info = explode(',', $arr_info); - $arr_info = explode('=', $arr_info[0]); - return $arr_info[1]; + protected function get_keyspace_count($db) { + $info = $this->redis->info(); + if (isset($info[$db])) { + $info = $info[$db]; + $info = explode(',', $info); + $info = explode('=', $info[0]); + return $info[1]; } else { return 0; } } public function testScan() { - if(version_compare($this->version, "2.8.0") < 0) { + if (version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped(); - return; - } // Key count - $i_key_count = $this->get_keyspace_count('db0'); + $key_count = $this->get_keyspace_count('db0'); // Have scan retry $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); // Scan them all $it = NULL; - while($arr_keys = $this->redis->scan($it)) { - $i_key_count -= count($arr_keys); + while ($keys = $this->redis->scan($it)) { + $key_count -= count($keys); } // Should have iterated all keys - $this->assertEquals(0, $i_key_count); + $this->assertEquals(0, $key_count); // Unique keys, for pattern matching - $str_uniq = uniqid() . '-' . uniqid(); - for($i=0;$i<10;$i++) { - $this->redis->set($str_uniq . "::$i", "bar::$i"); + $uniq = uniqid(); + for ($i = 0; $i < 10; $i++) { + $this->redis->set($uniq . "::$i", "bar::$i"); } // Scan just these keys using a pattern match $it = NULL; - while($arr_keys = $this->redis->scan($it, "*$str_uniq*")) { - $i -= count($arr_keys); + while ($keys = $this->redis->scan($it, "*$uniq*")) { + $i -= count($keys); } $this->assertEquals(0, $i); // SCAN with type is scheduled for release in Redis 6. - if (version_compare($this->version, "6.0.0") >= 0) { + if (version_compare($this->version, '6.0.0') >= 0) { // Use a unique ID so we can find our type keys $id = uniqid(); + $keys = []; // Create some simple keys and lists for ($i = 0; $i < 3; $i++) { - $str_simple = "simple:{$id}:$i"; - $str_list = "list:{$id}:$i"; + $simple = "simple:{$id}:$i"; + $list = "list:{$id}:$i"; - $this->redis->set($str_simple, $i); - $this->redis->del($str_list); - $this->redis->rpush($str_list, ['foo']); + $this->redis->set($simple, $i); + $this->redis->del($list); + $this->redis->rpush($list, ['foo']); - $arr_keys["STRING"][] = $str_simple; - $arr_keys["LIST"][] = $str_list; + $keys['STRING'][] = $simple; + $keys['LIST'][] = $list; } // Make sure we can scan for specific types - foreach ($arr_keys as $str_type => $arr_vals) { - foreach ([0, 10] as $i_count) { - $arr_resp = []; + foreach ($keys as $type => $vals) { + foreach ([0, 10] as $count) { + $resp = []; $it = NULL; - while ($arr_scan = $this->redis->scan($it, "*$id*", $i_count, $str_type)) { - $arr_resp = array_merge($arr_resp, $arr_scan); + while ($scan = $this->redis->scan($it, "*$id*", $count, $type)) { + $resp = array_merge($resp, $scan); } - sort($arr_vals); sort($arr_resp); - $this->assertEquals($arr_vals, $arr_resp); + $this->assertEqualsCanonicalizing($vals, $resp); } } } @@ -5971,35 +6459,35 @@ public function testScanPrefix() { $keyid = uniqid(); /* Set some keys with different prefixes */ - $arr_prefixes = ['prefix-a:', 'prefix-b:']; - foreach ($arr_prefixes as $str_prefix) { - $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix); - $this->redis->set("$keyid", "LOLWUT"); - $arr_all_keys["{$str_prefix}{$keyid}"] = true; + $prefixes = ['prefix-a:', 'prefix-b:']; + foreach ($prefixes as $prefix) { + $this->redis->setOption(Redis::OPT_PREFIX, $prefix); + $this->redis->set("$keyid", 'LOLWUT'); + $all_keys["{$prefix}{$keyid}"] = true; } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX); - foreach ($arr_prefixes as $str_prefix) { - $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix); + foreach ($prefixes as $prefix) { + $this->redis->setOption(Redis::OPT_PREFIX, $prefix); $it = NULL; - $arr_keys = $this->redis->scan($it, "*$keyid*"); - $this->assertEquals($arr_keys, ["{$str_prefix}{$keyid}"]); + $keys = $this->redis->scan($it, "*$keyid*"); + $this->assertEquals($keys, ["{$prefix}{$keyid}"]); } /* Unset the prefix option */ $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX); $it = NULL; - while ($arr_keys = $this->redis->scan($it, "*$keyid*")) { - foreach ($arr_keys as $str_key) { - unset($arr_all_keys[$str_key]); + while ($keys = $this->redis->scan($it, "*$keyid*")) { + foreach ($keys as $key) { + unset($all_keys[$key]); } } /* Should have touched every key */ - $this->assertTrue(count($arr_all_keys) == 0); + $this->assertEquals(0, count($all_keys)); } public function testMaxRetriesOption() { @@ -6010,190 +6498,347 @@ public function testMaxRetriesOption() { } public function testBackoffOptions() { - $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DEFAULT); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DEFAULT); + $algorithms = [ + Redis::BACKOFF_ALGORITHM_DEFAULT, + Redis::BACKOFF_ALGORITHM_CONSTANT, + Redis::BACKOFF_ALGORITHM_UNIFORM, + Redis::BACKOFF_ALGORITHM_EXPONENTIAL, + Redis::BACKOFF_ALGORITHM_EQUAL_JITTER, + Redis::BACKOFF_ALGORITHM_FULL_JITTER, + Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER + ]; + + foreach ($algorithms as $algorithm) { + $this->assertTrue($this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, $algorithm)); + $this->assertEquals($algorithm, $this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM)); + } + + // Invalid algorithm + $this->assertFalse($this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, 55555)); + + foreach ([Redis::OPT_BACKOFF_BASE, Redis::OPT_BACKOFF_CAP] as $option) { + foreach ([500, 750] as $value) { + $this->redis->setOption($option, $value); + $this->assertEquals($value, $this->redis->getOption($option)); + } + } + } + + public function testHashExpiration() { + if ( ! $this->haveCommand('HEXPIRE')) + $this->markTestSkipped(); + + $hexpire_cmds = [ + 'hexpire' => 10, + 'hpexpire' => 10000, + 'hexpireat' => time() + 10, + 'hpexpireat' => time() * 1000 + 10000, + ]; - $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_CONSTANT); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_CONSTANT); + $httl_cmds = ['httl', 'hpttl', 'hexpiretime', 'hpexpiretime']; - $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_UNIFORM); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_UNIFORM); + $hash = ['Picard' => 'Enterprise', 'Sisko' => 'Defiant']; + $keys = array_keys($hash); - $this->redis -> setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EXPONENTIAL); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EXPONENTIAL); + foreach ($hexpire_cmds as $exp_cmd => $ttl) { + $this->redis->del('hash'); + $this->redis->hmset('hash', $hash); - $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EQUAL_JITTER); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EQUAL_JITTER); + /* Set a TTL on one existing and one non-existing field */ + $res = $this->redis->{$exp_cmd}('hash', $ttl, ['Picard', 'nofield']); - $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_FULL_JITTER); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_FULL_JITTER); + $this->assertEquals($res, [1, -2]); - $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER); + foreach ($httl_cmds as $ttl_cmd) { + $res = $this->redis->{$ttl_cmd}('hash', $keys); + $this->assertIsArray($res); + $this->assertEquals(count($keys), count($res)); - $this->assertFalse($this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, 55555)); + /* Picard: has an expiry (>0), Siskto does not (<0) */ + $this->assertTrue($res[0] > 0); + $this->assertTrue($res[1] < 0); + } + + $this->redis->del('m'); + $this->redis->hmset('m', ['F' => 'V']); - $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 500); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 500); + // NX - Only set expiry if it doesn't have one + foreach ([[1], [0]] as $expected) { + $res = $this->redis->{$exp_cmd}('m', $ttl, ['F'], 'NX'); + $this->assertEquals($expected, $res); + } - $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 750); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 750); + // XX - Set if it has one + $res = $this->redis->{$exp_cmd}('m', $ttl, ['F'], 'XX'); + $this->assertEquals([1], $res); + $this->redis->hpersist('m', ['F']); + $res = $this->redis->{$exp_cmd}('m', $ttl, ['F'], 'XX'); + $this->assertEquals([0], $res); - $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 500); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 500); + // GT - should set if the new expiration is larger + $res = $this->redis->{$exp_cmd}('m', $ttl, ['F']); + $res = $this->redis->{$exp_cmd}('m', $ttl + 100, ['F'], 'GT'); + $this->assertEquals([1], $res); - $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 750); - $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 750); + // LT - should not set if the new expiration is smaller + $res = $this->redis->{$exp_cmd}('m', intval($ttl / 2), ['F'], 'LT'); + $this->assertTrue(is_array($res) && $res[0] > 0); + } } - public function testHScan() { - if (version_compare($this->version, "2.8.0") < 0) { + public function testHGetEx() { + if ( ! $this->minVersionCheck('8.0')) $this->markTestSkipped(); - return; + + $now = time(); + + $tests = [ + [['EX' => 10], 'httl', 0, 10], + [['PX' => 10000], 'hpttl', 0, 10000], + [['EXAT' => $now + 10], 'hexpiretime', $now, $now + 10], + [['PXAT' => $now * 1000 + 10000], 'hpexpiretime', $now * 1000, $now * 1000 + 10000], + ['PERSIST', 'httl', -1, -1], + [['PERSIST'], 'httl', -1,-1], + ]; + + $hash = ['ship' => 'Defiant', 'captain' => 'Sisko']; + + foreach ($tests as [$expireArg, $ttlFn, $minTtl, $maxTtl]) { + $this->redis->del('hash'); + $this->assertTrue($this->redis->hmset('hash', $hash)); + + $v = $this->redis->hgetex('hash', ['ship', 'captain'], $expireArg); + $this->assertEquals($hash, $v); + + $ttls = $this->redis->{$ttlFn}('hash', ['ship', 'captain']); + $this->assertIsArray($ttls); + + foreach ($ttls as $val) { + $this->assertBetween($val, $minTtl, $maxTtl); + } + } + } + + public function testHSetEx(): void { + if ( ! $this->minVersionCheck('8.0')) { + $this->markTestSkipped('HSETEX requires Redis 8+'); + } + + $now = time(); + $nowMs = $now * 1000; + $hash = ['a' => 'foo', 'b' => 'bar']; + + $cases = [ + [['EX', 5], 'httl'], + [['PX', 1234], 'hpttl'], + [['EXAT', $now + 5], 'hexpiretime'], + [['PXAT', $nowMs + 5000], 'hpexpiretime'], + ]; + + foreach ($cases as [[$ex, $n], $ttlFn]) { + $this->redis->del('hash'); + $this->assertTrue($this->redis->hMSet('hash', $hash)); + + $expireArg = [$ex => $n]; + $result = $this->redis->hsetex('hash', $hash, $expireArg); + $this->assertLTE(count($hash), $result); + + $got = $this->redis->hMGet('hash', array_keys($hash)); + $this->assertEquals($hash, $got); + + $ttls = $this->redis->{$ttlFn}('hash', array_keys($hash)); + $this->assertIsArray($ttls); + + foreach ($ttls as $ttl) { + $this->assertLTE($n, $ttl); + } + } + + $this->assertIsInt($this->redis->del('hash')); + $this->assertEquals( + 1, $this->redis->hsetex('hash', ['a' => 'v'], ['EX' => 20]) + ); + $this->assertEquals( + 1, $this->redis->hsetex('hash', ['a' => 'v2'], ['KEEPTTL']) + ); + + $ttl = $this->redis->httl('hash', ['a']); + $this->assertLTE(20, $ttl[0]); + + $this->assertIsInt($this->redis->del('hash')); + + // FNX - Only if none exist + foreach ([1, 0] as $expected) { + $ex = ['EX' => 20, 'FNX']; + $result = $this->redis->hsetex('hash', $hash, $ex); + $this->assertEquals($expected, $result); + } + + // FXX - Only if they all exist + foreach ([1, 0] as $expected) { + $ex = ['EX' => 20, 'FXX']; + $result = $this->redis->hsetex('hash', $hash, $ex); + $this->assertEquals($expected, $result); + + $k = array_rand($hash); + $this->redis->hdel('hash', $k); } + } + + public function testHGetDel() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); + + $this->assertIsInt($this->redis->del('hash')); + $hash = ['ship' => 'Defiant', 'captain' => 'Sisko']; + + $this->assertTrue($this->redis->hmset('hash', $hash)); + $this->assertEquals($hash, $this->redis->hgetall('hash')); + + $this->assertEquals(['captain' => 'Sisko'], $this->redis->hgetdel('hash', ['captain'])); + $this->assertEquals(['ship' => 'Defiant'], $this->redis->hgetall('hash')); + } + + public function testHScan() { + if (version_compare($this->version, '2.8.0') < 0) + $this->markTestSkipped(); // Never get empty sets $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->del('hash'); - $i_foo_mems = 0; + $foo_mems = 0; - for($i=0;$i<100;$i++) { - if($i>3) { + for ($i = 0; $i < 100; $i++) { + if ($i > 3) { $this->redis->hset('hash', "member:$i", "value:$i"); } else { $this->redis->hset('hash', "foomember:$i", "value:$i"); - $i_foo_mems++; + $foo_mems++; } } // Scan all of them $it = NULL; - while($arr_keys = $this->redis->hscan('hash', $it)) { - $i -= count($arr_keys); + while ($keys = $this->redis->hscan('hash', $it)) { + $i -= count($keys); } $this->assertEquals(0, $i); // Scan just *foomem* (should be 4) $it = NULL; - while($arr_keys = $this->redis->hscan('hash', $it, '*foomember*')) { - $i_foo_mems -= count($arr_keys); - foreach($arr_keys as $str_mem => $str_val) { - $this->assertTrue(strpos($str_mem, 'member')!==FALSE); - $this->assertTrue(strpos($str_val, 'value')!==FALSE); + while ($keys = $this->redis->hscan('hash', $it, '*foomember*')) { + $foo_mems -= count($keys); + foreach ($keys as $mem => $val) { + $this->assertStringContains('member', $mem); + $this->assertStringContains('value', $val); } } - $this->assertEquals(0, $i_foo_mems); + $this->assertEquals(0, $foo_mems); } public function testSScan() { - if (version_compare($this->version, "2.8.0") < 0) { + if (version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->del('set'); - for($i=0;$i<100;$i++) { + for ($i = 0; $i < 100; $i++) { $this->redis->sadd('set', "member:$i"); } // Scan all of them $it = NULL; - while($arr_keys = $this->redis->sscan('set', $it)) { - $i -= count($arr_keys); - foreach($arr_keys as $str_mem) { - $this->assertTrue(strpos($str_mem,'member')!==FALSE); + while ($keys = $this->redis->sscan('set', $it)) { + $i -= count($keys); + foreach ($keys as $mem) { + $this->assertStringContains('member', $mem); } } $this->assertEquals(0, $i); // Scan just ones with zero in them (0, 10, 20, 30, 40, 50, 60, 70, 80, 90) $it = NULL; - $i_w_zero = 0; - while($arr_keys = $this->redis->sscan('set', $it, '*0*')) { - $i_w_zero += count($arr_keys); + $w_zero = 0; + while ($keys = $this->redis->sscan('set', $it, '*0*')) { + $w_zero += count($keys); } - $this->assertEquals(10, $i_w_zero); + $this->assertEquals(10, $w_zero); } public function testZScan() { - if (version_compare($this->version, "2.8.0") < 0) { + if (version_compare($this->version, '2.8.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); $this->redis->del('zset'); - $i_tot_score = 0; - $i_p_score = 0; - $i_p_count = 0; - for($i=0;$i<2000;$i++) { - if($i<10) { + + [$t_score, $p_score, $p_count] = [0, 0, 0]; + for ($i = 0; $i < 2000; $i++) { + if ($i < 10) { $this->redis->zadd('zset', $i, "pmem:$i"); - $i_p_score += $i; - $i_p_count += 1; + $p_score += $i; + $p_count++; } else { $this->redis->zadd('zset', $i, "mem:$i"); } - $i_tot_score += $i; + $t_score += $i; } // Scan them all $it = NULL; - while($arr_keys = $this->redis->zscan('zset', $it)) { - foreach($arr_keys as $str_mem => $f_score) { - $i_tot_score -= $f_score; + while ($keys = $this->redis->zscan('zset', $it)) { + foreach ($keys as $mem => $f_score) { + $t_score -= $f_score; $i--; } } $this->assertEquals(0, $i); - $this->assertEquals((float)0, $i_tot_score); + $this->assertEquals(0., $t_score); - // Just scan "pmem" members + // Just scan 'pmem' members $it = NULL; - $i_p_score_old = $i_p_score; - $i_p_count_old = $i_p_count; - while($arr_keys = $this->redis->zscan('zset', $it, "*pmem*")) { - foreach($arr_keys as $str_mem => $f_score) { - $i_p_score -= $f_score; - $i_p_count -= 1; + $p_score_old = $p_score; + $p_count_old = $p_count; + while ($keys = $this->redis->zscan('zset', $it, '*pmem*')) { + foreach ($keys as $mem => $f_score) { + $p_score -= $f_score; + $p_count -= 1; } } - $this->assertEquals((float)0, $i_p_score); - $this->assertEquals(0, $i_p_count); + $this->assertEquals(0., $p_score); + $this->assertEquals(0, $p_count); // Turn off retrying and we should get some empty results $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); - $i_skips = 0; - $i_p_score = $i_p_score_old; - $i_p_count = $i_p_count_old; + [$skips, $p_score, $p_count] = [0, $p_score_old, $p_count_old]; + $it = NULL; - while(($arr_keys = $this->redis->zscan('zset', $it, "*pmem*")) !== FALSE) { - if(count($arr_keys) == 0) $i_skips++; - foreach($arr_keys as $str_mem => $f_score) { - $i_p_score -= $f_score; - $i_p_count -= 1; + while (($keys = $this->redis->zscan('zset', $it, '*pmem*')) !== FALSE) { + if (count($keys) == 0) $skips++; + foreach ($keys as $mem => $f_score) { + $p_score -= $f_score; + $p_count -= 1; } } // We should still get all the keys, just with several empty results - $this->assertTrue($i_skips > 0); - $this->assertEquals((float)0, $i_p_score); - $this->assertEquals(0, $i_p_count); + $this->assertGT(0, $skips); + $this->assertEquals(0., $p_score); + $this->assertEquals(0, $p_count); } /* Make sure we capture errors when scanning */ public function testScanErrors() { $this->redis->set('scankey', 'simplekey'); - foreach (['sScan', 'hScan', 'zScan'] as $str_method) { + foreach (['sScan', 'hScan', 'zScan'] as $method) { $it = NULL; - $this->redis->$str_method('scankey', $it); - $this->assertTrue(strpos($this->redis->getLastError(), 'WRONGTYPE') === 0); + $this->redis->$method('scankey', $it); + $this->assertEquals(0, strpos($this->redis->getLastError(), 'WRONGTYPE')); } } @@ -6201,75 +6846,72 @@ public function testScanErrors() { // HyperLogLog (PF) commands // - protected function createPFKey($str_key, $i_count) { - $arr_mems = []; - for($i=0;$i<$i_count;$i++) { - $arr_mems[] = uniqid() . '-' . $i; + protected function createPFKey($key, $count) { + $mems = []; + for ($i = 0; $i < $count; $i++) { + $mems[] = uniqid('pfmem:'); } // Estimation by Redis - $this->redis->pfadd($str_key, $i_count); + $this->redis->pfAdd($key, $count); } public function testPFCommands() { - // Isn't available until 2.8.9 - if (version_compare($this->version, "2.8.9") < 0) { + if (version_compare($this->version, '2.8.9') < 0) $this->markTestSkipped(); - return; - } - $str_uniq = uniqid(); - $arr_mems = []; + $mems = []; - for($i=0;$i<1000;$i++) { - if($i%2 == 0) { - $arr_mems[] = $str_uniq . '-' . $i; + for ($i = 0; $i < 1000; $i++) { + if ($i % 2 == 0) { + $mems[] = uniqid(); } else { - $arr_mems[] = $i; + $mems[] = $i; } } // How many keys to create - $i_keys = 10; + $key_count = 10; // Iterate prefixing/serialization options - foreach([Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP] as $str_ser) { - foreach(['', 'hl-key-prefix:'] as $str_prefix) { - $arr_keys = []; + foreach ($this->getSerializers() as $ser) { + foreach (['', 'hl-key-prefix:'] as $prefix) { + $keys = []; // Now add for each key - for($i=0;$i<$i_keys;$i++) { - $str_key = "{key}:$i"; - $arr_keys[] = $str_key; + for ($i = 0; $i < $key_count; $i++) { + $key = "{key}:$i"; + $keys[] = $key; // Clean up this key - $this->redis->del($str_key); + $this->redis->del($key); // Add to our cardinality set, and confirm we got a valid response - $this->assertTrue($this->redis->pfadd($str_key, $arr_mems)); + $this->assertGT(0, $this->redis->pfadd($key, $mems)); // Grab estimated cardinality - $i_card = $this->redis->pfcount($str_key); - $this->assertTrue(is_int($i_card)); + $card = $this->redis->pfcount($key); + $this->assertIsInt($card); // Count should be close - $this->assertLess(abs($i_card-count($arr_mems)), count($arr_mems) * .1); + $this->assertBetween($card, count($mems) * .9, count($mems) * 1.1); // The PFCOUNT on this key should be the same as the above returned response - $this->assertEquals($this->redis->pfcount($str_key), $i_card); + $this->assertEquals($card, $this->redis->pfcount($key)); } // Clean up merge key $this->redis->del('pf-merge-{key}'); // Merge the counters - $this->assertTrue($this->redis->pfmerge('pf-merge-{key}', $arr_keys)); + $this->assertTrue($this->redis->pfmerge('pf-merge-{key}', $keys)); // Validate our merged count - $i_redis_card = $this->redis->pfcount('pf-merge-{key}'); + $redis_card = $this->redis->pfcount('pf-merge-{key}'); // Merged cardinality should still be roughly 1000 - $this->assertLess(abs($i_redis_card-count($arr_mems)), count($arr_mems) * .1); + $this->assertBetween($redis_card, count($mems) * .9, + count($mems) * 1.1); // Clean up merge key $this->redis->del('pf-merge-{key}'); @@ -6294,15 +6936,14 @@ protected function addCities($key) { /* GEOADD */ public function testGeoAdd() { - if (!$this->minVersionCheck("3.2")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('3.2')) + $this->markTestSkipped(); $this->redis->del('geokey'); /* Add them one at a time */ foreach ($this->cities as $city => $longlat) { - $this->assertEquals($this->redis->geoadd('geokey', $longlat[0], $longlat[1], $city), 1); + $this->assertEquals(1, $this->redis->geoadd('geokey', $longlat[0], $longlat[1], $city)); } /* Add them again, all at once */ @@ -6317,9 +6958,8 @@ public function testGeoAdd() { /* GEORADIUS */ public function genericGeoRadiusTest($cmd) { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('3.2.0')) + $this->markTestSkipped(); /* Chico */ $city = 'Chico'; @@ -6330,28 +6970,28 @@ public function genericGeoRadiusTest($cmd) { /* Pre tested with redis-cli. We're just verifying proper delivery of distance and unit */ if ($cmd == 'georadius' || $cmd == 'georadius_ro') { - $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array($cmd, '{gk}', $lng, $lat, 500, 'mi'); + $this->assertEquals(['Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 30, 'mi')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 50, 'km')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 50000, 'm')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $lng, $lat, 150000, 'ft')); + $args = [$cmd, '{gk}', $lng, $lat, 500, 'mi']; /* Test a bad COUNT argument */ - foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi', Array('count' => $count))); + foreach ([-1, 0, 'notanumber'] as $count) { + $this->assertFalse(@$this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi', ['count' => $count])); } } else { - $this->assertEquals($this->redis->$cmd('{gk}', $city, 10, 'mi'), Array('Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $city, 30, 'mi'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $city, 50, 'km'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $city, 50000, 'm'), Array('Gridley','Chico')); - $this->assertEquals($this->redis->$cmd('{gk}', $city, 150000, 'ft'), Array('Gridley', 'Chico')); - $args = Array($cmd, '{gk}', $city, 500, 'mi'); + $this->assertEquals(['Chico'], $this->redis->$cmd('{gk}', $city, 10, 'mi')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $city, 30, 'mi')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $city, 50, 'km')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $city, 50000, 'm')); + $this->assertEquals(['Gridley', 'Chico'], $this->redis->$cmd('{gk}', $city, 150000, 'ft')); + $args = [$cmd, '{gk}', $city, 500, 'mi']; /* Test a bad COUNT argument */ - foreach (Array(-1, 0, 'notanumber') as $count) { - $this->assertFalse(@$this->redis->$cmd('{gk}', $city, 10, 'mi', Array('count' => $count))); + foreach ([-1, 0, 'notanumber'] as $count) { + $this->assertFalse(@$this->redis->$cmd('{gk}', $city, 10, 'mi', ['count' => $count])); } } @@ -6376,7 +7016,6 @@ public function genericGeoRadiusTest($cmd) { $base_subopts = $subopts; foreach ($realstoreopts as $store_type) { - for ($c = 0; $c < 3; $c++) { $subargs = $base_subargs; $subopts = $base_subopts; @@ -6419,47 +7058,42 @@ public function genericGeoRadiusTest($cmd) { } public function testGeoRadius() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('3.2.0')) + $this->markTestSkipped(); $this->genericGeoRadiusTest('georadius'); $this->genericGeoRadiusTest('georadius_ro'); } public function testGeoRadiusByMember() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('3.2.0')) + $this->markTestSkipped(); $this->genericGeoRadiusTest('georadiusbymember'); $this->genericGeoRadiusTest('georadiusbymember_ro'); } public function testGeoPos() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('3.2.0')) + $this->markTestSkipped(); $this->addCities('gk'); - $this->assertEquals($this->redis->geopos('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', ['geopos', 'gk', 'Chico', 'Sacramento'])); - $this->assertEquals($this->redis->geopos('gk', 'Cupertino'), $this->rawCommandArray('gk', ['geopos', 'gk', 'Cupertino'])); + $this->assertEquals($this->rawCommandArray('gk', ['geopos', 'gk', 'Chico', 'Sacramento']), $this->redis->geopos('gk', 'Chico', 'Sacramento')); + $this->assertEquals($this->rawCommandArray('gk', ['geopos', 'gk', 'Cupertino']), $this->redis->geopos('gk', 'Cupertino')); } public function testGeoHash() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('3.2.0')) + $this->markTestSkipped(); $this->addCities('gk'); - $this->assertEquals($this->redis->geohash('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', ['geohash', 'gk', 'Chico', 'Sacramento'])); - $this->assertEquals($this->redis->geohash('gk', 'Chico'), $this->rawCommandArray('gk', ['geohash', 'gk', 'Chico'])); + $this->assertEquals($this->rawCommandArray('gk', ['geohash', 'gk', 'Chico', 'Sacramento']), $this->redis->geohash('gk', 'Chico', 'Sacramento')); + $this->assertEquals($this->rawCommandArray('gk', ['geohash', 'gk', 'Chico']), $this->redis->geohash('gk', 'Chico')); } public function testGeoDist() { - if (!$this->minVersionCheck("3.2.0")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('3.2.0')) + $this->markTestSkipped(); $this->addCities('gk'); @@ -6473,13 +7107,12 @@ public function testGeoDist() { } public function testGeoSearch() { - if (!$this->minVersionCheck("6.2.0")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('6.2.0')) + $this->markTestSkipped(); $this->addCities('gk'); - $this->assertEquals($this->redis->geosearch('gk', 'Chico', 1, 'm'), ['Chico']); + $this->assertEquals(['Chico'], $this->redis->geosearch('gk', 'Chico', 1, 'm')); $this->assertValidate($this->redis->geosearch('gk', 'Chico', 1, 'm', ['withcoord', 'withdist', 'withhash']), function ($v) { $this->assertArrayKey($v, 'Chico', 'is_array'); $this->assertEquals(count($v['Chico']), 3); @@ -6490,25 +7123,51 @@ public function testGeoSearch() { }); } + public function testGeoSearchByPolygon() { + if ( ! $this->minValkeyVersionCheck('9.0.0')) + $this->markTestSkipped(); + + $this->addCities('ca:cities'); + + $res = $this->redis->geosearch('ca:cities', '', [ + -121.90, 39.65, -121.77, 39.65, -121.77, 39.80, -121.90, 39.80 + ], ''); + + $this->assertEquals(['Chico'], $res); + } + + public function testGeoSearchStoreByPolygon() { + if ( ! $this->minValkeyVersionCheck('9.0.0')) + $this->markTestSkipped(); + + $this->addCities('{geo}:cities'); + $this->assertEquals(1, $this->redis->geosearchstore('{geo}:dst', '{geo}:cities', '', [ + -121.90, 39.65, -121.77, 39.65, -121.77, 39.80, -121.90, 39.80 + ], '')); + + $this->assertEquals(['Chico'], $this->redis->geosearch('{geo}:dst', 'Chico', 1, 'm')); + } + public function testGeoSearchStore() { - if (!$this->minVersionCheck("6.2.0")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('6.2.0')) + $this->markTestSkipped(); $this->addCities('{gk}src'); - $this->assertEquals($this->redis->geosearchstore('{gk}dst', '{gk}src', 'Chico', 100, 'km'), 3); - $this->assertEquals($this->redis->geosearch('{gk}dst', 'Chico', 1, 'm'), ['Chico']); + $this->assertEquals(3, $this->redis->geosearchstore('{gk}dst', '{gk}src', 'Chico', 100, 'km')); + $this->assertEquals(['Chico'], $this->redis->geosearch('{gk}dst', 'Chico', 1, 'm')); } /* Test a 'raw' command */ public function testRawCommand() { - $this->redis->set('mykey','some-value'); - $str_result = $this->redis->rawCommand('get', 'mykey'); - $this->assertEquals($str_result, 'some-value'); + $key = uniqid(); + + $this->redis->set($key,'some-value'); + $result = $this->redis->rawCommand('get', $key); + $this->assertEquals($result, 'some-value'); $this->redis->del('mylist'); $this->redis->rpush('mylist', 'A', 'B', 'C', 'D'); - $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']); + $this->assertEquals(['A', 'B', 'C', 'D'], $this->redis->lrange('mylist', 0, -1)); } /* STREAMS */ @@ -6525,13 +7184,13 @@ protected function addStreamEntries($key, $count) { return $ids; } - protected function addStreamsAndGroups($arr_streams, $count, $arr_groups) { + protected function addStreamsAndGroups($streams, $count, $groups) { $ids = []; - foreach ($arr_streams as $str_stream) { - $ids[$str_stream] = $this->addStreamEntries($str_stream, $count); - foreach ($arr_groups as $str_group => $str_id) { - $this->redis->xGroup('CREATE', $str_stream, $str_group, $str_id); + foreach ($streams as $stream) { + $ids[$stream] = $this->addStreamEntries($stream, $count); + foreach ($groups as $group => $id) { + $this->redis->xGroup('CREATE', $stream, $group, $id); } } @@ -6539,13 +7198,13 @@ protected function addStreamsAndGroups($arr_streams, $count, $arr_groups) { } public function testXAdd() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); $this->redis->del('stream'); for ($i = 0; $i < 5; $i++) { - $id = $this->redis->xAdd("stream", '*', ['k1' => 'v1', 'k2' => 'v2']); - $this->assertEquals($this->redis->xLen('stream'), $i+1); + $id = $this->redis->xAdd('stream', '*', ['k1' => 'v1', 'k2' => 'v2']); + $this->assertEquals($i+1, $this->redis->xLen('stream')); /* Redis should return - */ $bits = explode('-', $id); @@ -6558,7 +7217,7 @@ public function testXAdd() { for ($i = 0; $i < 100; $i++) { $this->redis->xAdd('stream', '*', ['k' => 'v'], 10); } - $this->assertEquals($this->redis->xLen('stream'), 10); + $this->assertEquals(10, $this->redis->xLen('stream')); /* Not the greatest test but I'm unsure if approximate trimming is * totally deterministic, so just make sure we are able to add with @@ -6567,7 +7226,7 @@ public function testXAdd() { $this->assertEquals(count(explode('-', $id)), 2); /* Empty message should fail */ - $this->redis->xAdd('stream', '*', []); + @$this->redis->xAdd('stream', '*', []); } protected function doXRangeTest($reverse) { @@ -6604,11 +7263,11 @@ protected function doXRangeTest($reverse) { } public function testXRange() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); foreach ([false, true] as $reverse) { - foreach ($this->serializers as $serializer) { + foreach ($this->getSerializers() as $serializer) { foreach ([NULL, 'prefix:'] as $prefix) { $this->redis->setOption(Redis::OPT_PREFIX, $prefix); $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); @@ -6619,27 +7278,27 @@ public function testXRange() { } protected function testXLen() { - if (!$this->minVersionCheck("5.0")) + if ( ! $this->minVersionCheck('5.0')) $this->markTestSkipped(); $this->redis->del('{stream}'); for ($i = 0; $i < 5; $i++) { $this->redis->xadd('{stream}', '*', ['foo' => 'bar']); - $this->assertEquals($this->redis->xLen('{stream}'), $i+1); + $this->assertEquals($i+1, $this->redis->xLen('{stream}')); } } public function testXGroup() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); /* CREATE MKSTREAM */ - $str_key = 's:' . uniqid(); - $this->assertFalse($this->redis->xGroup('CREATE', $str_key, 'g0', 0)); - $this->assertTrue($this->redis->xGroup('CREATE', $str_key, 'g1', 0, true)); + $key = 's:' . uniqid(); + $this->assertFalse($this->redis->xGroup('CREATE', $key, 'g0', 0)); + $this->assertTrue($this->redis->xGroup('CREATE', $key, 'g1', 0, true)); /* XGROUP DESTROY */ - $this->assertEquals($this->redis->xGroup('DESTROY', $str_key, 'g1'), 1); + $this->assertEquals(1, $this->redis->xGroup('DESTROY', $key, 'g1')); /* Populate some entries in stream 's' */ $this->addStreamEntries('s', 2); @@ -6650,52 +7309,55 @@ public function testXGroup() { /* BUSYGROUP */ $this->redis->xGroup('CREATE', 's', 'mygroup', '$'); - $this->assertTrue(strpos($this->redis->getLastError(), 'BUSYGROUP') === 0); + $this->assertEquals(0, strpos($this->redis->getLastError(), 'BUSYGROUP')); /* SETID */ $this->assertTrue($this->redis->xGroup('SETID', 's', 'mygroup', '$')); $this->assertFalse($this->redis->xGroup('SETID', 's', 'mygroup', 'BAD_ID')); - $this->assertEquals($this->redis->xGroup('DELCONSUMER', 's', 'mygroup', 'myconsumer'),0); + $this->assertEquals(0, $this->redis->xGroup('DELCONSUMER', 's', 'mygroup', 'myconsumer')); - if (!$this->minVersionCheck('6.2.0')) + if ( ! $this->minVersionCheck('6.2.0')) return; /* CREATECONSUMER */ - $this->assertTrue($this->redis->del('s')); + $this->assertEquals(1, $this->redis->del('s')); $this->assertTrue($this->redis->xgroup('create', 's', 'mygroup', '$', true)); for ($i = 0; $i < 3; $i++) { - $this->assertTrue($this->redis->xgroup('createconsumer', 's', 'mygroup', "c:$i")); + $this->assertEquals(1, $this->redis->xgroup('createconsumer', 's', 'mygroup', "c:$i")); $info = $this->redis->xinfo('consumers', 's', 'mygroup'); - $this->assertTrue(is_array($info) && count($info) == $i + 1); + $this->assertIsArray($info, $i + 1); for ($j = 0; $j <= $i; $j++) { $this->assertTrue(isset($info[$j]) && isset($info[$j]['name']) && $info[$j]['name'] == "c:$j"); } } /* Make sure we don't erroneously send options that don't belong to the operation */ - $this->assertTrue($this->redis->xGroup('CREATECONSUMER', 's', 'mygroup', 'fake-consumer', true, 1337)); + $this->assertEquals(1, + $this->redis->xGroup('CREATECONSUMER', 's', 'mygroup', 'fake-consumer', true, 1337)); /* Make sure we handle the case where the user doesn't send enough arguments */ $this->redis->clearLastError(); $this->assertFalse(@$this->redis->xGroup('CREATECONSUMER')); - $this->assertEquals(NULL, $this->redis->getLastError()); + $this->assertNull($this->redis->getLastError()); $this->assertFalse(@$this->redis->xGroup('create')); - $this->assertEquals(NULL, $this->redis->getLastError()); + $this->assertNull($this->redis->getLastError()); - if (!$this->minVersionCheck('7.0.0')) + if ( ! $this->minVersionCheck('7.0.0')) return; /* ENTRIESREAD */ - $this->assertTrue($this->redis->del('s')); + $this->assertEquals(1, $this->redis->del('s')); $this->assertTrue($this->redis->xGroup('create', 's', 'mygroup', '$', true, 1337)); $info = $this->redis->xinfo('groups', 's'); - $this->assertTrue(isset($info[0]['entries-read']) && 1337 == (int)$info[0]['entries-read']); + $this->assertTrue(isset($info[0]['entries-read'])); + /* Starting with redis 8.2.2 returns 0 */ + $this->assertTrue((int)$info[0]['entries-read'] === 1337 || (int)$info[0]['entries-read'] === 0); } public function testXAck() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); for ($n = 1; $n <= 3; $n++) { $this->addStreamsAndGroups(['{s}'], 3, ['g1' => 0]); @@ -6707,7 +7369,7 @@ public function testXAck() { /* Now ACK $n messages */ $ids = array_slice($ids, 0, $n); - $this->assertEquals($this->redis->xAck('{s}', 'g1', $ids), $n); + $this->assertEquals($n, $this->redis->xAck('{s}', 'g1', $ids)); } /* Verify sending no IDs is a failure */ @@ -6715,8 +7377,8 @@ public function testXAck() { } protected function doXReadTest() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); $row = ['f1' => 'v1', 'f2' => 'v2']; $msgdata = [ @@ -6760,15 +7422,15 @@ protected function doXReadTest() { ['{stream}-1' => [$new_id => ['final' => 'row']]] ); - /* Emtpy query should fail */ - $this->assertFalse($this->redis->xRead([])); + /* Empty query should fail */ + $this->assertFalse(@$this->redis->xRead([])); } public function testXRead() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); - foreach ($this->serializers as $serializer) { + foreach ($this->getSerializers() as $serializer) { $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer); $this->doXReadTest(); } @@ -6777,7 +7439,7 @@ public function testXRead() { $m1 = round(microtime(true)*1000); $this->redis->xRead(['somestream' => '$'], -1, 100); $m2 = round(microtime(true)*1000); - $this->assertTrue($m2 - $m1 >= 100); + $this->assertGT(99, $m2 - $m1); } protected function compareStreamIds($redis, $control) { @@ -6796,12 +7458,12 @@ protected function compareStreamIds($redis, $control) { } public function testXReadGroup() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); /* Create some streams and groups */ $streams = ['{s}-1', '{s}-2']; - $groups = ['g1' => 0, 'g2' => 0]; + $groups = ['group1' => 0, 'group2' => 0]; /* I'm not totally sure why Redis behaves this way, but we have to * send '>' first and then send ID '0' for subsequent xReadGroup calls @@ -6814,7 +7476,7 @@ public function testXReadGroup() { $ids = $this->addStreamsAndGroups($streams, 1, $groups); /* Test that we get get the IDs we should */ - foreach (['g1', 'g2'] as $group) { + foreach (['group1', 'group2'] as $group) { foreach ($ids as $stream => $messages) { while ($ids[$stream]) { /* Read more messages */ @@ -6834,7 +7496,7 @@ public function testXReadGroup() { /* Test COUNT option */ for ($c = 1; $c <= 3; $c++) { $this->addStreamsAndGroups($streams, 3, $groups); - $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, $c); + $resp = $this->redis->xReadGroup('group1', 'consumer', $query1, $c); foreach ($resp as $stream => $smsg) { $this->assertEquals(count($smsg), $c); @@ -6843,33 +7505,30 @@ public function testXReadGroup() { /* Test COUNT option with NULL (should be ignored) */ $this->addStreamsAndGroups($streams, 3, $groups, NULL); - $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, NULL); + $resp = $this->redis->xReadGroup('group1', 'consumer', $query1, NULL); foreach ($resp as $stream => $smsg) { $this->assertEquals(count($smsg), 3); } /* Finally test BLOCK with a sloppy timing test */ - $t1 = $this->mstime(); + $tm1 = $this->mstime(); $qnew = ['{s}-1' => '>', '{s}-2' => '>']; - $this->redis->xReadGroup('g1', 'c1', $qnew, NULL, 100); - $t2 = $this->mstime(); - $this->assertTrue($t2 - $t1 >= 100); + $this->redis->xReadGroup('group1', 'c1', $qnew, 0, 100); + $this->assertGTE(100, $this->mstime() - $tm1); /* Make sure passing NULL to block doesn't block */ - $t1 = $this->mstime(); - $this->redis->xReadGroup('g1', 'c1', $qnew, NULL, NULL); - $t2 = $this->mstime(); - $this->assertTrue($t2 - $t1 < 100); + $tm1 = $this->mstime(); + $this->redis->xReadGroup('group1', 'c1', $qnew, NULL, NULL); + $this->assertLT(100, $this->mstime() - $tm1); /* Make sure passing bad values to BLOCK or COUNT immediately fails */ - $this->assertFalse(@$this->redis->xReadGroup('g1', 'c1', $qnew, -1)); - $this->assertFalse(@$this->redis->xReadGroup('g1', 'c1', $qnew, NULL, -1)); + $this->assertFalse(@$this->redis->xReadGroup('group1', 'c1', $qnew, -1)); + $this->assertFalse(@$this->redis->xReadGroup('group1', 'c1', $qnew, NULL, -1)); } public function testXPending() { - if (!$this->minVersionCheck("5.0")) { - return $this->markTestSkipped(); - } + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); $rows = 5; $this->addStreamsAndGroups(['s'], $rows, ['group' => 0]); @@ -6880,12 +7539,12 @@ public function testXPending() { for ($n = count($ids); $n >= 0; $n--) { $xp = $this->redis->xPending('s', 'group'); - $this->assertEquals($xp[0], count($ids)); + $this->assertEquals(count($ids), $xp[0]); /* Verify we're seeing the IDs themselves */ for ($idx = 1; $idx <= 2; $idx++) { if ($xp[$idx]) { - $this->assertPatternMatch($xp[$idx], "/^[0-9].*-[0-9].*/"); + $this->assertPatternMatch('/^[0-9].*-[0-9].*/', $xp[$idx]); } } @@ -6901,36 +7560,57 @@ public function testXPending() { } public function testXDel() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); for ($n = 5; $n > 0; $n--) { $ids = $this->addStreamEntries('s', 5); $todel = array_slice($ids, 0, $n); - $this->assertEquals($this->redis->xDel('s', $todel), count($todel)); + $this->assertEquals(count($todel), $this->redis->xDel('s', $todel)); } /* Empty array should fail */ - $this->assertFalse($this->redis->xDel('s', [])); + $this->assertFalse(@$this->redis->xDel('s', [])); + } + + public function testXDelex() { + if ( ! $this->haveCommand('XDELEX')) + $this->markTestSkipped(); + + $stream = 'xdelex-stream'; + $missing = '9999999999999-0'; + $modes = [NULL, 'KEEPREF', 'DELREF', 'ACKED']; + + foreach ($modes as $mode) { + $ids = $this->addStreamEntries($stream, 2); + $targets = [$ids[0], $missing]; + + $response = $mode === NULL + ? $this->redis->xdelex($stream, $targets) + : $this->redis->xdelex($stream, $targets, $mode); + + $this->assertIsArray($response, 2); + $this->assertEquals([1, -1], array_values($response)); + } } public function testXTrim() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); for ($maxlen = 0; $maxlen <= 50; $maxlen += 10) { $this->addStreamEntries('stream', 100); $trimmed = $this->redis->xTrim('stream', $maxlen); - $this->assertEquals($trimmed, 100 - $maxlen); + $this->assertEquals(100 - $maxlen, $trimmed); } /* APPROX trimming isn't easily deterministic, so just make sure we can call it with the flag */ $this->addStreamEntries('stream', 100); - $this->assertFalse($this->redis->xTrim('stream', 1, true) === false); + $this->assertEquals(0, $this->redis->xTrim('stream', 1, true)); /* We need Redis >= 6.2.0 for MINID and LIMIT options */ - if (!$this->minVersionCheck("6.2.0")) + if ( ! $this->minVersionCheck('6.2.0')) return; $this->assertEquals(1, $this->redis->del('stream')); @@ -6948,14 +7628,14 @@ public function testXTrim() { /* TODO: Figure oiut how to test LIMIT deterministically. For now just send a LIMIT and verify we don't get a failure from Redis. */ - $this->assertTrue(is_int($this->redis->xtrim('stream', 2, true, false, 3))); + $this->assertIsInt(@$this->redis->xtrim('stream', 2, false, false, 3)); } /* XCLAIM is one of the most complicated commands, with a great deal of different options * The following test attempts to verify every combination of every possible option. */ public function testXClaim() { - if (!$this->minVersionCheck("5.0")) - return $this->markTestSkipped(); + if ( ! $this->minVersionCheck('5.0')) + $this->markTestSkipped(); foreach ([0, 100] as $min_idle_time) { foreach ([false, true] as $justid) { @@ -6993,7 +7673,7 @@ public function testXClaim() { /* Now have pavlo XCLAIM them */ $cids = $this->redis->xClaim('s', 'group1', 'Pavlo', $min_idle_time, $oids, $opts); - if (!$justid) $cids = array_keys($cids); + if ( ! $justid) $cids = array_keys($cids); if ($min_idle_time == 0) { $this->assertEquals($cids, $oids); @@ -7002,7 +7682,7 @@ public function testXClaim() { * assigned to a PEL group */ $opts[] = 'FORCE'; $freturn = $this->redis->xClaim('f', 'group1', 'Test', 0, $fids, $opts); - if (!$justid) $freturn = array_keys($freturn); + if ( ! $justid) $freturn = array_keys($freturn); $this->assertEquals($freturn, $fids); if ($retrycount || $tvalue !== NULL) { @@ -7014,19 +7694,19 @@ public function testXClaim() { if ($tvalue !== NULL) { if ($ttype == 'IDLE') { /* If testing IDLE the value must be >= what we set */ - $this->assertTrue($pending[0][2] >= $tvalue); + $this->assertGTE($tvalue, $pending[0][2]); } else { /* Timing tests are notoriously irritating but I don't see * how we'll get >= 20,000 ms between XCLAIM and XPENDING no * matter how slow the machine/VM running the tests is */ - $this->assertTrue($pending[0][2] <= 20000); + $this->assertLT(20000, $pending[0][2]); } } } } else { /* We're verifying that we get no messages when we've set 100 seconds * as our idle time, which should match nothing */ - $this->assertEquals($cids, []); + $this->assertEquals([], $cids); } } } @@ -7041,7 +7721,12 @@ public function testXAutoClaim() { // Test an empty xautoclaim reply $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); - $this->assertEquals(['0-0', [], []], $res); + $this->assertTrue(is_array($res) && (count($res) == 2 || count($res) == 3)); + if (count($res) == 2) { + $this->assertEquals(['0-0', []], $res); + } else { + $this->assertEquals(['0-0', [], []], $res); + } $this->redis->xAdd('ships', '1424-74205', ['name' => 'Defiant']); @@ -7052,21 +7737,20 @@ public function testXAutoClaim() { $pending = $this->redis->xPending('ships', 'combatants'); $this->assertTrue($pending && isset($pending[3][0][0]) && $pending[3][0][0] == "Jem'Hadar"); - // Asssume control of the pending message with a different consumer. + // Assume control of the pending message with a different consumer. $res = $this->redis->xAutoClaim('ships', 'combatants', 'Sisko', 0, '0-0'); - $this->assertTrue($res && count($res) == 3 && $res[0] == '0-0' && - isset($res[1]['1424-74205']['name']) && - $res[1]['1424-74205']['name'] == 'Defiant'); + $this->assertTrue($res && (count($res) == 2 || count($res) == 3)); + $this->assertTrue(isset($res[1]['1424-74205']['name']) && + $res[1]['1424-74205']['name'] == 'Defiant'); // Now the 'Sisko' consumer should own the message $pending = $this->redis->xPending('ships', 'combatants'); $this->assertTrue(isset($pending[3][0][0]) && $pending[3][0][0] == 'Sisko'); } - public function testXInfo() - { - if (!$this->minVersionCheck("5.0")) + public function testXInfo() { + if ( ! $this->minVersionCheck('5.0')) $this->markTestSkipped(); /* Create some streams and groups */ @@ -7075,30 +7759,31 @@ public function testXInfo() $this->addStreamsAndGroups([$stream], 1, $groups); $info = $this->redis->xInfo('GROUPS', $stream); - $this->assertTrue(is_array($info)); + $this->assertIsArray($info); $this->assertEquals(count($info), count($groups)); foreach ($info as $group) { - $this->assertTrue(array_key_exists('name', $group)); - $this->assertTrue(array_key_exists($group['name'], $groups)); + $this->assertArrayKey($group, 'name'); + $this->assertArrayKey($groups, $group['name']); } $info = $this->redis->xInfo('STREAM', $stream); - $this->assertTrue(is_array($info)); - $this->assertTrue(array_key_exists('groups', $info)); - $this->assertEquals($info['groups'], count($groups)); + $this->assertIsArray($info); + $this->assertArrayKey($info, 'groups', function ($v) use ($groups) { + return count($groups) == $v; + }); + foreach (['first-entry', 'last-entry'] as $key) { - $this->assertTrue(array_key_exists($key, $info)); - $this->assertTrue(is_array($info[$key])); + $this->assertArrayKey($info, $key, 'is_array'); } /* Ensure that default/NULL arguments are ignored */ $info = $this->redis->xInfo('STREAM', $stream, NULL); - $this->assertTrue(is_array($info)); + $this->assertIsArray($info); $info = $this->redis->xInfo('STREAM', $stream, NULL, -1); - $this->assertTrue(is_array($info)); + $this->assertIsArray($info); /* XINFO STREAM FULL [COUNT N] Requires >= 6.0.0 */ - if (!$this->minVersionCheck("6.0")) + if ( ! $this->minVersionCheck('6.0')) return; /* Add some items to the stream so we can test COUNT */ @@ -7107,7 +7792,7 @@ public function testXInfo() } $info = $this->redis->xInfo('STREAM', $stream, 'full'); - $this->assertTrue(isset($info['groups'])); + $this->assertArrayKey($info, 'length', 'is_numeric'); for ($count = 1; $count < 5; $count++) { $info = $this->redis->xInfo('STREAM', $stream, 'full', $count); @@ -7125,9 +7810,9 @@ public function testXInfo() /* Make sure we can't erroneously send non-null args after null ones */ $this->redis->clearLastError(); $this->assertFalse(@$this->redis->xInfo('FOO', NULL, 'fail', 25)); - $this->assertEquals(NULL, $this->redis->getLastError()); + $this->assertNull($this->redis->getLastError()); $this->assertFalse(@$this->redis->xInfo('FOO', NULL, NULL, -2)); - $this->assertEquals(NULL, $this->redis->getLastError()); + $this->assertNull($this->redis->getLastError()); } /* Regression test for issue-1831 (XINFO STREAM on an empty stream) */ @@ -7137,372 +7822,780 @@ public function testXInfoEmptyStream() { $this->redis->xAdd('s', '*', ['foo' => 'bar']); $this->redis->xTrim('s', 0); - $arr_info = $this->redis->xInfo('STREAM', 's'); + $info = $this->redis->xInfo('STREAM', 's'); - $this->assertTrue(is_array($arr_info)); - $this->assertEquals(0, $arr_info['length']); - $this->assertEquals(NULL, $arr_info['first-entry']); - $this->assertEquals(NULL, $arr_info['last-entry']); + $this->assertIsArray($info); + $this->assertEquals(0, $info['length']); + $this->assertNull($info['first-entry']); + $this->assertNull($info['last-entry']); } - public function testInvalidAuthArgs() { - $obj_new = $this->newInstance(); + public function testVAdd() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); - $arr_args = [ - [], - [NULL, NULL], - ['foo', 'bar', 'baz'], - ['a','b','c','d'], - ['a','b','c'], - [['a','b'], 'a'], - [['a','b','c']], - [[NULL, 'pass']], - [[NULL, NULL]], - ]; + $this->assertIsInt($this->redis->del('v')); - foreach ($arr_args as $arr_arg) { - try { - if (is_array($arr_arg)) { - @call_user_func_array([$obj_new, 'auth'], $arr_arg); - } - } catch (Exception $ex) { - unset($ex); /* Suppress intellisense warning */ - } catch (ArgumentCountError $ex) { - unset($ex); /* Suppress intellisense warning */ - } + foreach ([1, 0] as $expected) { + $this->assertEquals($expected, $this->redis->vadd('v', [0.5, 1.0], 'e')); } - } - public function testAcl() { - if ( ! $this->minVersionCheck("6.0")) - return $this->markTestSkipped(); + $this->assertEquals(1, $this->redis->del('v')); - /* ACL USERS/SETUSER */ - $this->assertTrue(in_array('default', $this->redis->acl('USERS'))); - $this->assertTrue($this->redis->acl('SETUSER', 'admin', 'on', '>admin', '+@all')); - $this->assertTrue($this->redis->acl('SETUSER', 'noperm', 'on', '>noperm', '-@all')); - $this->assertInArray('default', $this->redis->acl('USERS')); + foreach ([1, 0] as $expected) { + /* With tons of options */ + $res = $this->redis->vadd('v', [3.14, 2.71], 'e', [ + 'REDUCE' => 2, 'EF' => 16, 'M' => 28, 'CAS', 'VALUES', 'Q8', + 'SETATTR' => ['foo' => 'bar'], + ]); + $this->assertEquals($expected, $res); + } + } - /* Verify ACL GETUSER has the correct hash and is in 'nice' format */ - $arr_admin = $this->redis->acl('GETUSER', 'admin'); - $this->assertInArray(hash('sha256', 'admin'), $arr_admin['passwords']); + public function testVSim() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); - /* Now nuke our 'admin' user and make sure it went away */ - $this->assertTrue($this->redis->acl('DELUSER', 'admin')); - $this->assertTrue(!in_array('admin', $this->redis->acl('USERS'))); + $captains = [ + 'Archer' => [[0.7628, 0.5403, 0.0729], 'Enterprise-NX01'], + 'Janeway' => [[0.7073, 0.2171, 0.2673], 'Voyager'], + 'Kirk' => [[0.2555, 0.4938, 0.6968], 'Enterprise'], + 'Picard' => [[0.0570, 0.3547, 0.0721], 'Enterprise-D'], + 'Pike' => [[0.7916, 0.8514, 0.2733], 'Enterprise'], + 'Sisko' => [[0.0697, 0.1455, 0.2886], 'Defiant'], + ]; - /* Try to log in with a bad username/password */ - $this->assertThrowsMatch($this->redis, - function($o) { $o->auth(['1337haxx00r', 'lolwut']); }, '/^WRONGPASS.*$/'); + $this->redis->del('captains'); - /* We attempted a bad login. We should have an ACL log entry */ - $arr_log = $this->redis->acl('log'); - if (! $arr_log || !is_array($arr_log)) { - $this->assertTrue(false); - return; + foreach ($captains as $captain => [$vector, $ship]) { + $opt = ['SETATTR' => ['ship' => $ship]]; + $res = $this->redis->vadd('captains', $vector, $captain, $opt); + $this->assertEquals(1, $res); } - /* Make sure our ACL LOG entries are nice for the user */ - $arr_entry = array_shift($arr_log); - $this->assertArrayKey($arr_entry, 'age-seconds', 'is_numeric'); - $this->assertArrayKey($arr_entry, 'count', 'is_int'); + /* We should infer ELE mode */ + $res = $this->redis->vSim('captains', 'Archer'); + $this->assertIsArray($res); + $this->assertEquals($res[0], 'Archer'); - /* ACL CAT */ - $cats = $this->redis->acl('CAT'); - foreach (['read', 'write', 'slow'] as $cat) { - $this->assertInArray($cat, $cats); - } + /* We should infer FP32 mode */ + $res = $this->redis->vsim('captains', $captains['Archer'][0]); + $this->assertIsArray($res); + $this->assertEquals($res[0], 'Archer'); - /* ACL CAT */ - $cats = $this->redis->acl('CAT', 'string'); - foreach (['get', 'set', 'setnx'] as $cat) { - $this->assertInArray($cat, $cats); + /* Reject FP32/VALUE mode with non-arrays */ + foreach (['Archer', 3.14, 42, new stdClass] as $e) { + foreach (['FP32', 'VALUES'] as $mode) { + $res = @$this->redis->vsim('captains', $e, [$mode]); + $this->assertFalse($res); + } } - /* ctype_xdigit even if PHP doesn't have it */ - $ctype_xdigit = function($v) { - if (function_exists('ctype_xdigit')) { - return ctype_xdigit($v); - } else { - return strspn(strtoupper($v), '0123456789ABCDEF') == strlen($v); - } - }; + /* VALUES */ + $opt = ['VALUES']; + $res = $this->redis->vsim('captains', $captains['Kirk'][0], $opt); + $this->assertIsArray($res); + $this->assertEquals($res[0], 'Kirk'); - /* ACL GENPASS/ACL GENPASS */ - $this->assertValidate($this->redis->acl('GENPASS'), $ctype_xdigit); - $this->assertValidate($this->redis->acl('GENPASS', 1024), $ctype_xdigit); + /* EF */ + $opt = ['EF' => 24]; + $res = $this->redis->vsim('captains', $captains['Pike'][0], $opt); + $this->assertIsArray($res); + $this->assertEquals($res[0], 'Pike'); - /* ACL WHOAMI */ - $this->assertValidate($this->redis->acl('WHOAMI'), 'strlen'); + /* FILTER + FILTER-EF */ + $opt = ['FILTER' => '.ship == "Defiant"', 'FILTER-EF' => 24]; + $res = $this->redis->vsim('captains', 'Archer', $opt); + $this->assertIsArray($res); + $this->assertEquals($res[0], 'Sisko'); - /* Finally make sure AUTH errors throw an exception */ - $r2 = $this->newInstance(true); + /* COUNT */ + $opt = ['COUNT' => 1]; + $res = $this->redis->vsim('captains', 'Sisko', $opt); + $this->assertIsArray($res); + $this->assertEquals($res[0], 'Sisko'); + $this->assertEquals(1, count($res)); - /* Test NOPERM exception */ - $this->assertTrue($r2->auth(['noperm', 'noperm'])); - $this->assertThrowsMatch($r2, function($r) { $r->set('foo', 'bar'); }, '/^NOPERM.*$/'); - } + /* WITHSCORES */ + $opt = ['WITHSCORES']; + $res = $this->redis->vsim('captains', 'Janeway', $opt); + $this->assertIsArray($res); + $this->assertGT(1, count($res)); - /* If we detect a unix socket make sure we can connect to it in a variety of ways */ - public function testUnixSocket() { - if ( ! file_exists("/tmp/redis.sock")) { - return $this->markTestSkipped(); + foreach ($res as $captain => $score) { + $this->assertIsString($captain); + $this->assertIsFloat($score); } - $arr_sock_tests = [ - ["/tmp/redis.sock"], - ["/tmp/redis.sock", null], - ["/tmp/redis.sock", 0], - ["/tmp/redis.sock", -1], - ]; + /* NOTHREAD + TRUTH */ + $opt = ['NOTHREAD', 'TRUTH']; + $res = $this->redis->vsim('captains', 'Picard', $opt); + $this->assertEquals($res[0], 'Picard'); + } - try { - foreach ($arr_sock_tests as $arr_args) { - $obj_r = new Redis(); + public function testVCard() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); - if (count($arr_args) == 2) { - @$obj_r->connect($arr_args[0], $arr_args[1]); - } else { - @$obj_r->connect($arr_args[0]); - } - if ($this->getAuth()) { - $this->assertTrue($obj_r->auth($this->getAuth())); - } - $this->assertTrue($obj_r->ping()); - } - } catch (Exception $ex) { - $this->assertTrue(false); + $this->assertIsInt($this->redis->del('v')); + + for ($i = 0; $i < 5; $i++) { + $this->assertEquals(1, $this->redis->vadd('v', [0.5, 1.0], "e{$i}")); + $this->assertEquals($i + 1, $this->redis->vcard('v')); } + + $this->assertEquals(1, $this->redis->del('v')); + $this->assertEquals(0, $this->redis->vcard('v')); } - /* Test high ports if we detect Redis running there */ - public function testHighPorts() { - $arr_ports = [32767, 32768, 32769]; - $arr_test_ports = []; + public function testVDim() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); + + foreach ([[0.5, 1.0], [0.5, 1.0, 1.5]] as $v) { + $this->assertIsInt($this->redis->del('v')); + $this->assertEquals(1, $this->redis->vadd('v', $v, 'e')); + $this->assertEquals(count($v), $this->redis->vdim('v')); + } + + $this->assertEquals(1, $this->redis->del('v')); + } + + public function testVInfo() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); + + $this->assertIsInt($this->redis->del('v')); + $this->assertEquals(1, $this->redis->vadd('v', [0.5, 1.0, 1.5], 'e')); + + $res = $this->redis->vinfo('v'); + $this->assertIsArray($res); + $this->assertArrayKey($res, 'vector-dim', function ($v) { + return $v === 3; + }); + + $this->assertEquals(1, $this->redis->del('v')); + } + + public function testVIsMember() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); + + $this->assertIsInt($this->redis->del('v')); + $this->assertEquals(1, $this->redis->vadd('v', [0.5, 1.0], 'exists')); + + $this->assertEquals(true, $this->redis->vismember('v', 'exists')); + $this->assertEquals(false, $this->redis->vismember('v', 'doesnotexist')); + } + + public function testVEmb() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); - foreach ($arr_ports as $port) { - if (is_resource(@fsockopen('localhost', $port))) { - $arr_test_ports[] = $port; + $this->assertIsInt($this->redis->del('v')); + + $this->assertEquals(1, $this->redis->vadd('v', [0.5, 1.0], 'e')); + + $res = $this->redis->vemb('v', 'e'); + $this->assertIsArray($res); + $this->assertTrue(filter_var($res[0], FILTER_VALIDATE_FLOAT) !== false); + + $res = $this->redis->vemb('v', 'e', true); + $this->assertIsArray($res); + $this->assertEquals('int8', $res[0]); + + $this->assertEquals(1, $this->redis->del('v')); + } + + public function testVRem() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); + + $this->assertIsInt($this->redis->del('v')); + + $captains = ['Sisko', 'Janeway']; + foreach ($captains as $captain) { + $v = [mt_rand() / mt_getrandmax(), mt_rand() / mt_getrandmax()]; + $this->assertEquals(1, $this->redis->vadd('v', $v, $captain)); + } + + while ($captains) { + $captain = array_shift($captains); + foreach ([1, 0] as $e) { + $this->assertEquals($e, $this->redis->vrem('v', $captain)); } + + $this->assertEquals(count($captains), $this->redis->vcard('v')); } - if ( ! $arr_test_ports) { - return $this->markTestSkipped(); + $this->assertEquals(0, $this->redis->vcard('v')); + } + + public function testVGetAttr() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); + + $attr = ['foo' => 'bar']; + $json = json_encode($attr); + + $this->assertIsInt($this->redis->del('v')); + $this->assertEquals(1, $this->redis->vadd( + 'v', [1.5, 2.5], 'e1', ['SETATTR' => $attr]) + ); + + $this->assertEquals(1, $this->redis->vadd('v', [1.5, 2.5], 'e2')); + + if (defined('Redis::SERIALIZER_JSON')) { + $expected = [false => $attr, true => $json]; + } else { + $expected = [false => $json, true => $json]; } - foreach ($arr_test_ports as $port) { - $obj_r = new Redis(); + foreach ($expected as $raw => $e) { + $this->assertEquals($e, $this->redis->vgetattr('v', 'e1', $raw)); + } + + foreach ([[], [false], [true]] as $arg) { + $this->assertEquals(false, $this->redis->vgetattr('v', 'e2', ...$arg)); + } + } + + public function testVSetAttr() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); + + $this->assertIsInt($this->redis->del('v')); + $this->assertEquals(1, $this->redis->vadd('v', [1.5, 2.5], 'e')); + + $attr = ['foo' => 'bar']; + + $this->assertEquals(1, $this->redis->vsetattr('v', 'e', json_encode($attr))); + + if (defined('Redis::SERIALIZER_JSON')) { + $this->assertEquals(1, $this->redis->vsetattr('v', 'e', $attr)); + } + } + + public function testVRandMember() { + if ( ! $this->minVersionCheck('8.0')) + $this->markTestSkipped(); + + $ships = ['Enterprise', 'Defiant', 'Voyager']; + + $this->assertIsInt($this->redis->del('v')); + foreach ($ships as $ship) { + $this->assertEquals(1, $this->redis->vadd('v', [0.5, 1.0], $ship)); + } + + $this->assertInArray($this->redis->vrandmember('v'), $ships); + $this->assertEqualsCanonicalizing( + $ships, + $this->redis->vrandmember('v', 2 * count($ships)) + ); + + $this->assertEquals( + 2 * count($ships), + count($this->redis->vrandmember('v', -2 * count($ships))) + ); + } + + public function testVRange() { + if ($this->haveCommand('VRANGE') === false) + $this->markTestSkipped(); + + $this->assertIsInt($this->redis->del('v')); + + for ($i = 0; $i < 10; $i++) { + $this->assertEquals(1, $this->redis->vadd('v', [$i / 10, $i / 10], "e{$i}")); + } + + $res = $this->redis->vrange('v', '-', '+'); + $this->assertIsArray($res); + $this->assertEquals(10, count($res)); + + $res = $this->redis->vrange('v', '-', '+', 3); + $this->assertIsArray($res); + $this->assertEquals(3, count($res)); + } + + public function testGcra() { + if ( ! $this->haveCommand('GCRA')) + $this->markTestSkipped(); + + $this->assertIsInt($this->redis->del('gcra')); + + $res = $this->redis->gcra('gcra', 5, 1, 1000); + $this->assertIsArray($res); + $this->assertEquals(5, count($res)); + $this->assertEquals(5, count(array_filter($res, 'is_int'))); + + $res = $this->redis->gcra('gcra', 5, 1, 1000, 5); + $this->assertIsArray($res); + $this->assertEquals(5, count($res)); + $this->assertEquals(5, count(array_filter($res, 'is_int'))); + } + + public function testInvalidAuthArgs() { + $client = $this->newInstance(); + + $args = [ + [], + [NULL, NULL], + ['foo', 'bar', 'baz'], + ['a', 'b', 'c', 'd'], + ['a', 'b', 'c'], + [['a', 'b'], 'a'], + [['a', 'b', 'c']], + [[NULL, 'pass']], + [[NULL, NULL]], + ]; + + foreach ($args as $arg) { try { - @$obj_r->connect('localhost', $port); + if (is_array($arg)) { + @call_user_func_array([$client, 'auth'], $arg); + } + } catch (Exception $ex) { + unset($ex); /* Suppress intellisense warning */ + } catch (ArgumentCountError $ex) { + unset($ex); /* Suppress intellisense warning */ + } + } + } + + public function testAcl() { + if ( ! $this->minVersionCheck('6.0')) + $this->markTestSkipped(); + + /* ACL USERS/SETUSER */ + $this->assertTrue($this->redis->acl('SETUSER', 'admin', 'on', '>admin', '+@all')); + $this->assertTrue($this->redis->acl('SETUSER', 'noperm', 'on', '>noperm', '-@all')); + $this->assertInArray('default', $this->redis->acl('USERS')); + + /* Verify ACL GETUSER has the correct hash and is in 'nice' format */ + $admin = $this->redis->acl('GETUSER', 'admin'); + $this->assertInArray(hash('sha256', 'admin'), $admin['passwords']); + + /* Now nuke our 'admin' user and make sure it went away */ + $this->assertEquals(1, $this->redis->acl('DELUSER', 'admin')); + $this->assertFalse(in_array('admin', $this->redis->acl('USERS'))); + + /* Try to log in with a bad username/password */ + $this->assertThrowsMatch($this->redis, + function($o) { $o->auth(['1337haxx00r', 'lolwut']); }, '/^WRONGPASS.*$/'); + + /* We attempted a bad login. We should have an ACL log entry */ + $log = $this->redis->acl('log'); + if ( ! $log || !is_array($log)) { + $this->assert('Expected an array from ACL LOG, got: ' . var_export($log, true)); + return; + } + + /* Make sure our ACL LOG entries are nice for the user */ + $entry = array_shift($log); + $this->assertArrayKey($entry, 'age-seconds', 'is_numeric'); + $this->assertArrayKey($entry, 'count', 'is_int'); + + /* ACL CAT */ + $cats = $this->redis->acl('CAT'); + foreach (['read', 'write', 'slow'] as $cat) { + $this->assertInArray($cat, $cats); + } + + /* ACL CAT */ + $cats = $this->redis->acl('CAT', 'string'); + foreach (['get', 'set', 'setnx'] as $cat) { + $this->assertInArray($cat, $cats); + } + + /* ctype_xdigit even if PHP doesn't have it */ + $ctype_xdigit = function($v) { + if (function_exists('ctype_xdigit')) { + return ctype_xdigit($v); + } else { + return strspn(strtoupper($v), '0123456789ABCDEF') == strlen($v); + } + }; + + /* ACL GENPASS/ACL GENPASS */ + $this->assertValidate($this->redis->acl('GENPASS'), $ctype_xdigit); + $this->assertValidate($this->redis->acl('GENPASS', 1024), $ctype_xdigit); + + /* ACL WHOAMI */ + $this->assertValidate($this->redis->acl('WHOAMI'), 'strlen'); + + /* Finally make sure AUTH errors throw an exception */ + $r2 = $this->newInstance(true); + + /* Test NOPERM exception */ + $this->assertTrue($r2->auth(['noperm', 'noperm'])); + $this->assertThrowsMatch($r2, function($r) { $r->set('foo', 'bar'); }, '/^NOPERM.*$/'); + } + + /* If we detect a unix socket make sure we can connect to it in a variety of ways */ + public function testUnixSocket() { + if ( ! file_exists('/tmp/redis.sock')) + $this->markTestSkipped(); + + $sock_tests = [ + ['/tmp/redis.sock'], + ['/tmp/redis.sock', null], + ['/tmp/redis.sock', 0], + ['/tmp/redis.sock', -1], + ]; + + try { + foreach ($sock_tests as $args) { + $redis = new Redis(); + + if (count($args) == 2) { + @$redis->connect($args[0], $args[1]); + } else { + @$redis->connect($args[0]); + } + if ($this->getAuth()) { + $this->assertTrue($redis->auth($this->getAuth())); + } + $this->assertTrue($redis->ping()); + } + } catch (Exception $ex) { + $this->assert("Exception: {$ex}"); + } + } + + protected function detectRedis($host, $port) { + $sock = @fsockopen($host, $port, $errno, $errstr, .1); + if ( ! $sock) + return false; + + stream_set_timeout($sock, 0, 100000); + + $ping_cmd = "*1\r\n$4\r\nPING\r\n"; + if (fwrite($sock, $ping_cmd) != strlen($ping_cmd)) + return false; + + return fread($sock, strlen("+PONG\r\n")) == "+PONG\r\n"; + } + + /* Test high ports if we detect Redis running there */ + public function testHighPorts() { + $ports = array_filter(array_map(function ($port) { + return $this->detectRedis('localhost', $port) ? $port : 0; + }, [32768, 32769, 32770])); + + if ( ! $ports) + $this->markTestSkipped(); + + foreach ($ports as $port) { + $redis = new Redis(); + try { + @$redis->connect('localhost', $port); if ($this->getAuth()) { - $this->assertTrue($obj_r->auth($this->getAuth())); + $this->assertTrue($redis->auth($this->getAuth())); } - $this->assertTrue($obj_r->ping()); + $this->assertTrue($redis->ping()); } catch(Exception $ex) { - $this->assertTrue(false); + $this->assert("Exception: $ex"); } } } - public function testSession_savedToRedis() - { - $this->setSessionHandler(); + protected function sessionRunner() { + $this->getAuthParts($user, $pass); + + return (new SessionHelpers\Runner()) + ->prefix($this->sessionPrefix()) + ->handler($this->sessionSaveHandler()) + ->savePath($this->sessionSavePath()); + } + + protected function testRequiresMode(string $mode) { + if (php_sapi_name() != $mode) { + $this->markTestSkipped("Test requires PHP running in '$mode' mode"); + } + } + + public function testSession_compression() { + $this->testRequiresMode('cli'); + + foreach ($this->getCompressors() as $name => $val) { + $data = "testing_compression_$name"; + + $runner = $this->sessionRunner() + ->maxExecutionTime(300) + ->lockingEnabled(true) + ->lockWaitTime(-1) + ->lockExpires(0) + ->data($data) + ->compression($name); - $sessionId = $this->generateSessionId(); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); + $this->assertEquals('SUCCESS', $runner->execFg()); - $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId)); - $this->assertTrue($sessionSuccessful); + $this->redis->setOption(Redis::OPT_COMPRESSION, $val); + $this->assertPatternMatch("/.*$data.*/", $this->redis->get($runner->getSessionKey())); + $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE); + } } - public function testSession_lockKeyCorrect() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); + public function testSession_savedToRedis() { + $this->testRequiresMode('cli'); - $this->startSessionProcess($sessionId, 5, true); + $runner = $this->sessionRunner(); - $maxwait = (ini_get('redis.session.lock_wait_time') * - ini_get('redis.session.lock_retries') / - 1000000.00); + $this->assertEquals('SUCCESS', $runner->execFg()); + $this->assertKeyExists($runner->getSessionKey()); + } - $exist = $this->waitForSessionLockKey($sessionId, $maxwait); - $this->assertTrue($exist); + protected function sessionWaitUsec() { + return ini_get('redis.session.lock_wait_time') * + ini_get('redis.session.lock_retries'); } - public function testSession_lockingDisabledByDefault() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 5, true, 300, false); - usleep(100000); + protected function sessionWaitSec() { + return $this->sessionWaitUsec() / 1000000.0; + } - $start = microtime(true); - $sessionSuccessful = $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, false); - $end = microtime(true); - $elapsedTime = $end - $start; + public function testSession_lockKeyCorrect() { + $this->testRequiresMode('cli'); - $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); - $this->assertTrue($elapsedTime < 1); - $this->assertTrue($sessionSuccessful); + $runner = $this->sessionRunner()->sleep(5); + + $this->assertTrue($runner->execBg()); + + if ( ! $runner->waitForLockKey($this->redis, $this->sessionWaitSec())) { + $this->externalCmdFailure($runner->getCmd(), $runner->output(), + "Failed waiting for session lock key '{$runner->getSessionLockKey()}'", + $runner->getExitCode()); + } } - public function testSession_lockReleasedOnClose() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 1, true); - $sleep = ini_get('redis.session.lock_wait_time') * ini_get('redis.session.lock_retries'); - usleep($sleep + 10000); - $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK')); + public function testSession_lockingDisabledByDefault() { + $this->testRequiresMode('cli'); + + $runner = $this->sessionRunner() + ->lockingEnabled(false) + ->sleep(5); + + $this->assertEquals('SUCCESS', $runner->execFg()); + $this->assertKeyMissing($runner->getSessionLockKey()); } - public function testSession_lock_ttlMaxExecutionTime() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 10, true, 2); + public function testSession_lockReleasedOnClose() { + $this->testRequiresMode('cli'); + + $runner = $this->sessionRunner() + ->sleep(1) + ->lockingEnabled(true); + + $this->assertTrue($runner->execBg()); + usleep($this->sessionWaitUsec() + 100000); + $this->assertKeyMissing($runner->getSessionLockKey()); + } + + public function testSession_lock_ttlMaxExecutionTime() { + $this->testRequiresMode('cli'); + + $runner1 = $this->sessionRunner() + ->sleep(10) + ->maxExecutionTime(2); + + $this->assertTrue($runner1->execBg()); usleep(100000); - $start = microtime(true); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); - $end = microtime(true); - $elapsedTime = $end - $start; + $runner2 = $this->sessionRunner() + ->id($runner1->getId()) + ->sleep(0); - $this->assertTrue($elapsedTime < 3); - $this->assertTrue($sessionSuccessful); + $st = microtime(true); + $this->assertEquals('SUCCESS', $runner2->execFg()); + $el = microtime(true) - $st; + $this->assertLT(4, $el); } - public function testSession_lock_ttlLockExpire() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 10, true, 300, true, null, -1, 2); + public function testSession_lock_ttlLockExpire() { + $this->testRequiresMode('cli'); + + $runner1 = $this->sessionRunner() + ->sleep(10) + ->maxExecutionTime(300) + ->lockingEnabled(true) + ->lockExpires(2); + + $this->assertTrue($runner1->execBg()); usleep(100000); - $start = microtime(true); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false); - $end = microtime(true); - $elapsedTime = $end - $start; + $runner2 = $this->sessionRunner() + ->id($runner1->getId()) + ->sleep(0); - $this->assertTrue($elapsedTime < 3); - $this->assertTrue($sessionSuccessful); + $st = microtime(true); + $this->assertEquals('SUCCESS', $runner2->execFg()); + $this->assertLT(3, microtime(true) - $st); } - public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 2, true, 300, true, null, -1, 1, 'firstProcess'); + public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { + $this->testRequiresMode('cli'); + + $id = 'test-id'; + + $runner = $this->sessionRunner() + ->sleep(2) + ->lockingEnabled(true) + ->lockExpires(1) + ->data('firstProcess'); + + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0) + ->lockingEnabled(true) + ->lockExpires(10) + ->data('secondProcess'); + + $this->assertTrue($runner->execBg()); usleep(1500000); // 1.5 sec - $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 10, 'secondProcess'); - sleep(1); + $this->assertEquals('SUCCESS', $runner2->execFg()); - $this->assertTrue($writeSuccessful); - $this->assertEquals('secondProcess', $this->getSessionData($sessionId)); + $this->assertEquals('secondProcess', $runner->getData()); } - public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $writeSuccessful = $this->startSessionProcess($sessionId, 2, false, 300, true, null, -1, 1, 'firstProcess'); + public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { + $this->testRequiresMode('cli'); - $this->assertFalse($writeSuccessful); - $this->assertTrue('firstProcess' !== $this->getSessionData($sessionId)); + $runner = $this->sessionRunner() + ->sleep(2) + ->lockingEnabled(true) + ->lockExpires(1) + ->data('firstProcess'); + + $this->assertNotEquals('SUCCESS', $runner->execFg()); + $this->assertNotEquals('firstProcess', $runner->getData()); } public function testSession_correctLockRetryCount() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); + $this->testRequiresMode('cli'); - /* Start another process and wait until it has the lock */ - $this->startSessionProcess($sessionId, 10, true); - if ( ! $this->waitForSessionLockKey($sessionId, 2)) { - $this->assertTrue(false); - return; + $runner = $this->sessionRunner() + ->sleep(10); + + $this->assertTrue($runner->execBg()); + if ( ! $runner->waitForLockKey($this->redis, 2)) { + $this->externalCmdFailure($runner->getCmd(), $runner->output(), + 'Failed waiting for session lock key', + $runner->getExitCode()); } - $t1 = microtime(true); - $ok = $this->startSessionProcess($sessionId, 0, false, 10, true, 100000, 10); - if ( ! $this->assertFalse($ok)) return; - $t2 = microtime(true); + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0) + ->maxExecutionTime(10) + ->lockingEnabled(true) + ->lockWaitTime(100000) + ->lockRetries(10); + + $st = microtime(true); + $ex = $runner2->execFg(); + if (stripos($ex, 'SUCCESS') !== false) { + $this->externalCmdFailure($runner2->getCmd(), $ex, + 'Expected failure but lock was acquired!', + $runner2->getExitCode()); + } + $et = microtime(true); - $this->assertTrue($t2 - $t1 >= 1 && $t2 - $t1 <= 3); + $this->assertBetween($et - $st, 1, 3); } - public function testSession_defaultLockRetryCount() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 10, true); + public function testSession_defaultLockRetryCount() { + $this->testRequiresMode('cli'); - $keyname = $this->sessionPrefix . $sessionId . '_LOCK'; - $begin = microtime(true); + $runner = $this->sessionRunner() + ->sleep(10); - if ( ! $this->waitForSessionLockKey($sessionId, 3)) { - $this->assertTrue(false); - return; - } + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0) + ->lockingEnabled(true) + ->maxExecutionTime(10) + ->lockWaitTime(20000) + ->lockRetries(0); + + $this->assertTrue($runner->execBg()); - $start = microtime(true); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 20000, 0); - $end = microtime(true); - $elapsedTime = $end - $start; + if ( ! $runner->waitForLockKey($this->redis, 3)) { + $this->externalCmdFailure($runner->getCmd(), $runner->output(), + 'Failed waiting for session lock key', + $runner->getExitCode()); + } - $this->assertTrue($elapsedTime > 2 && $elapsedTime < 3); - $this->assertFalse($sessionSuccessful); + $st = microtime(true); + $this->assertNotEquals('SUCCESS', $runner2->execFg()); + $et = microtime(true); + $this->assertBetween($et - $st, 2, 3); } - public function testSession_noUnlockOfOtherProcess() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); + public function testSession_noUnlockOfOtherProcess() { + $this->testRequiresMode('cli'); + + $st = microtime(true); + + $sleep = 3; - $t1 = microtime(true); + $runner = $this->sessionRunner() + ->sleep($sleep) + ->maxExecutionTime(3); + + $tm1 = microtime(true); /* 1. Start a background process, and wait until we are certain * the lock was attained. */ - $nsec = 3; - $this->startSessionProcess($sessionId, $nsec, true, $nsec); - if ( ! $this->waitForSessionLockKey($sessionId, 1)) { - $this->assertFalse(true); + $this->assertTrue($runner->execBg()); + if ( ! $runner->waitForLockKey($this->redis, 1)) { + $this->assert('Failed waiting for session lock key'); return; } /* 2. Attempt to lock the same session. This should force us to * wait until the first lock is released. */ - $t2 = microtime(true); - $ok = $this->startSessionProcess($sessionId, 0, false); - $t3 = microtime(true); + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0); + + $tm2 = microtime(true); + $this->assertEquals('SUCCESS', $runner2->execFg()); + $tm3 = microtime(true); - /* 3. Verify that we did in fact have to wait for this lock */ - $this->assertTrue($ok); - $this->assertTrue($t3 - $t2 >= $nsec - ($t2 - $t1)); + /* 3. Verify we had to wait for this lock */ + $this->assertGTE($sleep - ($tm2 - $tm1), $tm3 - $tm2); } - public function testSession_lockWaitTime() - { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 1, true, 300); + public function testSession_lockWaitTime() { + $this->testRequiresMode('cli'); + + $runner = $this->sessionRunner() + ->sleep(1) + ->maxExecutionTime(300); + + $runner2 = $this->sessionRunner() + ->id($runner->getId()) + ->sleep(0) + ->maxExecutionTime(300) + ->lockingEnabled(true) + ->lockWaitTime(3000000); + + $this->assertTrue($runner->execBg()); usleep(100000); - $start = microtime(true); - $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, 3000000); - $end = microtime(true); - $elapsedTime = $end - $start; + $st = microtime(true); + $this->assertEquals('SUCCESS', $runner2->execFg()); + $et = microtime(true); - $this->assertTrue($elapsedTime > 2.5); - $this->assertTrue($elapsedTime < 3.5); - $this->assertTrue($sessionSuccessful); + $this->assertBetween($et - $st, 2.5, 3.5); } public function testMultipleConnect() { $host = $this->redis->GetHost(); $port = $this->redis->GetPort(); - for($i = 0; $i < 5; $i++) { + for ($i = 0; $i < 5; $i++) { $this->redis->connect($host, $port); if ($this->getAuth()) { $this->assertTrue($this->redis->auth($this->getAuth())); @@ -7511,412 +8604,326 @@ public function testMultipleConnect() { } } + public function testConnectDatabaseSelect() { + $options = [ + 'host' => $this->getHost(), + 'port' => $this->getPort(), + 'database' => 2, + ]; + + if ($this->getAuth()) { + $options['auth'] = $this->getAuth(); + } + + $redis = new Redis($options); + $this->assertEquals(2, $redis->getDBNum()); + $this->assertEquals(2, $redis->client('info')['db']); + + $this->assertTrue($redis->select(1)); + + $this->assertEquals(1, $redis->getDBNum()); + $this->assertEquals(1, $redis->client('info')['db']); + } + public function testConnectException() { $host = 'github.com'; - if (gethostbyname($host) === $host) { - return $this->markTestSkipped('online test'); - } + if (gethostbyname($host) === $host) + $this->markTestSkipped('online test'); + $redis = new Redis(); try { $redis->connect($host, 6379, 0.01); } catch (Exception $e) { - $this->assertTrue(strpos($e, "timed out") !== false); + $this->assertStringContains('timed out', $e->getMessage()); } } - public function testTlsConnect() - { - if (($fp = @fsockopen($this->getHost(), 6378)) == NULL) - return $this->markTestSkipped(); + /* Regression test for GitHub PR #2802). Make sure we don't leak the + * context array when explicitly reconnecting in a loop */ + public function testTlsReconnect() { + $tls_port = $this->getTlsPort(); + if (($fp = @fsockopen($this->getHost(), $tls_port)) == NULL) + $this->markTestSkipped(); + + fclose($fp); + + $redis = new Redis; + + $context = ['stream' => [ + 'verify_peer_name' => false, + 'verify_peer' => false, + ]]; + + $base = memory_get_usage(true); + + for ($i = 0; $i < 20; $i++) { + $redis->connect('tls://' . $this->getHost(), $tls_port, 0, null, 0, + 0, $context); + } + + $final = memory_get_usage(true); + + $this->assertLTE(10000, $final - $base); + } + + public function testTlsConnect() { + $tls_port = $this->getTlsPort(); + + if (($fp = @fsockopen($this->getHost(), $tls_port)) == NULL) + $this->markTestSkipped(); fclose($fp); foreach (['localhost' => true, '127.0.0.1' => false] as $host => $verify) { $redis = new Redis(); - $this->assertTrue($redis->connect('tls://' . $host, 6378, 0, null, 0, 0, [ + $this->assertTrue($redis->connect('tls://' . $host, $tls_port, 0, null, 0, 0, [ 'stream' => ['verify_peer_name' => $verify, 'verify_peer' => false] ])); } } - public function testReset() - { - // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + public function testReset() { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->assertTrue($this->redis->multi()->select(2)->set('foo', 'bar')->reset()); $this->assertEquals(Redis::ATOMIC, $this->redis->getMode()); $this->assertEquals(0, $this->redis->getDBNum()); } - public function testCopy() - { - // Only available since 6.2.0 - if (version_compare($this->version, '6.2.0') < 0) { + public function testCopy() { + if (version_compare($this->version, '6.2.0') < 0) $this->markTestSkipped(); - return; - } $this->redis->del('{key}dst'); $this->redis->set('{key}src', 'foo'); $this->assertTrue($this->redis->copy('{key}src', '{key}dst')); - $this->assertEquals('foo', $this->redis->get('{key}dst')); + $this->assertKeyEquals('foo', '{key}dst'); $this->redis->set('{key}src', 'bar'); $this->assertFalse($this->redis->copy('{key}src', '{key}dst')); - $this->assertEquals('foo', $this->redis->get('{key}dst')); + $this->assertKeyEquals('foo', '{key}dst'); $this->assertTrue($this->redis->copy('{key}src', '{key}dst', ['replace' => true])); - $this->assertEquals('bar', $this->redis->get('{key}dst')); + $this->assertKeyEquals('bar', '{key}dst'); } - public function testCommand() - { + public function testCommand() { $commands = $this->redis->command(); - $this->assertTrue(is_array($commands)); + $this->assertIsArray($commands); $this->assertEquals(count($commands), $this->redis->command('count')); - $infos = $this->redis->command('info'); - $this->assertTrue(is_array($infos)); - $this->assertEquals(count($infos), count($commands)); + + if ( ! $this->is_keydb && $this->minVersionCheck('7.0')) { + $infos = $this->redis->command('info'); + $this->assertIsArray($infos); + $this->assertEquals(count($infos), count($commands)); + } if (version_compare($this->version, '7.0') >= 0) { $docs = $this->redis->command('docs'); - $this->assertTrue(is_array($docs)); + $this->assertIsArray($docs); $this->assertEquals(count($docs), 2 * count($commands)); $list = $this->redis->command('list', 'filterby', 'pattern', 'lol*'); - $this->assertTrue(is_array($list)); - $this->assertEquals($list, ['lolwut']); + $this->assertIsArray($list); + $this->assertEquals(['lolwut'], $list); } } public function testFunction() { - if (version_compare($this->version, '7.0') < 0) { + if (version_compare($this->version, '7.0') < 0) $this->markTestSkipped(); - return; - } + $this->assertTrue($this->redis->function('flush', 'sync')); $this->assertEquals('mylib', $this->redis->function('load', "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return args[1] end)")); $this->assertEquals('foo', $this->redis->fcall('myfunc', [], ['foo'])); $payload = $this->redis->function('dump'); $this->assertEquals('mylib', $this->redis->function('load', 'replace', "#!lua name=mylib\nredis.register_function{function_name='myfunc', callback=function(keys, args) return args[1] end, flags={'no-writes'}}")); $this->assertEquals('foo', $this->redis->fcall_ro('myfunc', [], ['foo'])); - $this->assertEquals($this->redis->function('stats'), ['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]]); + $this->assertEquals(['running_script' => false, 'engines' => ['LUA' => ['libraries_count' => 1, 'functions_count' => 1]]], $this->redis->function('stats')); $this->assertTrue($this->redis->function('delete', 'mylib')); $this->assertTrue($this->redis->function('restore', $payload)); - $this->assertEquals($this->redis->function('list'), [['library_name' => 'mylib', 'engine' => 'LUA', 'functions' => [['name' => 'myfunc', 'description' => false,'flags' => []]]]]); + $this->assertEquals([['library_name' => 'mylib', 'engine' => 'LUA', 'functions' => [['name' => 'myfunc', 'description' => false,'flags' => []]]]], $this->redis->function('list')); $this->assertTrue($this->redis->function('delete', 'mylib')); } + protected function execWaitAOF() { + return $this->redis->waitaof(0, 0, 0); + } + + public function testWaitAOF() { + if ( ! $this->minVersionCheck('7.2.0')) + $this->markTestSkipped(); + + $res = $this->execWaitAOF(); + $this->assertValidate($res, function ($v) { + if ( ! is_array($v) || count($v) != 2) + return false; + return isset($v[0]) && is_int($v[0]) && + isset($v[1]) && is_int($v[1]); + }); + } + /* Make sure we handle a bad option value gracefully */ public function testBadOptionValue() { $this->assertFalse(@$this->redis->setOption(pow(2, 32), false)); } - public function testSession_regenerateSessionId_noLock_noDestroy() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId); + protected function regenerateIdHelper(bool $lock, bool $destroy, bool $proxy) { + $this->testRequiresMode('cli'); - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); - } + $data = uniqid('regenerate-id:'); + $runner = $this->sessionRunner() + ->sleep(0) + ->maxExecutionTime(300) + ->lockingEnabled(true) + ->lockRetries(1) + ->data($data); - public function testSession_regenerateSessionId_noLock_withDestroy() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + $this->assertEquals('SUCCESS', $runner->execFg()); - $newSessionId = $this->regenerateSessionId($sessionId, false, true); + $new_id = $runner->regenerateId($lock, $destroy, $proxy); - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->assertNotEquals($runner->getId(), $new_id); + $this->assertEquals($runner->getData(), $runner->getData()); } - public function testSession_regenerateSessionId_withLock_noDestroy() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); + public function testSession_regenerateSessionId_noLock_noDestroy() { + $this->regenerateIdHelper(false, false, false); + } - $newSessionId = $this->regenerateSessionId($sessionId, true); + public function testSession_regenerateSessionId_noLock_withDestroy() { + $this->regenerateIdHelper(false, true, false); + } - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + public function testSession_regenerateSessionId_withLock_noDestroy() { + $this->regenerateIdHelper(true, false, false); } public function testSession_regenerateSessionId_withLock_withDestroy() { - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, true, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(true, true, false); } public function testSession_regenerateSessionId_noLock_noDestroy_withProxy() { - if (!interface_exists('SessionHandlerInterface')) { - $this->markTestSkipped('session handler interface not available in PHP < 5.4'); - } - - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, false, false, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(false, false, true); } public function testSession_regenerateSessionId_noLock_withDestroy_withProxy() { - if (!interface_exists('SessionHandlerInterface')) { - $this->markTestSkipped('session handler interface not available in PHP < 5.4'); - } - - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, false, true, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(false, true, true); } public function testSession_regenerateSessionId_withLock_noDestroy_withProxy() { - if (!interface_exists('SessionHandlerInterface')) { - $this->markTestSkipped('session handler interface not available in PHP < 5.4'); - } - - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, true, false, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(true, false, true); } public function testSession_regenerateSessionId_withLock_withDestroy_withProxy() { - if (!interface_exists('SessionHandlerInterface')) { - $this->markTestSkipped('session handler interface not available in PHP < 5.4'); - } - - $this->setSessionHandler(); - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar'); - - $newSessionId = $this->regenerateSessionId($sessionId, true, true, true); - - $this->assertTrue($newSessionId !== $sessionId); - $this->assertEquals('bar', $this->getSessionData($newSessionId)); + $this->regenerateIdHelper(true, true, true); } - public function testSession_ttl_equalsToSessionLifetime() - { - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); - $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); + public function testSession_ttl_equalsToSessionLifetime() { + $this->testRequiresMode('cli'); - $this->assertEquals(600, $ttl); + $runner = $this->sessionRunner()->lifetime(600); + $this->assertEquals('SUCCESS', $runner->execFg()); + $this->assertEquals(600, $this->redis->ttl($runner->getSessionKey())); } - public function testSession_ttl_resetOnWrite() - { - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); - $this->redis->expire($this->sessionPrefix . $sessionId, 9999); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); - $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); + public function testSession_ttl_resetOnWrite() { + $this->testRequiresMode('cli'); - $this->assertEquals(600, $ttl); - } + $runner1 = $this->sessionRunner()->lifetime(600); + $this->assertEquals('SUCCESS', $runner1->execFg()); - public function testSession_ttl_resetOnRead() - { - $sessionId = $this->generateSessionId(); - $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600); - $this->redis->expire($this->sessionPrefix . $sessionId, 9999); - $this->getSessionData($sessionId, 600); - $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId); + $runner2 = $this->sessionRunner()->id($runner1->getId())->lifetime(1800); + $this->assertEquals('SUCCESS', $runner2->execFg()); - $this->assertEquals(600, $ttl); + $this->assertEquals(1800, $this->redis->ttl($runner2->getSessionKey())); } - private function setSessionHandler() - { - $host = $this->getHost() ?: 'localhost'; + public function testSession_ttl_resetOnRead() { + $this->testRequiresMode('cli'); - @ini_set('session.save_handler', 'redis'); - @ini_set('session.save_path', 'tcp://' . $host . ':6379'); - } + $data = uniqid(__FUNCTION__); - /** - * @return string - */ - private function generateSessionId() - { - if (function_exists('session_create_id')) { - return session_create_id(); - } else if (function_exists('random_bytes')) { - return bin2hex(random_bytes(8)); - } else if (function_exists('openssl_random_pseudo_bytes')) { - return bin2hex(openssl_random_pseudo_bytes(8)); - } else { - return uniqid(); - } + $runner = $this->sessionRunner()->lifetime(600)->data($data); + $this->assertEquals('SUCCESS', $runner->execFg()); + $this->redis->expire($runner->getSessionKey(), 9999); + + $this->assertEquals($data, $runner->getData()); + $this->assertEquals(600, $this->redis->ttl($runner->getSessionKey())); } - /** - * @param string $sessionId - * @param int $sleepTime - * @param bool $background - * @param int $maxExecutionTime - * @param bool $locking_enabled - * @param int $lock_wait_time - * @param int $lock_retries - * @param int $lock_expires - * @param string $sessionData - * - * @param int $sessionLifetime - * - * @return bool - * @throws Exception - */ - private function startSessionProcess($sessionId, $sleepTime, $background, $maxExecutionTime = 300, $locking_enabled = true, $lock_wait_time = null, $lock_retries = -1, $lock_expires = 0, $sessionData = '', $sessionLifetime = 1440) - { - if (substr(php_uname(), 0, 7) == "Windows"){ + public function testDigest() { + if ( ! $this->haveCommand('DIGEST')) $this->markTestSkipped(); - return true; - } else { - $commandParameters = [$this->getFullHostPath(), $this->sessionSaveHandler, $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData, $sessionLifetime]; - if ($locking_enabled) { - $commandParameters[] = '1'; - if ($lock_wait_time != null) { - $commandParameters[] = $lock_wait_time; - } - } - $commandParameters = array_map('escapeshellarg', $commandParameters); - - $command = self::getPhpCommand('startSession.php') . implode(' ', $commandParameters); - $command .= $background ? ' 2>/dev/null > /dev/null &' : ' 2>&1'; - exec($command, $output); - return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')) ? true : false; - } - } + $key = 'foo'; + $val = 'bar'; - /** - * @param string $session_id - * @param string $max_wait_sec - * - * Sometimes we want to block until a session lock has been detected - * This is better and faster than arbitrarily sleeping. If we don't - * detect the session key within the specified maximum number of - * seconds, the function returns failure. - * - * @return bool - */ - private function waitForSessionLockKey($session_id, $max_wait_sec) { - $now = microtime(true); - $key = $this->sessionPrefix . $session_id . '_LOCK'; + $this->redis->set($key, $val); + $digest = $this->redis->digest($key); + $this->assertTrue(is_string($digest) && strlen($digest) == 16); - do { - usleep(10000); - $exists = $this->redis->exists($key); - } while (!$exists && microtime(true) <= $now + $max_wait_sec); + /* In PHP >= 8.1 we can verify the value */ + if (PHP_VERSION_ID >= 80100) { + $this->assertEquals($this->redis->_digest($val), $digest); - return $exists || $this->redis->exists($key); - } + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); + $this->assertTrue($this->redis->set($key, $val)); + $this->assertEquals( + $this->redis->_digest($val), + $this->redis->digest($key) + ); - /** - * @param string $sessionId - * @param int $sessionLifetime - * - * @return string - */ - private function getSessionData($sessionId, $sessionLifetime = 1440) - { - $command = self::getPhpCommand('getSessionData.php') . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . escapeshellarg($sessionId) . ' ' . escapeshellarg($sessionLifetime); - exec($command, $output); + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); + } else { + /* Make sure we don't crash when XXH3 isn't available */ + try { + $this->redis->_digest('foo'); + } catch (Exception $ex) { - return $output[0]; + } + } } - /** - * @param string $sessionId - * @param bool $locking - * @param bool $destroyPrevious - * @param bool $sessionProxy - * - * @return string - */ - private function regenerateSessionId($sessionId, $locking = false, $destroyPrevious = false, $sessionProxy = false) - { - $args = array_map('escapeshellarg', [$sessionId, $locking, $destroyPrevious, $sessionProxy]); + public function testDelEx() { + if ( ! $this->haveCommand('DELEX')) + $this->markTestSkipped(); - $command = self::getPhpCommand('regenerateSessionId.php') . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . implode(' ', $args); + $this->assertIsInt($this->redis->del('captain')); + $this->assertTrue($this->redis->set('captain', 'picard')); - exec($command, $output); + $this->assertEquals(0, $this->redis->delex('captain', ['ifeq' => 'not-picard'])); + $this->assertEquals(0, $this->redis->delex('captain', ['ifne' => 'picard'])); + $this->assertEquals(1, $this->redis->delex('captain', ['ifeq' => 'picard'])); - return $output[0]; - } + $this->assertTrue($this->redis->set('captain', 'Sisko')); + $digest = $this->redis->digest('captain'); + $this->assertEquals(0, $this->redis->delex('captain', ['ifdne' => $digest])); + $this->assertEquals(1, $this->redis->delex('captain', ['ifdeq' => $digest])); - /** - * Return command to launch PHP with built extension enabled - * taking care of environment (TEST_PHP_EXECUTABLE and TEST_PHP_ARGS) - * - * @param string $script - * - * @return string - */ - private function getPhpCommand($script) - { - static $cmd = NULL; + $this->assertTrue($this->redis->set('captain', 'Janeway')); + $this->assertEquals(1, $this->redis->delex('captain')); - if (!$cmd) { - $cmd = (getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY); + foreach ([[], null] as $arg) { + $this->assertTrue($this->redis->set('captain', 'Archer')); + $this->assertEquals(1, $this->redis->delex('captain', $arg)); + } + } - if ($test_args = getenv('TEST_PHP_ARGS')) { - $cmd .= ' '; - $cmd .= $test_args; - } else { - /* Only append specific extension directives if PHP hasn't been compiled with what we need statically */ - $modules = shell_exec("$cmd --no-php-ini -m"); - - /* Determine if we need to specifically add extensions */ - $arr_extensions = array_filter( - ['redis', 'igbinary', 'msgpack', 'json'], - function ($module) use ($modules) { - return strpos($modules, $module) === false; - } - ); + public function testAnonymousClassSerializationFailure() { + $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); - /* If any are needed add them to the command */ - if ($arr_extensions) { - $cmd .= ' --no-php-ini'; - foreach ($arr_extensions as $str_extension) { - /* We want to use the locally built redis extension */ - if ($str_extension == 'redis') { - $str_extension = dirname(__DIR__) . '/modules/redis'; - } + $obj = new class() {}; - $cmd .= " --define extension=$str_extension.so"; - } - } - } - } + $this->assertThrowsMatch(null, function () use ($obj) { + $this->redis->set('payload', $obj); + }, "/Serialization of 'class@anonymous' is not allowed/"); - return $cmd . ' ' . __DIR__ . '/' . $script . ' '; + /* Ensure extension remains stable after failure */ + $this->assertTrue($this->redis->set('after_failure', 'ok')); + $this->assertEquals('ok', $this->redis->get('after_failure')); } } ?> diff --git a/tests/SessionHelpers.php b/tests/SessionHelpers.php new file mode 100644 index 0000000000..c2fa12056f --- /dev/null +++ b/tests/SessionHelpers.php @@ -0,0 +1,358 @@ + null, + 'save-path' => null, + 'id' => null, + 'sleep' => 0, + 'max-execution-time' => 300, + 'locking-enabled' => true, + 'lock-wait-time' => null, + 'lock-retries' => -1, + 'lock-expires' => 0, + 'data' => '', + 'lifetime' => 1440, + 'compression' => 'none', + ]; + + private $prefix = NULL; + private $output_file = NULL; + private $exit_code = -1; + private $cmd = NULL; + private $pid; + private $output; + + public function __construct() { + $this->args['id'] = $this->createId(); + } + + public function __destruct() { + if ($this->output_file) { + unlink($this->output_file); + } + } + + public function getExitCode(): int { + return $this->exit_code; + } + + public function getCmd(): ?string { + return $this->cmd; + } + + public function getId(): ?string { + return $this->args['id']; + } + + public function prefix(string $prefix): self { + $this->prefix = $prefix; + return $this; + } + + public function getSessionKey(): string { + return $this->prefix . $this->getId(); + } + + public function getSessionLockKey(): string { + return $this->getSessionKey() . '_LOCK'; + } + + protected function set($setting, $v): self { + $this->args[$setting] = $v; + return $this; + } + + public function handler(string $handler): self { + return $this->set('handler', $handler); + } + + public function savePath(string $path): self { + return $this->set('save-path', $path); + } + + public function id(string $id): self { + return $this->set('id', $id); + } + + public function sleep(int $sleep): self { + return $this->set('sleep', $sleep); + } + + public function maxExecutionTime(int $time): self { + return $this->set('max-execution-time', $time); + } + + public function lockingEnabled(bool $enabled): self { + return $this->set('locking-enabled', $enabled); + } + + public function lockWaitTime(int $time): self { + return $this->set('lock-wait-time', $time); + } + + public function lockRetries(int $retries): self { + return $this->set('lock-retries', $retries); + } + + public function lockExpires(int $expires): self { + return $this->set('lock-expires', $expires); + } + + public function data(string $data): self { + return $this->set('data', $data); + } + + public function lifetime(int $lifetime): self { + return $this->set('lifetime', $lifetime); + } + + public function compression(string $compression): self { + return $this->set('compression', $compression); + } + + protected function validateArgs(array $required) { + foreach ($required as $req) { + if ( ! isset($this->args[$req]) || $this->args[$req] === null) + throw new \Exception("Command requires '$req' arg"); + } + } + + private function createId(): string { + if (function_exists('session_create_id')) + return session_create_id(); + + return uniqid(); + } + + private function getTmpFileName() { + return tempnam(sys_get_temp_dir(), 'session'); + } + + /* + * @param $client Redis client + * @param string $max_wait_sec + * + * Sometimes we want to block until a session lock has been detected + * This is better and faster than arbitrarily sleeping. If we don't + * detect the session key within the specified maximum number of + * seconds, the function returns failure. + * + * @return bool + */ + public function waitForLockKey($redis, $max_wait_sec) { + $now = microtime(true); + + do { + if ($redis->exists($this->getSessionLockKey())) + return true; + usleep(10000); + } while (microtime(true) <= $now + $max_wait_sec); + + return false; + } + + private function appendCmdArgs(array $args): string { + $append = []; + + foreach ($args as $arg => $val) { + if ( ! $val) + continue; + + if (is_string($val)) + $val = escapeshellarg($val); + + $append[] = "--$arg"; + $append[] = $val; + } + + return implode(' ', $append); + } + + private function buildPhpCmd(string $script, array $args): string { + return PhpSpawner::cmd($script) . ' ' . $this->appendCmdArgs($args); + } + + private function startSessionCmd(): string { + return $this->buildPhpCmd(self::start_script, $this->args); + } + + public function output(?int $timeout = NULL): ?string { + if ($this->output) { + var_dump("early return"); + return $this->output; + } + + if ( ! $this->output_file || ! $this->pid) { + throw new \Exception("Process was not started in the background"); + } + + $st = microtime(true); + + do { + if (pcntl_waitpid($this->pid, $exit_code, WNOHANG) == 0) + break; + usleep(100000); + } while ((microtime(true) - $st) < $timeout); + + if ( ! file_exists($this->output_file)) + return ""; + + $this->output = file_get_contents($this->output_file); + $this->output_file = NULL; + $this->exit_code = $exit_code; + $this->pid = NULL; + + return $this->output; + } + + public function execBg(): bool { + if ($this->cmd) + throw new \Exception("Command already executed!"); + + $output_file = $this->getTmpFileName(); + + $this->cmd = $this->startSessionCmd(); + $this->cmd .= " >$output_file 2>&1 & echo $!"; + + $pid = exec($this->cmd, $output, $exit_code); + $this->exit_code = $exit_code; + + if ($this->exit_code || !is_numeric($pid)) + return false; + + $this->pid = (int)$pid; + $this->output_file = $output_file; + + return true; + } + + public function execFg() { + if ($this->cmd) + throw new \Exception("Command already executed!"); + + $this->cmd = $this->startSessionCmd() . ' 2>&1'; + + exec($this->cmd, $output, $exit_code); + $this->exit_code = $exit_code; + $this->output = implode("\n", array_filter($output)); + + return $this->output; + } + + private function regenerateIdCmd($locking, $destroy, $proxy): string { + $this->validateArgs(['handler', 'id', 'save-path']); + + $args = [ + 'handler' => $this->args['handler'], + 'save-path' => $this->args['save-path'], + 'id' => $this->args['id'], + 'locking-enabled' => !!$locking, + 'destroy' => !!$destroy, + 'proxy' => !!$proxy, + ]; + + return $this->buildPhpCmd(self::regenerate_id_script, $args); + } + + public function regenerateId($locking = false, $destroy = false, $proxy = false) { + if ( ! $this->cmd) + throw new \Exception("Cannot regenerate id before starting session!"); + + $cmd = $this->regenerateIdCmd($locking, $destroy, $proxy); + + exec($cmd, $output, $exit_code); + + if ($exit_code != 0) + return false; + + return $output[0]; + } + + private function getDataCmd(?int $lifetime): string { + $this->validateArgs(['handler', 'save-path', 'id']); + + $args = [ + 'handler' => $this->args['handler'], + 'save-path' => $this->args['save-path'], + 'id' => $this->args['id'], + 'lifetime' => is_int($lifetime) ? $lifetime : $this->args['lifetime'], + ]; + + return $this->buildPhpCmd(self::get_data_script, $args); + } + + public function getData(?int $lifetime = NULL): string { + $cmd = $this->getDataCmd($lifetime); + + exec($cmd, $output, $exit_code); + if ($exit_code != 0) { + return implode("\n", $output); + } + + return $output[0]; + } +} diff --git a/tests/TestRedis.php b/tests/TestRedis.php index 843a28f884..303d05b951 100644 --- a/tests/TestRedis.php +++ b/tests/TestRedis.php @@ -1,86 +1,128 @@ 'Redis_Test', + 'redisarray' => 'Redis_Array_Test', + 'rediscluster' => 'Redis_Cluster_Test', + 'redissentinel' => 'Redis_Sentinel_Test' + ]; + + /* Return early if the class is one of our built-in ones */ + if (isset($valid_classes[$class])) + return $valid_classes[$class]; + + /* Try to load it */ + return TestSuite::loadTestClass($class); +} + +function raHosts($host, $ports) { + if ( ! is_array($ports)) + $ports = [6379, 6380, 6381, 6382]; + + return array_map(function ($port) use ($host) { + return sprintf("%s:%d", $host, $port); + }, $ports); +} /* Make sure errors go to stdout and are shown */ error_reporting(E_ALL); ini_set( 'display_errors','1'); /* Grab options */ -$arr_args = getopt('', ['host:', 'port:', 'class:', 'test:', 'nocolors', 'user:', 'auth:']); +$opt = getopt('', ['host:', 'port:', 'tls-port:', 'class:', 'test:', 'nocolors', 'user:', 'auth:']); + +/* The test class(es) we want to run */ +$classes = getClassArray($opt['class'] ?? 'redis'); -/* Grab the test the user is trying to run */ -$arr_valid_classes = ['redis', 'redisarray', 'rediscluster', 'redissentinel']; -$str_class = isset($arr_args['class']) ? strtolower($arr_args['class']) : 'redis'; -$boo_colorize = !isset($arr_args['nocolors']); +$colorize = !isset($opt['nocolors']); /* Get our test filter if provided one */ -$str_filter = isset($arr_args['test']) ? $arr_args['test'] : NULL; +$filter = $opt['test'] ?? NULL; /* Grab override host/port if it was passed */ -$str_host = isset($arr_args['host']) ? $arr_args['host'] : '127.0.0.1'; -$i_port = isset($arr_args['port']) ? intval($arr_args['port']) : 6379; +$host = $opt['host'] ?? '127.0.0.1'; +$port = $opt['port'] ?? 6379; +$tls_port = $opt['tls-port'] ?? 6378; /* Get optional username and auth (password) */ -$str_user = isset($arr_args['user']) ? $arr_args['user'] : NULL; -$str_auth = isset($arr_args['auth']) ? $arr_args['auth'] : NULL; - -/* Massage the actual auth arg */ -$auth = NULL; -if ($str_user && $str_auth) { - $auth = [$str_user, $str_auth]; -} else if ($str_auth) { - $auth = $str_auth; -} else if ($str_user) { - echo TestSuite::make_warning("User passed without a password, ignoring!\n"); -} +$user = $opt['user'] ?? NULL; +$auth = $opt['auth'] ?? NULL; -/* Validate the class is known */ -if (!in_array($str_class, $arr_valid_classes)) { - echo "Error: Valid test classes are Redis, RedisArray, RedisCluster and RedisSentinel!\n"; - exit(1); +if ($user && $auth) { + $auth = [$user, $auth]; +} else if ($user && ! $auth) { + echo TestSuite::make_warning("User passed without a password!\n"); } /* Toggle colorization in our TestSuite class */ -TestSuite::flagColorization($boo_colorize); +TestSuite::flagColorization($colorize); /* Let the user know this can take a bit of time */ echo "Note: these tests might take up to a minute. Don't worry :-)\n"; -echo "Using PHP version " . PHP_VERSION . " (" . (PHP_INT_SIZE*8) . " bits)\n"; - -/* Depending on the classes being tested, run our tests on it */ -echo "Testing class "; -if ($str_class == 'redis') { - echo TestSuite::make_bold("Redis") . "\n"; - exit(TestSuite::run("Redis_Test", $str_filter, $str_host, $i_port, $auth)); -} else if ($str_class == 'redisarray') { - echo TestSuite::make_bold("RedisArray") . "\n"; - global $useIndex; - foreach(array(true, false) as $useIndex) { - echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n"; - - /* The various RedisArray subtests we can run */ - $arr_ra_tests = [ - 'Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', - 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test' - ]; - - foreach ($arr_ra_tests as $str_test) { - /* Run until we encounter a failure */ - if (run_tests($str_test, $str_filter, $str_host, $auth) != 0) { - exit(1); +echo "Using PHP version " . PHP_VERSION . " (" . (PHP_INT_SIZE * 8) . " bits)\n"; + +foreach ($classes as $class) { + $class = getTestClass($class); + + /* Depending on the classes being tested, run our tests on it */ + echo "Testing class "; + if ($class == 'Redis_Array_Test') { + echo TestSuite::make_bold("RedisArray") . "\n"; + + $full_ring = raHosts($host, $port); + $sub_ring = array_slice($full_ring, 0, -1); + + echo TestSuite::make_bold("Full Ring: ") . implode(' ', $full_ring) . "\n"; + echo TestSuite::make_bold(" New Ring: ") . implode(' ', $sub_ring) . "\n"; + + foreach([true, false] as $useIndex) { + echo "\n". ($useIndex ? "WITH" : "WITHOUT") . " per-node index:\n"; + + /* The various RedisArray subtests we can run */ + $test_classes = [ + 'Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test', + 'Redis_Multi_Exec_Test', 'Redis_Distributor_Test' + ]; + + foreach ($test_classes as $test_class) { + /* Run until we encounter a failure */ + if (run_ra_tests($test_class, $filter, $host, $full_ring, $sub_ring, $auth) != 0) { + exit(1); + } } } + } else { + echo TestSuite::make_bold($class) . "\n"; + if (TestSuite::run("$class", $filter, $host, $port, $auth, $tls_port)) + exit(1); } -} else if ($str_class == 'rediscluster') { - echo TestSuite::make_bold("RedisCluster") . "\n"; - exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host, $i_port, $auth)); -} else { - echo TestSuite::make_bold("RedisSentinel") . "\n"; - exit(TestSuite::run("Redis_Sentinel_Test", $str_filter, $str_host, $i_port, $auth)); } + +/* Success */ +exit(0); + ?> diff --git a/tests/TestSuite.php b/tests/TestSuite.php index a6006c4fab..30cde97beb 100644 --- a/tests/TestSuite.php +++ b/tests/TestSuite.php @@ -7,16 +7,20 @@ class TestSkippedException extends Exception {} class TestSuite { /* Host and port the unit tests will use */ - private $str_host; - private $i_port = 6379; + private string $host; + private ?int $port = 6379; + private ?int $tls_port = 6378; /* Redis authentication we'll use */ private $auth; /* Redis server version */ protected $version; + protected string $valkey_version; + protected bool $is_keydb; + protected bool $is_valkey; - private static $_boo_colorize = false; + private static bool $colorize = false; private static $BOLD_ON = "\033[1m"; private static $BOLD_OFF = "\033[0m"; @@ -29,141 +33,310 @@ class TestSuite private static $YELLOW = "\033[0;33m"; private static $RED = "\033[0;31m"; - public static $errors = []; - public static $warnings = []; + public static array $errors = []; + public static array $warnings = []; - public function __construct($str_host, $i_port, $auth) { - $this->str_host = $str_host; - $this->i_port = $i_port; + public function __construct(string $host, ?int $port, $auth, ?int $tls_port = 6378) { + $this->host = $host; + $this->port = $port; $this->auth = $auth; + $this->tls_port = $tls_port; } - public function getHost() { return $this->str_host; } - public function getPort() { return $this->i_port; } + public function getHost() { return $this->host; } + public function getPort() { return $this->port; } + public function getTlsPort() { return $this->tls_port; } public function getAuth() { return $this->auth; } - public static function getAvailableCompression() { - $result[] = Redis::COMPRESSION_NONE; - if (defined('Redis::COMPRESSION_LZF')) - $result[] = Redis::COMPRESSION_LZF; - if (defined('Redis::COMPRESSION_LZ4')) - $result[] = Redis::COMPRESSION_LZ4; - if (defined('Redis::COMPRESSION_ZSTD')) - $result[] = Redis::COMPRESSION_ZSTD; + public static function errorMessage(string $fmt, ...$args) { + $msg = vsprintf($fmt . "\n", $args); - return $result; + if (defined('STDERR')) { + fwrite(STDERR, $msg); + } else { + echo $msg; + } } - /** - * Returns the fully qualified host path, - * which may be used directly for php.ini parameters like session.save_path - * - * @return null|string - */ - protected function getFullHostPath() - { - return $this->str_host - ? 'tcp://' . $this->str_host . ':' . $this->i_port - : null; + public static function make_bold(string $msg) { + return self::$colorize ? self::$BOLD_ON . $msg . self::$BOLD_OFF : $msg; + } + + public static function make_success(string $msg) { + return self::$colorize ? self::$GREEN . $msg . self::$BOLD_OFF : $msg; + } + + public static function make_fail(string $msg) { + return self::$colorize ? self::$RED . $msg . self::$BOLD_OFF : $msg; + } + + public static function make_warning(string $msg) { + return self::$colorize ? self::$YELLOW . $msg . self::$BOLD_OFF : $msg; } - public static function make_bold($str_msg) { - return self::$_boo_colorize - ? self::$BOLD_ON . $str_msg . self::$BOLD_OFF - : $str_msg; + protected function printArg($v) { + if (is_null($v)) + return '(null)'; + else if ($v === false || $v === true) + return $v ? '(true)' : '(false)'; + else if (is_string($v)) + return "'$v'"; + else + return print_r($v, true); } - public static function make_success($str_msg) { - return self::$_boo_colorize - ? self::$GREEN . $str_msg . self::$BOLD_OFF - : $str_msg; + protected function findTestFunction($bt) { + $i = 0; + while (isset($bt[$i])) { + if (substr($bt[$i]['function'], 0, 4) == 'test') + return $bt[$i]['function']; + $i++; + } + return NULL; } - public static function make_fail($str_msg) { - return self::$_boo_colorize - ? self::$RED . $str_msg . self::$BOLD_OFF - : $str_msg; + protected function assertionTrace(?string $fmt = NULL, ...$args) { + $prefix = 'Assertion failed:'; + + $lines = []; + + $bt = debug_backtrace(); + + $msg = $fmt ? vsprintf($fmt, $args) : NULL; + + $fn = $this->findTestFunction($bt); + $lines []= sprintf("%s %s - %s", $prefix, self::make_bold($fn), + $msg ? $msg : '(no message)'); + + array_shift($bt); + + for ($i = 0; $i < count($bt); $i++) { + $file = $bt[$i]['file']; + $line = $bt[$i]['line']; + $fn = $bt[$i+1]['function'] ?? $bt[$i]['function']; + + $lines []= sprintf("%s %s:%d (%s)%s", + str_repeat(' ', strlen($prefix)), $file, $line, + $fn, $msg ? " $msg" : ''); + + if (substr($fn, 0, 4) == 'test') + break; + } + + return implode("\n", $lines) . "\n"; } - public static function make_warning($str_msg) { - return self::$_boo_colorize - ? self::$YELLOW . $str_msg . self::$BOLD_OFF - : $str_msg; + protected function assert($fmt, ...$args) { + self::$errors []= $this->assertionTrace($fmt, ...$args); } - protected function assertFalse($bool) { - if(!$bool) + protected function assertKeyEquals($expected, $key, $redis = NULL): bool { + $actual = ($redis ??= $this->redis)->get($key); + if ($actual === $expected) return true; - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + self::$errors []= $this->assertionTrace("%s !== %s", $this->printArg($actual), + $this->printArg($expected)); return false; } - protected function assertTrue($bool, $msg='') { - if($bool) + protected function assertKeyEqualsWeak($expected, $key, $redis = NULL): bool { + $actual = ($redis ??= $this->redis)->get($key); + if ($actual == $expected) return true; - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed: %s:%d (%s) %s\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); + self::$errors []= $this->assertionTrace("%s != %s", $this->printArg($actual), + $this->printArg($expected)); + + return false; + } + + protected function assertKeyExists($key, $redis = NULL): bool { + if (($redis ??= $this->redis)->exists($key)) + return true; + + self::$errors []= $this->assertionTrace("Key '%s' does not exist.", $key); return false; } - protected function assertInArray($ele, $arr, $cb = NULL) { - if ($cb && !is_callable($cb)) - die("Fatal: assertInArray callback must be callable!\n"); + protected function assertKeyMissing($key, $redis = NULL): bool { + if ( ! ($redis ??= $this->redis)->exists($key)) + return true; + + self::$errors []= $this->assertionTrace("Key '%s' exists but shouldn't.", $key); - if (($in = in_array($ele, $arr)) && (!$cb || $cb($arr[array_search($ele, $arr)]))) + return false; + } + + protected function assertTrue($value): bool { + if ($value === true) return true; + self::$errors []= $this->assertionTrace("%s !== %s", $this->printArg($value), + $this->printArg(true)); - $bt = debug_backtrace(false); - $ex = $in ? 'validation' : 'missing'; - self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $ele); + return false; + } + + protected function assertFalse($value): bool { + if ($value === false) + return true; + + self::$errors []= $this->assertionTrace("%s !== %s", $this->printArg($value), + $this->printArg(false)); + + return false; + } + + protected function assertNull($value): bool { + if ($value === NULL) + return true; + + self::$errors []= $this->assertionTrace("%s !== %s", $this->printArg($value), + $this->printArg(NULL)); return false; } - protected function assertArrayKey($arr, $key, $cb = NULL) { - if ($cb && !is_callable($cb)) - die("Fatal: assertArrayKey callback must be callable\n"); + protected function assertInArray($ele, $arr, ?callable $cb = NULL): bool { + $cb ??= function ($v) { return true; }; - if (($exists = isset($arr[$key])) && (!$cb || $cb($arr[$key]))) + $key = array_search($ele, $arr); + + if ($key !== false && ($valid = $cb($ele))) return true; - $bt = debug_backtrace(false); - $ex = $exists ? 'validation' : 'missing'; - self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $key); + self::$errors []= $this->assertionTrace("%s %s %s", $this->printArg($ele), + $key === false ? 'missing from' : 'is invalid in', + $this->printArg($arr)); + + return false; + } + + protected function assertIsString($v): bool { + if (is_string($v)) + return true; + + self::$errors []= $this->assertionTrace("%s is not a string", $this->printArg($v)); + + return false; + } + + protected function assertIsBool($v): bool { + if (is_bool($v)) + return true; + + self::$errors []= $this->assertionTrace("%s is not a boolean", $this->printArg($v)); + + return false; + } + + protected function assertIsInt($v): bool { + if (is_int($v)) + return true; + + self::$errors []= $this->assertionTrace("%s is not an integer", $this->printArg($v)); + + return false; + } + + protected function assertIsFloat($v): bool { + if (is_float($v)) + return true; + + self::$errors []= $this->assertionTrace("%s is not a float", $this->printArg($v)); + + return false; + } + + protected function assertIsObject($v, ?string $type = NULL): bool { + if ( ! is_object($v)) { + self::$errors []= $this->assertionTrace("%s is not an object", $this->printArg($v)); + return false; + } else if ( $type !== NULL && !($v InstanceOf $type)) { + self::$errors []= $this->assertionTrace("%s is not an instance of %s", + $this->printArg($v), $type); + return false; + } + + return true; + } + + protected function assertSameType($expected, $actual): bool { + if (gettype($expected) === gettype($actual)) + return true; + + self::$errors []= $this->assertionTrace("%s is not the same type as %s", + $this->printArg($actual), + $this->printArg($expected)); + + return false; + } + + protected function assertIsArray($v, ?int $size = null): bool { + if ( ! is_array($v)) { + self::$errors []= $this->assertionTrace("%s is not an array", $this->printArg($v)); + return false; + } + + if ( ! is_null($size) && count($v) != $size) { + self::$errors []= $this->assertionTrace("Array size %d != %d", count($v), $size); + return false; + } + + return true; + } + + protected function assertArrayKey($arr, $key, ?callable $cb = NULL): bool { + $cb ??= function ($v) { return true; }; + + if (($exists = isset($arr[$key])) && $cb($arr[$key])) + return true; + + if ($exists) { + $msg = sprintf("%s is invalid in %s", $this->printArg($arr[$key]), + $this->printArg($arr)); + } else { + $msg = sprintf("%s is not a key in %s", $this->printArg($key), + $this->printArg($arr)); + } + + self::$errors []= $this->assertionTrace($msg); return false; } - protected function assertValidate($val, $cb) { - if ( ! is_callable($cb)) - die("Fatal: Callable assertValidate callback required\n"); + protected function assertArrayKeyEquals($arr, $key, $value): bool { + if ( ! isset($arr[$key])) { + self::$errors []= $this->assertionTrace( + "Key '%s' not found in %s", $key, $this->printArg($arr)); + return false; + } + + if ($arr[$key] !== $value) { + self::$errors []= $this->assertionTrace( + "Value '%s' != '%s' for key '%s' in %s", + $arr[$key], $value, $key, $this->printArg($arr)); + return false; + } + + return true; + } + protected function assertValidate($val, callable $cb): bool { if ($cb($val)) return true; - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + self::$errors []= $this->assertionTrace("%s is invalid.", $this->printArg($val)); return false; } - protected function assertThrowsMatch($arg, $cb, $regex = NULL) { + protected function assertThrowsMatch($arg, callable $cb, $regex = NULL): bool { $threw = $match = false; - if ( ! is_callable($cb)) - die("Fatal: Callable assertThrows callback required\n"); - try { $cb($arg); } catch (Exception $ex) { @@ -174,132 +347,293 @@ protected function assertThrowsMatch($arg, $cb, $regex = NULL) { if ($threw && $match) return true; - $bt = debug_backtrace(false); $ex = !$threw ? 'no exception' : "no match '$regex'"; - self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s]\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex); + + self::$errors []= $this->assertionTrace("[$ex]"); return false; } - protected function assertLess($a, $b) { - if($a < $b) - return; + protected function assertLTE($maximum, $value): bool { + if ($value <= $maximum) + return true; - $bt = debug_backtrace(false); - self::$errors[] = sprintf("Assertion failed (%s >= %s): %s: %d (%s\n", - print_r($a, true), print_r($b, true), - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + self::$errors []= $this->assertionTrace("%s > %s", $value, $maximum); + + return false; } - protected function assertEquals($a, $b) { - if($a === $b) - return; + protected function assertLT($minimum, $value): bool { + if ($value < $minimum) + return true; + + self::$errors []= $this->assertionTrace("%s >= %s", $value, $minimum); - $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed (%s !== %s): %s:%d (%s)\n", - print_r($a, true), print_r($b, true), - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + return false; + } + + protected function assertGT($maximum, $value): bool { + if ($value > $maximum) + return true; + + self::$errors [] = $this->assertionTrace("%s <= %s", $maximum, $value); + + return false; } - protected function assertPatternMatch($str_test, $str_regex) { - if (preg_match($str_regex, $str_test)) - return; + protected function assertGTE($minimum, $value): bool { + if ($value >= $minimum) + return true; + + self::$errors [] = $this->assertionTrace("%s < %s", $minimum, $value); + + return false; + } + protected function externalCmdFailure($cmd, $output, $msg = NULL, $exit_code = NULL) { $bt = debug_backtrace(false); - self::$errors []= sprintf("Assertion failed ('%s' doesnt match '%s'): %s:%d (%s)\n", - $str_test, $str_regex, $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]); + + $lines[] = sprintf("Assertion failed: %s:%d (%s)", + $bt[0]['file'], $bt[0]['line'], + self::make_bold($bt[0]['function'])); + + + if ($msg) + $lines[] = sprintf(" Message: %s", $msg); + if ($exit_code !== NULL) + $lines[] = sprintf(" Exit code: %d", $exit_code); + $lines[] = sprintf( " Command: %s", $cmd); + if ($output) + $lines[] = sprintf(" Output: %s", $output); + + self::$errors[] = implode("\n", $lines) . "\n"; + } + + protected function assertBetween($value, $min, $max, bool $exclusive = false): bool { + if ($min > $max) + [$max, $min] = [$min, $max]; + + if ($exclusive) { + if ($value > $min && $value < $max) + return true; + } else { + if ($value >= $min && $value <= $max) + return true; + } + + self::$errors []= $this->assertionTrace(sprintf("'%s' not between '%s' and '%s'", + $value, $min, $max)); + + return false; + } + + /* Replica of PHPUnit's assertion. Basically are two arrays the same without + ' respect to order. */ + protected function assertEqualsCanonicalizing($expected, $actual, $keep_keys = false): bool { + if ($expected InstanceOf Traversable) + $expected = iterator_to_array($expected); + + if ($actual InstanceOf Traversable) + $actual = iterator_to_array($actual); + + if ($keep_keys) { + asort($expected); + asort($actual); + } else { + sort($expected); + sort($actual); + } + + if ($expected === $actual) + return true; + + self::$errors []= $this->assertionTrace("%s !== %s", + $this->printArg($actual), + $this->printArg($expected)); + + return false; } - protected function markTestSkipped($msg='') { + protected function assertEqualsWeak($expected, $actual): bool { + if ($expected == $actual) + return true; + + self::$errors []= $this->assertionTrace("%s != %s", $this->printArg($actual), + $this->printArg($expected)); + + return false; + } + + protected function assertEquals($expected, $actual): bool { + if ($expected === $actual) + return true; + + self::$errors[] = $this->assertionTrace("%s !== %s", $this->printArg($actual), + $this->printArg($expected)); + + return false; + } + + public function assertNotEquals($wrong_value, $test_value): bool { + if ($wrong_value !== $test_value) + return true; + + self::$errors []= $this->assertionTrace("%s === %s", $this->printArg($wrong_value), + $this->printArg($test_value)); + + return false; + } + + protected function assertStringContains(string $needle, $haystack): bool { + if ( ! is_string($haystack)) { + self::$errors []= $this->assertionTrace("'%s' is not a string", $this->printArg($haystack)); + return false; + } + + if (strstr($haystack, $needle) !== false) + return true; + + self::$errors []= $this->assertionTrace("'%s' not found in '%s'", $needle, $haystack); + } + + protected function assertPatternMatch(string $pattern, string $value): bool { + if (preg_match($pattern, $value)) + return true; + + self::$errors []= $this->assertionTrace("'%s' doesnt match '%s'", $value, + $pattern); + + return false; + } + + protected function markTestSkipped(string $msg = '') { $bt = debug_backtrace(false); + self::$warnings []= sprintf("Skipped test: %s:%d (%s) %s\n", - $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg); + $bt[0]["file"], $bt[0]["line"], + $bt[1]["function"], $msg); throw new TestSkippedException($msg); } - private static function getMaxTestLen($arr_methods, $str_limit) { - $i_result = 0; + private static function getMaxTestLen(array $methods, ?string $limit): int { + $result = 0; - foreach ($arr_methods as $obj_method) { - $str_name = strtolower($obj_method->name); + foreach ($methods as $obj_method) { + $name = strtolower($obj_method->name); - if (substr($str_name, 0, 4) != 'test') + if (substr($name, 0, 4) != 'test') continue; - if ($str_limit && !strstr($str_name, $str_limit)) + if ($limit && !strstr($name, $limit)) continue; - if (strlen($str_name) > $i_result) { - $i_result = strlen($str_name); + if (strlen($name) > $result) { + $result = strlen($name); } } - return $i_result; + + return $result; + } + + private static function findFile($path, $file) { + $files = glob($path . '/*', GLOB_NOSORT); + + foreach ($files as $test) { + $test = basename($test); + if (strcasecmp($file, $test) == 0) + return $path . '/' . $test; + } + + return NULL; + } + + /* Small helper method that tries to load a custom test case class */ + public static function loadTestClass($class) { + $filename = "{$class}.php"; + + if (($sp = getenv('PHPREDIS_TEST_SEARCH_PATH'))) { + $fullname = self::findFile($sp, $filename); + } else { + $fullname = self::findFile(__DIR__, $filename); + } + + if ( ! $fullname) + die("Fatal: Couldn't find $filename\n"); + + require_once($fullname); + + if ( ! class_exists($class)) + die("Fatal: Loaded '$filename' but didn't find class '$class'\n"); + + /* Loaded the file and found the class, return it */ + return $class; } /* Flag colorization */ - public static function flagColorization($boo_override) { - self::$_boo_colorize = $boo_override && function_exists('posix_isatty') && - posix_isatty(STDOUT); + public static function flagColorization(bool $override) { + self::$colorize = $override && function_exists('posix_isatty') && + defined('STDOUT') && posix_isatty(STDOUT); } - public static function run($className, $str_limit = NULL, $str_host = NULL, $i_port = NULL, $auth = NULL) { - /* Lowercase our limit arg if we're passed one */ - $str_limit = $str_limit ? strtolower($str_limit) : $str_limit; + public static function run($class_name, ?string $limit = NULL, + ?string $host = NULL, ?int $port = NULL, + $auth = NULL, ?int $tls_port = 6378) + { + if ($limit) + $limit = strtolower($limit); - $rc = new ReflectionClass($className); + $rc = new ReflectionClass($class_name); $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC); - $i_max_len = self::getMaxTestLen($methods, $str_limit); + $max_test_len = self::getMaxTestLen($methods, $limit); foreach($methods as $m) { $name = $m->name; - if(substr($name, 0, 4) !== 'test') + if (substr($name, 0, 4) !== 'test') continue; /* If we're trying to limit to a specific test and can't match the * substring, skip */ - if ($str_limit && strstr(strtolower($name), $str_limit)===FALSE) { + if ($limit && stristr($name, $limit) === false) { continue; } - $str_out_name = str_pad($name, $i_max_len + 1); - echo self::make_bold($str_out_name); + $padded_name = str_pad($name, $max_test_len + 1); + echo self::make_bold($padded_name); - $count = count($className::$errors); - $rt = new $className($str_host, $i_port, $auth); + $count = count($class_name::$errors); + $rt = new $class_name($host, $port, $auth, $tls_port); try { $rt->setUp(); $rt->$name(); - if ($count === count($className::$errors)) { - $str_msg = self::make_success('PASSED'); + if ($count === count($class_name::$errors)) { + $result = self::make_success('PASSED'); } else { - $str_msg = self::make_fail('FAILED'); + $result = self::make_fail('FAILED'); } - //echo ($count === count($className::$errors)) ? "." : "F"; } catch (Exception $e) { /* We may have simply skipped the test */ if ($e instanceof TestSkippedException) { - $str_msg = self::make_warning('SKIPPED'); + $result = self::make_warning('SKIPPED'); } else { - $className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n"; - $str_msg = self::make_fail('FAILED'); + $class_name::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n"; + $result = self::make_fail('FAILED'); } } - echo "[" . $str_msg . "]\n"; + echo "[" . $result . "]\n"; } echo "\n"; - echo implode('', $className::$warnings) . "\n"; + echo implode('', $class_name::$warnings) . "\n"; - if(empty($className::$errors)) { + if (empty($class_name::$errors)) { echo "All tests passed. \o/\n"; return 0; } - echo implode('', $className::$errors); + echo implode('', $class_name::$errors); return 1; } } diff --git a/tests/getSessionData.php b/tests/getSessionData.php index d49256c218..cf2ad08bd8 100644 --- a/tests/getSessionData.php +++ b/tests/getSessionData.php @@ -1,22 +1,33 @@