mirror of
https://github.com/predis/predis.git
synced 2026-08-18 22:22:32 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35142cfbcf | |||
| 0f1d4675cf | |||
| 465bd45147 | |||
| 3bfe3897a7 | |||
| 0f966af8e3 | |||
| e3e0475826 | |||
| 767c488d35 | |||
| 1205026fb1 | |||
| 9c314c5b5f | |||
| d05a62e82c | |||
| 6769e0de56 |
@@ -1,6 +0,0 @@
|
||||
[codespell]
|
||||
skip=./.git,./src/Command/Redis/Search/FTSUGGET.php,./examples/Commands/Search/ft_sug_add_get_del_len.php
|
||||
check-hidden=
|
||||
check-filenames=
|
||||
builtin=clear,rare,informal,usage,code,names
|
||||
ignore-words-list=master,masters,slave,slaves,whitelist,cas,exat,smove,SUGGET,sugget,ro,DOF,dof
|
||||
@@ -1 +0,0 @@
|
||||
coverage_clover: build/logs/clover-*.xml
|
||||
@@ -1,27 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
block_comment_start = /*
|
||||
block_comment = *
|
||||
block_comment_end = */
|
||||
|
||||
[*.php]
|
||||
max_line_length = 150
|
||||
|
||||
[*.{md,yml,yaml,neon,sh}]
|
||||
indent_size = 2
|
||||
|
||||
[VERSION]
|
||||
insert_final_newline = false
|
||||
|
||||
[tests/**.php]
|
||||
max_line_length = unset
|
||||
|
||||
[{src/ClientInterface.php,src/ClientContextInterface.php}]
|
||||
max_line_length = unset
|
||||
@@ -1,20 +0,0 @@
|
||||
* text=auto
|
||||
|
||||
/.github export-ignore
|
||||
/bin export-ignore
|
||||
/examples export-ignore
|
||||
/tests export-ignore
|
||||
/.codespellrc export-ignore linguist-language=INI
|
||||
/.coveralls.yml export-ignore
|
||||
/.editorconfig export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.php-cs-fixer.dist.php export-ignore
|
||||
/CHANGELOG.md export-ignore linguist-documentation
|
||||
/CONTRIBUTING.md export-ignore linguist-documentation
|
||||
/FAQ.md export-ignore linguist-documentation
|
||||
/VERSION export-ignore
|
||||
/phpunit.relay.xml export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/phpstan.dist.neon export-ignore
|
||||
/phpstan-tests.dist.neon export-ignore
|
||||
@@ -1,2 +0,0 @@
|
||||
* @predis/maintainers
|
||||
src/Relay* @tillkruss
|
||||
@@ -1,6 +0,0 @@
|
||||
The Predis code of conduct is derived from the Ruby code of conduct. Any violations of the code of conduct may be reported to [Till Krüss](https://till.im):
|
||||
|
||||
- Participants will be tolerant of opposing views.
|
||||
- Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
|
||||
- When interpreting the words and actions of others, participants should always assume good intentions.
|
||||
- Behavior that can be reasonably considered harassment will not be tolerated.
|
||||
@@ -1,2 +0,0 @@
|
||||
github: tillkruss
|
||||
custom: "https://www.paypal.me/tillkruss"
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Run command '...'
|
||||
2. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Versions (please complete the following information):**
|
||||
- Predis: [e.g. 1.1.2]
|
||||
- PHP [e.g. 8.0.0]
|
||||
- Redis Server [e.g. 6.0.0]
|
||||
- OS [e.g. Ubuntu 20.10]
|
||||
|
||||
**Code sample**
|
||||
If applicable, a small snippet of code that reproduces the issue.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -1,96 +0,0 @@
|
||||
---
|
||||
|
||||
services:
|
||||
|
||||
redis-official:
|
||||
image: ${REDIS_IMAGE_NAME:-redis:8.0}
|
||||
container_name: redis-official
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "PING" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
ports:
|
||||
- "6379:6379"
|
||||
command:
|
||||
- --requirepass "foobar"
|
||||
profiles:
|
||||
- all
|
||||
- official
|
||||
- standalone
|
||||
|
||||
redis-clients:
|
||||
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:8.0.2}
|
||||
container_name: redis-standalone
|
||||
environment:
|
||||
- TLS_ENABLED=yes
|
||||
- REDIS_CLUSTER=no
|
||||
- REDIS_PASSWORD=foobar
|
||||
- PORT=6379
|
||||
- TLS_PORT=6666
|
||||
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save "" --requirepass "foobar"}
|
||||
ports:
|
||||
- 6379:6379
|
||||
- 6666:6666 # TLS port
|
||||
volumes:
|
||||
- "./dockers/standalone:/redis/work"
|
||||
profiles:
|
||||
- all
|
||||
- clients
|
||||
- standalone
|
||||
|
||||
redis-unprotected:
|
||||
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:8.0.2}
|
||||
container_name: redis-unprotected
|
||||
environment:
|
||||
- TLS_ENABLED=no
|
||||
- REDIS_CLUSTER=no
|
||||
- PORT=6379
|
||||
- TLS_PORT=6666
|
||||
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
|
||||
ports:
|
||||
- 6380:6379
|
||||
volumes:
|
||||
- "./dockers/unprotected:/redis/work"
|
||||
profiles:
|
||||
- all
|
||||
- clients
|
||||
- unprotected
|
||||
|
||||
redis-cluster:
|
||||
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:8.0.2}
|
||||
container_name: redis-cluster
|
||||
environment:
|
||||
- REDIS_CLUSTER=yes
|
||||
- REDIS_PASSWORD=foobar
|
||||
- NODES=6
|
||||
- REPLICAS=1
|
||||
- TLS_ENABLED=yes
|
||||
- PORT=6372
|
||||
- TLS_PORT=27379
|
||||
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save "" --requirepass "foobar"}
|
||||
ports:
|
||||
- "6372-6377:6372-6377"
|
||||
volumes:
|
||||
- "./dockers/cluster:/redis/work"
|
||||
profiles:
|
||||
- all
|
||||
- clients
|
||||
- cluster
|
||||
|
||||
redis-stack:
|
||||
image: ${REDIS_STACK_IMAGE_NAME:-redislabs/client-libs-test:rs-7.4.0-v2}
|
||||
container_name: redis-stack
|
||||
ports:
|
||||
- "6479:6379"
|
||||
environment:
|
||||
- REDIS_CLUSTER=no
|
||||
- REDIS_PASSWORD=foobar
|
||||
- PORT=6379
|
||||
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --save "" --requirepass "foobar"}
|
||||
volumes:
|
||||
- "./dockers/redis-stack:/redis/work"
|
||||
profiles:
|
||||
- all
|
||||
- clients
|
||||
- stack
|
||||
@@ -1,51 +0,0 @@
|
||||
name-template: 'v$NEXT_MINOR_VERSION'
|
||||
tag-template: 'v$NEXT_MINOR_VERSION'
|
||||
change-template: '- $TITLE (#$NUMBER)'
|
||||
filter-by-commitish: true
|
||||
commitish: v3.x
|
||||
|
||||
autolabeler:
|
||||
- label: 'maintenance'
|
||||
files:
|
||||
- '*.md'
|
||||
- '.github/*'
|
||||
- label: 'bug'
|
||||
branch:
|
||||
- '/bug-.+'
|
||||
- label: 'maintenance'
|
||||
branch:
|
||||
- '/maintenance-.+'
|
||||
- label: 'feature'
|
||||
branch:
|
||||
- '/feature-.+'
|
||||
|
||||
categories:
|
||||
- title: 'Breaking Changes'
|
||||
labels:
|
||||
- 'breakingchange'
|
||||
- title: 'Experimental Features'
|
||||
labels:
|
||||
- 'experimental'
|
||||
- title: 'New Features'
|
||||
labels:
|
||||
- 'feature'
|
||||
- 'enhancement'
|
||||
- title: 'Bug Fixes'
|
||||
labels:
|
||||
- 'fix'
|
||||
- 'bugfix'
|
||||
- 'bug'
|
||||
- 'BUG'
|
||||
- title: 'Maintenance'
|
||||
label: 'maintenance'
|
||||
|
||||
exclude-labels:
|
||||
- 'skip-changelog'
|
||||
|
||||
template: |
|
||||
$CHANGES
|
||||
|
||||
## Contributors
|
||||
We'd like to thank all the contributors who worked on this release!
|
||||
|
||||
$CONTRIBUTORS
|
||||
@@ -1,29 +0,0 @@
|
||||
matrix:
|
||||
- name: Markdown
|
||||
expect_match: false
|
||||
apsell:
|
||||
lang: en
|
||||
d: en_US
|
||||
ignore-case: true
|
||||
dictionary:
|
||||
wordlists:
|
||||
- .github/wordlist.txt
|
||||
output: wordlist.dic
|
||||
pipeline:
|
||||
- pyspelling.filters.markdown:
|
||||
markdown_extensions:
|
||||
- markdown.extensions.extra:
|
||||
- pyspelling.filters.html:
|
||||
comments: false
|
||||
attributes:
|
||||
- alt
|
||||
ignores:
|
||||
- ':matches(code, pre)'
|
||||
- code
|
||||
- pre
|
||||
- blockquote
|
||||
- img
|
||||
sources:
|
||||
- 'README.md'
|
||||
- 'FAQ.md'
|
||||
- 'docs/**'
|
||||
@@ -1,54 +0,0 @@
|
||||
ACLs
|
||||
Autoloading
|
||||
CAS
|
||||
Customizable
|
||||
ElastiCache
|
||||
FPM
|
||||
GC
|
||||
IANA
|
||||
Lua
|
||||
PSR
|
||||
Packagist
|
||||
PhpRedis
|
||||
Predis
|
||||
README
|
||||
Redis
|
||||
SHA
|
||||
SSL
|
||||
TCP
|
||||
OSS
|
||||
CLI
|
||||
TLS
|
||||
URI
|
||||
autoload
|
||||
autoloader
|
||||
autoloading
|
||||
backend
|
||||
backends
|
||||
behaviour
|
||||
customizable
|
||||
dataset
|
||||
de
|
||||
DevOPS
|
||||
extensibility
|
||||
keyspace
|
||||
keyspaces
|
||||
localhost
|
||||
namespace
|
||||
pipelining
|
||||
pluggable
|
||||
rebalanced
|
||||
rebalancing
|
||||
redis
|
||||
runtime
|
||||
sharding
|
||||
stunnel
|
||||
variadic
|
||||
commitish
|
||||
Kubernetes
|
||||
programmatically
|
||||
unhandled
|
||||
invalidations
|
||||
Sharded
|
||||
sharded
|
||||
Valkey
|
||||
@@ -1,9 +0,0 @@
|
||||
# Workflows
|
||||
|
||||
## Delete runs by workflow name
|
||||
|
||||
```bash
|
||||
gh run list --workflow 'redis-server-tests.yml' --limit 1000 --json databaseId \
|
||||
| jq '.[].databaseId' \
|
||||
| xargs -I % gh api --silent -X DELETE /repos/predis/predis/actions/runs/%
|
||||
```
|
||||
@@ -1,174 +0,0 @@
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow
|
||||
|
||||
name: Linters
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v2.**
|
||||
- v3.**
|
||||
pull_request: null
|
||||
|
||||
permissions: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
byte_level:
|
||||
name: Byte-level
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check file permissions
|
||||
run: test "$(find . -type f -not -path './.git/*' -executable)" = "./bin/create-command-test"
|
||||
|
||||
- name: "Find non-printable ASCII characters"
|
||||
run: |
|
||||
! LC_ALL=C.UTF-8 find . -type f -name '*.php' -print0 \
|
||||
| xargs --null -- grep --perl-regexp --with-filename --line-number '[^ -~ü]'
|
||||
|
||||
syntax_errors:
|
||||
name: Syntax errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "8.1"
|
||||
coverage: none
|
||||
tools: parallel-lint
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check source code for syntax errors
|
||||
run: composer exec -- parallel-lint bin/ examples/ src/ tests/
|
||||
|
||||
static_analysis:
|
||||
name: Static Analysis
|
||||
needs:
|
||||
- byte_level
|
||||
- syntax_errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "8.2"
|
||||
coverage: none
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Validate Composer configuration
|
||||
run: composer validate --no-interaction --strict
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ramsey/composer-install@v2
|
||||
with:
|
||||
dependency-versions: highest
|
||||
|
||||
- name: Check PSR-4 mapping
|
||||
run: composer dump-autoload --no-interaction --optimize --strict-psr
|
||||
|
||||
- name: Perform static analysis
|
||||
run: composer run phpstan
|
||||
|
||||
- name: Perform static analysis on tests
|
||||
run: composer run phpstan -- --configuration=phpstan-tests.dist.neon
|
||||
|
||||
coding_standards:
|
||||
name: Coding Standards
|
||||
needs:
|
||||
- byte_level
|
||||
- syntax_errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Set up PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "8.1"
|
||||
coverage: none
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check EditorConfig configuration
|
||||
run: test -f .editorconfig
|
||||
|
||||
- name: Check adherence to EditorConfig
|
||||
uses: greut/eclint-action@v0
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ramsey/composer-install@v2
|
||||
with:
|
||||
dependency-versions: highest
|
||||
|
||||
- name: Check coding style
|
||||
run: composer exec -- php-cs-fixer fix --diff --dry-run --allow-risky=yes --using-cache=no
|
||||
|
||||
- name: Search for TODO-s and FIXME-s
|
||||
run: |
|
||||
! git grep --extended-regexp --ignore-case '\b(TODO|FIXME)\b' -- ':/' ':!tests/*' ':!*/linters\.yml'
|
||||
|
||||
exported_files:
|
||||
name: Exported files
|
||||
needs:
|
||||
- byte_level
|
||||
- syntax_errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check exported files
|
||||
run: |
|
||||
EXPECTED="LICENSE,README.md,autoload.php,composer.json"
|
||||
CURRENT="$(
|
||||
git archive HEAD \
|
||||
| tar --list --exclude="src" --exclude="src/*" --exclude="bin" --exclude="bin/*" \
|
||||
| paste --serial --delimiters=","
|
||||
)"
|
||||
echo "CURRENT =${CURRENT}"
|
||||
echo "EXPECTED=${EXPECTED}"
|
||||
test "${CURRENT}" = "${EXPECTED}"
|
||||
|
||||
changelog:
|
||||
|
||||
name: Changelog
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
if: >-
|
||||
github.event_name == 'pull_request' &&
|
||||
!contains(github.event.head_commit.message, 'nochangelog') &&
|
||||
!contains(github.event.head_commit.message, 'no-changelog') &&
|
||||
!contains(github.event.head_commit.message, 'no changelog') &&
|
||||
!contains(github.event.pull_request.labels.*.name, 'no-changelog')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for CHANGELOG entry
|
||||
env:
|
||||
TARGET: ${{ github.event.pull_request.base.ref }}
|
||||
run: |
|
||||
FILES_CHANGED=$(git diff --name-only origin/$TARGET...HEAD | grep -E 'CHANGELOG\.md' -c)
|
||||
if [ "$FILES_CHANGED" != "1" ]; then
|
||||
echo "CHANGELOG.md was not updated";
|
||||
exit 1;
|
||||
fi;
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Release Drafter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v2.**
|
||||
- v3.**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
update_release_draft:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
name: Update release draft
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
with:
|
||||
config-name: release-drafter-config.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,14 +0,0 @@
|
||||
name: spellcheck
|
||||
on:
|
||||
pull_request:
|
||||
jobs:
|
||||
check-spelling:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Check Spelling
|
||||
uses: rojopolis/spellcheck-github-actions@0.33.1
|
||||
with:
|
||||
config_path: .github/spellcheck-settings.yml
|
||||
task_name: Markdown
|
||||
@@ -1,203 +0,0 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v2.**
|
||||
- v3.**
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
|
||||
predis:
|
||||
name: PHP ${{ matrix.php }} (Redis ${{ matrix.redis }})
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php:
|
||||
- '7.2'
|
||||
- '8.0'
|
||||
- '8.3'
|
||||
- '8.4'
|
||||
redis:
|
||||
- '4.0'
|
||||
- '7.2'
|
||||
- '7.4'
|
||||
- '8.0'
|
||||
- '8.2'
|
||||
- '8.4'
|
||||
|
||||
steps:
|
||||
|
||||
- name: Resolve container name
|
||||
run: |
|
||||
# Mapping of original redis versions to client test containers
|
||||
declare -A redis_clients_version_mapping=(
|
||||
["8.4"]="8.4-M01-pre"
|
||||
["8.2"]="8.2.2-pre"
|
||||
["8.0"]="8.0.2"
|
||||
["7.4"]="7.4.2"
|
||||
["7.2"]="7.2.7"
|
||||
)
|
||||
|
||||
# Mapping of redis version to stack version
|
||||
declare -A redis_stack_version_mapping=(
|
||||
["7.4"]="rs-7.4.0-v3"
|
||||
["7.2"]="rs-7.2.0-v15"
|
||||
)
|
||||
|
||||
if [[ -v redis_clients_version_mapping[${{ matrix.redis }}] ]]; then
|
||||
echo "REDIS_IMAGE_NAME=redislabs/client-libs-test:${redis_clients_version_mapping[${{ matrix.redis }}]}" >> $GITHUB_ENV
|
||||
echo "REDIS_STACK_IMAGE_NAME=redislabs/client-libs-test:${redis_stack_version_mapping[${{ matrix.redis }}]}" >> $GITHUB_ENV
|
||||
echo "DOCKER_SERVICE=redis-clients" >> $GITHUB_ENV
|
||||
|
||||
redis_major_version=$(echo "${{ matrix.redis }}" | grep -oP '^\d+')
|
||||
|
||||
# Some configuration options available since Redis > 7
|
||||
if (( redis_major_version < 7 )); then
|
||||
echo "REDIS_EXTRA_ARGS="--tls-auth-clients optional --save ''"" >> $GITHUB_ENV
|
||||
else
|
||||
# Since 8.0 modules are bundled with core
|
||||
echo "REDIS_STACK_SERVER_PORT=6379" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
else
|
||||
echo "REDIS_IMAGE_NAME=redis:${{ matrix.redis }}" >> $GITHUB_ENV
|
||||
echo "DOCKER_SERVICE=redis-official" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Start Redis standalone image
|
||||
uses: hoverkraft-tech/compose-action@v2.0.1
|
||||
with:
|
||||
compose-file: .github/docker-compose.yml
|
||||
services: ${{ env.DOCKER_SERVICE }}
|
||||
|
||||
- name: Start Redis unprotected image
|
||||
uses: hoverkraft-tech/compose-action@v2.0.1
|
||||
if: ${{ matrix.redis > '4.0' }}
|
||||
with:
|
||||
compose-file: .github/docker-compose.yml
|
||||
services: redis-unprotected
|
||||
|
||||
- name: Start Redis stack image
|
||||
id: stack_infra
|
||||
uses: hoverkraft-tech/compose-action@v2.0.1
|
||||
if: ${{ matrix.redis >= '7.2' && matrix.redis < '8.0' }}
|
||||
with:
|
||||
compose-file: .github/docker-compose.yml
|
||||
services: redis-stack
|
||||
|
||||
- name: Start Redis cluster image
|
||||
id: cluster_infra
|
||||
uses: hoverkraft-tech/compose-action@v2.0.1
|
||||
if: ${{ matrix.redis > '4.0' }}
|
||||
with:
|
||||
compose-file: .github/docker-compose.yml
|
||||
services: redis-cluster
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: relay-0.12.0
|
||||
coverage: ${{ (matrix.php == '8.4' && matrix.redis == '8.0') && 'xdebug' || 'none' }}
|
||||
|
||||
- name: Install Composer dependencies
|
||||
uses: ramsey/composer-install@v2
|
||||
with:
|
||||
dependency-versions: highest
|
||||
composer-options: ${{ matrix.php == '8.0' && '--ignore-platform-reqs' || '' }}
|
||||
|
||||
- name: Run tests
|
||||
if: ${{ matrix.php != '8.4' || matrix.redis != '8.0' }}
|
||||
run: vendor/bin/phpunit
|
||||
|
||||
- name: Run tests with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
|
||||
run: vendor/bin/phpunit --coverage-php build/cov/coverage-predis.cov --coverage-filter ./src
|
||||
|
||||
- name: Run tests using Relay
|
||||
if: ${{ matrix.php != '8.4' && matrix.redis >= '8.0' }}
|
||||
run: vendor/bin/phpunit -c phpunit.relay.xml
|
||||
|
||||
- name: Run tests using Relay with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
|
||||
run: vendor/bin/phpunit -c phpunit.relay.xml --coverage-php build/cov/coverage-relay.cov --coverage-filter ./src
|
||||
|
||||
- name: Run tests against unprotected Redis
|
||||
if: ${{ (matrix.php != '8.4' || matrix.redis != '8.0') && matrix.redis > '4.0'}}
|
||||
run: vendor/bin/phpunit --group unprotected
|
||||
|
||||
- name: Run tests against unprotected Redis with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
|
||||
run: vendor/bin/phpunit --group unprotected --coverage-php build/cov/coverage-unprotected.cov --coverage-filter ./src
|
||||
|
||||
- name: Run stack tests
|
||||
if: ${{ (matrix.php != '8.4' || matrix.redis != '8.0') && matrix.redis >= '7.2' }}
|
||||
run: vendor/bin/phpunit --group realm-stack
|
||||
|
||||
- name: Run stack tests with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
|
||||
run: vendor/bin/phpunit --group realm-stack --coverage-php build/cov/coverage-stack.cov --coverage-filter ./src
|
||||
|
||||
- name: Run stack tests using Relay
|
||||
if: ${{ (matrix.php != '8.4' || matrix.redis != '8.0') && matrix.redis >= '7.2' }}
|
||||
run: vendor/bin/phpunit --group realm-stack -c phpunit.relay.xml
|
||||
|
||||
- name: Run stack tests using Relay with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
|
||||
run: vendor/bin/phpunit --group realm-stack -c phpunit.relay.xml --coverage-php build/cov/coverage-stack-relay.cov --coverage-filter ./src
|
||||
|
||||
- name: Run tests against cluster
|
||||
if: ${{ (matrix.php != '8.4' || matrix.redis != '8.0') && steps.cluster_infra.conclusion == 'success' }}
|
||||
run: |
|
||||
vendor/bin/phpunit --group cluster
|
||||
|
||||
- name: Run tests against cluster with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' && steps.cluster_infra.conclusion == 'success' }}
|
||||
run: |
|
||||
vendor/bin/phpunit --group cluster --coverage-php build/cov/coverage-cluster.cov --coverage-filter ./src
|
||||
|
||||
- name: Run tests against cluster using Relay
|
||||
if: ${{ matrix.php != '8.4' && matrix.redis == '8.0' }}
|
||||
run: |
|
||||
sleep 5
|
||||
vendor/bin/phpunit -c phpunit.relay.xml --group cluster
|
||||
|
||||
- name: Run tests against cluster using Relay with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
|
||||
run: |
|
||||
sleep 5
|
||||
vendor/bin/phpunit -c phpunit.relay.xml --group cluster --coverage-php build/cov/coverage-cluster-relay.cov --coverage-filter ./src
|
||||
|
||||
- name: Merge coverage reports
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
|
||||
run: php vendor/bin/phpcov merge --clover build/logs/clover.xml build/cov
|
||||
|
||||
- name: Send coverage to Coveralls
|
||||
uses: coverallsapp/github-action@v2
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '8.0' }}
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
parallel: true
|
||||
|
||||
finish:
|
||||
name: Finish Coverall
|
||||
needs: predis
|
||||
if: ${{ always() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Coveralls Finished
|
||||
uses: coverallsapp/github-action@v2
|
||||
with:
|
||||
parallel-finished: true
|
||||
@@ -1,8 +0,0 @@
|
||||
/build
|
||||
/vendor
|
||||
.php-version
|
||||
.php_cs.cache
|
||||
.phpunit.result.cache
|
||||
.php-cs-fixer.cache
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
$PREDIS_HEADER = <<<EOS
|
||||
This file is part of the Predis package.
|
||||
|
||||
(c) 2009-2020 Daniele Alessandri
|
||||
(c) 2021-2025 Till Krüss
|
||||
|
||||
For the full copyright and license information, please view the LICENSE
|
||||
file that was distributed with this source code.
|
||||
EOS;
|
||||
|
||||
$fixer = new PhpCsFixer\Config;
|
||||
|
||||
$fixer->setParallelConfig(
|
||||
\PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()
|
||||
);
|
||||
|
||||
$fixer->setRules([
|
||||
'@PHP71Migration' => true,
|
||||
'header_comment' => ['header' => $PREDIS_HEADER],
|
||||
'@Symfony' => true,
|
||||
'phpdoc_separation' => false,
|
||||
'phpdoc_annotation_without_dot' => false,
|
||||
'no_superfluous_phpdoc_tags' => false,
|
||||
'no_unneeded_curly_braces' => false,
|
||||
'no_unneeded_braces' => false,
|
||||
'global_namespace_import' => true,
|
||||
'yoda_style' => false,
|
||||
'single_line_throw' => false,
|
||||
'concat_space' => ['spacing' => 'one'],
|
||||
'increment_style' => false,
|
||||
'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['array_destructuring', 'arrays']]
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->in(__DIR__ . '/bin')
|
||||
->in(__DIR__ . '/examples')
|
||||
->in(__DIR__ . '/src')
|
||||
->in(__DIR__ . '/tests')
|
||||
);
|
||||
|
||||
return $fixer;
|
||||
-349
@@ -1,349 +0,0 @@
|
||||
## Changelog
|
||||
|
||||
## Unreleased
|
||||
### Added
|
||||
- Added cluster support for `XADD`, `XDEL` and `XRANGE` (#1587)
|
||||
- Added prefixable interface for `HEXPIRE` and `HEXPIRETIME` (#1592)
|
||||
|
||||
### Changed
|
||||
- Refactor pipeline data writing depends on connection type (#1586)
|
||||
- Improved compatiblity with Relay (#1597)
|
||||
|
||||
### Maintenance
|
||||
- Added testing with 8.4-M01 (#1593)
|
||||
|
||||
## v3.2.0 (2025-08-05)
|
||||
### Added
|
||||
- Added support for `XDELEX` and `XACKDEL` (#1580)
|
||||
- Added missing VSIM argument (#1582)
|
||||
|
||||
### Changed
|
||||
- Extended `XTRIM` and `XADD` commands with new parameters (#1580)
|
||||
|
||||
### Maintenance
|
||||
- Updated Redis 8.2 test image (#1583)
|
||||
- Added test coverage for updated Vamana (#1584)
|
||||
|
||||
## v3.1.0 (2025-07-22)
|
||||
### Added
|
||||
- Add experimental support for vector sets commands (#1550)
|
||||
- Added support for `XACK` command (#1555)
|
||||
- Added support for `XCLAIM` command (#1557)
|
||||
- Added support for `XPENDING` command (#1558)
|
||||
- Added support for `XSETID` command (#1559)
|
||||
- Added validation and support for the new `BITOP` command operations (#1566)
|
||||
|
||||
### Changed
|
||||
- Handle and retry `LOADING` errors from Sentinel replicas (#1536)
|
||||
- Retry all exceptions from Sentinel replicas (#1577)
|
||||
|
||||
### Fixed
|
||||
- Fixed PHP 8.4 deprecated call to `stream_context_set_option()` (#1545)
|
||||
- Fixed return type for `ZCOUNT` to be `int` (#1547)
|
||||
- Fixed throwing `CommunicationException` when stream is EOF (#1548)
|
||||
- Removed automatic `conn_uid` parameter assignment (#1552)
|
||||
- Fixed wrong command API call on prefix processing (#1554)
|
||||
- Fixed `XREAD` response parsing while read null (#1563)
|
||||
- Fixed `XINFO` command responses parsing (#1560)
|
||||
- Marked missing commands as Prefixable (#1576)
|
||||
|
||||
## v3.0.1 (2025-05-16)
|
||||
### Fixed
|
||||
- Send `default` user if only password is used for authentication (#1535)
|
||||
|
||||
## v3.0.0 (2025-05-02)
|
||||
### Added
|
||||
- ⚠️ Added support for transactions for clustered connections (#1497)
|
||||
- Added support for RESP3 communication protocol (#1047)
|
||||
- Added support for RESP3 push notifications (#1316)
|
||||
- Added support for Sharded Pub/Sub (#1303)
|
||||
- Added support for `XGROUP` commands (#1324)
|
||||
- Added support for `XREADGROUP` command (#1327)
|
||||
- Added support for `XAUTOCLAIM` command (#1328)
|
||||
- Added support for `XINFO` commands (#1331)
|
||||
- Added support for Redis 7.4 `XREAD` and `HSCAN` interface changes (#1452)
|
||||
|
||||
### Changed
|
||||
- ⚠️ Refactored Relay integration (#1423)
|
||||
- ⚠️ Changed interface of `CLIENT` command (#1337)
|
||||
- ⚠️ Changed interfaces for commands to support Redis 6.2 — 7.0 arguments (#1330)
|
||||
- ⚠️ Changed default search `DIALECT` to `2` (#1516)
|
||||
- ⚠️ Implemented PSR-7 compatible stream abstraction (#1450)
|
||||
- ⚠️ Improved pipeline abstractions (#1438)
|
||||
- ⚠️ Improved connection handshake (#1431)
|
||||
|
||||
### Fixed
|
||||
- Fixed single persistent connection using the same resource (#1512)
|
||||
|
||||
### Maintenance
|
||||
- Revisit incompatible tests against Relay (#1537)
|
||||
|
||||
## v2.4.0 (2025-04-30)
|
||||
### Added
|
||||
- Added new hash-field expiration commands (#1520)
|
||||
- Added missing `FT._LIST` and `BITFIELD_RO` commands (#1521)
|
||||
|
||||
### Changed
|
||||
- Update `WATCH` command to accept `string|string[]` (#1476)
|
||||
- Optimize cluster slotmap with compact slot range object (#1493)
|
||||
|
||||
### Fixed
|
||||
- Fixed `EVAL_RO` cluster support (#1449)
|
||||
- Fixed PHP 8.4 compatibility with `stream_context_set_option()` (#1503)
|
||||
- Prevent named arguments runtime failure (#1509)
|
||||
- Mark `GEOSEARCH` as read-only to ensure execution on replica (#1481)
|
||||
- Fixed protocol loss during redis cluster `MOVED` / `ASK` (#1530)
|
||||
|
||||
### Maintenance
|
||||
- Added CI testing with Redis 8.0 (#1510)
|
||||
- Added test coverage for compatibility with Redis 8.0 (#1513)
|
||||
- Use parallel on PHP-CS-Fixer (#1489)
|
||||
|
||||
## v2.3.0 (2024-11-21)
|
||||
### Added
|
||||
- Added `GeoShapeField` field (#1467)
|
||||
- Added hash expiration commands (#1456)
|
||||
- Added support for time series `IGNORE` filter (#1458)
|
||||
- Added `XREAD` commands (#1459)
|
||||
- Added `NOVALUES` argument support to `HSCAN` (#1459)
|
||||
- Added support for search `INDEXMISSING` and `INDEXEMPTY` arguments (#1464)
|
||||
|
||||
### Changed
|
||||
- Explicitly mark nullable parameters as nullable (#1448)
|
||||
- Filter out available replicas based on link status flag (#1440)
|
||||
- Respect `prefix` for `ZPOPMIN`, `ZPOPMAX`, `ZMSCORE`, `LMOVE`, `BLMOVE`, `SMISMEMBER` and `GEOSEARCH` (#1451, #1453, #1455, #1468)
|
||||
- Updated test infrastructure (#1510)
|
||||
-
|
||||
## v2.2.0 (2023-06-14)
|
||||
|
||||
Predis v2.2.0 introduces official support for [Redis Stack](https://redis.io/docs/stack/) as well as a [Relay](https://github.com/cachewerk/relay) integration for substantially [faster read performance](https://github.com/predis/predis/wiki/Using-Relay).
|
||||
|
||||
### Added
|
||||
- Added support for [Relay](https://github.com/predis/predis/wiki/Using-Relay) (#1263)
|
||||
- Added support for `FCALL_RO` command (#1191)
|
||||
- Added support for Redis `JSON`, `Bloom`, `Search` and `TimeSeries` module (#1253)
|
||||
- Added support for `ACL SETUSER, GETUSER, DRYRUN` commands (#1193)
|
||||
|
||||
### Changed
|
||||
- Minor code style and type-hint changes (#1311)
|
||||
|
||||
### Fixed
|
||||
- Fixed prefixes for `XTRIM` and `XREVRANGE` commands (#1230)
|
||||
- Fixed `fclose()` being called on invalid stream resource (#1199)
|
||||
- Fixed `BitByte` and `ExpireOptions` traits skip processing on null values (#1169)
|
||||
- Fixed missing `@return` annotations (#1265)
|
||||
- Fixed `GETDEL` prefixing (#1306)
|
||||
|
||||
## v2.1.2 (2023-03-02)
|
||||
|
||||
### Added
|
||||
- Added stream commands to `KeyPrefixProcessor` (#1051)
|
||||
- Added `ReplicationStrategy::$loadBalancing` option to disable replica reads (#1168)
|
||||
- Added support for `FCALL` and `FUNCTIONS` commands (#1049)
|
||||
- Added support for `PEXPIRETIME` command (#1031)
|
||||
- Added support for `EXPIRETIME` command (#1029)
|
||||
- Added support for `EVAL_RO` command (#1032)
|
||||
- Added support for `LCS` command (#1035)
|
||||
- Added support for `SORT_RO` command (#1044)
|
||||
- Added support for `SINTERCARD` command (#1027)
|
||||
- Added support for `EVALSHA_RO` command (#1034)
|
||||
- Added support for new arguments for `BITPOS` and `BITCOUNT` commands (#1045)
|
||||
- Added support for new arguments for `EXPIRE` and `EXPIREAT` commands (#1046)
|
||||
|
||||
### Bug Fixes
|
||||
- Fixed deprecated function call syntax
|
||||
|
||||
### Deprecated
|
||||
- Further deprecated phpiredis and webdis integration (#1179)
|
||||
|
||||
### Maintenance
|
||||
- Applied coding standards
|
||||
- Pass PHPStan level 2
|
||||
|
||||
## v2.1.1 (2023-01-17)
|
||||
|
||||
### Bug Fixes
|
||||
- Fix `@template` in `Predis\Client` (#1017)
|
||||
- Fix support options array in `ZINTERSTORE` and `ZUNIONSTORE` (#1018)
|
||||
|
||||
### Deprecated
|
||||
- Deprecated phpiredis and webdis connections
|
||||
|
||||
## v2.1.0 (2023-01-16)
|
||||
|
||||
### New Features
|
||||
- Implemented `GETEX` command (#872)
|
||||
- Implemented `GETDEL` command (#869)
|
||||
- Implemented `COPY` command (#866)
|
||||
- Implemented `FAILOVER` command (#875)
|
||||
- Implemented `LMOVE` command (#863)
|
||||
- Implemented `LMPOP` command (#1013)
|
||||
- Implemented `HRANDFIELD` command (#870)
|
||||
- Implemented `SMISMEMBER` command (#871)
|
||||
- Implemented `ZMPOP` command (#831)
|
||||
- Implemented `BLMOVE` command (#865)
|
||||
- Implemented `BLMPOP` command (#1015)
|
||||
- Implemented `BZMPOP` command (#833)
|
||||
- Implemented `BZPOPMIN` command (#862)
|
||||
- Implemented `BZPOPMAX` command (#864)
|
||||
- Implemented `ZUNION` command (#860)
|
||||
- Implemented `ZINTER` command (#859)
|
||||
- Implemented `ZINTERCARD` command (#861)
|
||||
- Implemented `ZRANGESTORE` command (#829)
|
||||
- Implemented `ZDIFFSTORE` command (#828)
|
||||
- Implemented `ZDIFF` command (#826)
|
||||
- Implemented `ZRANDMEMBER` command (#825)
|
||||
- Implemented `ZMSCORE` (#823)
|
||||
- Implemented `GEOSEARCH` command (#867)
|
||||
- Implemented `GEOSEARCHSTORE` command (#873)
|
||||
|
||||
### Bug Fixes
|
||||
- Added annotations to suppress PHP 8.1 return type deprecation warning (#810)
|
||||
|
||||
### Maintenance
|
||||
- Added mixin annotations for traits (#835)
|
||||
|
||||
## v2.0.3 (2022-10-11)
|
||||
|
||||
- Improved PHP 8.2 support
|
||||
- Call `is_resource()` before reading/writing
|
||||
- Added partial support for Redis Stream commands
|
||||
- Fixed Sentinel authentication issue
|
||||
|
||||
## v2.0.2 (2022-09-06)
|
||||
|
||||
- Fixed PHP 8.2 deprecation notice: Use of "static" in callables
|
||||
|
||||
## v2.0.1 (2022-09-04)
|
||||
|
||||
- Added retry interval to `RedisCluster` with a default of `10ms`
|
||||
- Avoid PHP 8.2 warning in `Connection\Parameters`
|
||||
- Fixed Sentinel reconnect issue in long-running PHP processes
|
||||
|
||||
## v2.0.0 (2022-06-08)
|
||||
|
||||
- Dropped support for PHP 7.1 and older
|
||||
|
||||
- Accepted values for some client options have changed, this is the new list of
|
||||
accepted values:
|
||||
|
||||
- `aggregate`: callable returning an aggregate connection.
|
||||
- `cluster`: string value (`predis`, `redis`), callable returning an aggregate
|
||||
connection.
|
||||
- `replication`: string value (`predis`, `sentinel`), callable returning an
|
||||
aggregate connection.
|
||||
- `commands`: command factory, named array mapping command IDs to PHP classes,
|
||||
callable returning a command factory or a named array.
|
||||
- `connections`: connection factory, callable object returning a connection
|
||||
factory, named array mapping URI schemes to PHP classes, string identifying
|
||||
a supported combination of configurations for the connection factory.
|
||||
- `prefix`: string value, command processor, callable.
|
||||
- `exceptions`: boolean.
|
||||
|
||||
Note that both the `cluster` and `replication` options now return a closure
|
||||
acting as initializer instead of an aggregate connection instance.
|
||||
|
||||
- The `connections` client option now accepts certain string values identifying
|
||||
certain combinations of configurations for the connection factory. Currently
|
||||
this is used to provide a short way to configure Predis to load our phpiredis
|
||||
based connection backends simply, accepted values are:
|
||||
|
||||
- `phpiredis-stream` maps `Phpiredis\Connection\PhpiredisStreamConnection` to
|
||||
`tcp`, `redis`, `unix` URI schemes.
|
||||
- `phpiredis-socket` maps `Phpiredis\Connection\PhpiredisSocketConnection` to
|
||||
`tcp`, `redis`, `unix` URI schemes.
|
||||
- `phpiredis-stream` is simply an alias of `phpiredis-stream`.
|
||||
|
||||
- Added the new `Predis\Cluster\Hash\PhpiredisCRC16` class using ext-phpiredis
|
||||
to speed-up the generation of the CRC16 hash of keys for redis-cluster. Predis
|
||||
automatically uses this class when ext-phpiredis is loaded, but it is possible
|
||||
to configure the hash generator using the new `crc16` client option (accepted
|
||||
values `predis`, `phpiredis` or an hash generator instance).
|
||||
|
||||
- Replication backends now use the `role` parameter instead of `alias` in order
|
||||
to distinguish the role of a connection. Accepted values are `master`, `slave`
|
||||
and, for redis-sentinel, `sentinel`. This led to a redesign of how connections
|
||||
can be retrieved from replication backends: the method getConnectionById() now
|
||||
retrieves a connection only by its ID (ip:port pair), to get a connection by
|
||||
its alias there is the new method getConnectionByAlias(). This method is not
|
||||
supported by the redis-sentinel backend due to its dynamic nature (connections
|
||||
are retrieved and initialized at runtime from sentinels) but it is possible to
|
||||
get a single connection from the pool by using its ID. It is also possible to
|
||||
retrieve a connection by role using the method getConnectionByRole().
|
||||
|
||||
- The concept of connection ID (ip:port pair) and connection alias (the `alias`
|
||||
parameter) in `Predis\Connection\Cluster\PredisCluster` has been separated.
|
||||
This change does not affect distribution and it is safe for existing clusters.
|
||||
|
||||
- Client option classes now live in the `Predis\Configuration\Option` namespace.
|
||||
|
||||
- Classes for Redis commands have been moved into the new `Predis\Command\Redis`
|
||||
namespace and each class name mirrors the respective Redis command ID.
|
||||
|
||||
- The concept of server profiles is gone, the library now uses a single command
|
||||
factory to create instances of commands classes. The `profile` option has been
|
||||
replaced by the `commands` option accepting `Predis\Command\FactoryInterface`
|
||||
to customize the underlying command factory. The default command factory class
|
||||
used by Predis is `Predis\Command\RedisFactory` and it still allows developers
|
||||
to define or override commands with their own implementations. In addition to
|
||||
that, `Predis\Command\RedisFactory` relies on a convention-over-configuration
|
||||
approach by looking for a suitable class with the same name as the command ID
|
||||
in the `Predis\Command\Redis` when the internal class map does not contain a
|
||||
class associated.
|
||||
|
||||
- The method `Predis\Client::getClientFor($connectionID)` has been replaced by
|
||||
`getClientBy($selector, $value, $callable = null)` which is more flexible as
|
||||
it is not limited to picking a connection from the underlying replication or
|
||||
cluster backend by ID, but allows users to specify a `$selector` that can be
|
||||
either `id` (the old behavior), `key`, `slot` or `command`. The client uses
|
||||
duck-typing instead of type-checking to verify that the underlying connection
|
||||
implements a method that matches the specified selector which means that some
|
||||
selectors may not be available to all kinds of connection backends.
|
||||
|
||||
- The method `Predis\Client::getConnectionById($connectionID)` has been removed.
|
||||
|
||||
- Changed the signature for the constructor of `Predis\Command\RawCommand`.
|
||||
|
||||
- The `Predis\Connection\Aggregate` namespace has been split into two separate
|
||||
namespaces for cluster backends (`Predis\Connection\Cluster`) and replication
|
||||
backends (`Predis\Connection\Replication`).
|
||||
|
||||
- The method `Predis\Connection\AggregateConnectionInterface::getConnection()`
|
||||
has been renamed to `getConnectionByCommand()`.
|
||||
|
||||
- The methods `switchToMaster()` and `switchToSlave()` have been promoted to be
|
||||
part of `Predis\Connection\Replication\ReplicationInterface` while the method
|
||||
`switchTo($connection)` has been removed from it.
|
||||
|
||||
- The method `Predis\Connection\Cluster\PredisCluster::executeCommandOnNodes()`
|
||||
has been removed as it is possible to achieve the same by iterating over the
|
||||
connection or, even better, over the client instance in order to execute the
|
||||
same command against all of the registered connections.
|
||||
|
||||
- The class `Predis\CommunicationException` now uses the correct default types
|
||||
for the `$message` (string) and `$code` (integer) parameters.
|
||||
|
||||
- The method `onConnectionError()` in `Predis\Connection\AbstractConnection`
|
||||
class now passes the second argument as an integer value `0` as its default
|
||||
value instead of `null`.
|
||||
|
||||
- Support Pub/Sub and Pipelines when using replication
|
||||
|
||||
- The class `Predis\Transaction\AbortedMultiExecException` now uses the correct
|
||||
default types for the `$code` (integer) parameter.
|
||||
|
||||
- __FIX__: using `strval` in `getScanOptions()` method, part of
|
||||
`Predis\Collection\Iterator\CursorBasedIterator` to make sure we retrieve the
|
||||
string value of `$this->match` and not passing `null` to `strlen()` function.
|
||||
|
||||
- __FIX__: the value returned from `getArgument()` in `isReadOperation()` method,
|
||||
part of `Predis\Replication\ReplicationStrategy` class, is checked to not pass
|
||||
`null` to `sha1` function.
|
||||
|
||||
- __FIX__: the value returned from `getArgument()` in `parseResponse()`method,
|
||||
part of `Predis\Command\Redis\SENTINEL` class, is checked to not pass `null`
|
||||
to `strtolower()` function.
|
||||
|
||||
## v2.0.0-beta.1 (2022-05-26)
|
||||
|
||||
Same as v2.0.0
|
||||
@@ -1,97 +0,0 @@
|
||||
# Frequently asked questions about Predis #
|
||||
|
||||
## What is the point of Predis? ##
|
||||
|
||||
The main point of Predis is about offering a highly customizable and extensible client for Redis,
|
||||
that can be easily extended by developers while still being reasonably fast. With Predis you can
|
||||
swap almost any class with your own custom implementation: you can have custom connection classes,
|
||||
new distribution strategies for client-side sharding, or handlers to replace or add Redis commands.
|
||||
All of this can be achieved without messing with the source code of the library and directly in your
|
||||
own application. Given the fast pace at which Redis is developed and adds new features, this can be
|
||||
a great asset since it allows developers to add new and still missing features or commands or change
|
||||
the standard behaviour of the library without the need to break dependencies in production code (at
|
||||
least to some degree).
|
||||
|
||||
## Does Predis support UNIX domain sockets and persistent connections? ##
|
||||
|
||||
Yes. Obviously persistent connections actually work only when using PHP configured as a persistent
|
||||
process reused by the web server (see [PHP-FPM](http://php-fpm.org)).
|
||||
|
||||
## Does Predis support SSL-encrypted connections? ##
|
||||
|
||||
Yes. Encrypted connections are mostly useful when connecting to Redis instances exposed by various
|
||||
cloud hosting providers without the need to configure an SSL proxy, but you should also take into
|
||||
account the general performances degradation especially during the connect() operation when the TLS
|
||||
handshake must be performed to secure the connection. Persistent SSL-encrypted connections may help
|
||||
in that respect, but they are supported only when running on PHP >= 7.0.0.
|
||||
|
||||
## Does Predis support transparent (de)serialization of values? ##
|
||||
|
||||
When using [Relay](https://github.com/cachewerk/relay) as the underlying client, several
|
||||
serialization and compression algorithms are supported. This slightly increases CPU usage,
|
||||
but significantly reduces bytes sent over the network and Redis memory usage.
|
||||
|
||||
Without Relay, Predis will not serialize data and will never do that by default. The reason
|
||||
behind this decision is that serialization is usually something that developers prefer to
|
||||
customize depending on their needs and can not be easily generalized when using Redis because
|
||||
of the many possible access patterns for your data. This does not mean that it is impossible
|
||||
to have such a feature since you can leverage the extensibility of this library to define
|
||||
your own serialization-aware commands. You can find more details about how to do that
|
||||
[on this issue](http://github.com/predis/predis/issues/29#issuecomment-1202624).
|
||||
|
||||
## How can I force Predis to connect to Redis before sending any command? ##
|
||||
|
||||
Explicitly connecting to Redis is usually not needed since the client initializes connections lazily
|
||||
only when they are needed. Admittedly, this behavior can be inconvenient in certain scenarios when
|
||||
you absolutely need to perform an upfront check to determine if the server is up and running and
|
||||
eventually catch exceptions on failures. Forcing the client to open the underlying connection can be
|
||||
done by invoking `Predis\Client::connect()`:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client();
|
||||
|
||||
try {
|
||||
$client->connect();
|
||||
} catch (Predis\Connection\ConnectionException $exception) {
|
||||
// We could not connect to Redis! Your handling code goes here.
|
||||
}
|
||||
|
||||
$client->info();
|
||||
```
|
||||
|
||||
## How Predis abstracts Redis commands? ##
|
||||
|
||||
The approach used to implement Redis commands is quite simple: by default each command follows the
|
||||
same signature as defined on the [Redis documentation](http://redis.io/commands) which makes things
|
||||
pretty easy if you already know how Redis works or you need to look up how to use certain commands.
|
||||
Alternatively, variadic commands can accept an array for keys or values (depending on the command)
|
||||
instead of a list of arguments. Commands such as [`RPUSH`](http://redis.io/commands/rpush) and
|
||||
[`HMSET`](http://redis.io/commands/hmset) are great examples:
|
||||
|
||||
```php
|
||||
$client->rpush('my:list', 'value1', 'value2', 'value3'); // plain method arguments
|
||||
$client->rpush('my:list', ['value1', 'value2', 'value3']); // single argument array
|
||||
|
||||
$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // plain method arguments
|
||||
$client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // single named array
|
||||
```
|
||||
|
||||
An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which modifiers are passed
|
||||
[using a named array](tests/Predis/Command/KeySortTest.php#L54-L75).
|
||||
|
||||
## When should I use Relay? ##
|
||||
|
||||
If you care about performance, __always__. [Relay][relay] is free to use.
|
||||
|
||||
## When should I use PhpRedis? ###
|
||||
|
||||
Predis is fast enough when Redis is located on the same machine as PHP.
|
||||
|
||||
[PhpRedis][phpredis] and [Relay][relay] perform significantly better when network I/O is involved,
|
||||
due to its ability to compress data by ~75%. Fewer bytes and received sent over the network
|
||||
[means faster operations][performance], and potentially cost savings when network traffic isn't
|
||||
free (e.g. AWS ElastiCache Inter-AZ transfer costs).
|
||||
|
||||
[phpredis]: https://github.com/phpredis/phpredis
|
||||
[relay]: [https://github.com/phpredis/phpredis](https://github.com/cachewerk/relay)
|
||||
[performance]: https://akalongman.medium.com/phpredis-vs-predis-comparison-on-real-production-data-a819b48cbadb
|
||||
@@ -1,22 +1,22 @@
|
||||
MIT License
|
||||
Copyright (c) 2009-2010 Daniele Alessandri
|
||||
qq
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
Copyright (c) 2009-2020 Daniele Alessandri (original work)
|
||||
Copyright (c) 2021-2024 Till Krüss (modified work)
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
# Predis #
|
||||
|
||||
## About ##
|
||||
|
||||
Predis is a flexible and feature-complete PHP client library for the Redis key-value
|
||||
database.
|
||||
|
||||
Predis is currently a work-in-progress and it comes in two flavors:
|
||||
|
||||
- the mainline client library, which targets PHP 5.3.x and leverages a lot of the
|
||||
features introduced in this new version of the PHP interpreter.
|
||||
- a backport to PHP 5.2.x for those who can not upgrade their environment yet
|
||||
(it admittedly has a lower priority compared to the mainline library, although we
|
||||
try to keep the two versions aligned as much as possible).
|
||||
|
||||
Please refer to the TODO file to see which issues are still pending and what is due
|
||||
to be implemented soon in Predis.
|
||||
|
||||
|
||||
## Features ##
|
||||
|
||||
- Client-side sharding (support for consistent hashing of keys)
|
||||
- Command pipelining on single and multiple connections (transparent)
|
||||
- Lazy connections (connections to Redis instances are only established just in time)
|
||||
- Flexible system to define and register your own set of commands to a client instance
|
||||
|
||||
|
||||
## Quick examples ##
|
||||
|
||||
See the [official wiki](http://wiki.github.com/nrk/predis) of the project for a more
|
||||
complete coverage of all the features available in Predis.
|
||||
|
||||
### Connecting to a local instance of Redis ###
|
||||
|
||||
You don't have to specify a tcp host and port when connecting to Redis instances
|
||||
running on the localhost on the default port:
|
||||
|
||||
$redis = new Predis_Client();
|
||||
$redis->set('library', 'predis');
|
||||
$value = $redis->get('library');
|
||||
|
||||
|
||||
### Pipelining multiple commands to a remote instance of Redis ##
|
||||
|
||||
Pipelining helps with performances when there is the need to issue many commands
|
||||
to a server in one go:
|
||||
|
||||
$redis = new Predis_Client('redis://10.0.0.1:6379/');
|
||||
$pipe = $redis->pipeline();
|
||||
$pipe->ping();
|
||||
$pipe->incrby('counter', 10);
|
||||
$pipe->incrby('counter', 30);
|
||||
$pipe->get('counter');
|
||||
$replies = $pipe->execute();
|
||||
|
||||
|
||||
### Pipelining multiple commands to multiple instances of Redis (sharding) ##
|
||||
|
||||
Predis supports data sharding using consistent-hashing on keys on the client side.
|
||||
Furthermore, a pipeline can be initialized on a cluster of redis instances in the
|
||||
same exact way they are created on single connection. Sharding is still transparent
|
||||
to the user:
|
||||
|
||||
$redis = Predis_Client::create(
|
||||
array('host' => '10.0.0.1', 'port' => 6379),
|
||||
array('host' => '10.0.0.2', 'port' => 6379)
|
||||
);
|
||||
|
||||
$pipe = $redis->pipeline();
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
|
||||
$pipe->get("key:$i");
|
||||
}
|
||||
$replies = $pipe->flushPipeline();
|
||||
|
||||
|
||||
### Definition and runtime registration of new commands on the client ###
|
||||
|
||||
Let's suppose Redis just added the support for a brand new feature associated
|
||||
with a new command. If you want to start using the above mentioned new feature
|
||||
right away without messing with Predis source code or waiting for it to find
|
||||
its way into a stable Predis release, then you can start off by creating a new
|
||||
class that matches the command type and its behaviour and then bind it to a
|
||||
client instance at runtime. Actually, it is easier done than said:
|
||||
|
||||
class BrandNewRedisCommand extends Predis_InlineCommand {
|
||||
public function getCommandId() { return 'NEWCMD'; }
|
||||
}
|
||||
|
||||
$redis = new Predis_Client();
|
||||
$redis->registerCommand('BrandNewRedisCommand', 'newcmd');
|
||||
$redis->newcmd();
|
||||
|
||||
|
||||
## Development ##
|
||||
|
||||
Predis is fully backed up by a test suite which tries to cover all the aspects of the
|
||||
client library and the interaction of every single command with a Redis server. If you
|
||||
want to work on Predis, it is highly recommended that you first run the test suite to
|
||||
be sure that everything is OK, and report strange behaviours or bugs.
|
||||
|
||||
The recommended way to contribute to Predis is to fork the project on GitHub, fix or
|
||||
add features on your newly created repository and then submit issues on the Predis
|
||||
issue tracker with a link to your repository. Obviously, you can use any other Git
|
||||
hosting provider of you preference. Diff patches will be accepted too, even though
|
||||
they are not the preferred way to contribute to Predis.
|
||||
|
||||
When modifying Predis please be sure that no warnings or notices are emitted by PHP
|
||||
by running the interpreter in your development environment with the "error_reporting"
|
||||
variable set to E_ALL.
|
||||
|
||||
|
||||
## Dependencies ##
|
||||
|
||||
- PHP >= 5.3.0 (for the mainline client library)
|
||||
- PHP >= 5.2.6 (for the backported client library)
|
||||
- PHPUnit (needed to run the test suite)
|
||||
|
||||
## Links ##
|
||||
|
||||
### Project ###
|
||||
- [Source code](http://github.com/nrk/predis/)
|
||||
- [Wiki](http://wiki.github.com/nrk/predis/)
|
||||
- [Issue tracker](http://github.com/nrk/predis/issues)
|
||||
|
||||
### Related ###
|
||||
- [Redis](http://code.google.com/p/redis/)
|
||||
- [PHP](http://php.net/)
|
||||
- [PHPUnit](http://www.phpunit.de/)
|
||||
- [Git](http://git-scm.com/)
|
||||
|
||||
## Author ##
|
||||
|
||||
[Daniele Alessandri](mailto:suppakilla@gmail.com)
|
||||
|
||||
|
||||
## License ##
|
||||
|
||||
The code for Predis is distributed under the terms of the MIT license (see LICENSE).
|
||||
@@ -1,692 +0,0 @@
|
||||
# Predis #
|
||||
|
||||
[![Software license][ico-license]](LICENSE)
|
||||
[![Latest stable][ico-version-stable]][link-releases]
|
||||
[![Latest development][ico-version-dev]][link-releases]
|
||||
[![Monthly installs][ico-downloads-monthly]][link-downloads]
|
||||
[![Build status][ico-build]][link-actions]
|
||||
[![Coverage Status][ico-coverage]][link-coverage]
|
||||
|
||||
A flexible and feature-complete [Redis](http://redis.io) / [Valkey](https://github.com/valkey-io/valkey) client for PHP 7.2 and newer.
|
||||
|
||||
More details about this project can be found on the [frequently asked questions](FAQ.md).
|
||||
|
||||
|
||||
## Main features ##
|
||||
|
||||
- Support for Redis from __3.0__ to __8.0__.
|
||||
- Support for clustering using client-side sharding and pluggable keyspace distributors.
|
||||
- Support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
|
||||
- Support for master-slave replication setups and [redis-sentinel](http://redis.io/topics/sentinel).
|
||||
- Transparent key prefixing of keys using a customizable prefix strategy.
|
||||
- Command pipelining on both single nodes and clusters (client-side sharding only).
|
||||
- Abstraction for Redis transactions (Redis >= 2.0) and CAS operations (Redis >= 2.2).
|
||||
- Abstraction for Lua scripting (Redis >= 2.6) and automatic switching between `EVALSHA` or `EVAL`.
|
||||
- Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators.
|
||||
- Connections are established lazily by the client upon the first command and can be persisted.
|
||||
- Connections can be established via TCP/IP (also TLS/SSL-encrypted) or UNIX domain sockets.
|
||||
- Support for custom connection classes for providing different network or protocol backends.
|
||||
- Flexible system for defining custom commands and override the default ones.
|
||||
|
||||
|
||||
## How to _install_ and use Predis ##
|
||||
|
||||
This library can be found on [Packagist](http://packagist.org/packages/predis/predis) for an easier
|
||||
management of projects dependencies using [Composer](http://packagist.org/about-composer).
|
||||
Compressed archives of each release are [available on GitHub](https://github.com/predis/predis/releases).
|
||||
|
||||
```shell
|
||||
composer require predis/predis
|
||||
```
|
||||
|
||||
|
||||
### Loading the library ###
|
||||
|
||||
Predis relies on the autoloading features of PHP to load its files when needed and complies with the
|
||||
[PSR-4 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md).
|
||||
Autoloading is handled automatically when dependencies are managed through Composer, but it is also
|
||||
possible to leverage its own autoloader in projects or scripts lacking any autoload facility:
|
||||
|
||||
```php
|
||||
// Prepend a base path if Predis is not available in your "include_path".
|
||||
require 'Predis/Autoloader.php';
|
||||
|
||||
Predis\Autoloader::register();
|
||||
```
|
||||
|
||||
|
||||
### Connecting to Redis ###
|
||||
|
||||
When creating a client instance without passing any connection parameter, Predis assumes `127.0.0.1`
|
||||
and `6379` as default host and port. The default timeout for the `connect()` operation is 5 seconds:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client();
|
||||
$client->set('foo', 'bar');
|
||||
$value = $client->get('foo');
|
||||
```
|
||||
|
||||
Connection parameters can be supplied either in the form of URI strings or named arrays. The latter
|
||||
is the preferred way to supply parameters, but URI strings can be useful when parameters are read
|
||||
from non-structured or partially-structured sources:
|
||||
|
||||
```php
|
||||
// Parameters passed using a named array:
|
||||
$client = new Predis\Client([
|
||||
'scheme' => 'tcp',
|
||||
'host' => '10.0.0.1',
|
||||
'port' => 6379,
|
||||
]);
|
||||
|
||||
// Same set of parameters, passed using an URI string:
|
||||
$client = new Predis\Client('tcp://10.0.0.1:6379');
|
||||
```
|
||||
|
||||
Password protected servers can be accessed by adding `password` to the parameters set. When ACLs are
|
||||
enabled on Redis >= 6.0, both `username` and `password` are required for user authentication.
|
||||
|
||||
It is also possible to connect to local instances of Redis using UNIX domain sockets, in this case
|
||||
the parameters must use the `unix` scheme and specify a path for the socket file:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client(['scheme' => 'unix', 'path' => '/path/to/redis.sock']);
|
||||
$client = new Predis\Client('unix:/path/to/redis.sock');
|
||||
```
|
||||
|
||||
The client can leverage TLS/SSL encryption to connect to secured remote Redis instances without the
|
||||
need to configure an SSL proxy like stunnel. This can be useful when connecting to nodes running on
|
||||
various cloud hosting providers. Encryption can be enabled with using the `tls` scheme and an array
|
||||
of suitable [options](http://php.net/manual/context.ssl.php) passed via the `ssl` parameter:
|
||||
|
||||
```php
|
||||
// Named array of connection parameters:
|
||||
$client = new Predis\Client([
|
||||
'scheme' => 'tls',
|
||||
'ssl' => ['cafile' => 'private.pem', 'verify_peer' => true],
|
||||
]);
|
||||
|
||||
// Same set of parameters, but using an URI string:
|
||||
$client = new Predis\Client('tls://127.0.0.1?ssl[cafile]=private.pem&ssl[verify_peer]=1');
|
||||
```
|
||||
|
||||
The connection schemes [`redis`](http://www.iana.org/assignments/uri-schemes/prov/redis) (alias of
|
||||
`tcp`) and [`rediss`](http://www.iana.org/assignments/uri-schemes/prov/rediss) (alias of `tls`) are
|
||||
also supported, with the difference that URI strings containing these schemes are parsed following
|
||||
the rules described on their respective IANA provisional registration documents.
|
||||
|
||||
The actual list of supported connection parameters can vary depending on each connection backend so
|
||||
it is recommended to refer to their specific documentation or implementation for details.
|
||||
|
||||
Predis can aggregate multiple connections when providing an array of connection parameters and the
|
||||
appropriate option to instruct the client about how to aggregate them (clustering, replication or a
|
||||
custom aggregation logic). Named arrays and URI strings can be mixed when providing configurations
|
||||
for each node:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client([
|
||||
'tcp://10.0.0.1?alias=first-node', ['host' => '10.0.0.2', 'alias' => 'second-node'],
|
||||
], [
|
||||
'cluster' => 'predis',
|
||||
]);
|
||||
```
|
||||
|
||||
See the [aggregate connections](#aggregate-connections) section of this document for more details.
|
||||
|
||||
Connections to Redis are lazy meaning that the client connects to a server only if and when needed.
|
||||
While it is recommended to let the client do its own stuff under the hood, there may be times when
|
||||
it is still desired to have control of when the connection is opened or closed: this can easily be
|
||||
achieved by invoking `$client->connect()` and `$client->disconnect()`. Please note that the effect
|
||||
of these methods on aggregate connections may differ depending on each specific implementation.
|
||||
|
||||
#### Persistent connections ####
|
||||
|
||||
To increase a performance of your application you may set up a client to use persistent TCP connection, this way
|
||||
client saves a time on socket creation and connection handshake. By default, connection is created on first-command
|
||||
execution and will be automatically closed by GC before the process is being killed.
|
||||
However, if your application is backed by PHP-FPM the processes are idle, and you may set up it to be persistent and
|
||||
reusable across multiple script execution within the same process.
|
||||
|
||||
To enable the persistent connection mode you should provide following configuration:
|
||||
|
||||
```php
|
||||
// Standalone
|
||||
$client = new Predis\Client(['persistent' => true]);
|
||||
|
||||
// Cluster
|
||||
$client = new Predis\Client(
|
||||
['tcp://host:port', 'tcp://host:port', 'tcp://host:port'],
|
||||
['cluster' => 'redis', 'parameters' => ['persistent' => true]]
|
||||
);
|
||||
```
|
||||
|
||||
**Important**
|
||||
|
||||
If you operate on multiple clients within the same application, and they communicate with the same resource, by default
|
||||
they will share the same socket (that's the default behaviour of persistent sockets). So in this case you would need
|
||||
to additionally provide a `conn_uid` identifier for each client, this way each client will create its own socket so
|
||||
the connection context won't be shared across clients. This socket behaviour explained
|
||||
[here](https://www.php.net/manual/en/function.stream-socket-client.php#105393)
|
||||
|
||||
```php
|
||||
// Standalone
|
||||
$client1 = new Predis\Client(['persistent' => true, 'conn_uid' => 'id_1']);
|
||||
$client2 = new Predis\Client(['persistent' => true, 'conn_uid' => 'id_2']);
|
||||
|
||||
// Cluster
|
||||
$client1 = new Predis\Client(
|
||||
['tcp://host:port', 'tcp://host:port', 'tcp://host:port'],
|
||||
['cluster' => 'redis', 'parameters' => ['persistent' => true, 'conn_uid' => 'id_1']]
|
||||
);
|
||||
$client2 = new Predis\Client(
|
||||
['tcp://host:port', 'tcp://host:port', 'tcp://host:port'],
|
||||
['cluster' => 'redis', 'parameters' => ['persistent' => true, 'conn_uid' => 'id_2']]
|
||||
);
|
||||
```
|
||||
|
||||
### Client configuration ###
|
||||
|
||||
Many aspects and behaviors of the client can be configured by passing specific client options to the
|
||||
second argument of `Predis\Client::__construct()`:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client($parameters, ['prefix' => 'sample:']);
|
||||
```
|
||||
|
||||
Options are managed using a mini DI-alike container and their values can be lazily initialized only
|
||||
when needed. The client options supported by default in Predis are:
|
||||
|
||||
- `prefix`: prefix string applied to every key found in commands.
|
||||
- `exceptions`: whether the client should throw or return responses upon Redis errors.
|
||||
- `connections`: list of connection backends or a connection factory instance.
|
||||
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable).
|
||||
- `replication`: specifies a replication backend (`predis`, `sentinel` or callable).
|
||||
- `aggregate`: configures the client with a custom aggregate connection (callable).
|
||||
- `parameters`: list of default connection parameters for aggregate connections.
|
||||
- `commands`: specifies a command factory instance to use through the library.
|
||||
- `readTimeout`: (cluster only) Timeout between read operations while loop over connections.
|
||||
|
||||
Users can also provide custom options with values or callable objects (for lazy initialization) that
|
||||
are stored in the options container for later use through the library.
|
||||
|
||||
|
||||
### Aggregate connections ###
|
||||
|
||||
Aggregate connections are the foundation upon which Predis implements clustering and replication and
|
||||
they are used to group multiple connections to single Redis nodes and hide the specific logic needed
|
||||
to handle them properly depending on the context. Aggregate connections usually require an array of
|
||||
connection parameters along with the appropriate client option when creating a new client instance.
|
||||
|
||||
#### Cluster ####
|
||||
|
||||
Predis can be configured to work in clustering mode with a traditional client-side sharding approach
|
||||
to create a cluster of independent nodes and distribute the keyspace among them. This approach needs
|
||||
some sort of external health monitoring of nodes and requires the keyspace to be rebalanced manually
|
||||
when nodes are added or removed:
|
||||
|
||||
```php
|
||||
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||
$options = ['cluster' => 'predis'];
|
||||
|
||||
$client = new Predis\Client($parameters);
|
||||
```
|
||||
|
||||
Along with Redis 3.0, a new supervised and coordinated type of clustering was introduced in the form
|
||||
of [redis-cluster](http://redis.io/topics/cluster-tutorial). This kind of approach uses a different
|
||||
algorithm to distribute the keyspaces, with Redis nodes coordinating themselves by communicating via
|
||||
a gossip protocol to handle health status, rebalancing, nodes discovery and request redirection. In
|
||||
order to connect to a cluster managed by redis-cluster, the client requires a list of its nodes (not
|
||||
necessarily complete since it will automatically discover new nodes if necessary) and the `cluster`
|
||||
client options set to `redis`:
|
||||
|
||||
```php
|
||||
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||
$options = ['cluster' => 'redis'];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
```
|
||||
|
||||
#### Redis Gears with cluster ####
|
||||
|
||||
Since Redis v7.2, Redis Gears module is a part of Redis Stack bundle. Client supports a variety of
|
||||
Redis Gears commands that can be used with OSS cluster API. Currently, before using any Redis
|
||||
Gears commands against OSS cluster Redis server needs to be aware of cluster topology.
|
||||
|
||||
`REDISGEARS_2.REFRESHCLUSTER` command should be called against **each master node** (read replicas
|
||||
should be ignored) **on cluster creation and each time cluster topology changes**.
|
||||
|
||||
In most cases this actions should be performed from the CLI interface by the administrator, DevOPS
|
||||
or even Kubernetes, depends on your infrastructure managing process. However, client provides an API
|
||||
to do this programmatically.
|
||||
|
||||
```php
|
||||
/** @var \Predis\Connection\Cluster\ClusterInterface $connection */
|
||||
$connection->executeCommandOnEachNode(
|
||||
new \Predis\Command\RawCommand('REDISGEARS_2.REFRESHCLUSTER')
|
||||
);
|
||||
```
|
||||
|
||||
#### Replication ####
|
||||
|
||||
The client can be configured to operate in a single master / multiple slaves setup to provide better
|
||||
service availability. When using replication, Predis recognizes read-only commands and sends them to
|
||||
a random slave in order to provide some sort of load-balancing and switches to the master as soon as
|
||||
it detects a command that performs any kind of operation that would end up modifying the keyspace or
|
||||
the value of a key. Instead of raising a connection error when a slave fails, the client attempts to
|
||||
fall back to a different slave among the ones provided in the configuration.
|
||||
|
||||
The basic configuration needed to use the client in replication mode requires one Redis server to be
|
||||
identified as the master (this can be done via connection parameters by setting the `role` parameter
|
||||
to `master`) and one or more slaves (in this case setting `role` to `slave` for slaves is optional):
|
||||
|
||||
```php
|
||||
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||
$options = ['replication' => 'predis'];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
```
|
||||
|
||||
The above configuration has a static list of servers and relies entirely on the client's logic, but
|
||||
it is possible to rely on [`redis-sentinel`](http://redis.io/topics/sentinel) for a more robust HA
|
||||
environment with sentinel servers acting as a source of authority for clients for service discovery.
|
||||
The minimum configuration required by the client to work with redis-sentinel is a list of connection
|
||||
parameters pointing to a bunch of sentinel instances, the `replication` option set to `sentinel` and
|
||||
the `service` option set to the name of the service:
|
||||
|
||||
```php
|
||||
$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||
$options = ['replication' => 'sentinel', 'service' => 'mymaster'];
|
||||
|
||||
$client = new Predis\Client($sentinels, $options);
|
||||
```
|
||||
|
||||
If the master and slave nodes are configured to require an authentication from clients, a password
|
||||
must be provided via the global `parameters` client option. This option can also be used to specify
|
||||
a different database index. The client options array would then look like this:
|
||||
|
||||
```php
|
||||
$options = [
|
||||
'replication' => 'sentinel',
|
||||
'service' => 'mymaster',
|
||||
'parameters' => [
|
||||
'password' => $secretpassword,
|
||||
'database' => 10,
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
While Predis is able to distinguish commands performing write and read-only operations, `EVAL` and
|
||||
`EVALSHA` represent a corner case in which the client switches to the master node because it cannot
|
||||
tell when a Lua script is safe to be executed on slaves. While this is indeed the default behavior,
|
||||
when certain Lua scripts do not perform write operations it is possible to provide an hint to tell
|
||||
the client to stick with slaves for their execution:
|
||||
|
||||
```php
|
||||
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||
$options = ['replication' => function () {
|
||||
// Set scripts that won't trigger a switch from a slave to the master node.
|
||||
$strategy = new Predis\Replication\ReplicationStrategy();
|
||||
$strategy->setScriptReadOnly($LUA_SCRIPT);
|
||||
|
||||
return new Predis\Connection\Replication\MasterSlaveReplication($strategy);
|
||||
}];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
$client->eval($LUA_SCRIPT, 0); // Sticks to slave using `eval`...
|
||||
$client->evalsha(sha1($LUA_SCRIPT), 0); // ... and `evalsha`, too.
|
||||
```
|
||||
|
||||
The [`examples`](examples/) directory contains a few scripts that demonstrate how the client can be
|
||||
configured and used to leverage replication in both basic and complex scenarios.
|
||||
|
||||
|
||||
### Command pipelines ###
|
||||
|
||||
Pipelining can help with performances when many commands need to be sent to a server by reducing the
|
||||
latency introduced by network round-trip timings. Pipelining also works with aggregate connections.
|
||||
The client can execute the pipeline inside a callable block or return a pipeline instance with the
|
||||
ability to chain commands thanks to its fluent interface:
|
||||
|
||||
```php
|
||||
// Executes a pipeline inside the given callable block:
|
||||
$responses = $client->pipeline(function ($pipe) {
|
||||
for ($i = 0; $i < 1000; $i++) {
|
||||
$pipe->set("key:$i", str_pad($i, 4, '0', 0));
|
||||
$pipe->get("key:$i");
|
||||
}
|
||||
});
|
||||
|
||||
// Returns a pipeline that can be chained thanks to its fluent interface:
|
||||
$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute();
|
||||
```
|
||||
|
||||
|
||||
### Transactions ###
|
||||
|
||||
The client provides an abstraction for Redis transactions based on `MULTI` and `EXEC` with a similar
|
||||
interface to command pipelines:
|
||||
|
||||
```php
|
||||
// Executes a transaction inside the given callable block:
|
||||
$responses = $client->transaction(function ($tx) {
|
||||
$tx->set('foo', 'bar');
|
||||
$tx->get('foo');
|
||||
});
|
||||
|
||||
// Returns a transaction that can be chained thanks to its fluent interface:
|
||||
$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute();
|
||||
```
|
||||
|
||||
This abstraction can perform check-and-set operations thanks to `WATCH` and `UNWATCH` and provides
|
||||
automatic retries of transactions aborted by Redis when `WATCH`ed keys are touched. For an example
|
||||
of a transaction using CAS you can see [the following example](examples/transaction_using_cas.php).
|
||||
|
||||
#### Support for clustered connections ####
|
||||
|
||||
Since Predis v3.0 transactions could be used with clustered connections. However, it has some limitations due to the
|
||||
fact that Redis doesn't support distributed transactions. All keys in the transaction context should operate on the same
|
||||
hash slot, due to this limitation it's recommended to use `{}` syntax to make sure that all keys will be mapped to the same hash
|
||||
slot. Apart from it no additional configuration needed on a client side.
|
||||
|
||||
```php
|
||||
$redis = $this->getClient();
|
||||
|
||||
$response = $redis->transaction(function (MultiExec $tx) {
|
||||
$tx->set('{foo}foo', 'value');
|
||||
$tx->set('{foo}bar', 'value');
|
||||
$tx->set('{foo}baz', 'value');
|
||||
});
|
||||
|
||||
// ['OK', 'OK', 'OK']
|
||||
```
|
||||
|
||||
|
||||
### Adding new commands ###
|
||||
|
||||
While we try to update Predis to stay up to date with all the commands available in Redis, you might
|
||||
prefer to stick with an old version of the library or provide a different way to filter arguments or
|
||||
parse responses for specific commands. To achieve that, Predis provides the ability to implement new
|
||||
command classes to define or override commands in the default command factory used by the client:
|
||||
|
||||
```php
|
||||
// Define a new command by extending Predis\Command\Command:
|
||||
class BrandNewRedisCommand extends Predis\Command\Command
|
||||
{
|
||||
public function getId()
|
||||
{
|
||||
return 'NEWCMD';
|
||||
}
|
||||
}
|
||||
|
||||
// Inject your command in the current command factory:
|
||||
$client = new Predis\Client($parameters, [
|
||||
'commands' => [
|
||||
'newcmd' => 'BrandNewRedisCommand',
|
||||
],
|
||||
]);
|
||||
|
||||
$response = $client->newcmd();
|
||||
```
|
||||
|
||||
There is also a method to send raw commands without filtering their arguments or parsing responses.
|
||||
Users must provide the list of arguments for the command as an array, following the signatures as
|
||||
defined by the [Redis documentation for commands](http://redis.io/commands):
|
||||
|
||||
```php
|
||||
$response = $client->executeRaw(['SET', 'foo', 'bar']);
|
||||
```
|
||||
|
||||
|
||||
### Script commands ###
|
||||
|
||||
While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using
|
||||
directly [`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha),
|
||||
Predis offers script commands as an higher level abstraction built upon them to make things simple.
|
||||
Script commands can be registered in the command factory used by the client and are accessible as if
|
||||
they were plain Redis commands, but they define Lua scripts that get transmitted to the server for
|
||||
remote execution. Internally they use [`EVALSHA`](http://redis.io/commands/evalsha) by default and
|
||||
identify a script by its SHA1 hash to save bandwidth, but [`EVAL`](http://redis.io/commands/eval)
|
||||
is used as a fall back when needed:
|
||||
|
||||
```php
|
||||
// Define a new script command by extending Predis\Command\ScriptCommand:
|
||||
class ListPushRandomValue extends Predis\Command\ScriptCommand
|
||||
{
|
||||
public function getKeysCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function getScript()
|
||||
{
|
||||
return <<<LUA
|
||||
math.randomseed(ARGV[1])
|
||||
local rnd = tostring(math.random())
|
||||
redis.call('lpush', KEYS[1], rnd)
|
||||
return rnd
|
||||
LUA;
|
||||
}
|
||||
}
|
||||
|
||||
// Inject the script command in the current command factory:
|
||||
$client = new Predis\Client($parameters, [
|
||||
'commands' => [
|
||||
'lpushrand' => 'ListPushRandomValue',
|
||||
],
|
||||
]);
|
||||
|
||||
$response = $client->lpushrand('random_values', $seed = mt_rand());
|
||||
```
|
||||
|
||||
|
||||
### Customizable connection backends ###
|
||||
|
||||
Predis can use different connection backends to connect to Redis. The builtin Relay integration
|
||||
leverages the [Relay](https://github.com/cachewerk/relay) extension for PHP for major performance
|
||||
gains, by caching a partial replica of the Redis dataset in PHP shared runtime memory.
|
||||
|
||||
```php
|
||||
$client = new Predis\Client('tcp://127.0.0.1', [
|
||||
'connections' => 'relay',
|
||||
]);
|
||||
```
|
||||
|
||||
Developers can create their own connection classes to support whole new network backends, extend
|
||||
existing classes or provide completely different implementations. Connection classes must implement
|
||||
`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
|
||||
|
||||
```php
|
||||
class MyConnectionClass implements Predis\Connection\NodeConnectionInterface
|
||||
{
|
||||
// Implementation goes here...
|
||||
}
|
||||
|
||||
// Use MyConnectionClass to handle connections for the `tcp` scheme:
|
||||
$client = new Predis\Client('tcp://127.0.0.1', [
|
||||
'connections' => ['tcp' => 'MyConnectionClass'],
|
||||
]);
|
||||
```
|
||||
|
||||
For a more in-depth insight on how to create new connection backends you can refer to the actual
|
||||
implementation of the standard connection classes available in the `Predis\Connection` namespace.
|
||||
|
||||
## RESP3 ##
|
||||
|
||||
### Connection ###
|
||||
To establish the connection using the [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md) protocol, you need to set parameter `protocol => 3`. The default protocol is RESP2.
|
||||
|
||||
You can pass parameter as configuration option in array or as a query parameter in `redis_url`
|
||||
|
||||
```php
|
||||
// Configuration option
|
||||
$client = new \Predis\Client(['protocol' => 3]);
|
||||
|
||||
// Redis URL
|
||||
$client = new \Predis\Client('redis://localhost:6379?protocol=3');
|
||||
|
||||
// ["proto" => "3"]
|
||||
$client->executeRaw(['HELLO']);
|
||||
```
|
||||
|
||||
### Command responses ###
|
||||
RESP3 protocol introduce a variety of new [response types](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#resp3-types),
|
||||
so on the client-side we have more explicit understanding on data types we retrieve from server. Here's some examples to show the difference
|
||||
between RESP2 and RESP3 responses.
|
||||
|
||||
#### Float responses ####
|
||||
``` php
|
||||
// RESP2 connection
|
||||
$client = new \Predis\Client();
|
||||
|
||||
$client->geoadd('my_geo', 11.111, 22.222, 'member1');
|
||||
|
||||
// [[0 => string(20) "11.11099988222122192", 1 => string(20) "22.22200052541037252"]]
|
||||
// RESP2 returns float values as simple strings.
|
||||
var_dump($client->geopos('my_geo', ['member1']));
|
||||
|
||||
// RESP3 connection
|
||||
$client = new \Predis\Client(['protocol' => 3]);
|
||||
|
||||
// [[0 => float(11.110999882221222), 1 => float(22.222000525410373)]]
|
||||
// RESP3 introduces new double type, that corresponds to PHP float.
|
||||
var_dump($client->geopos('my_geo', ['member1']));
|
||||
```
|
||||
|
||||
#### Aggregate types ####
|
||||
In RESP3 new aggregate type [Map](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#map-type)
|
||||
was introduced, that represents the sequence of field-value pairs. So it simplifies parsing, since we don't need to specify
|
||||
parsing strategy per command (RESP2) and instead relies on the type defined by protocol (RESP3).
|
||||
|
||||
In most cases RESP2 responses shouldn't differ from RESP3, since we added additional parsing for those
|
||||
command that return field-value pairs. However, since RESP2 requires additional parsing, it could be that some commands
|
||||
had lack of it and return unhandled responses. In this case there would be difference like this:
|
||||
|
||||
```php
|
||||
$client = new \Predis\Client();
|
||||
|
||||
// RESP2: ['field', 'value]
|
||||
$client->commandThatReturnsFieldValuePair('key');
|
||||
|
||||
$client = new \Predis\Client(['protocol' => 3]);
|
||||
|
||||
// RESP3: ['field' => 'value]
|
||||
$client->commandThatReturnsFieldValuePair('key');
|
||||
```
|
||||
|
||||
Feel free to open PR or GitHub issue if you face those protocol mismatching.
|
||||
|
||||
### Push notifications ###
|
||||
RESP3 introduce a concept of [push connection](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md#push-type),
|
||||
is the one where server could send asynchronous data to client which was not explicitly requested. Predis 3.0 provides
|
||||
an API to establish this kind of connection as separate blocking process (worker) and invoke callbacks depends on push
|
||||
notification message type.
|
||||
|
||||
#### Consumer ####
|
||||
First of all, you need to set up a consumer connection and provide an optional callback that will be executed before
|
||||
event loop will be started. It allows you to subscribe on channels, enable keys invalidations tracking or enable monitor
|
||||
connection, any Redis command to let server know that you want to receive push notification within this connection.
|
||||
|
||||
```php
|
||||
// Make sure that RESP3 protocol enabled and read_write_timeout set 0,
|
||||
// so connection won't be killed by timeout.
|
||||
$client = new Predis\Client(['read_write_timeout' => 0, 'protocol' => 3]);
|
||||
|
||||
// Create push notifications consumer.
|
||||
// Provides callback where current consumer subscribes to few channels before
|
||||
// enter the loop.
|
||||
$push = $client->push(static function (ClientInterface $client) {
|
||||
$response = $client->subscribe('channel', 'control');
|
||||
$status = ($response[2] === 1) ? 'OK' : 'FAILED';
|
||||
echo "Channel subscription status: {$status}\n";
|
||||
});
|
||||
```
|
||||
|
||||
#### Dispatcher loop ####
|
||||
Dispatcher object allows you to attach a callback to given push notification type and run the actual worker process that
|
||||
listen for incoming push notifications. To be able to stop blocking process in runtime you can specify a condition and
|
||||
call `$dispatcher->stop()` method from given callback. In this example we're waiting for specific message `terminate`
|
||||
within `control` channel that we subscribed to before entering the loop.
|
||||
|
||||
```php
|
||||
// Storage for incoming notifications.
|
||||
$messages = [];
|
||||
|
||||
// Create dispatcher for push notifications.
|
||||
$dispatcher = new Predis\Consumer\Push\DispatcherLoop($push);
|
||||
|
||||
$dispatcher->attachCallback(
|
||||
PushResponseInterface::MESSAGE_DATA_TYPE,
|
||||
static function (array $payload, DispatcherLoopInterface $dispatcher) {
|
||||
global $messages;
|
||||
[$channel, $message] = $payload;
|
||||
|
||||
if ($channel === 'control' && $message === 'terminate') {
|
||||
echo "Terminating notification consumer.\n";
|
||||
$dispatcher->stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$messages[] = $message;
|
||||
echo "Received message: {$message}\n";
|
||||
}
|
||||
);
|
||||
|
||||
// Run consumer loop with attached callbacks.
|
||||
$dispatcher->run();
|
||||
|
||||
// Count all messages that were received during consumer loop.
|
||||
$messagesCount = count($messages);
|
||||
echo "We received: {$messagesCount} messages\n";
|
||||
```
|
||||
|
||||
This example shows a simple script to count all incoming messages from push notifications that we receive from
|
||||
subscribed channels until stop condition will be met. Examples available in `examples/` folder.
|
||||
|
||||
### Sharded pub/sub ###
|
||||
From Redis 7.0, sharded Pub/Sub is introduced in which shard channels are assigned to slots by the same algorithm used
|
||||
to assign keys to slots.
|
||||
|
||||
Predis 3.0 provides an API that allows to use pub/sub for Cluster connections using sharded pub/sub from Redis.
|
||||
You don't need to specify any additional configuration to enable sharded pub/sub, it will be automatically enabled if
|
||||
Cluster connection is using.
|
||||
|
||||
Implementation looks pretty much the same as Push notification, so you need to set up consumer
|
||||
and run it over Dispatcher loop object. All examples available in `examples/` folder.
|
||||
## Development ##
|
||||
|
||||
|
||||
### Reporting bugs and contributing code ###
|
||||
|
||||
Contributions to Predis are highly appreciated either in the form of pull requests for new features,
|
||||
bug fixes, or just bug reports. We only ask you to adhere to issue and pull request templates.
|
||||
|
||||
|
||||
### Test suite ###
|
||||
|
||||
__ATTENTION__: Do not ever run the test suite shipped with Predis against instances of Redis running
|
||||
in production environments or containing data you are interested in!
|
||||
|
||||
Predis has a comprehensive test suite covering every aspect of the library and that can optionally
|
||||
perform integration tests against a running instance of Redis (required >= 2.4.0 in order to verify
|
||||
the correct behavior of the implementation of each command. Integration tests for unsupported Redis
|
||||
commands are automatically skipped. If you do not have Redis up and running, integration tests can
|
||||
be disabled. See [the tests README](tests/README.md) for more details about testing this library.
|
||||
|
||||
Predis uses GitHub Actions for continuous integration and the history for past and current builds can be
|
||||
found [on its actions page](https://github.com/predis/predis/actions).
|
||||
|
||||
### License ###
|
||||
|
||||
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
|
||||
|
||||
[ico-license]: https://img.shields.io/github/license/predis/predis.svg?style=flat-square
|
||||
[ico-version-stable]: https://img.shields.io/github/v/tag/predis/predis?label=stable&style=flat-square
|
||||
[ico-version-dev]: https://img.shields.io/github/v/tag/predis/predis?include_prereleases&label=pre-release&style=flat-square
|
||||
[ico-downloads-monthly]: https://img.shields.io/packagist/dm/predis/predis.svg?style=flat-square
|
||||
[ico-build]: https://img.shields.io/github/actions/workflow/status/predis/predis/tests.yml?branch=main&style=flat-square
|
||||
[ico-coverage]: https://img.shields.io/coverallsCoverage/github/predis/predis?style=flat-square
|
||||
|
||||
[link-releases]: https://github.com/predis/predis/releases
|
||||
[link-actions]: https://github.com/predis/predis/actions
|
||||
[link-downloads]: https://packagist.org/packages/predis/predis/stats
|
||||
[link-coverage]: https://coveralls.io/github/predis/predis
|
||||
@@ -0,0 +1,6 @@
|
||||
* Documentation! The README is obviously not enought to show how to use
|
||||
Predis as it does not cover all of its features.
|
||||
|
||||
* The included test suite covers almost all the Redis server commands, but a
|
||||
full battery of tests targeting specific functions of this library is still
|
||||
missing.
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__.'/src/Autoloader.php';
|
||||
|
||||
Predis\Autoloader::register();
|
||||
@@ -1,274 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
// This script can be used to automatically generate a file with the scheleton
|
||||
// of a test case to test a Redis command by specifying the name of the class
|
||||
// in the Predis\Command namespace (only classes in this namespace are valid).
|
||||
// For example, to generate a test case for SET (which is represented by the
|
||||
// Predis\Command\Redis\StringSet class):
|
||||
//
|
||||
// $ ./bin/create-command-test --class=StringSet
|
||||
//
|
||||
// Here is a list of optional arguments:
|
||||
//
|
||||
// --realm: each command has its own realm (commands that operate on strings,
|
||||
// lists, sets and such) but while this realm is usually inferred from the name
|
||||
// of the specified class, sometimes it can be useful to override it with a
|
||||
// custom one.
|
||||
//
|
||||
// --output: write the generated test case to the specified path instead of
|
||||
// the default one.
|
||||
//
|
||||
// --overwrite: pre-existing test files are not overwritten unless this option
|
||||
// is explicitly specified.
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\PrefixableCommandInterface;
|
||||
|
||||
class CommandTestCaseGenerator
|
||||
{
|
||||
private $options;
|
||||
|
||||
public function __construct(array $options)
|
||||
{
|
||||
if (!isset($options['class'])) {
|
||||
throw new RuntimeException("Missing 'class' option.");
|
||||
}
|
||||
|
||||
if (!isset($options['realm'])) {
|
||||
throw new RuntimeException("Missing 'realm' option.");
|
||||
}
|
||||
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public static function fromCommandLine()
|
||||
{
|
||||
$parameters = array(
|
||||
'c:' => 'class:',
|
||||
'r::' => 'realm::',
|
||||
'o::' => 'output::',
|
||||
'x::' => 'overwrite::'
|
||||
);
|
||||
|
||||
$getops = getopt(implode(array_keys($parameters)), $parameters);
|
||||
|
||||
$options = array(
|
||||
'overwrite' => false,
|
||||
'tests' => __DIR__.'/../tests/Predis',
|
||||
);
|
||||
|
||||
foreach ($getops as $option => $value) {
|
||||
switch ($option) {
|
||||
case 'c':
|
||||
case 'class':
|
||||
$options['class'] = $value;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
case 'realm':
|
||||
$options['realm'] = $value;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
case 'output':
|
||||
$options['output'] = $value;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
case 'overwrite':
|
||||
$options['overwrite'] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($options['class'])) {
|
||||
throw new RuntimeException("Missing 'class' option.");
|
||||
}
|
||||
|
||||
if (!isset($options['realm'])) {
|
||||
throw new RuntimeException("Missing 'realm' option.");
|
||||
}
|
||||
|
||||
$options['fqn'] = "Predis\\Command\\Redis\\{$options['class']}";
|
||||
$options['path'] = "Command/Redis/{$options['class']}.php";
|
||||
|
||||
$source = __DIR__.'/../src/'.$options['path'];
|
||||
if (!file_exists($source)) {
|
||||
throw new RuntimeException("Cannot find class file for {$options['fqn']} in $source.");
|
||||
}
|
||||
|
||||
if (!isset($options['output'])) {
|
||||
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', '_Test.php', $options['path']));
|
||||
}
|
||||
|
||||
return new self($options);
|
||||
}
|
||||
|
||||
protected function getTestRealm()
|
||||
{
|
||||
if (empty($this->options['realm'])) {
|
||||
throw new RuntimeException('Invalid value for realm has been specified (empty).');
|
||||
}
|
||||
|
||||
return $this->options['realm'];
|
||||
}
|
||||
|
||||
public function generate()
|
||||
{
|
||||
$reflection = new ReflectionClass($class = $this->options['fqn']);
|
||||
|
||||
if (!$reflection->isInstantiable()) {
|
||||
throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed.");
|
||||
}
|
||||
|
||||
if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) {
|
||||
throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface.");
|
||||
}
|
||||
|
||||
/*
|
||||
* @var CommandInterface
|
||||
*/
|
||||
$instance = $reflection->newInstance();
|
||||
|
||||
$buffer = $this->getTestCaseBuffer($instance);
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$options = $this->options;
|
||||
if (file_exists($options['output']) && !$options['overwrite']) {
|
||||
throw new RuntimeException("File {$options['output']} already exist. Specify the --overwrite option to overwrite the existing file.");
|
||||
}
|
||||
file_put_contents($options['output'], $this->generate());
|
||||
}
|
||||
|
||||
protected function getTestCaseBuffer(CommandInterface $instance)
|
||||
{
|
||||
$id = $instance->getId();
|
||||
$fqn = get_class($instance);
|
||||
$fqnParts = explode('\\', $fqn);
|
||||
$class = array_pop($fqnParts) . "Test";
|
||||
$realm = $this->getTestRealm();
|
||||
|
||||
$buffer =<<<PHP
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
* @group realm-$realm
|
||||
*/
|
||||
class $class extends PredisCommandTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
{
|
||||
return '$fqn';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
{
|
||||
return '$id';
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments(): void
|
||||
{
|
||||
\$this->markTestIncomplete('This test has not been implemented yet.');
|
||||
|
||||
\$arguments = array(/* add arguments */);
|
||||
\$expected = array(/* add arguments */);
|
||||
|
||||
\$command = \$this->getCommand();
|
||||
\$command->setArguments(\$arguments);
|
||||
|
||||
\$this->assertSame(\$expected, \$command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse(): void
|
||||
{
|
||||
\$this->markTestIncomplete('This test has not been implemented yet.');
|
||||
|
||||
\$raw = null;
|
||||
\$expected = null;
|
||||
|
||||
\$command = \$this->getCommand();
|
||||
|
||||
\$this->assertSame(\$expected, \$command->parseResponse(\$raw));
|
||||
}
|
||||
|
||||
PHP;
|
||||
|
||||
if ($instance instanceof PrefixableCommandInterface) {
|
||||
$buffer .=<<<PHP
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeys(): void
|
||||
{
|
||||
\$this->markTestIncomplete('This test has not been implemented yet.');
|
||||
|
||||
\$arguments = array(/* add arguments */);
|
||||
\$expected = array(/* add arguments */);
|
||||
|
||||
\$command = \$this->getCommandWithArgumentsArray(\$arguments);
|
||||
\$command->prefixKeys('prefix:');
|
||||
|
||||
\$this->assertSame(\$expected, \$command->getArguments());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeysIgnoredOnEmptyArguments(): void
|
||||
{
|
||||
\$command = \$this->getCommand();
|
||||
\$command->prefixKeys('prefix:');
|
||||
|
||||
\$this->assertSame(array(), \$command->getArguments());
|
||||
}
|
||||
|
||||
PHP;
|
||||
}
|
||||
|
||||
return "$buffer}\n";
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
require __DIR__.'/../autoload.php';
|
||||
|
||||
$generator = CommandTestCaseGenerator::fromCommandLine();
|
||||
$generator->save();
|
||||
@@ -1,53 +0,0 @@
|
||||
{
|
||||
"name": "predis/predis",
|
||||
"type": "library",
|
||||
"description": "A flexible and feature-complete Redis/Valkey client for PHP.",
|
||||
"keywords": ["nosql", "redis", "predis"],
|
||||
"homepage": "http://github.com/predis/predis",
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
"issues": "https://github.com/predis/predis/issues"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Till Krüss",
|
||||
"homepage": "https://till.im",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tillkruss"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"psr/http-message": "^1.0|^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.3",
|
||||
"phpstan/phpstan": "^1.9",
|
||||
"phpunit/phpunit": "^8.0 || ~9.4.4",
|
||||
"phpunit/phpcov": "^6.0 || ^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
|
||||
},
|
||||
"scripts": {
|
||||
"phpstan": "phpstan analyse",
|
||||
"style": "php-cs-fixer fix --diff --dry-run",
|
||||
"style:fix": "php-cs-fixer fix"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Predis\\": "src/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"preferred-install": "dist"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
require_once 'SharedConfigurations.php';
|
||||
|
||||
// when you have a whole set of consecutive commands to send to
|
||||
// a redis server, you can use a pipeline to improve performances.
|
||||
|
||||
$redis = Predis_Client::create($configurations);
|
||||
|
||||
$pipe = $redis->pipeline();
|
||||
$pipe->ping();
|
||||
$pipe->flushdb();
|
||||
$pipe->incrby('counter', 10);
|
||||
$pipe->incrby('counter', 30);
|
||||
$pipe->exists('counter');
|
||||
$pipe->get('counter');
|
||||
$pipe->mget('does_not_exist', 'counter');
|
||||
$replies = $pipe->execute();
|
||||
|
||||
print_r($replies);
|
||||
|
||||
/* OUTPUT:
|
||||
Array
|
||||
(
|
||||
[0] => 1
|
||||
[1] => 1
|
||||
[2] => 10
|
||||
[3] => 40
|
||||
[4] => 1
|
||||
[5] => 40
|
||||
[6] => Array
|
||||
(
|
||||
[0] =>
|
||||
[1] => 40
|
||||
)
|
||||
|
||||
)
|
||||
*/
|
||||
?>
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of JSON.DEBUG command usage:
|
||||
|
||||
// 1. Set JSON object
|
||||
$client = new Client();
|
||||
|
||||
$client->jsonset('key', '$', '{"key1":"value1","key2":"value2"}');
|
||||
|
||||
// 2. Dump information about json memory usage in bytes
|
||||
$response = $client->jsondebug->memory('key', '$');
|
||||
|
||||
echo 'Response:' . "\n";
|
||||
print_r($response);
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\AggregateArguments;
|
||||
use Predis\Command\Argument\Search\CreateArguments;
|
||||
use Predis\Command\Argument\Search\SchemaFields\AbstractField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\NumericField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.AGGREGATE command usage:
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$ftCreateArguments = (new CreateArguments())->prefix(['user:']);
|
||||
$schema = [
|
||||
new TextField('name'),
|
||||
new TextField('country'),
|
||||
new NumericField('dob', '', AbstractField::SORTABLE),
|
||||
];
|
||||
|
||||
$client->ftcreate('idx', $schema, $ftCreateArguments);
|
||||
|
||||
// 2. Add documents
|
||||
$client->hset('user:0', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600);
|
||||
$client->hset('user:1', 'name', 'Vlad', 'country', 'Israel', 'dob', 782265600);
|
||||
$client->hset('user:2', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600);
|
||||
|
||||
// 3. Execute aggregation query
|
||||
$ftAggregateArguments = (new AggregateArguments())
|
||||
->apply('year(@dob)', 'birth')
|
||||
->groupBy('@country', '@birth')
|
||||
->reduce('COUNT', true, 'country_birth_Vlad_count')
|
||||
->sortBy(0, '@birth', 'DESC');
|
||||
|
||||
$response = $client->ftaggregate('idx', '@name: "Vlad"', $ftAggregateArguments);
|
||||
|
||||
// Response grouped by user country and birth year, with users count in each group, sorted by birth year from DESC.
|
||||
echo 'Response:' . "\n";
|
||||
print_r($response);
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.ALTER command usage:
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$schema = [
|
||||
new TextField('text_field'),
|
||||
];
|
||||
$client->ftcreate('index_alter', $schema);
|
||||
|
||||
echo 'Default index attributes:' . "\n";
|
||||
$defaultAttributes = $client->ftinfo('index_alter');
|
||||
print_r($defaultAttributes[7]);
|
||||
|
||||
// 2. Add additional attribute to existing index
|
||||
$schema = [
|
||||
new TextField('new_field_name'),
|
||||
];
|
||||
|
||||
$client->ftalter('index_alter', $schema);
|
||||
|
||||
echo 'Updated index attributes:' . "\n";
|
||||
$updatedAttributes = $client->ftinfo('index_alter');
|
||||
print_r($updatedAttributes[7]);
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.CONFIG HELP command usage:
|
||||
|
||||
// 1. Dump helpful information about FT.CONFIG MAXEXPANSIONS option
|
||||
$client = new Client();
|
||||
|
||||
echo 'Response:' . "\n";
|
||||
print_r(
|
||||
$client->ftconfig->help('MAXEXPANSIONS')
|
||||
);
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.CONFIG SET command usage:
|
||||
|
||||
// 1. Change default timeout setting
|
||||
$client = new Client();
|
||||
|
||||
echo 'Response:' . "\n";
|
||||
|
||||
print_r(
|
||||
$client->ftconfig->set('TIMEOUT', 42)
|
||||
);
|
||||
print_r(
|
||||
$client->ftconfig->get('TIMEOUT')
|
||||
);
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\AggregateArguments;
|
||||
use Predis\Command\Argument\Search\CreateArguments;
|
||||
use Predis\Command\Argument\Search\SchemaFields\AbstractField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\NumericField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.CURSOR DEL command usage
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$ftCreateArguments = (new CreateArguments())->prefix(['user:']);
|
||||
$schema = [
|
||||
new TextField('name'),
|
||||
new TextField('country'),
|
||||
new NumericField('dob', '', AbstractField::SORTABLE),
|
||||
];
|
||||
|
||||
$client->ftcreate('index_cursor_del', $schema, $ftCreateArguments);
|
||||
|
||||
// 2. Add documents
|
||||
$client->hset('user:0', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600);
|
||||
$client->hset('user:1', 'name', 'Vlad', 'country', 'Israel', 'dob', 782265600);
|
||||
$client->hset('user:2', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600);
|
||||
|
||||
// 3. Execute aggregation query
|
||||
$ftAggregateArguments = (new AggregateArguments())
|
||||
->apply('year(@dob)', 'birth')
|
||||
->groupBy('@country', '@birth')
|
||||
->reduce('COUNT', true, 'country_birth_Vlad_count')
|
||||
->sortBy(0, '@birth', 'DESC')
|
||||
->withCursor(1);
|
||||
|
||||
[$_, $cursor] = $client->ftaggregate('index_cursor_del', '@name: "Vlad"', $ftAggregateArguments);
|
||||
|
||||
// 4. Explicitly remove returned cursor
|
||||
$client->ftcursor->del('index_cursor_del', $cursor);
|
||||
|
||||
echo "Cursor with cursorId - {$cursor} was removed";
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\AggregateArguments;
|
||||
use Predis\Command\Argument\Search\CreateArguments;
|
||||
use Predis\Command\Argument\Search\SchemaFields\AbstractField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\NumericField;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.CURSOR READ command usage
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$ftCreateArguments = (new CreateArguments())->prefix(['user:']);
|
||||
$schema = [
|
||||
new TextField('name'),
|
||||
new TextField('country'),
|
||||
new NumericField('dob', '', AbstractField::SORTABLE),
|
||||
];
|
||||
|
||||
$client->ftcreate('index_cursor_read', $schema, $ftCreateArguments);
|
||||
|
||||
// 2. Add documents
|
||||
$client->hset('user:0', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600);
|
||||
$client->hset('user:1', 'name', 'Vlad', 'country', 'Israel', 'dob', 782265600);
|
||||
$client->hset('user:2', 'name', 'Vlad', 'country', 'Ukraine', 'dob', 813801600);
|
||||
|
||||
// 3. Execute aggregation query
|
||||
$ftAggregateArguments = (new AggregateArguments())
|
||||
->apply('year(@dob)', 'birth')
|
||||
->groupBy('@country', '@birth')
|
||||
->reduce('COUNT', true, 'country_birth_Vlad_count')
|
||||
->sortBy(0, '@birth', 'DESC')
|
||||
->withCursor(1);
|
||||
|
||||
[$response, $cursor] = $client->ftaggregate('index_cursor_read', '@name: "Vlad"', $ftAggregateArguments);
|
||||
|
||||
// 4. Processing response in loop until cursorId exists
|
||||
$actualResponse = [];
|
||||
$cursors = [];
|
||||
|
||||
while ($cursor) {
|
||||
$actualResponse[] = $response[1];
|
||||
$cursors[] = $cursor;
|
||||
[$response, $cursor] = $client->ftcursor->read('index_cursor_read', $cursor);
|
||||
}
|
||||
|
||||
echo "Response: \n";
|
||||
print_r($actualResponse);
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.EXPLAIN command usage:
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$schema = [
|
||||
new TextField('text_field'),
|
||||
];
|
||||
$client->ftcreate('index_explain', $schema);
|
||||
|
||||
// 2. Run query explanations
|
||||
$response = $client->ftexplain('index_explain', '(foo bar)|(hello world) @date:[100 200]|@date:[500 +inf]');
|
||||
|
||||
echo 'Response:' . "\n";
|
||||
print_r($response);
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\ProfileArguments;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.PROFILE command usage:
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$schema = [
|
||||
new TextField('text_field'),
|
||||
];
|
||||
$client->ftcreate('index_profile', $schema);
|
||||
|
||||
// 2. Create FT.PROFILE command arguments
|
||||
$arguments = (new ProfileArguments())
|
||||
->search()
|
||||
->query('query');
|
||||
|
||||
// 3. Run profile query
|
||||
$response = $client->ftprofile('index_profile', $arguments);
|
||||
|
||||
echo 'Response:' . "\n";
|
||||
print_r($response);
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
use Predis\Command\Argument\Search\SpellcheckArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.SPELLCHECK command usage:
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$schema = [
|
||||
new TextField('text_field'),
|
||||
];
|
||||
$client->ftcreate('index_spellcheck', $schema);
|
||||
|
||||
// 2. Add dictionary with terms
|
||||
$client->ftdictadd('dict', 'hello', 'help');
|
||||
|
||||
// 3. Perform spelling correction query
|
||||
$response = $client->ftspellcheck(
|
||||
'index_spellcheck',
|
||||
'held',
|
||||
(new SpellcheckArguments())->distance(2)->terms('dict')
|
||||
);
|
||||
|
||||
echo 'Response:' . "\n";
|
||||
print_r($response);
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\SugAddArguments;
|
||||
use Predis\Command\Argument\Search\SugGetArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.SUGADD, FT.SUGGET, FT.SUGDEL, FT.SUGLEN commands usage:
|
||||
|
||||
// 1. Add suggestion to key with payload
|
||||
$client = new Client();
|
||||
|
||||
$client->ftsugadd('key', 'hello', 2, (new SugAddArguments())->payload('payload'));
|
||||
|
||||
echo 'Suggestions dictionary length: ' . $client->ftsuglen('key') . "\n";
|
||||
|
||||
// 2. Perform fuzzy search by prefix to get previous suggestion with payload
|
||||
$response = $client->ftsugget('key', 'hellp', (new SugGetArguments())->fuzzy()->withPayloads());
|
||||
|
||||
echo 'Suggestion for "hellp" prefix:' . "\n";
|
||||
print_r($response);
|
||||
|
||||
// 3. Removes previous suggestion from key
|
||||
$client->ftsugdel('key', 'hello');
|
||||
$response = $client->ftsugget('key', 'hello');
|
||||
|
||||
echo 'Suggestions, after removing "hello" suggestion:' . "\n";
|
||||
print_r($response);
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.SYNDUMP command usage:
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$schema = [
|
||||
new TextField('text_field'),
|
||||
];
|
||||
$client->ftcreate('index_syndump', $schema);
|
||||
|
||||
// 2. Add synonyms group with terms
|
||||
$client->ftsynupdate('index_syndump', 'synonym1', null, 'term1', 'term2');
|
||||
|
||||
// 3. Dump terms with synonyms
|
||||
$response = $client->ftsyndump('index_syndump');
|
||||
|
||||
echo 'Response:' . "\n";
|
||||
print_r($response);
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TextField;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.SYNUPDATE command usage:
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$schema = [
|
||||
new TextField('text_field'),
|
||||
];
|
||||
$client->ftcreate('index_synupdate', $schema);
|
||||
|
||||
// 2. Add synonyms into synonym group
|
||||
$response = $client->ftsynupdate('index_synupdate', 'synonym1', null, 'term1', 'term2');
|
||||
|
||||
echo 'Response:' . "\n";
|
||||
print_r($response);
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\Search\CreateArguments;
|
||||
use Predis\Command\Argument\Search\SchemaFields\TagField;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of FT.TAGVALS command usage:
|
||||
|
||||
// 1. Create index
|
||||
$client = new Client();
|
||||
|
||||
$schema = [
|
||||
new TagField('tag_field'),
|
||||
];
|
||||
$client->ftcreate('index_tagvals', $schema, (new CreateArguments())->prefix(['prefix:']));
|
||||
|
||||
// 2. Add indexed tags
|
||||
$client->hset('prefix:1', 'tag_field', 'Hello, World');
|
||||
$client->hset('prefix:2', 'tag_field', 'Hey, World');
|
||||
|
||||
// 3. Unique tags value query
|
||||
$response = $client->fttagvals('index_tagvals', 'tag_field');
|
||||
|
||||
echo 'Response:' . "\n";
|
||||
print_r($response);
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\AddArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.ADD command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$client->tscreate('temperature:2:32', $arguments);
|
||||
|
||||
// 2. Add sample into newly created time series
|
||||
$addArguments = (new AddArguments())
|
||||
->retentionMsecs(31536000000);
|
||||
|
||||
$response = $client->tsadd('temperature:2:32', 123123123123, 27, $addArguments);
|
||||
|
||||
echo "Timeseries was added with timestamp: {$response}";
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\AlterArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.ALTER command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$response = $client->tscreate('temperature:2:32', $arguments);
|
||||
|
||||
echo "Time series creation status: {$response}\n";
|
||||
|
||||
// 2. Update Duplicate policy for time series above
|
||||
$arguments = (new AlterArguments())
|
||||
->duplicatePolicy(CommonArguments::POLICY_FIRST);
|
||||
|
||||
$response = $client->tsalter('temperature:2:32', $arguments);
|
||||
$output = ($response == 'OK') ? 'Duplicate policy was successfully updated' : $response;
|
||||
|
||||
echo $output;
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.CREATE command usage:
|
||||
|
||||
// Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$response = $client->tscreate('temperature:2:32', $arguments);
|
||||
|
||||
echo "Time series creation status: {$response}";
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.CREATERULE command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->labels('type', 'temp', 'location', 'TLV');
|
||||
|
||||
$createResponse = $client->tscreate('temp:TLV', $arguments);
|
||||
echo "Original time series creation status: {$createResponse}\n";
|
||||
|
||||
$createResponse = $client->tscreate('dailyAvgTemp:TLV', $arguments);
|
||||
echo "Compacted time series creation status: {$createResponse}\n";
|
||||
|
||||
$createRuleResponse = $client->tscreaterule('temp:TLV', 'dailyAvgTemp:TLV', 'avg', 1000);
|
||||
echo "Compacted rule for compacted time series creation status: {$createRuleResponse}\n";
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\AddArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
use Predis\Command\Argument\TimeSeries\DecrByArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.DECRBY command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$client->tscreate('temperature:2:32', $arguments);
|
||||
|
||||
// 2. Add sample into newly created time series
|
||||
$addArguments = (new AddArguments())
|
||||
->retentionMsecs(31536000000);
|
||||
|
||||
$response = $client->tsadd('temperature:2:32', 123123123123, 27, $addArguments);
|
||||
|
||||
echo "Timeseries was added with timestamp: {$response}\n";
|
||||
|
||||
// 3. Increasing value and timestamp
|
||||
$client->tsdecrby('temperature:2:32', 1, (new DecrByArguments())->timestamp(123123123124));
|
||||
$response = $client->tsget('temperature:2:32');
|
||||
|
||||
echo "Decreased value to - {$response[1]} and timestamp to {$response[0]}";
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.GET command usage:
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$client->tscreate('temperature:2:32', $arguments);
|
||||
|
||||
// 2. Add samples into time series
|
||||
$client->tsadd('temperature:2:32', 123123123123, 27);
|
||||
$client->tsadd('temperature:2:32', 123123123124, 28);
|
||||
$client->tsadd('temperature:2:32', 123123123125, 29);
|
||||
|
||||
$response = $client->tsget('temperature:2:32');
|
||||
|
||||
echo "Sample with highest timestamp - {$response[0]} and value {$response[1]}\n";
|
||||
|
||||
// 3. Removes 2 samples with the highest timestamps
|
||||
$response = $client->tsdel('temperature:2:32', 123123123124, 123123123125);
|
||||
|
||||
echo "Removed {$response} samples from timeseries.\n";
|
||||
|
||||
// 3. Retrieve a timestamp with the highest timestamp
|
||||
$response = $client->tsget('temperature:2:32');
|
||||
|
||||
echo "New sample with highest timestamp - {$response[0]} and value {$response[1]}";
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.CREATERULE command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->labels('type', 'temp', 'location', 'TLV');
|
||||
|
||||
$createResponse = $client->tscreate('temp:TLV', $arguments);
|
||||
echo "Original time series creation status: {$createResponse}\n";
|
||||
|
||||
$createResponse = $client->tscreate('dailyAvgTemp:TLV', $arguments);
|
||||
echo "Compacted time series creation status: {$createResponse}\n";
|
||||
|
||||
$createRuleResponse = $client->tscreaterule('temp:TLV', 'dailyAvgTemp:TLV', 'avg', 1000);
|
||||
echo "Compacted rule for compacted time series creation status: {$createRuleResponse}\n";
|
||||
|
||||
// 2. Remove compaction rule
|
||||
$deleteRuleResponse = $client->tsdeleterule('temp:TLV', 'dailyAvgTemp:TLV');
|
||||
echo "Compacted rule for compacted time series deletion status: {$deleteRuleResponse}\n";
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.GET command usage:
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$client->tscreate('temperature:2:32', $arguments);
|
||||
|
||||
// 2. Add samples into time series
|
||||
$client->tsadd('temperature:2:32', 123123123123, 27);
|
||||
$client->tsadd('temperature:2:32', 123123123124, 28);
|
||||
$client->tsadd('temperature:2:32', 123123123125, 29);
|
||||
|
||||
// 3. Retrieve a timestamp with highest timestamp
|
||||
$response = $client->tsget('temperature:2:32');
|
||||
|
||||
echo "Sample with highest timestamp - {$response[0]} and value - {$response[1]}";
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\AddArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
use Predis\Command\Argument\TimeSeries\IncrByArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.INCRBY command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$client->tscreate('temperature:2:32', $arguments);
|
||||
|
||||
// 2. Add sample into newly created time series
|
||||
$addArguments = (new AddArguments())
|
||||
->retentionMsecs(31536000000);
|
||||
|
||||
$response = $client->tsadd('temperature:2:32', 123123123123, 27, $addArguments);
|
||||
|
||||
echo "Timeseries was added with timestamp: {$response}\n";
|
||||
|
||||
// 3. Increasing value and timestamp
|
||||
$client->tsincrby('temperature:2:32', 1, (new IncrByArguments())->timestamp(123123123124));
|
||||
$response = $client->tsget('temperature:2:32');
|
||||
|
||||
echo "Increased value to - {$response[1]} and timestamp to {$response[0]}";
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.CREATE command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$client->tscreate('temperature:2:32', $arguments);
|
||||
|
||||
// 2. Show info about given time series
|
||||
print_r($client->tsinfo('temperature:2:32'));
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.MADD command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$client->tscreate('temperature:2:32', $arguments);
|
||||
$client->tscreate('temperature:2:33', $arguments);
|
||||
|
||||
// 2. Add samples into few time series
|
||||
$response = $client->tsmadd('temperature:2:32', 123123123123, 27, 'temperature:2:33', 123123123124, 28);
|
||||
$stringResponse = implode(', ', $response);
|
||||
|
||||
echo "Samples was added to time series - timestamps: {$stringResponse}";
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CommonArguments;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
use Predis\Command\Argument\TimeSeries\MGetArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.MGET command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->retentionMsecs(60000)
|
||||
->duplicatePolicy(CommonArguments::POLICY_MAX)
|
||||
->labels('type', 'temp', 'sensor_id', 2, 'area_id', 32);
|
||||
|
||||
$client->tscreate('temperature:2:32', $arguments);
|
||||
$client->tscreate('temperature:2:33', $arguments);
|
||||
|
||||
// 2. Add samples into time series
|
||||
$client->tsadd('temperature:2:32', 123123123123, 27);
|
||||
$client->tsadd('temperature:2:33', 123123123124, 27);
|
||||
|
||||
// 3. Get sample from multiple time series matching given filter expression, with selected labels only
|
||||
$response = $client->tsmget((new MGetArguments())->selectedLabels('type'), 'type=temp');
|
||||
|
||||
echo "Sample from time series, with label = 'type':\n";
|
||||
print_r($response);
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
use Predis\Command\Argument\TimeSeries\MRangeArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.MRANGE command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$response = $client->tscreate('stock:A', (new CreateArguments())->labels('type', 'stock', 'name', 'A'));
|
||||
echo "Time series A creation status: {$response}\n";
|
||||
|
||||
$response = $client->tscreate('stock:B', (new CreateArguments())->labels('type', 'stock', 'name', 'B'));
|
||||
echo "Time series B creation status: {$response}\n";
|
||||
|
||||
// 2. Add samples into both time series
|
||||
$response = $client->tsmadd('stock:A', 1000, 100, 'stock:A', 1010, 110, 'stock:A', 1020, 120);
|
||||
$stringResponse = implode(', ', $response);
|
||||
echo "Added samples into time series A with following timestamps: {$stringResponse}\n";
|
||||
|
||||
$response = $client->tsmadd('stock:B', 1000, 120, 'stock:B', 1010, 110, 'stock:B', 1020, 100);
|
||||
$stringResponse = implode(', ', $response);
|
||||
echo "Added samples into time series B with following timestamps: {$stringResponse}\n";
|
||||
|
||||
// 3. Query range across both time series filtered by "type" and grouped by max type
|
||||
$mrangeArguments = (new MRangeArguments())
|
||||
->withLabels()
|
||||
->filter('type=stock')
|
||||
->groupBy('type', 'max');
|
||||
|
||||
$response = $client->tsmrange('-', '+', $mrangeArguments);
|
||||
|
||||
echo "Response:\n";
|
||||
print_r($response);
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
use Predis\Command\Argument\TimeSeries\MRangeArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.MREVRANGE command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$response = $client->tscreate('stock:A', (new CreateArguments())->labels('type', 'stock', 'name', 'A'));
|
||||
echo "Time series A creation status: {$response}\n";
|
||||
|
||||
$response = $client->tscreate('stock:B', (new CreateArguments())->labels('type', 'stock', 'name', 'B'));
|
||||
echo "Time series B creation status: {$response}\n";
|
||||
|
||||
// 2. Add samples into both time series
|
||||
$response = $client->tsmadd('stock:A', 1000, 100, 'stock:A', 1010, 110, 'stock:A', 1020, 120);
|
||||
$stringResponse = implode(', ', $response);
|
||||
echo "Added samples into time series A with following timestamps: {$stringResponse}\n";
|
||||
|
||||
$response = $client->tsmadd('stock:B', 1000, 120, 'stock:B', 1010, 110, 'stock:B', 1020, 100);
|
||||
$stringResponse = implode(', ', $response);
|
||||
echo "Added samples into time series B with following timestamps: {$stringResponse}\n";
|
||||
|
||||
// 3. Query range across both time series filtered by "type" and grouped by max type
|
||||
$mrangeArguments = (new MRangeArguments())
|
||||
->withLabels()
|
||||
->filter('type=stock')
|
||||
->groupBy('type', 'max');
|
||||
|
||||
$response = $client->tsmrevrange('-', '+', $mrangeArguments);
|
||||
|
||||
echo "Response:\n";
|
||||
print_r($response);
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.QUERYINDEX command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$arguments = (new CreateArguments())
|
||||
->labels('type', 'temp', 'location', 'TLV');
|
||||
|
||||
$createResponse = $client->tscreate('temp:TLV', $arguments);
|
||||
echo "Time series with location TLV creation status: {$createResponse}\n";
|
||||
|
||||
$anotherArguments = (new CreateArguments())
|
||||
->labels('type', 'temp', 'location', 'JER');
|
||||
|
||||
$createResponse = $client->tscreate('temp:JER', $anotherArguments);
|
||||
echo "Time series with location JER creation status: {$createResponse}\n";
|
||||
|
||||
echo "Returns all keys with location=TLV:\n";
|
||||
print_r($client->tsqueryindex('location=TLV'));
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
use Predis\Command\Argument\TimeSeries\RangeArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.RANGE command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$createArguments = (new CreateArguments())->labels('type', 'temp', 'location', 'TLV');
|
||||
$createResponse = $client->tscreate('temp:TLV', $createArguments);
|
||||
|
||||
echo "Time series creation status: {$createResponse}\n";
|
||||
|
||||
// 2. Add samples into time series
|
||||
$maddResponse = $client->tsmadd('temp:TLV', 1000, 30, 'temp:TLV', 1010, 35, 'temp:TLV', 1020, 9999, 'temp:TLV', 1030, 40);
|
||||
$stringResponse = implode(', ', $maddResponse);
|
||||
|
||||
echo "Samples was added with following timestamps: {$stringResponse}\n";
|
||||
|
||||
// 3. Query samples by values in the given range
|
||||
$rangeArguments = (new RangeArguments())->filterByValue(-100, 100);
|
||||
$rangeResponse = $client->tsrange('temp:TLV', '-', '+', $rangeArguments);
|
||||
|
||||
echo "Samples with temperature in range -100 to 100 degrees:\n";
|
||||
print_r($rangeResponse);
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
use Predis\Command\Argument\TimeSeries\CreateArguments;
|
||||
use Predis\Command\Argument\TimeSeries\RangeArguments;
|
||||
|
||||
require __DIR__ . '/../../shared.php';
|
||||
|
||||
// Example of TS.REVRANGE command usage:
|
||||
|
||||
// 1. Create time series
|
||||
$client = new Client();
|
||||
|
||||
$createArguments = (new CreateArguments())->labels('type', 'temp', 'location', 'TLV');
|
||||
$createResponse = $client->tscreate('temp:TLV', $createArguments);
|
||||
|
||||
echo "Time series creation status: {$createResponse}\n";
|
||||
|
||||
// 2. Add samples into time series
|
||||
$maddResponse = $client->tsmadd('temp:TLV', 1000, 30, 'temp:TLV', 1010, 35, 'temp:TLV', 1020, 9999, 'temp:TLV', 1030, 40);
|
||||
$stringResponse = implode(', ', $maddResponse);
|
||||
|
||||
echo "Samples was added with following timestamps: {$stringResponse}\n";
|
||||
|
||||
// 3. Query samples by values in the given range
|
||||
$rangeArguments = (new RangeArguments())->filterByValue(-100, 100);
|
||||
$rangeResponse = $client->tsrevrange('temp:TLV', '-', '+', $rangeArguments);
|
||||
|
||||
echo "Samples with temperature in range -100 to 100 degrees in reverse order:\n";
|
||||
print_r($rangeResponse);
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/../shared.php';
|
||||
|
||||
// Example of ACL DRYRUN command usage:
|
||||
|
||||
// 1. Set user with permissions to call only 'SET' command.
|
||||
$client = new Client($single_server);
|
||||
$response = $client->acl->setUser('Test_dry', '+SET', '~*');
|
||||
$created = ($response == 'OK') ? 'Yes' : 'No';
|
||||
|
||||
echo "User with username 'Test' was created: {$created}. Permissions only to use SET command\n";
|
||||
|
||||
// 2. Dry run 'SET' command under 'Test_dry' user
|
||||
$response = $client->acl->dryRun('Test_dry', 'SET', 'foo', 'bar');
|
||||
|
||||
echo 'Dry run "SET" command.' . "\n";
|
||||
echo 'Response: ' . $response . "\n";
|
||||
|
||||
// 3. Dry run 'GET' command under 'Test_dry' user
|
||||
$response = $client->acl->dryRun('Test_dry', 'GET', 'foo');
|
||||
|
||||
echo 'Dry run "GET" command.' . "\n";
|
||||
echo 'Response: ' . $response;
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/../shared.php';
|
||||
|
||||
// Example of ACL GETUSER command usage:
|
||||
|
||||
// 1. Set user
|
||||
$client = new Client($single_server);
|
||||
$response = $client->acl->setUser('Test');
|
||||
|
||||
// 2. Retrieve user rules:
|
||||
|
||||
echo 'Rules: ' . "\n";
|
||||
print_r(
|
||||
$client->acl->getUser('Test')
|
||||
);
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/../shared.php';
|
||||
|
||||
// Example of ACL SETUSER command usage:
|
||||
|
||||
// 1. Set user
|
||||
$client = new Client($single_server);
|
||||
$response = $client->acl->setUser('Test');
|
||||
$created = ($response == 'OK') ? 'Yes' : 'No';
|
||||
|
||||
echo "User with username 'Test' was created: {$created}";
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/../shared.php';
|
||||
|
||||
// Example of FCALL_RO command usage:
|
||||
|
||||
// 1. Set key-value pair
|
||||
$client = new Client($single_server);
|
||||
$client->set('foo', 'bar');
|
||||
|
||||
echo "Set key 'foo' with value 'bar'\n";
|
||||
|
||||
// 2. Load redis function with 'no-writes' flag
|
||||
$client->function->load(
|
||||
"#!lua name=mylib
|
||||
redis.register_function{
|
||||
function_name='myfunc',
|
||||
callback=function(keys, args) return redis.call('GET', keys[1]) end,
|
||||
flags={'no-writes'}
|
||||
}"
|
||||
);
|
||||
|
||||
echo 'Loaded custom function that perform GET command against provided key.' . "\n";
|
||||
|
||||
// 3. Call function above with given key
|
||||
$response = $client->fcall_ro('myfunc', ['foo']);
|
||||
|
||||
echo "Function returned value against provided key 'foo' is '{$response}'";
|
||||
|
||||
// 4. Delete test library
|
||||
$client->function->delete('mylib');
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/../shared.php';
|
||||
|
||||
// Example of WAITAOF command usage:
|
||||
|
||||
// 1. Enable appendonly mode if it's not (command works only in appendonly mode)
|
||||
$client = new Client($single_server);
|
||||
$info = $client->info();
|
||||
$enabled = false;
|
||||
|
||||
if ($info['Persistence']['aof_enabled'] === '0') {
|
||||
$client->config('set', 'appendonly', 'yes');
|
||||
$enabled = true;
|
||||
}
|
||||
|
||||
// 2. Set key value pair
|
||||
$response = $client->set('foo', 'bar');
|
||||
echo "Key-value pair set status: {$response}\n";
|
||||
|
||||
// 3. Run WAITAOF command to make sure that all previous writes was fsynced
|
||||
$response = $client->waitaof(1, 0, 0);
|
||||
|
||||
echo "Quantity of local instances that was fsynced - {$response[0]}, quantity of replicas - {$response[1]}";
|
||||
|
||||
// 4. Disable appendonly mode if it was enabled during script execution
|
||||
if ($enabled) {
|
||||
$client->config('set', 'appendonly', 'no');
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
require_once 'SharedConfigurations.php';
|
||||
|
||||
// redis can set keys and their relative values in one go
|
||||
// using MSET, then the same values can be retrieved with
|
||||
// a single command using MGET.
|
||||
|
||||
$mkv = array(
|
||||
'usr:0001' => 'First user',
|
||||
'usr:0002' => 'Second user',
|
||||
'usr:0003' => 'Third user'
|
||||
);
|
||||
|
||||
$redis = Predis_Client::create($configurations);
|
||||
|
||||
$redis->mset($mkv);
|
||||
$retval = $redis->mget(array_keys($mkv));
|
||||
|
||||
print_r($retval);
|
||||
|
||||
/* OUTPUT:
|
||||
Array
|
||||
(
|
||||
[0] => First user
|
||||
[1] => Second user
|
||||
[2] => Third user
|
||||
)
|
||||
*/
|
||||
?>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
require_once '../lib/Predis.php';
|
||||
|
||||
$configurations = array(
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'database' => 15
|
||||
);
|
||||
?>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
require_once 'SharedConfigurations.php';
|
||||
|
||||
// simple set and get scenario
|
||||
|
||||
$redis = Predis_Client::create($configurations);
|
||||
|
||||
$redis->set('library', 'predis');
|
||||
$retval = $redis->get('library');
|
||||
|
||||
print_r($retval);
|
||||
|
||||
/* OUTPUT
|
||||
predis
|
||||
*/
|
||||
?>
|
||||
@@ -1,118 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// Developers can implement Predis\Distribution\DistributorInterface to create
|
||||
// their own distributors used by the client to distribute keys among a cluster
|
||||
// of servers.
|
||||
|
||||
use Predis\Cluster\Distributor\DistributorInterface;
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
use Predis\Cluster\PredisStrategy;
|
||||
use Predis\Connection\Cluster\PredisCluster;
|
||||
|
||||
class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
|
||||
{
|
||||
private $nodes;
|
||||
private $nodesCount;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->nodes = [];
|
||||
$this->nodesCount = 0;
|
||||
}
|
||||
|
||||
public function add($node, $weight = null)
|
||||
{
|
||||
$this->nodes[] = $node;
|
||||
++$this->nodesCount;
|
||||
}
|
||||
|
||||
public function remove($node)
|
||||
{
|
||||
$this->nodes = array_filter($this->nodes, function ($n) use ($node) {
|
||||
return $n !== $node;
|
||||
});
|
||||
|
||||
$this->nodesCount = count($this->nodes);
|
||||
}
|
||||
|
||||
public function getSlot($hash)
|
||||
{
|
||||
return $this->nodesCount > 1 ? abs($hash % $this->nodesCount) : 0;
|
||||
}
|
||||
|
||||
public function getBySlot($slot)
|
||||
{
|
||||
return $this->nodes[$slot] ?? null;
|
||||
}
|
||||
|
||||
public function getByHash($hash)
|
||||
{
|
||||
if (!$this->nodesCount) {
|
||||
throw new RuntimeException('No connections.');
|
||||
}
|
||||
|
||||
$slot = $this->getSlot($hash);
|
||||
$node = $this->getBySlot($slot);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function get($value)
|
||||
{
|
||||
$hash = $this->hash($value);
|
||||
$node = $this->getByHash($hash);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function hash($value)
|
||||
{
|
||||
return crc32($value);
|
||||
}
|
||||
|
||||
public function getHashGenerator()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
$options = [
|
||||
'cluster' => function () {
|
||||
$distributor = new NaiveDistributor();
|
||||
$strategy = new PredisStrategy($distributor);
|
||||
$cluster = new PredisCluster($strategy);
|
||||
|
||||
return $cluster;
|
||||
},
|
||||
];
|
||||
|
||||
$client = new Predis\Client($multiple_servers, $options);
|
||||
|
||||
for ($i = 0; $i < 100; ++$i) {
|
||||
$client->set("key:$i", str_pad($i, 4, '0', 0));
|
||||
$client->get("key:$i");
|
||||
}
|
||||
|
||||
$server1 = $client->getClientBy('alias', 'first')->info();
|
||||
$server2 = $client->getClientBy('alias', 'second')->info();
|
||||
|
||||
if (isset($server1['Keyspace'], $server2['Keyspace'])) {
|
||||
$server1 = $server1['Keyspace'];
|
||||
$server2 = $server2['Keyspace'];
|
||||
}
|
||||
|
||||
printf("Server '%s' has %d keys while server '%s' has %d keys.\n",
|
||||
'first', $server1['db15']['keys'], 'second', $server2['db15']['keys']
|
||||
);
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// This is an example of how you can easily extend an existing connection class
|
||||
// and trace the execution of commands for debugging purposes. This can be quite
|
||||
// useful as a starting point to understand how your application interacts with
|
||||
// Redis.
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Connection\StreamConnection;
|
||||
|
||||
class SimpleDebuggableConnection extends StreamConnection
|
||||
{
|
||||
private $tstart = 0;
|
||||
private $debugBuffer = [];
|
||||
|
||||
public function connect()
|
||||
{
|
||||
$this->tstart = microtime(true);
|
||||
|
||||
parent::connect();
|
||||
}
|
||||
|
||||
private function storeDebug(CommandInterface $command, $direction)
|
||||
{
|
||||
$firstArg = $command->getArgument(0);
|
||||
$timestamp = round(microtime(true) - $this->tstart, 4);
|
||||
|
||||
$debug = $command->getId();
|
||||
$debug .= isset($firstArg) ? " $firstArg " : ' ';
|
||||
$debug .= "$direction $this";
|
||||
$debug .= " [{$timestamp}s]";
|
||||
|
||||
$this->debugBuffer[] = $debug;
|
||||
}
|
||||
|
||||
public function writeRequest(CommandInterface $command)
|
||||
{
|
||||
parent::writeRequest($command);
|
||||
|
||||
$this->storeDebug($command, '->');
|
||||
}
|
||||
|
||||
public function readResponse(CommandInterface $command)
|
||||
{
|
||||
$response = parent::readResponse($command);
|
||||
$this->storeDebug($command, '<-');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function getDebugBuffer()
|
||||
{
|
||||
return $this->debugBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
$options = [
|
||||
'connections' => [
|
||||
'tcp' => 'SimpleDebuggableConnection',
|
||||
],
|
||||
];
|
||||
|
||||
$client = new Predis\Client($single_server, $options);
|
||||
$client->set('foo', 'bar');
|
||||
$client->get('foo');
|
||||
$client->info();
|
||||
|
||||
var_export($client->getConnection()->getDebugBuffer());
|
||||
|
||||
/* OUTPUT:
|
||||
array (
|
||||
0 => 'SELECT 15 -> 127.0.0.1:6379 [0.0008s]',
|
||||
1 => 'SELECT 15 <- 127.0.0.1:6379 [0.001s]',
|
||||
2 => 'SET foo -> 127.0.0.1:6379 [0.001s]',
|
||||
3 => 'SET foo <- 127.0.0.1:6379 [0.0011s]',
|
||||
4 => 'GET foo -> 127.0.0.1:6379 [0.0013s]',
|
||||
5 => 'GET foo <- 127.0.0.1:6379 [0.0015s]',
|
||||
6 => 'INFO -> 127.0.0.1:6379 [0.0019s]',
|
||||
7 => 'INFO <- 127.0.0.1:6379 [0.0022s]',
|
||||
)
|
||||
*/
|
||||
@@ -1,80 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// This is a basic example on how to use the Predis\DispatcherLoop class.
|
||||
//
|
||||
// To see this example in action you can just use redis-cli and publish some
|
||||
// messages to the 'events' and 'control' channel, e.g.:
|
||||
|
||||
// ./redis-cli
|
||||
// PUBLISH events first
|
||||
// PUBLISH events second
|
||||
// PUBLISH events third
|
||||
// PUBLISH control terminate_dispatcher
|
||||
|
||||
// Create a client and disable r/w timeout on the socket
|
||||
$client = new Predis\Client($single_server + ['read_write_timeout' => 0]);
|
||||
|
||||
// Return an initialized PubSub consumer instance from the client.
|
||||
$pubsub = $client->pubSubLoop();
|
||||
|
||||
// Create a dispatcher loop instance and attach a bunch of callbacks.
|
||||
$dispatcher = new Predis\Consumer\PubSub\DispatcherLoop($pubsub);
|
||||
|
||||
// Demonstrate how to use a callable class as a callback for the dispatcher loop.
|
||||
class EventsListener implements Countable
|
||||
{
|
||||
private $events;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->events = [];
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->events);
|
||||
}
|
||||
|
||||
public function getEvents()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
public function __invoke($payload, $dispatcher)
|
||||
{
|
||||
$this->events[] = $payload;
|
||||
}
|
||||
}
|
||||
|
||||
// Attach our callable class to the dispatcher.
|
||||
$dispatcher->attachCallback('events', $events = new EventsListener());
|
||||
|
||||
// Attach a function to control the dispatcher loop termination with a message.
|
||||
$dispatcher->attachCallback('control', function ($payload, $dispatcher) {
|
||||
if ($payload === 'terminate_dispatcher') {
|
||||
$dispatcher->stop();
|
||||
}
|
||||
});
|
||||
|
||||
// Run the dispatcher loop until the callback attached to the 'control' channel
|
||||
// receives 'terminate_dispatcher' as a message.
|
||||
$dispatcher->run();
|
||||
|
||||
// Display our achievements!
|
||||
echo "We received {$events->count()} messages!", PHP_EOL;
|
||||
|
||||
// Say goodbye :-)
|
||||
$version = redis_version($client->info());
|
||||
echo "Goodbye from Redis $version!", PHP_EOL;
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
$client = new Predis\Client($single_server);
|
||||
|
||||
// Plain old SET and GET example...
|
||||
$client->set('library', 'predis');
|
||||
$response = $client->get('library');
|
||||
|
||||
var_export($response);
|
||||
echo PHP_EOL;
|
||||
/* OUTPUT: 'predis' */
|
||||
|
||||
// Redis has the MSET and MGET commands to set or get multiple keys in one go,
|
||||
// cases like this Predis accepts arguments for variadic commands both as a list
|
||||
// of arguments or an array containing all of the keys and/or values.
|
||||
$mkv = [
|
||||
'uid:0001' => '1st user',
|
||||
'uid:0002' => '2nd user',
|
||||
'uid:0003' => '3rd user',
|
||||
];
|
||||
|
||||
$client->mset($mkv);
|
||||
$response = $client->mget(array_keys($mkv));
|
||||
|
||||
var_export($response);
|
||||
echo PHP_EOL;
|
||||
/* OUTPUT:
|
||||
array (
|
||||
0 => '1st user',
|
||||
1 => '2nd user',
|
||||
2 => '3rd user',
|
||||
)
|
||||
*/
|
||||
|
||||
// Predis can also send "raw" commands to Redis. The difference between sending
|
||||
// commands to Redis the usual way and the "raw" way is that in the latter case
|
||||
// their arguments are not filtered nor responses coming from Redis are parsed.
|
||||
|
||||
$response = $client->executeRaw([
|
||||
'MGET', 'uid:0001', 'uid:0002', 'uid:0003',
|
||||
]);
|
||||
|
||||
var_export($response);
|
||||
echo PHP_EOL;
|
||||
/* OUTPUT:
|
||||
array (
|
||||
0 => '1st user',
|
||||
1 => '2nd user',
|
||||
2 => '3rd user',
|
||||
)
|
||||
*/
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// Predis can prefix keys found in commands arguments before sending commands to
|
||||
// Redis, even for complex commands such as SORT, ZUNIONSTORE and ZINTERSTORE.
|
||||
// Prefixing keys can be useful to create user-level namespaces for you keyspace
|
||||
// thus reducing the need for separate logical databases in certain scenarios.
|
||||
|
||||
$client = new Predis\Client($single_server, ['prefix' => 'nrk:']);
|
||||
|
||||
$client->mset(['foo' => 'bar', 'lol' => 'wut']);
|
||||
var_export($client->mget('foo', 'lol'));
|
||||
/*
|
||||
array (
|
||||
0 => 'bar',
|
||||
1 => 'wut',
|
||||
)
|
||||
*/
|
||||
|
||||
var_export($client->keys('*'));
|
||||
/*
|
||||
array (
|
||||
0 => 'nrk:foo',
|
||||
1 => 'nrk:lol',
|
||||
)
|
||||
*/
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// This example will not work with versions of Redis < 2.6.
|
||||
//
|
||||
// Additionally to the EVAL command, the Predis\Command\ScriptCommand class can
|
||||
// be used to leverage an higher level abstraction for Lua scripting that makes
|
||||
// scripts appear just like any other command on the client-side. This is basic
|
||||
// example on how a script-based INCREX command can be defined:
|
||||
|
||||
use Predis\Command\ScriptCommand;
|
||||
|
||||
class IncrementExistingKeysBy extends ScriptCommand
|
||||
{
|
||||
public function getKeysCount()
|
||||
{
|
||||
// Tell Predis to use all the arguments but the last one as arguments
|
||||
// for KEYS. The last one will be used to populate ARGV.
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function getScript()
|
||||
{
|
||||
return <<<LUA
|
||||
local cmd, insert = redis.call, table.insert
|
||||
local increment, results = ARGV[1], { }
|
||||
|
||||
for idx, key in ipairs(KEYS) do
|
||||
if cmd('exists', key) == 1 then
|
||||
insert(results, idx, cmd('incrby', key, increment))
|
||||
else
|
||||
insert(results, idx, false)
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
LUA;
|
||||
}
|
||||
}
|
||||
|
||||
$client = new Predis\Client($single_server, [
|
||||
'commands' => [
|
||||
'increxby' => 'IncrementExistingKeysBy',
|
||||
],
|
||||
]);
|
||||
|
||||
$client->mset('foo', 10, 'foobar', 100);
|
||||
|
||||
var_export($client->increxby('foo', 'foofoo', 'foobar', 50));
|
||||
|
||||
/*
|
||||
array (
|
||||
0 => 60,
|
||||
1 => NULL,
|
||||
2 => 150,
|
||||
)
|
||||
*/
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// This is a basic example on how to use the Predis\Monitor\Consumer class. You
|
||||
// can use redis-cli to send commands to the same Redis instance your client is
|
||||
// connected to, and then type "ECHO QUIT_MONITOR" in redis-cli when you want to
|
||||
// exit the monitor loop and terminate this script in a graceful way.
|
||||
|
||||
// Create a client and disable r/w timeout on the socket.
|
||||
$client = new Predis\Client($single_server + ['read_write_timeout' => 0]);
|
||||
|
||||
// Use only one instance of DateTime, we will update the timestamp later.
|
||||
$timestamp = new DateTime();
|
||||
|
||||
foreach (($monitor = $client->monitor()) as $event) {
|
||||
$timestamp->setTimestamp((int) $event->timestamp);
|
||||
|
||||
// If we notice a ECHO command with the message QUIT_MONITOR, we stop the
|
||||
// monitor consumer and then break the loop.
|
||||
if ($event->command === 'ECHO' && $event->arguments === '"QUIT_MONITOR"') {
|
||||
echo 'Exiting the monitor loop...', PHP_EOL;
|
||||
$monitor->stop();
|
||||
break;
|
||||
}
|
||||
|
||||
echo "* Received {$event->command} on DB {$event->database} at {$timestamp->format(DateTime::W3C)}", PHP_EOL;
|
||||
if (isset($event->arguments)) {
|
||||
echo " Arguments: {$event->arguments}", PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
// Say goodbye :-)
|
||||
$version = redis_version($client->info());
|
||||
echo "Goodbye from Redis $version!", PHP_EOL;
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// When you have a whole set of consecutive commands to send to a redis server,
|
||||
// you can use a pipeline to dramatically improve performances. Pipelines can
|
||||
// greatly reduce the effects of network round-trips.
|
||||
|
||||
$client = new Predis\Client($single_server);
|
||||
|
||||
$responses = $client->pipeline(function ($pipe) {
|
||||
$pipe->flushdb();
|
||||
$pipe->incrby('counter', 10);
|
||||
$pipe->incrby('counter', 30);
|
||||
$pipe->exists('counter');
|
||||
$pipe->get('counter');
|
||||
$pipe->mget('does_not_exist', 'counter');
|
||||
});
|
||||
|
||||
var_export($responses);
|
||||
|
||||
/* OUTPUT:
|
||||
array (
|
||||
0 => Predis\Response\Status::__set_state(array(
|
||||
'payload' => 'OK',
|
||||
)),
|
||||
1 => 10,
|
||||
2 => 40,
|
||||
3 => true,
|
||||
4 => '40',
|
||||
5 => array (
|
||||
0 => NULL,
|
||||
1 => '40',
|
||||
),
|
||||
)
|
||||
*/
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// Starting from Redis 2.0 clients can subscribe and listen for events published
|
||||
// on certain channels using a Publish/Subscribe (PUB/SUB) approach.
|
||||
|
||||
// Create a client and disable r/w timeout on the socket
|
||||
$client = new Predis\Client($single_server + ['read_write_timeout' => 0]);
|
||||
|
||||
// Initialize a new pubsub consumer.
|
||||
$pubsub = $client->pubSubLoop();
|
||||
|
||||
// Subscribe to your channels
|
||||
$pubsub->subscribe('control_channel', 'notifications');
|
||||
|
||||
// Start processing the pubsub messages. Open a terminal and use redis-cli
|
||||
// to push messages to the channels. Examples:
|
||||
// redis-cli PUBLISH notifications "this is a test"
|
||||
// redis-cli PUBLISH control_channel quit_loop
|
||||
foreach ($pubsub as $message) {
|
||||
switch ($message->kind) {
|
||||
case 'subscribe':
|
||||
echo "Subscribed to {$message->channel}", PHP_EOL;
|
||||
break;
|
||||
|
||||
case 'message':
|
||||
if ($message->channel == 'control_channel') {
|
||||
if ($message->payload == 'quit_loop') {
|
||||
echo 'Aborting pubsub loop...', PHP_EOL;
|
||||
$pubsub->unsubscribe();
|
||||
} else {
|
||||
echo "Received an unrecognized command: {$message->payload}.", PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
echo "Received the following message from {$message->channel}:",
|
||||
PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Always unset the pubsub consumer instance when you are done! The
|
||||
// class destructor will take care of cleanups and prevent protocol
|
||||
// desynchronizations between the client and the server.
|
||||
unset($pubsub);
|
||||
|
||||
// Say goodbye :-)
|
||||
$version = redis_version($client->info());
|
||||
echo "Goodbye from Redis $version!", PHP_EOL;
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\ClientInterface;
|
||||
use Predis\Consumer\Push\PushResponseInterface;
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// 1. Create client with RESP3 protocol specified. Push notifications allowed only in RESP3 mode.
|
||||
$client = new Predis\Client($single_server + ['read_write_timeout' => 0, 'protocol' => 3]);
|
||||
|
||||
// 2. Create push notifications consumer. Provides callback where current consumer subscribes to few channels before enter the loop.
|
||||
$push = $client->push(static function (ClientInterface $client) {
|
||||
$response = $client->subscribe('channel', 'control');
|
||||
$status = ($response[2] === 1) ? 'OK' : 'FAILED';
|
||||
echo "Channel subscription status: {$status}\n";
|
||||
});
|
||||
|
||||
// 3. Run consumer that will handle message data type push notifications. And stops if certain message will be sent to control channel.
|
||||
// Send following commands via redis-cli to test:
|
||||
//
|
||||
// PUBLISH channel message1
|
||||
// PUBLISH channel message2
|
||||
// PUBLISH channel message3
|
||||
// PUBLISH control terminate
|
||||
// Data types should be changed in near future. Instead of Message data type it should be one of kind data types.
|
||||
|
||||
foreach ($push as $notification) {
|
||||
if ((null !== $notification) && $notification->getDataType() === PushResponseInterface::MESSAGE_DATA_TYPE) {
|
||||
if ($notification[1] === 'control' && $notification[2] === 'terminate') {
|
||||
echo "Terminating notification consumer.\n";
|
||||
$push->stop();
|
||||
break;
|
||||
}
|
||||
|
||||
$message = $notification[2];
|
||||
|
||||
echo "Received message: {$message}\n";
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\ClientInterface;
|
||||
use Predis\Consumer\DispatcherLoopInterface;
|
||||
use Predis\Consumer\Push\DispatcherLoop;
|
||||
use Predis\Consumer\Push\PushResponseInterface;
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// 1. Create client with RESP3 protocol specified. Push notifications allowed only in RESP3 mode.
|
||||
$client = new Predis\Client($single_server + ['read_write_timeout' => 0, 'protocol' => 3]);
|
||||
|
||||
// 2. Create push notifications consumer. Provides callback where current consumer subscribes to few channels before enter the loop.
|
||||
$push = $client->push(static function (ClientInterface $client) {
|
||||
$response = $client->subscribe('channel', 'control');
|
||||
$status = ($response[2] === 1) ? 'OK' : 'FAILED';
|
||||
echo "Channel subscription status: {$status}\n";
|
||||
});
|
||||
|
||||
// 3. Storage for upcoming notifications.
|
||||
$messages = [];
|
||||
|
||||
// 4. Create dispatcher for push notifications.
|
||||
$dispatcher = new DispatcherLoop($push);
|
||||
|
||||
// 5. Attach callback for message data type. Print every message and store them in storage.
|
||||
// Send following commands via redis-cli to test:
|
||||
//
|
||||
// PUBLISH channel message1
|
||||
// PUBLISH channel message2
|
||||
// PUBLISH channel message3
|
||||
// PUBLISH control terminate
|
||||
// Data types should be changed in near future. Instead of Message data type it should be one of kind data types.
|
||||
|
||||
$dispatcher->attachCallback(
|
||||
PushResponseInterface::MESSAGE_DATA_TYPE,
|
||||
static function (array $payload, DispatcherLoopInterface $dispatcher) {
|
||||
global $messages;
|
||||
[$channel, $message] = $payload;
|
||||
|
||||
if ($channel === 'control' && $message === 'terminate') {
|
||||
echo "Terminating notification consumer.\n";
|
||||
$dispatcher->stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$messages[] = $message;
|
||||
echo "Received message: {$message}\n";
|
||||
}
|
||||
);
|
||||
|
||||
// 6. Run consumer loop with attached callbacks.
|
||||
$dispatcher->run();
|
||||
|
||||
// 7. Count all messages that were received during consumer loop.
|
||||
$messagesCount = count($messages);
|
||||
echo "We received: {$messagesCount} messages\n";
|
||||
@@ -1,99 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
use Predis\Collection\Iterator;
|
||||
|
||||
// Starting with Redis 2.8, clients can iterate incrementally over collections
|
||||
// without blocking the server like it happens when a command such as KEYS is
|
||||
// executed on a Redis instance storing millions of keys. These commands are:
|
||||
//
|
||||
// - SCAN (iterates over the keyspace)
|
||||
// - SSCAN (iterates over members of a set)
|
||||
// - ZSCAN (iterates over members and ranks of a sorted set)
|
||||
// - HSCAN (iterates over fields and values of an hash).
|
||||
|
||||
// Predis provides a specialized abstraction for each command based on standard
|
||||
// SPL iterators making it possible to easily consume SCAN-based iterations in
|
||||
// your PHP code.
|
||||
//
|
||||
// See http://redis.io/commands/scan for more details.
|
||||
//
|
||||
|
||||
$client = new Predis\Client($single_server);
|
||||
|
||||
// Prepare some keys for our example
|
||||
$client->del('predis:set', 'predis:zset', 'predis:hash');
|
||||
for ($i = 0; $i < 5; ++$i) {
|
||||
$client->sadd('predis:set', "member:$i");
|
||||
$client->zadd('predis:zset', -$i, "member:$i");
|
||||
$client->hset('predis:hash', "field:$i", "value:$i");
|
||||
}
|
||||
|
||||
// === Keyspace iterator based on SCAN ===
|
||||
echo 'Scan the keyspace matching only our prefixed keys:', PHP_EOL;
|
||||
foreach (new Iterator\Keyspace($client, 'predis:*') as $key) {
|
||||
echo " - $key", PHP_EOL;
|
||||
}
|
||||
|
||||
/* OUTPUT
|
||||
Scan the keyspace matching only our prefixed keys:
|
||||
- predis:zset
|
||||
- predis:set
|
||||
- predis:hash
|
||||
*/
|
||||
|
||||
// === Set iterator based on SSCAN ===
|
||||
echo 'Scan members of `predis:set`:', PHP_EOL;
|
||||
foreach (new Iterator\SetKey($client, 'predis:set') as $member) {
|
||||
echo " - $member", PHP_EOL;
|
||||
}
|
||||
|
||||
/* OUTPUT
|
||||
Scan members of `predis:set`:
|
||||
- member:1
|
||||
- member:4
|
||||
- member:0
|
||||
- member:3
|
||||
- member:2
|
||||
*/
|
||||
|
||||
// === Sorted set iterator based on ZSCAN ===
|
||||
echo 'Scan members and ranks of `predis:zset`:', PHP_EOL;
|
||||
foreach (new Iterator\SortedSetKey($client, 'predis:zset') as $member => $rank) {
|
||||
echo " - $member [rank: $rank]", PHP_EOL;
|
||||
}
|
||||
|
||||
/* OUTPUT
|
||||
Scan members and ranks of `predis:zset`:
|
||||
- member:4 [rank: -4]
|
||||
- member:3 [rank: -3]
|
||||
- member:2 [rank: -2]
|
||||
- member:1 [rank: -1]
|
||||
- member:0 [rank: 0]
|
||||
*/
|
||||
|
||||
// === Hash iterator based on HSCAN ===
|
||||
echo 'Scan fields and values of `predis:hash`:', PHP_EOL;
|
||||
foreach (new Iterator\HashKey($client, 'predis:hash') as $field => $value) {
|
||||
echo " - $field => $value", PHP_EOL;
|
||||
}
|
||||
|
||||
/* OUTPUT
|
||||
Scan fields and values of `predis:hash`:
|
||||
- field:0 => value:0
|
||||
- field:1 => value:1
|
||||
- field:2 => value:2
|
||||
- field:3 => value:3
|
||||
- field:4 => value:4
|
||||
*/
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// Enable igbinary serializer as well as LZ4 compression
|
||||
$options = [
|
||||
'serializer' => 'igbinary',
|
||||
'compression' => 'lz4',
|
||||
];
|
||||
|
||||
$client = new Predis\Client($single_server + $options, [
|
||||
'connections' => 'relay',
|
||||
]);
|
||||
|
||||
$quote = (object) [
|
||||
'author' => 'Jean-Luc Picard',
|
||||
'text' => 'I look forward to your report Mr. Broccoli.',
|
||||
];
|
||||
|
||||
// Serialize object and apply LZ4 compression, then write key to Redis
|
||||
$client->set('quote', $client->pack($quote));
|
||||
|
||||
// NOTE: In Predis v3.x serialization and compression will happen
|
||||
// automatically without the need to call `pack()` and `unpack()`
|
||||
|
||||
// Retrieve raw binary value from Redis
|
||||
$raw = $client->get('quote');
|
||||
|
||||
// Decompress and unserialize binary value
|
||||
$data = $client->unpack($raw);
|
||||
|
||||
var_dump($quote == $data); // true
|
||||
|
||||
var_dump($data);
|
||||
|
||||
/*
|
||||
object(stdClass)#11 (2) {
|
||||
["author"]=>string(15) "Jean-Luc Picard"
|
||||
["text"]=>string(43) "I look forward to your report Mr. Broccoli."
|
||||
}
|
||||
*/
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
$options = [
|
||||
'timeout' => 1.0,
|
||||
'read_write_timeout' => 1.0,
|
||||
|
||||
// Relay specific options
|
||||
'cache' => true,
|
||||
// 'compression' => 'lz4',
|
||||
// 'serializer' => 'igbinary',
|
||||
];
|
||||
|
||||
$client = new Predis\Client($single_server + $options, [
|
||||
'connections' => 'relay',
|
||||
]);
|
||||
|
||||
// Write key to Redis
|
||||
$client->set('torpedo', mt_rand());
|
||||
|
||||
// Retrieve key from Redis
|
||||
$client->get('torpedo');
|
||||
|
||||
// Retrieve key from Relay (without talking to Redis)
|
||||
// This key is now available to all PHP workers in this FPM pool
|
||||
$client->get('torpedo');
|
||||
|
||||
// For debugging only:
|
||||
var_export(
|
||||
$client->getConnection()->getClient()->_getKeys()
|
||||
);
|
||||
|
||||
/*
|
||||
array (
|
||||
'torpedo' => array (
|
||||
0 => array (
|
||||
'type' => 'string',
|
||||
'local-len' => 10,
|
||||
'remote-len' => 10,
|
||||
'size' => 10,
|
||||
),
|
||||
),
|
||||
)
|
||||
*/
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
$key = null;
|
||||
|
||||
$client = new Predis\Client($single_server, [
|
||||
'connections' => 'relay',
|
||||
]);
|
||||
|
||||
/** @var Predis\Connection\RelayConnection $relay */
|
||||
$relay = $client->getConnection();
|
||||
|
||||
// establish connection
|
||||
$client->ping();
|
||||
|
||||
// register `FLUSH*` callback
|
||||
$relay->onFlushed(
|
||||
static function (Relay\Event $event) use (&$key) {
|
||||
echo 'Redis was flushed, unsetting $key...' . PHP_EOL;
|
||||
$key = null;
|
||||
}
|
||||
);
|
||||
|
||||
// register `INVALIDATE` callback
|
||||
$relay->onInvalidated(
|
||||
static function (Relay\Event $event) use (&$key) {
|
||||
if ($event->key === 'library') {
|
||||
echo "The `{$event->key}` key was invalidated, unsetting \$key..." . PHP_EOL;
|
||||
$key = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Write key to Redis
|
||||
$client->set('library', mt_rand());
|
||||
|
||||
// Retrieve key once from Redis, then cached in Relay and $key
|
||||
$key = $client->get('library');
|
||||
|
||||
while (true) {
|
||||
echo '$key is: ' . var_export($key, true) . PHP_EOL;
|
||||
|
||||
// To trigger our event callbacks, we need to either interact with Relay:
|
||||
$client->get(mt_rand());
|
||||
|
||||
// ... or alternatively dispatch events directly on Relay:
|
||||
$relay->dispatchEvents();
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// Create a Relay client and disable r/w timeout on the connection
|
||||
$client = new Predis\Client(
|
||||
$single_server + ['read_write_timeout' => 0],
|
||||
['connections' => 'relay']
|
||||
);
|
||||
|
||||
// Initialize a new pubsub consumer.
|
||||
$pubsub = $client->pubSubLoop();
|
||||
|
||||
// When using Relay you cannot use foreach-loops to iterate
|
||||
// over messages instead use a callback function
|
||||
$poorMansKafka = function ($message, $client) {
|
||||
switch ($message->kind) {
|
||||
case 'subscribe':
|
||||
echo "Subscribed to {$message->channel}", PHP_EOL;
|
||||
break;
|
||||
|
||||
case 'message':
|
||||
case 'pmessage':
|
||||
if ($message->channel == 'control_channel') {
|
||||
if ($message->payload == 'quit_loop') {
|
||||
echo 'Aborting pubsub loop...', PHP_EOL;
|
||||
$client->unsubscribe();
|
||||
} else {
|
||||
echo "Received an unrecognized command: {$message->payload}.", PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
echo "Received the message from `{$message->channel}` channel:",
|
||||
PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Subscribe to your channels and start processing the messages.
|
||||
$pubsub->subscribe('control_channel', 'notifications', $poorMansKafka);
|
||||
|
||||
// Open a terminal and use redis-cli to push messages to the channels. Examples:
|
||||
// redis-cli PUBLISH notifications "this is a test"
|
||||
// redis-cli PUBLISH control_channel quit_loop
|
||||
|
||||
// When using Relay, there is no need to unset the pubsub consumer instance when you are done
|
||||
|
||||
// Say goodbye :-)
|
||||
$version = redis_version($client->info());
|
||||
echo "Goodbye from Redis $version!", PHP_EOL;
|
||||
@@ -1,83 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// Predis allows to set Lua scripts as read-only operations for replication.
|
||||
// This works for both EVAL and EVALSHA and also for the client-side abstraction
|
||||
// built upon them (Predis\Command\ScriptCommand). This example shows a slightly
|
||||
// more complex configuration that injects a new script command in the command
|
||||
// factory used by the client and marks it as a read-only operation so that it
|
||||
// will be executed on slaves.
|
||||
|
||||
use Predis\Command\ScriptCommand;
|
||||
use Predis\Connection\Replication\MasterSlaveReplication;
|
||||
use Predis\Replication\ReplicationStrategy;
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
// Define a new script command that returns all the fields of a variable number
|
||||
// of hashes with a single roundtrip.
|
||||
|
||||
class HashMultipleGetAll extends ScriptCommand
|
||||
{
|
||||
public const BODY = <<<LUA
|
||||
local hashes = {}
|
||||
for _, key in pairs(KEYS) do
|
||||
table.insert(hashes, key)
|
||||
table.insert(hashes, redis.call('hgetall', key))
|
||||
end
|
||||
return hashes
|
||||
LUA;
|
||||
|
||||
public function getScript()
|
||||
{
|
||||
return self::BODY;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
$parameters = [
|
||||
'tcp://127.0.0.1:6381?role=master&database=15',
|
||||
'tcp://127.0.0.1:6382?role=slave&alias=slave-01&database=15',
|
||||
];
|
||||
|
||||
$options = [
|
||||
'commands' => [
|
||||
'hmgetall' => 'HashMultipleGetAll',
|
||||
],
|
||||
'replication' => function () {
|
||||
$strategy = new ReplicationStrategy();
|
||||
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
|
||||
|
||||
$replication = new MasterSlaveReplication($strategy);
|
||||
|
||||
return $replication;
|
||||
},
|
||||
];
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
|
||||
// Execute the following commands on the master server using redis-cli:
|
||||
// $ ./redis-cli HMSET metavars foo bar hoge piyo
|
||||
// $ ./redis-cli HMSET servers master host1 slave host2
|
||||
|
||||
$hashes = $client->hmgetall('metavars', 'servers');
|
||||
|
||||
$replication = $client->getConnection();
|
||||
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionByAlias('slave-01');
|
||||
|
||||
echo 'Is still on slave? ', $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL;
|
||||
var_export($hashes);
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// Predis supports redis-sentinel to provide high availability in master / slave
|
||||
// scenarios. The only but relevant difference with a basic replication scenario
|
||||
// is that sentinel servers can manage the master server and its slaves based on
|
||||
// their state, which means that they are able to provide an authoritative and
|
||||
// updated configuration to clients thus avoiding static configurations for the
|
||||
// replication servers and their roles.
|
||||
|
||||
// Instead of connection parameters pointing to redis nodes, we provide a list
|
||||
// of instances of redis-sentinel. Users should always provide a timeout value
|
||||
// low enough to not hinder operations just in case a sentinel is unreachable
|
||||
// but Predis uses a default value of 100 milliseconds for sentinel parameters
|
||||
// without an explicit timeout value.
|
||||
//
|
||||
// NOTE: in real-world scenarios sentinels should be running on different hosts!
|
||||
$sentinels = [
|
||||
'tcp://127.0.0.1:5380?timeout=0.100',
|
||||
'tcp://127.0.0.1:5381?timeout=0.100',
|
||||
'tcp://127.0.0.1:5382?timeout=0.100',
|
||||
];
|
||||
|
||||
$client = new Predis\Client($sentinels, [
|
||||
'replication' => 'sentinel',
|
||||
'service' => 'mymaster',
|
||||
]);
|
||||
|
||||
// Read operation.
|
||||
$exists = $client->exists('foo') ? 'yes' : 'no';
|
||||
$current = $client->getConnection()->getCurrent()->getParameters();
|
||||
echo "Does 'foo' exist on {$current->role}? $exists.", PHP_EOL;
|
||||
|
||||
// Write operation.
|
||||
$client->set('foo', 'bar');
|
||||
$current = $client->getConnection()->getCurrent()->getParameters();
|
||||
echo "Now 'foo' has been set to 'bar' on {$current->role}!", PHP_EOL;
|
||||
|
||||
// Read operation.
|
||||
$bar = $client->get('foo');
|
||||
$current = $client->getConnection()->getCurrent()->getParameters();
|
||||
echo "We fetched 'foo' from {$current->role} and its value is '$bar'.", PHP_EOL;
|
||||
|
||||
/* OUTPUT:
|
||||
Does 'foo' exist on slave-127.0.0.1:6381? yes.
|
||||
Now 'foo' has been set to 'bar' on master!
|
||||
We fetched 'foo' from master and its value is 'bar'.
|
||||
*/
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// Predis supports master / slave replication scenarios where write operations
|
||||
// are performed on the master server and read operations are executed against
|
||||
// one of the slaves. The behavior of commands or EVAL scripts can be customized
|
||||
// at will. As soon as a write operation is performed the client switches to the
|
||||
// master server for all the subsequent requests (either reads and writes).
|
||||
//
|
||||
// This example must be executed using the second Redis server configured as the
|
||||
// slave of the first one (see the "SLAVEOF" command).
|
||||
//
|
||||
|
||||
$parameters = [
|
||||
'tcp://127.0.0.1:6381?role=master&database=15',
|
||||
'tcp://127.0.0.1:6382?role=slave&database=15',
|
||||
];
|
||||
|
||||
$options = ['replication' => 'predis'];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
|
||||
// Read operation.
|
||||
$exists = $client->exists('foo') ? 'yes' : 'no';
|
||||
$current = $client->getConnection()->getCurrent()->getParameters();
|
||||
echo "Does 'foo' exist on {$current->role}? $exists.", PHP_EOL;
|
||||
|
||||
// Write operation.
|
||||
$client->set('foo', 'bar');
|
||||
$current = $client->getConnection()->getCurrent()->getParameters();
|
||||
echo "Now 'foo' has been set to 'bar' on {$current->role}!", PHP_EOL;
|
||||
|
||||
// Read operation.
|
||||
$bar = $client->get('foo');
|
||||
$current = $client->getConnection()->getCurrent()->getParameters();
|
||||
echo "We fetched 'foo' from {$current->role} and its value is '$bar'.", PHP_EOL;
|
||||
|
||||
/* OUTPUT:
|
||||
Does 'foo' exist on slave? yes.
|
||||
Now 'foo' has been set to 'bar' on master!
|
||||
We fetched 'foo' from master and its value is 'bar'.
|
||||
*/
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// This example demonstrates how to use Predis to save PHP sessions on Redis.
|
||||
//
|
||||
// The value of `session.gc_maxlifetime` in `php.ini` will be used by default as
|
||||
// the TTL for keys holding session data but this value can be overridden when
|
||||
// creating the session handler instance using the `gc_maxlifetime` option.
|
||||
//
|
||||
// NOTE: this class requires PHP >= 5.4 but can be used on PHP 5.3 if a polyfill
|
||||
// for SessionHandlerInterface is provided either by you or an external package
|
||||
// like `symfony/http-foundation`.
|
||||
//
|
||||
// See http://www.php.net/class.sessionhandlerinterface.php for more details.
|
||||
//
|
||||
|
||||
if (!interface_exists('SessionHandlerInterface')) {
|
||||
exit('ATTENTION: the session handler implemented by Predis requires PHP >= 5.4.0 ' .
|
||||
"or a polyfill for SessionHandlerInterface provided by an external package.\n");
|
||||
}
|
||||
|
||||
// Instantiate a new client just like you would normally do. Using a prefix for
|
||||
// keys will effectively prefix all session keys with the specified string.
|
||||
$client = new Predis\Client($single_server, ['prefix' => 'sessions:']);
|
||||
|
||||
// Set `gc_maxlifetime` to specify a time-to-live of 5 seconds for session keys.
|
||||
$handler = new Predis\Session\Handler($client, ['gc_maxlifetime' => 5]);
|
||||
|
||||
// Register the session handler.
|
||||
$handler->register();
|
||||
|
||||
// We just set a fixed session ID only for the sake of our example.
|
||||
session_id('example_session_id');
|
||||
|
||||
session_start();
|
||||
|
||||
if (isset($_SESSION['foo'])) {
|
||||
echo "Session has `foo` set to {$_SESSION['foo']}", PHP_EOL;
|
||||
} else {
|
||||
$_SESSION['foo'] = $value = mt_rand();
|
||||
echo "Empty session, `foo` has been set with $value", PHP_EOL;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// This is a basic example on how to use the Predis\DispatcherLoop class in sharded pub/sub mode.
|
||||
//
|
||||
// Both channels belong to different shards. So in sharded mode we can subscribe
|
||||
// and receive messages from different shard channels.
|
||||
//
|
||||
// To see this example in action you can just use redis-cli and publish some
|
||||
// messages to the '{channels}_events' and 'control' channel, e.g.:
|
||||
|
||||
// ./redis-cli
|
||||
// SPUBLISH {channels}_events first
|
||||
// SPUBLISH {channels}_events second
|
||||
// SPUBLISH {channels}_events third
|
||||
// SPUBLISH control terminate_dispatcher
|
||||
|
||||
// 1. Create client and setup RW timeout to 0.
|
||||
$client = new Client(
|
||||
[
|
||||
'tcp://127.0.0.1:6372?read_write_timeout=0',
|
||||
'tcp://127.0.0.1:6373?read_write_timeout=0',
|
||||
'tcp://127.0.0.1:6374?read_write_timeout=0',
|
||||
], [
|
||||
'cluster' => 'redis',
|
||||
]);
|
||||
|
||||
// 2. Run pub/sub loop.
|
||||
$pubSub = $client->pubSubLoop();
|
||||
|
||||
// 3. Create a dispatcher loop instance and attach a bunch of callbacks.
|
||||
$dispatcher = new Predis\Consumer\PubSub\DispatcherLoop($pubSub);
|
||||
|
||||
// 4. Demonstrate how to use a callable class as a callback for the dispatcher loop.
|
||||
class EventsListener implements Countable
|
||||
{
|
||||
private $events;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->events = [];
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->events);
|
||||
}
|
||||
|
||||
public function getEvents()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
public function __invoke($payload, $dispatcher)
|
||||
{
|
||||
$this->events[] = $payload;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Attach our callable class to the dispatcher.
|
||||
$dispatcher->attachCallback('{channels}_events', $events = new EventsListener());
|
||||
|
||||
// 6. Attach a function to control the dispatcher loop termination with a message.
|
||||
$dispatcher->attachCallback('control', function ($payload, $dispatcher) {
|
||||
if ($payload === 'terminate_dispatcher') {
|
||||
$dispatcher->stop();
|
||||
}
|
||||
});
|
||||
|
||||
// 7. Run the dispatcher loop until the callback attached to the 'control' channel
|
||||
// receives 'terminate_dispatcher' as a message.
|
||||
$dispatcher->run();
|
||||
|
||||
// Display our achievements!
|
||||
echo "We received {$events->count()} messages!", PHP_EOL;
|
||||
|
||||
// Say goodbye :-)
|
||||
echo 'Goodbye from Redis!', PHP_EOL;
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Predis\Client;
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// 1. Create client and setup RW timeout to 0.
|
||||
$client = new Client(
|
||||
[
|
||||
'tcp://127.0.0.1:6372?read_write_timeout=0',
|
||||
'tcp://127.0.0.1:6373?read_write_timeout=0',
|
||||
'tcp://127.0.0.1:6374?read_write_timeout=0',
|
||||
], [
|
||||
'cluster' => 'redis',
|
||||
]);
|
||||
|
||||
// 2. Run pub/sub loop. Sharded channels belongs to different shards.
|
||||
$pubSub = $client->pubSubLoop();
|
||||
$pubSub->ssubscribe('{channels}_notifications');
|
||||
$pubSub->ssubscribe('control_channel');
|
||||
|
||||
// Start processing the pubsup messages. Open a terminal and use redis-cli
|
||||
// to push messages to the channels. Examples:
|
||||
// ./redis-cli SPUBLISH {channels}_notifications "this is a test"
|
||||
// ./redis-cli SPUBLISH control_channel quit_loop
|
||||
foreach ($pubSub as $message) {
|
||||
switch ($message->kind) {
|
||||
case 'ssubscribe':
|
||||
echo "Subscribed to {$message->channel}", PHP_EOL;
|
||||
break;
|
||||
|
||||
case 'message':
|
||||
if ($message->channel == 'control_channel') {
|
||||
if ($message->payload == 'quit_loop') {
|
||||
echo 'Aborting pubsub loop...', PHP_EOL;
|
||||
$pubSub->sunsubscribe();
|
||||
} else {
|
||||
echo "Received an unrecognized command: {$message->payload}.", PHP_EOL;
|
||||
}
|
||||
} else {
|
||||
echo "Received the following message from {$message->channel}:",
|
||||
PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Always unset the pubsub consumer instance when you are done! The
|
||||
// class destructor will take care of cleanups and prevent protocol
|
||||
// desynchronizations between the client and the server.
|
||||
unset($pubsub);
|
||||
|
||||
// Say goodbye :-)
|
||||
echo 'Goodbye from Redis!', PHP_EOL;
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
exit('Example scripts are meant to be executed locally via CLI.');
|
||||
}
|
||||
|
||||
require __DIR__ . '/../autoload.php';
|
||||
|
||||
function redis_version($info)
|
||||
{
|
||||
if (isset($info['Server']['redis_version'])) {
|
||||
return $info['Server']['redis_version'];
|
||||
} elseif (isset($info['redis_version'])) {
|
||||
return $info['redis_version'];
|
||||
} else {
|
||||
return 'unknown version';
|
||||
}
|
||||
}
|
||||
|
||||
$single_server = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'database' => 15,
|
||||
];
|
||||
|
||||
$multiple_servers = [
|
||||
[
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'database' => 15,
|
||||
'alias' => 'first',
|
||||
],
|
||||
[
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6380,
|
||||
'database' => 15,
|
||||
'alias' => 'second',
|
||||
],
|
||||
];
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
|
||||
// This is an implementation of an atomic client-side ZPOP using the support for
|
||||
// check-and-set (CAS) operations with MULTI/EXEC transactions, as described in
|
||||
// "WATCH explained" from http://redis.io/topics/transactions
|
||||
//
|
||||
// First, populate your database with a tiny sample data set:
|
||||
//
|
||||
// ./redis-cli
|
||||
// SELECT 15
|
||||
// ZADD zset 1 a 2 b 3 c
|
||||
//
|
||||
// Then execute this script four times and see its output.
|
||||
//
|
||||
|
||||
function zpop($client, $key)
|
||||
{
|
||||
$element = null;
|
||||
$options = [
|
||||
'cas' => true, // Initialize with support for CAS operations
|
||||
'watch' => $key, // Key that needs to be WATCHed to detect changes
|
||||
'retry' => 3, // Number of retries on aborted transactions, after
|
||||
// which the client bails out with an exception.
|
||||
];
|
||||
|
||||
$client->transaction($options, function ($tx) use ($key, &$element) {
|
||||
@[$element] = $tx->zrange($key, 0, 0);
|
||||
|
||||
if (isset($element)) {
|
||||
$tx->multi(); // With CAS, MULTI *must* be explicitly invoked.
|
||||
$tx->zrem($key, $element);
|
||||
}
|
||||
});
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
$client = new Predis\Client($single_server);
|
||||
$zpopped = zpop($client, 'zset');
|
||||
|
||||
echo isset($zpopped) ? "ZPOPed $zpopped" : 'Nothing to ZPOP!', PHP_EOL;
|
||||
+1540
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
||||
parameters:
|
||||
level: 0
|
||||
|
||||
paths:
|
||||
- tests/
|
||||
|
||||
ignoreErrors:
|
||||
# Uses func_get_args()
|
||||
- message: "#^Static method Predis\\\\Command\\\\RawCommand\\:\\:create\\(\\) invoked with 0 parameters, at least 1 required\\.$#"
|
||||
count: 1
|
||||
path: tests/Predis/Command/RawCommandTest.php
|
||||
# Mockery
|
||||
- message: "#^Call to an undefined method Predis\\\\Configuration\\\\Option\\\\AggregateTest\\:\\:getMockConnectionClass\\(\\)\\.$#"
|
||||
count: 2
|
||||
path: tests/Predis/Configuration/Option/AggregateTest.php
|
||||
@@ -1,50 +0,0 @@
|
||||
parameters:
|
||||
level: 2
|
||||
|
||||
paths:
|
||||
- src/
|
||||
|
||||
ignoreErrors:
|
||||
# Tricky ones
|
||||
- message: "#^Cannot cast Predis\\\\Response\\\\ResponseInterface to string\\.$#"
|
||||
count: 1
|
||||
path: src/Client.php
|
||||
- message: "#^Cannot cast Predis\\\\Connection\\\\ConnectionInterface to string\\.$#"
|
||||
count: 1
|
||||
path: src/Client.php
|
||||
# "new static" in non-final classes
|
||||
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
|
||||
count: 3
|
||||
path: src/Client.php
|
||||
- message: "#^Unsafe usage of new static\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Connection/Parameters.php
|
||||
# Using an interface as concrete type
|
||||
- message: "#^Call to an undefined method Predis\\\\Command\\\\FactoryInterface\\:\\:define\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Configuration/Option/Commands.php
|
||||
- message: "#^Call to an undefined method Predis\\\\Command\\\\FactoryInterface\\:\\:undefine\\(\\)\\.$#"
|
||||
count: 1
|
||||
path: src/Configuration/Option/Commands.php
|
||||
- message: "#^Access to an undefined property Predis\\\\Connection\\\\ParametersInterface\\:\\:\\$weight\\.$#"
|
||||
count: 1
|
||||
path: src/Connection/Cluster/PredisCluster.php
|
||||
- message: "#^Variable \\$response might not be defined\\.$#"
|
||||
count: 2
|
||||
path: src/Connection/Cluster/RedisCluster.php
|
||||
- message: "#^Access to an undefined property Predis\\\\Connection\\\\ParametersInterface\\:\\:\\$role\\.$#"
|
||||
count: 1
|
||||
path: src/Connection/Replication/MasterSlaveReplication.php
|
||||
- message: "#^Access to an undefined property Predis\\\\Connection\\\\ParametersInterface\\:\\:\\$role\\.$#"
|
||||
count: 1
|
||||
path: src/Connection/Replication/SentinelReplication.php
|
||||
# try/catch problems
|
||||
- message: "#^Variable \\$connection might not be defined\\.$#"
|
||||
count: 1
|
||||
path: src/Connection/Replication/MasterSlaveReplication.php
|
||||
- message: "#^Variable \\$response might not be defined\\.$#"
|
||||
count: 1
|
||||
path: src/Connection/Replication/MasterSlaveReplication.php
|
||||
- message: "#^Variable \\$response might not be defined\\.$#"
|
||||
count: 1
|
||||
path: src/Connection/Replication/SentinelReplication.php
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
bootstrap="tests/bootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="true"
|
||||
>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Predis Test Suite">
|
||||
<directory phpVersion="7.4">tests/Predis/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<logging>
|
||||
<log type="coverage-php" target="build/cov/coverage.cov"/>
|
||||
</logging>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>relay-incompatible</group>
|
||||
<group>relay-resp3</group>
|
||||
<group>realm-stack</group>
|
||||
<group>ext-curl</group>
|
||||
<group>cluster</group>
|
||||
<group>gears</group>
|
||||
<group>unprotected</group>
|
||||
</exclude>
|
||||
</groups>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<php>
|
||||
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
|
||||
<const name="REDIS_SERVER_PORT" value="6379" />
|
||||
<const name="REDIS_UNPROTECTED_SERVER_PORT" value="6380" />
|
||||
<const name="REDIS_SERVER_DBNUM" value="0" />
|
||||
<const name="REDIS_PASSWORD" value="foobar" />
|
||||
<env name="USE_RELAY" value="true" />
|
||||
<env name="REDIS_STACK_SERVER_PORT" value="6479" />
|
||||
|
||||
<!-- Redis Cluster -->
|
||||
<!-- Only master nodes endpoints included -->
|
||||
<const
|
||||
name="REDIS_CLUSTER_ENDPOINTS"
|
||||
value="127.0.0.1:6372?password=foobar,127.0.0.1:6373?password=foobar,127.0.0.1:6374?password=foobar"
|
||||
/>
|
||||
</php>
|
||||
</phpunit>
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
bootstrap="tests/bootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="true"
|
||||
>
|
||||
<php>
|
||||
<ini name="error_reporting" value="E_ALL & ~E_DEPRECATED"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Predis Test Suite">
|
||||
<directory>tests/Predis/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<logging>
|
||||
<log type="coverage-php" target="build/cov/coverage.cov"/>
|
||||
</logging>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>realm-stack</group>
|
||||
<group>ext-relay</group>
|
||||
<group>ext-curl</group>
|
||||
<group>cluster</group>
|
||||
<group>gears</group>
|
||||
<group>gears-cluster</group>
|
||||
<group>unprotected</group>
|
||||
<!-- <group>connected</group> -->
|
||||
<!-- <group>disconnected</group> -->
|
||||
<!-- <group>commands</group> -->
|
||||
<!-- <group>slow</group> -->
|
||||
</exclude>
|
||||
</groups>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<php>
|
||||
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
|
||||
<const name="REDIS_SERVER_PORT" value="6379" />
|
||||
<const name="REDIS_UNPROTECTED_SERVER_PORT" value="6380" />
|
||||
<const name="REDIS_SERVER_DBNUM" value="0" />
|
||||
<const name="REDIS_PASSWORD" value="foobar" />
|
||||
<env name="USE_RELAY" value="false" />
|
||||
<env name="REDIS_STACK_SERVER_PORT" value="6479" />
|
||||
|
||||
<!-- Redis Cluster -->
|
||||
<!-- Only master nodes endpoints included -->
|
||||
<const
|
||||
name="REDIS_CLUSTER_ENDPOINTS"
|
||||
value="127.0.0.1:6372?password=foobar,127.0.0.1:6373?password=foobar,127.0.0.1:6374?password=foobar"
|
||||
/>
|
||||
</php>
|
||||
</phpunit>
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis;
|
||||
|
||||
/**
|
||||
* Implements a lightweight PSR-0 compliant autoloader for Predis.
|
||||
*
|
||||
* @author Eric Naeseth <eric@thumbtack.com>
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Autoloader
|
||||
{
|
||||
private $directory;
|
||||
private $prefix;
|
||||
private $prefixLength;
|
||||
|
||||
/**
|
||||
* @param string $baseDirectory Base directory where the source files are located.
|
||||
*/
|
||||
public function __construct($baseDirectory = __DIR__)
|
||||
{
|
||||
$this->directory = $baseDirectory;
|
||||
$this->prefix = __NAMESPACE__ . '\\';
|
||||
$this->prefixLength = strlen($this->prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the autoloader class with the PHP SPL autoloader.
|
||||
*
|
||||
* @param bool $prepend Prepend the autoloader on the stack instead of appending it.
|
||||
*/
|
||||
public static function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register([new self(), 'autoload'], true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a class from a file using its fully qualified name.
|
||||
*
|
||||
* @param string $className Fully qualified name of a class.
|
||||
*/
|
||||
public function autoload($className)
|
||||
{
|
||||
if (0 === strpos($className, $this->prefix)) {
|
||||
$parts = explode('\\', substr($className, $this->prefixLength));
|
||||
$filepath = $this->directory . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $parts) . '.php';
|
||||
|
||||
if (is_file($filepath)) {
|
||||
require $filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user