mirror of
https://github.com/predis/predis.git
synced 2026-08-20 19:21:51 +00:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 84060b9034 | |||
| d4a7bd52be | |||
| 16a9d4e041 | |||
| 0b92e8d156 | |||
| 1f7b534072 | |||
| 0c0cbbafff | |||
| 8aec51b34f | |||
| eeb1e8b7a7 | |||
| c3a44b8e2e | |||
| 255beac137 | |||
| d473328a22 | |||
| 0d25e7b0bb | |||
| be3b84ca14 | |||
| 7d0597c62c | |||
| 833e46b79d | |||
| a22858438d | |||
| f221d0c81e | |||
| 0b5bfdfe33 | |||
| 3700d48771 | |||
| 653d3f08ab | |||
| 2d2de7837b | |||
| fc18e6456a | |||
| 6f2064ed90 | |||
| e2e645a8bd | |||
| 2219964b7e | |||
| 381e44b6e6 | |||
| 808693bfbd | |||
| 33eab38314 | |||
| 82e404660e | |||
| 6d60a42400 | |||
| 95624de5c4 | |||
| 2ee135099c | |||
| e26e3b423a | |||
| 946fac4454 | |||
| 4e976a3eb6 | |||
| baaf26fe5b | |||
| 6a0348fbea | |||
| 8365a6be77 | |||
| f028e428be | |||
| c13b82557d | |||
| 30d01c40d4 | |||
| 6459546e25 | |||
| dede828008 | |||
| 5c0716575e | |||
| 90bffb1a5a | |||
| 64ee96c312 | |||
| bff5a9729e | |||
| 5cd7b774c4 | |||
| 354d1ef921 | |||
| 9e3a8da94d | |||
| 35003713a2 | |||
| cd991e6370 | |||
| c59853afb8 | |||
| d4982b6c43 | |||
| 96f4af816d | |||
| 22ff3ff992 | |||
| 77b75e0738 | |||
| eeea28b275 | |||
| aa067d8fd9 | |||
| ce17c5383a | |||
| c4c36cae7a | |||
| 7a170b3d81 | |||
| 17e7bce22e | |||
| 7a5d6bac3e | |||
| 9d6c031ead | |||
| 692718afb4 | |||
| 0776377412 | |||
| e4b0512d42 | |||
| 75ca2f8111 | |||
| 37b1c88115 | |||
| a4e6130568 |
@@ -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
|
||||
+5
-15
@@ -1,3 +1,5 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
@@ -6,22 +8,10 @@ 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
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 120
|
||||
|
||||
[*.{md,yml,yaml,neon,sh}]
|
||||
[*.{md,yml}]
|
||||
indent_size = 2
|
||||
|
||||
[VERSION]
|
||||
insert_final_newline = false
|
||||
|
||||
[tests/**.php]
|
||||
max_line_length = unset
|
||||
|
||||
[{src/ClientInterface.php,src/ClientContextInterface.php}]
|
||||
max_line_length = unset
|
||||
|
||||
+3
-13
@@ -1,20 +1,10 @@
|
||||
* 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
|
||||
/.php_cs export-ignore
|
||||
/.travis.yml export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/phpstan.dist.neon export-ignore
|
||||
/phpstan-tests.dist.neon export-ignore
|
||||
/phpunit.xml.travisci export-ignore
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Default
|
||||
* @tillkruss
|
||||
|
||||
# Redis commands
|
||||
src/Command/Redis/* @predis/team @tillkruss
|
||||
|
||||
# Redis Stack
|
||||
src/Command/Redis/Json/* @predis/team
|
||||
src/Command/Redis/Search/* @predis/team
|
||||
src/Command/Redis/TopK/* @predis/team
|
||||
src/Command/Redis/TDigest/* @predis/team
|
||||
src/Command/Redis/BloomFilter/* @predis/team
|
||||
src/Command/Redis/CuckooFilter/* @predis/team
|
||||
src/Command/Redis/CountMinSketch/* @predis/team
|
||||
@@ -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:7.4}
|
||||
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:7.4.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:7.4.2}
|
||||
container_name: redis-unprotected
|
||||
environment:
|
||||
- TLS_ENABLED=no
|
||||
- REDIS_CLUSTER=no
|
||||
- PORT=6379
|
||||
- TLS_PORT=6666
|
||||
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
|
||||
ports:
|
||||
- 6380:6379
|
||||
volumes:
|
||||
- "./dockers/unprotected:/redis/work"
|
||||
profiles:
|
||||
- all
|
||||
- clients
|
||||
- unprotected
|
||||
|
||||
redis-cluster:
|
||||
image: ${REDIS_IMAGE_NAME:-redislabs/client-libs-test:7.4.2}
|
||||
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-comitish: 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,53 +0,0 @@
|
||||
ACLs
|
||||
Autoloading
|
||||
CAS
|
||||
Customizable
|
||||
ElastiCache
|
||||
FPM
|
||||
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'
|
||||
|
||||
steps:
|
||||
|
||||
- name: Resolve container name
|
||||
run: |
|
||||
# Mapping of original redis versions to client test containers
|
||||
declare -A redis_clients_version_mapping=(
|
||||
["8.0"]="8.0.1-pre"
|
||||
["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
|
||||
coverage: ${{ (matrix.php == '8.4' && matrix.redis == '7.4') && '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 != '7.4' }}
|
||||
run: vendor/bin/phpunit
|
||||
|
||||
- name: Run tests with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
run: vendor/bin/phpunit --coverage-php build/cov/coverage-predis.cov --coverage-filter ./src
|
||||
|
||||
- name: Run tests using Relay
|
||||
if: ${{ matrix.php != '8.4' && matrix.redis >= '7.2' }}
|
||||
run: vendor/bin/phpunit -c phpunit.relay.xml
|
||||
|
||||
- name: Run tests using Relay with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
run: vendor/bin/phpunit -c phpunit.relay.xml --coverage-php build/cov/coverage-relay.cov --coverage-filter ./src
|
||||
|
||||
- name: Run tests against unprotected Redis
|
||||
if: ${{ (matrix.php != '8.4' || matrix.redis != '7.4') && 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 == '7.4' }}
|
||||
run: vendor/bin/phpunit --group unprotected --coverage-php build/cov/coverage-unprotected.cov --coverage-filter ./src
|
||||
|
||||
- name: Run stack tests
|
||||
if: ${{ (matrix.php != '8.4' || matrix.redis != '7.4') && matrix.redis >= '7.2' }}
|
||||
run: vendor/bin/phpunit --group realm-stack
|
||||
|
||||
- name: Run stack tests with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' && steps.stack_infra.conclusion == 'success' }}
|
||||
run: vendor/bin/phpunit --group realm-stack --coverage-php build/cov/coverage-stack.cov --coverage-filter ./src
|
||||
env:
|
||||
REDIS_STACK_SERVER_PORT: 6479
|
||||
|
||||
- name: Run stack tests using Relay
|
||||
if: ${{ matrix.php != '8.4' && matrix.redis == '7.4' }}
|
||||
run: vendor/bin/phpunit --group realm-stack -c phpunit.relay.xml
|
||||
|
||||
- name: Run stack tests using Relay with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
run: vendor/bin/phpunit --group realm-stack -c phpunit.relay.xml --coverage-php build/cov/coverage-stack-relay.cov --coverage-filter ./src
|
||||
env:
|
||||
REDIS_STACK_SERVER_PORT: 6479
|
||||
|
||||
- name: Run tests against cluster
|
||||
if: ${{ (matrix.php != '8.4' || matrix.redis != '7.4') && steps.cluster_infra.conclusion == 'success' }}
|
||||
run: |
|
||||
vendor/bin/phpunit --group cluster
|
||||
|
||||
- name: Run tests against cluster with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' && steps.cluster_infra.conclusion == 'success' }}
|
||||
run: |
|
||||
vendor/bin/phpunit --group cluster --coverage-php build/cov/coverage-cluster.cov --coverage-filter ./src
|
||||
|
||||
- name: Run tests against cluster using Relay
|
||||
if: ${{ matrix.php != '8.4' && matrix.redis == '7.4' }}
|
||||
run: |
|
||||
sleep 5
|
||||
vendor/bin/phpunit -c phpunit.relay.xml --group cluster
|
||||
|
||||
- name: Run tests against cluster using Relay with coverage
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
run: |
|
||||
sleep 5
|
||||
vendor/bin/phpunit -c phpunit.relay.xml --group cluster --coverage-php build/cov/coverage-cluster-relay.cov --coverage-filter ./src
|
||||
|
||||
- name: Merge coverage reports
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
run: php vendor/bin/phpcov merge --clover build/logs/clover.xml build/cov
|
||||
|
||||
- name: Send coverage to Coveralls
|
||||
uses: coverallsapp/github-action@v2
|
||||
if: ${{ matrix.php == '8.4' && matrix.redis == '7.4' }}
|
||||
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
|
||||
+6
-5
@@ -1,8 +1,9 @@
|
||||
/build
|
||||
/vendor
|
||||
*.tgz
|
||||
*.phar
|
||||
.php-version
|
||||
.php_cs.cache
|
||||
.phpunit.result.cache
|
||||
.php-cs-fixer.cache
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
package.xml
|
||||
composer.lock
|
||||
experiments/
|
||||
vendor/
|
||||
|
||||
@@ -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;
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
$PREDIS_HEADER = <<<EOS
|
||||
This file is part of the Predis package.
|
||||
|
||||
(c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
|
||||
For the full copyright and license information, please view the LICENSE
|
||||
file that was distributed with this source code.
|
||||
EOS;
|
||||
|
||||
Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($PREDIS_HEADER);
|
||||
|
||||
return Symfony\CS\Config\Config::create()
|
||||
->setUsingCache(true)
|
||||
->level(Symfony\CS\FixerInterface::SYMFONY_LEVEL)
|
||||
->fixers(array(
|
||||
// Symfony
|
||||
'-unalign_equals',
|
||||
'-unalign_double_arrow',
|
||||
|
||||
// Contribs
|
||||
'header_comment',
|
||||
'ordered_use',
|
||||
'phpdoc_order',
|
||||
'long_array_syntax',
|
||||
))
|
||||
->finder(
|
||||
Symfony\CS\Finder\DefaultFinder::create()
|
||||
->in(__DIR__.'/bin')
|
||||
->in(__DIR__.'/src')
|
||||
->in(__DIR__.'/tests')
|
||||
->in(__DIR__.'/examples')
|
||||
);
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
language: php
|
||||
sudo: false
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- hhvm
|
||||
branches:
|
||||
except:
|
||||
- v0.5
|
||||
- v0.6
|
||||
- php5.2_backport
|
||||
- documentation
|
||||
services: redis-server
|
||||
before_script:
|
||||
- composer self-update
|
||||
- composer install --no-interaction --prefer-source --dev
|
||||
script:
|
||||
- vendor/bin/phpunit -c phpunit.xml.travisci
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
fast_finish: true
|
||||
+801
-240
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
## Filing bug reports ##
|
||||
|
||||
Bugs or feature requests can be posted on the [GitHub issues](http://github.com/nrk/predis/issues)
|
||||
section of the project.
|
||||
|
||||
When reporting bugs, in addition to the obvious description of your issue you __must__ always provide
|
||||
some essential information about your environment such as:
|
||||
|
||||
1. version of Predis (check the `VERSION` file or the `Predis\Client::VERSION` constant).
|
||||
2. version of Redis (check `redis_version` returned by [`INFO`](http://redis.io/commands/info)).
|
||||
3. version of PHP.
|
||||
4. name and version of the operating system.
|
||||
5. when possible, a small snippet of code that reproduces the issue.
|
||||
|
||||
__Think about it__: we do not have a crystal ball and cannot predict things or peer into the unknown
|
||||
so please provide as much details as possible to help us isolating issues and fix them.
|
||||
|
||||
__Never__ use GitHub issues to post generic questions about Predis! When you have questions about
|
||||
how Predis works or how it can be used, please just hop me an email and I will get back to you as
|
||||
soon as possible.
|
||||
|
||||
|
||||
## Contributing code ##
|
||||
|
||||
If you want to work on Predis, it is highly recommended that you first run the test suite in order
|
||||
to check that everything is OK and report strange behaviours or bugs. When modifying Predis please
|
||||
make sure that no warnings or notices are emitted by PHP running the interpreter in your development
|
||||
environment with the `error_reporting` variable set to `E_ALL | E_STRICT`.
|
||||
|
||||
The recommended way to contribute to Predis is to fork the project on GitHub, create topic branches
|
||||
on your newly created repository to fix bugs or add new features (possibly with tests covering your
|
||||
modifications) and then open a pull request with a description of the applied changes. Obviously you
|
||||
can use any other Git hosting provider of your preference.
|
||||
|
||||
We always aim for consistency in our code base so you should follow basic coding rules as defined by
|
||||
[PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
|
||||
and [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
|
||||
and stick with the conventions used in Predis to name classes and interfaces. Indentation should be
|
||||
done with 4 spaces and code should be wrapped at 100 columns (please try to stay within this limit
|
||||
even if the above mentioned official coding guidelines set the soft limit to 120 columns).
|
||||
|
||||
Please follow these [commit guidelines](http://git-scm.com/book/ch5-2.html#Commit-Guidelines) when
|
||||
committing your code to Git and always write a meaningful (not necessarily extended) description of
|
||||
your changes before opening pull requests.
|
||||
@@ -1,9 +1,10 @@
|
||||
# Frequently asked questions about Predis #
|
||||
# Some frequently asked questions about Predis #
|
||||
________________________________________________
|
||||
|
||||
## What is the point of 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
|
||||
that can be easily extended by developers while still being reasonabily 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
|
||||
@@ -12,34 +13,21 @@ a great asset since it allows developers to add new and still missing features o
|
||||
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? ##
|
||||
### 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? ##
|
||||
### Does Predis support transparent (de)serialization of values? ###
|
||||
|
||||
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.
|
||||
No and it will not ever 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/nrk/predis/issues/29#issuecomment-1202624).
|
||||
|
||||
## 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? ##
|
||||
### 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
|
||||
@@ -59,7 +47,7 @@ try {
|
||||
$client->info();
|
||||
```
|
||||
|
||||
## How Predis abstracts Redis commands? ##
|
||||
### 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
|
||||
@@ -79,19 +67,103 @@ $client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // single n
|
||||
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.
|
||||
# Speaking about performances... #
|
||||
_________________________________________________
|
||||
|
||||
## When should I use PhpRedis? ###
|
||||
|
||||
Predis is fast enough when Redis is located on the same machine as PHP.
|
||||
### Predis is a pure-PHP implementation: it can not be fast enough! ###
|
||||
|
||||
[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).
|
||||
It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you a
|
||||
couple of easy numbers with a simple test that uses a single client and is executed by PHP 5.5.6
|
||||
against a local instance of Redis 2.8 that runs under Ubuntu 13.10 on a Intel Q6600:
|
||||
|
||||
[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
|
||||
```
|
||||
21000 SET/sec using 12 bytes for both key and value.
|
||||
21000 GET/sec while retrieving the very same values.
|
||||
0.130 seconds to fetch 30000 keys using _KEYS *_.
|
||||
```
|
||||
|
||||
How does it compare with [__phpredis__](http://github.com/nicolasff/phpredis), a nice C extension
|
||||
providing an efficient client for Redis?
|
||||
|
||||
```
|
||||
30100 SET/sec using 12 bytes for both key and value
|
||||
29400 GET/sec while retrieving the very same values
|
||||
0.035 seconds to fetch 30000 keys using "KEYS *"".
|
||||
```
|
||||
|
||||
Wow __phpredis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so
|
||||
lower numbers are quite expected but there is a fundamental flaw in them: is this really how you are
|
||||
going to use Redis in your application? Are you really going to send thousands of commands using a
|
||||
for-loop on each page request using a single client instance? If so... well I guess you are probably
|
||||
doing something wrong. Also, if you need to `SET` or `GET` multiple keys you should definitely use
|
||||
commands such as `MSET` and `MGET`. You can also use pipelining to get more performances when this
|
||||
technique can be used.
|
||||
|
||||
There is one more thing: we have tested the overhead of Predis by connecting on a localhost instance
|
||||
of Redis but how these numbers change when we hit the physical network by connecting to remote Redis
|
||||
instances?
|
||||
|
||||
```
|
||||
Using Predis:
|
||||
3200 SET/sec using 12 bytes for both key and value
|
||||
3200 GET/sec while retrieving the very same values
|
||||
0.132 seconds to fetch 30000 keys using "KEYS *".
|
||||
|
||||
Using phpredis:
|
||||
3500 SET/sec using 12 bytes for both key and value
|
||||
3500 GET/sec while retrieving the very same values
|
||||
0.045 seconds to fetch 30000 keys using "KEYS *".
|
||||
```
|
||||
|
||||
There you go, you get almost the same average numbers and the reason is simple: network latency is a
|
||||
real performance killer and you cannot do (almost) anything about that. As a disclaimer, remember
|
||||
that we are measuring the overhead of client libraries implementations and the effects of network
|
||||
round-trip times, so we are not really measuring how fast Redis is. Redis shines best with thousands
|
||||
of concurrent clients doing requests! Also, actual performances should be measured according to how
|
||||
your application will use Redis.
|
||||
|
||||
### I am convinced, but performances for multi-bulk responses are still worse ###
|
||||
|
||||
Fair enough, but there is an option available if you need even more speed and consists on installing
|
||||
__[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) and let the
|
||||
client use it. __phpiredis__ is another C extension that wraps __hiredis__ (the official C client
|
||||
library for Redis) with a thin layer exposing its features to PHP. You can then choose between two
|
||||
different connection classes:
|
||||
|
||||
- `Predis\Connection\PhpiredisStreamConnection` (using native PHP streams).
|
||||
- `Predis\Connection\PhpiredisSocketConnection` (requires `ext-socket`).
|
||||
|
||||
You will now get the benefits of a faster protocol serializer and parser just by adding a couple of
|
||||
lines of code:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client('tcp://127.0.0.1', array(
|
||||
'connections' => array(
|
||||
'tcp' => 'Predis\Connection\PhpiredisStreamConnection',
|
||||
'unix' => 'Predis\Connection\PhpiredisSocketConnection',
|
||||
),
|
||||
));
|
||||
```
|
||||
|
||||
Dead simple. Nothing changes in the way you use the library in your application. So how fast is it
|
||||
our basic benchmark script now? There are not much improvements for inline or short bulk responses
|
||||
like the ones returned by `SET` and `GET`, but the speed for parsing multi-bulk responses is now on
|
||||
par with phpredis:
|
||||
|
||||
```
|
||||
Fatching 30000 keys with _KEYS *_ using Predis paired with phpiredis::
|
||||
|
||||
0.035 seconds from a local Redis instance
|
||||
0.047 seconds from a remote Redis instance
|
||||
```
|
||||
|
||||
### If I need an extension to get better performances, why not using phpredis? ###
|
||||
|
||||
Good question. Generically speaking if you need absolute uber-speed using Redis on the localhost and
|
||||
you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you
|
||||
do not need any kind of extensibility or guaranteed backwards compatibility with different versions
|
||||
of Redis (Predis currently supports from 1.2 up to 2.8 and the current development version), then
|
||||
using __phpredis__ makes absolutely sense. Otherwise, Predis is perfect for the job and by adding
|
||||
__phpiredis__ you can get a nice speed bump almost for free.
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
MIT License
|
||||
Copyright (c) 2009-2015 Daniele Alessandri
|
||||
|
||||
Copyright (c) 2009-2020 Daniele Alessandri (original work)
|
||||
Copyright (c) 2021-2024 Till Krüss (modified work)
|
||||
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:
|
||||
|
||||
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 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.
|
||||
|
||||
@@ -1,43 +1,50 @@
|
||||
# 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]
|
||||
[](https://packagist.org/packages/predis/predis)
|
||||
[](https://packagist.org/packages/predis/predis)
|
||||
[](https://packagist.org/packages/predis/predis)
|
||||
[](https://travis-ci.org/nrk/predis)
|
||||
[](http://hhvm.h4cc.de/package/predis/predis)
|
||||
|
||||
A flexible and feature-complete [Redis](http://redis.io) / [Valkey](https://github.com/valkey-io/valkey) client for PHP 7.2 and newer.
|
||||
Predis is a flexible and feature-complete [Redis](http://redis.io) client library for PHP >= 5.3.
|
||||
|
||||
More details about this project can be found on the [frequently asked questions](FAQ.md).
|
||||
By default this library does not require any additional C extension, but it can be optionally paired
|
||||
with [phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of serializing and parsing
|
||||
the [Redis RESP Protocol](http://redis.io/topics/protocol). An asynchronous implementation of Predis
|
||||
is available through [Predis\Async](https://github.com/nrk/predis-async) (__experimental__).
|
||||
|
||||
Predis can be used with [HHVM](http://www.hhvm.com) >= 2.3.0 but there are no guarantees you will
|
||||
not run into unexpected issues (especially when the JIT compiler is enabled via `Eval.Jit = true`)
|
||||
due to HHVM being still under heavy development and not yet 100% compatible with the _standard_ PHP.
|
||||
|
||||
More details about this project can be found on the [frequently asked questions](FAQ.md) and on the
|
||||
[wiki](https://github.com/nrk/predis/wiki).
|
||||
|
||||
|
||||
## 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`.
|
||||
- Support for a wide range of Redis versions (from __2.0__ to __3.0__) using profiles.
|
||||
- Clustering via client-side sharding using consistent hashing or custom distributors.
|
||||
- Smart support for [redis-cluster](http://redis.io/topics/cluster-tutorial) (Redis >= 3.0).
|
||||
- Support for master-slave replication (write operations on master, read operations on slaves).
|
||||
- Transparent key prefixing for all known Redis commands using a customizable prefixing strategy.
|
||||
- Command pipelining (works on both single nodes and aggregate connections).
|
||||
- Abstraction for Redis transactions (Redis >= 2.0) supporting CAS operations (Redis >= 2.2).
|
||||
- Abstraction for Lua scripting (Redis >= 2.6) with 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.
|
||||
- Connections to Redis are established lazily by the client upon the first command.
|
||||
- Support for both TCP/IP and UNIX domain sockets and persistent connections.
|
||||
- Support for [Webdis](http://webd.is) (requires both `ext-curl` and `ext-phpiredis`).
|
||||
- Support for custom connection classes for providing different network or protocol backends.
|
||||
- Flexible system for defining custom commands and override the default ones.
|
||||
- Flexible system for defining custom commands and server profiles.
|
||||
|
||||
|
||||
## How to _install_ and use Predis ##
|
||||
## How to 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
|
||||
```
|
||||
Predis is available on [Packagist](http://packagist.org/packages/predis/predis) which allows a quick
|
||||
_installation_ using [Composer](http://packagist.org/about-composer). Alternatively, the library can
|
||||
be found on our [own PEAR channel](http://pear.nrk.io) for a more traditional installation via PEAR.
|
||||
Ultimately, archives of each release are [available on GitHub](https://github.com/nrk/predis/tags).
|
||||
|
||||
|
||||
### Loading the library ###
|
||||
@@ -45,7 +52,7 @@ composer require predis/predis
|
||||
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:
|
||||
possible to leverage its own autoloader in projects or scripts not having any autoload facility:
|
||||
|
||||
```php
|
||||
// Prepend a base path if Predis is not available in your "include_path".
|
||||
@@ -54,6 +61,12 @@ require 'Predis/Autoloader.php';
|
||||
Predis\Autoloader::register();
|
||||
```
|
||||
|
||||
It is possible to create a [phar](http://www.php.net/manual/en/intro.phar.php) archive directly from
|
||||
the repository by launching `bin/create-phar`. The phar contains a stub defining its own autoloader
|
||||
so you just need to `require()` it to start using the library. Ultimately it is possible to generate
|
||||
a single big PHP file containing all the source code simply by launching `bin/create-single-file`,
|
||||
but this practice __is not__ encouraged.
|
||||
|
||||
|
||||
### Connecting to Redis ###
|
||||
|
||||
@@ -82,194 +95,73 @@ $client = new Predis\Client([
|
||||
$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.
|
||||
Starting with Predis v1.0.2 the client also understands the `redis` scheme in URI strings as defined
|
||||
by the [provisional IANA registration](http://www.iana.org/assignments/uri-schemes/prov/redis).
|
||||
|
||||
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
|
||||
When an array of connection parameters is provided, Predis automatically works in cluster mode using
|
||||
client-side sharding. Both 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',
|
||||
'tcp://10.0.0.1?alias=first-node',
|
||||
['host' => '10.0.0.2', 'alias' => 'second-node'],
|
||||
]);
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### 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()`:
|
||||
Various aspects of the client can be configured simply by passing options to the second argument of
|
||||
`Predis\Client::__construct()`:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client($parameters, ['prefix' => 'sample:']);
|
||||
$client = new Predis\Client($parameters, ['profile' => '2.8', '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:
|
||||
Options are managed through a mini DI-alike container while their values can be lazily initialized
|
||||
only when needed. This is a list of the options supported by default:
|
||||
|
||||
- `prefix`: prefix string applied to every key found in commands.
|
||||
- `profile`: which profile to use in order to match a specific version of Redis.
|
||||
- `prefix`: a prefix string that is automatically applied to keys 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.
|
||||
- `connections`: connection backends or a connection factory to be used by the client.
|
||||
- `cluster`: which backend to use for clustering (`predis`, `redis` or custom configuration).
|
||||
- `replication`: which backend to use for replication (predis or custom configuration).
|
||||
- `aggregate`: custom connections aggregator (overrides both `cluster` and `replication`).
|
||||
|
||||
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.
|
||||
Users can provide custom options with their values or lazy callable initializers 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')
|
||||
);
|
||||
```
|
||||
Predis is able to aggregate multiple connections which is the base for clustering and replication.
|
||||
By default the client implements a cluster of nodes using either client-side sharding (default) or
|
||||
a Redis-backed solution using [redis-cluster](http://redis.io/topics/cluster-tutorial).
|
||||
As for replication, Predis can handle a single-master and multiple-slaves setup by executing read
|
||||
operations on slaves and switching to the master only for write operations. The replication behavior
|
||||
is fully configurable.
|
||||
|
||||
#### 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):
|
||||
The client can be configured to operate in a master-slave setup by executing read-only commands on
|
||||
slave nodes and automatically switch to the master node as soon as it detects a command that will
|
||||
perform a write operation. This is the basic configuration needed to work with replication:
|
||||
|
||||
```php
|
||||
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||
$options = ['replication' => 'predis'];
|
||||
// Parameters require one master node specifically marked with `alias=master`.
|
||||
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2?alias=slave-01'];
|
||||
$options = ['replication' => true];
|
||||
|
||||
$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,
|
||||
@@ -277,13 +169,13 @@ when certain Lua scripts do not perform write operations it is possible to provi
|
||||
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'];
|
||||
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2?alias=slave-01'];
|
||||
$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);
|
||||
return new Predis\Connection\Aggregate\MasterSlaveReplication($strategy);
|
||||
}];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
@@ -291,8 +183,29 @@ $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.
|
||||
The `examples` directory contains two complete scripts showing how replication can be configured for
|
||||
[basic](examples/replication_simple.php) and [complex](examples/replication_complex.php) scenarios.
|
||||
|
||||
|
||||
#### Cluster ####
|
||||
|
||||
Simply passing an array of connection parameters to the client constructor configures Predis to work
|
||||
in cluster mode using client-side sharding. If you, on the other hand, want to leverage Redis >= 3.0
|
||||
nodes coordinated by redis-cluster, then the client must be initialized like this:
|
||||
|
||||
```php
|
||||
$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2'];
|
||||
$options = ['cluster' => 'redis'];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
```
|
||||
|
||||
When using redis-cluster it is not necessary to pass all of the nodes that compose your cluster, you
|
||||
can specify only a few nodes and the client will automatically fetch the full and updated slots map
|
||||
directly from Redis by contacting one of the servers.
|
||||
|
||||
__NOTE__: our support for redis-cluster does not currently consider master / slave replication but
|
||||
this feature will be added in a future release of the library.
|
||||
|
||||
|
||||
### Command pipelines ###
|
||||
@@ -336,32 +249,13 @@ This abstraction can perform check-and-set operations thanks to `WATCH` and `UNW
|
||||
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:
|
||||
command classes to define or override commands in the default server profiles used by the client:
|
||||
|
||||
```php
|
||||
// Define a new command by extending Predis\Command\Command:
|
||||
@@ -373,12 +267,9 @@ class BrandNewRedisCommand extends Predis\Command\Command
|
||||
}
|
||||
}
|
||||
|
||||
// Inject your command in the current command factory:
|
||||
$client = new Predis\Client($parameters, [
|
||||
'commands' => [
|
||||
'newcmd' => 'BrandNewRedisCommand',
|
||||
],
|
||||
]);
|
||||
// Inject your command in the current profile:
|
||||
$client = new Predis\Client();
|
||||
$client->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand');
|
||||
|
||||
$response = $client->newcmd();
|
||||
```
|
||||
@@ -395,9 +286,9 @@ $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),
|
||||
[`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha) directly,
|
||||
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
|
||||
Script commands can be registered in the server profile 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)
|
||||
@@ -423,12 +314,9 @@ LUA;
|
||||
}
|
||||
}
|
||||
|
||||
// Inject the script command in the current command factory:
|
||||
$client = new Predis\Client($parameters, [
|
||||
'commands' => [
|
||||
'lpushrand' => 'ListPushRandomValue',
|
||||
],
|
||||
]);
|
||||
// Inject the script command in the current profile:
|
||||
$client = new Predis\Client();
|
||||
$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue');
|
||||
|
||||
$response = $client->lpushrand('random_values', $seed = mt_rand());
|
||||
```
|
||||
@@ -436,13 +324,17 @@ $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.
|
||||
Predis can use different connection backends to connect to Redis. Two of them leverage a third party
|
||||
extension such as [phpiredis](https://github.com/nrk/phpiredis) resulting in major performance gains
|
||||
especially when dealing with big multibulk responses. While one is based on PHP streams, the other
|
||||
is based on socket resources provided by `ext-socket`. Both support TCP/IP and UNIX domain sockets:
|
||||
|
||||
```php
|
||||
$client = new Predis\Client('tcp://127.0.0.1', [
|
||||
'connections' => 'relay',
|
||||
'connections' => [
|
||||
'tcp' => 'Predis\Connection\PhpiredisStreamConnection', // PHP stream resources
|
||||
'unix' => 'Predis\Connection\PhpiredisSocketConnection', // ext-socket resources
|
||||
],
|
||||
]);
|
||||
```
|
||||
|
||||
@@ -465,156 +357,16 @@ $client = new Predis\Client('tcp://127.0.0.1', [
|
||||
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.
|
||||
bug fixes, or just bug reports. We only ask you to adhere to a [basic set of rules](CONTRIBUTING.md)
|
||||
before submitting your changes or filing bugs on the issue tracker to make it easier for everyone to
|
||||
stay consistent while working on the project.
|
||||
|
||||
|
||||
### Test suite ###
|
||||
@@ -622,27 +374,36 @@ bug fixes, or just bug reports. We only ask you to adhere to issue and pull requ
|
||||
__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 has a comprehensive test suite covering every aspect of the library. This test suite performs
|
||||
integration tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct
|
||||
behavior of the implementation of each command and automatically skips commands not defined in the
|
||||
specified Redis profile. If you do not have Redis up and running, integration tests can be disabled.
|
||||
By default the test suite is configured to execute integration tests using the profile for Redis 2.8
|
||||
(which is the current stable version of Redis) but can optionally target a Redis instance built from
|
||||
the `unstable` branch by modifying `phpunit.xml` and setting `REDIS_SERVER_VERSION` to `dev` so that
|
||||
the development server profile will be used. You can refer to [the tests README](tests/README.md)
|
||||
for more detailed information about testing Predis.
|
||||
|
||||
Predis uses Travis CI for continuous integration and the history for past and current builds can be
|
||||
found [on its project page](http://travis-ci.org/nrk/predis).
|
||||
|
||||
|
||||
## Other ##
|
||||
|
||||
|
||||
### Project related links ###
|
||||
|
||||
- [Source code](https://github.com/nrk/predis)
|
||||
- [Wiki](https://wiki.github.com/nrk/predis)
|
||||
- [Issue tracker](https://github.com/nrk/predis/issues)
|
||||
- [PEAR channel](http://pear.nrk.io)
|
||||
|
||||
|
||||
### Author ###
|
||||
|
||||
- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN))
|
||||
|
||||
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
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
+25
-24
@@ -4,6 +4,8 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
@@ -13,7 +15,7 @@
|
||||
// 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):
|
||||
// Predis\Command\StringSet class):
|
||||
//
|
||||
// $ ./bin/generate-command-test --class=StringSet
|
||||
//
|
||||
@@ -43,11 +45,6 @@ class CommandTestCaseGenerator
|
||||
if (!isset($options['class'])) {
|
||||
throw new RuntimeException("Missing 'class' option.");
|
||||
}
|
||||
|
||||
if (!isset($options['realm'])) {
|
||||
throw new RuntimeException("Missing 'realm' option.");
|
||||
}
|
||||
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
@@ -95,12 +92,8 @@ class CommandTestCaseGenerator
|
||||
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";
|
||||
$options['fqn'] = "Predis\\Command\\{$options['class']}";
|
||||
$options['path'] = "Command/{$options['class']}.php";
|
||||
|
||||
$source = __DIR__.'/../src/'.$options['path'];
|
||||
if (!file_exists($source)) {
|
||||
@@ -108,7 +101,7 @@ class CommandTestCaseGenerator
|
||||
}
|
||||
|
||||
if (!isset($options['output'])) {
|
||||
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', '_Test.php', $options['path']));
|
||||
$options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', 'Test.php', $options['path']));
|
||||
}
|
||||
|
||||
return new self($options);
|
||||
@@ -116,11 +109,18 @@ class CommandTestCaseGenerator
|
||||
|
||||
protected function getTestRealm()
|
||||
{
|
||||
if (empty($this->options['realm'])) {
|
||||
throw new RuntimeException('Invalid value for realm has been specified (empty).');
|
||||
if (isset($this->options['realm'])) {
|
||||
if (!$this->options['realm']) {
|
||||
throw new RuntimeException('Invalid value for realm has been sepcified (empty).');
|
||||
}
|
||||
return $this->options['realm'];
|
||||
}
|
||||
|
||||
return $this->options['realm'];
|
||||
$fqnParts = explode('\\', $this->options['fqn']);
|
||||
$class = array_pop($fqnParts);
|
||||
list($realm,) = preg_split('/([[:upper:]][[:lower:]]+)/', $class, 2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
return strtolower($realm);
|
||||
}
|
||||
|
||||
public function generate()
|
||||
@@ -130,7 +130,6 @@ class CommandTestCaseGenerator
|
||||
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.");
|
||||
}
|
||||
@@ -168,11 +167,13 @@ class CommandTestCaseGenerator
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
@@ -183,7 +184,7 @@ class $class extends PredisCommandTestCase
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
protected function getExpectedCommand()
|
||||
{
|
||||
return '$fqn';
|
||||
}
|
||||
@@ -191,7 +192,7 @@ class $class extends PredisCommandTestCase
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
protected function getExpectedId()
|
||||
{
|
||||
return '$id';
|
||||
}
|
||||
@@ -199,7 +200,7 @@ class $class extends PredisCommandTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testFilterArguments(): void
|
||||
public function testFilterArguments()
|
||||
{
|
||||
\$this->markTestIncomplete('This test has not been implemented yet.');
|
||||
|
||||
@@ -215,7 +216,7 @@ class $class extends PredisCommandTestCase
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testParseResponse(): void
|
||||
public function testParseResponse()
|
||||
{
|
||||
\$this->markTestIncomplete('This test has not been implemented yet.');
|
||||
|
||||
@@ -235,7 +236,7 @@ PHP;
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeys(): void
|
||||
public function testPrefixKeys()
|
||||
{
|
||||
\$this->markTestIncomplete('This test has not been implemented yet.');
|
||||
|
||||
@@ -251,7 +252,7 @@ PHP;
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeysIgnoredOnEmptyArguments(): void
|
||||
public function testPrefixKeysIgnoredOnEmptyArguments()
|
||||
{
|
||||
\$command = \$this->getCommand();
|
||||
\$command->prefixKeys('prefix:');
|
||||
|
||||
Executable
+233
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
// In order to be able to execute this script to create a PEAR package of Predis
|
||||
// the `pear` binary must be available and executable in your $PATH.
|
||||
// The parts used to parse author and version strings are taken from Onion (used
|
||||
// by this library in the past) just to keep on relying on the package.ini file
|
||||
// to simplify things. We might consider to switch to using the PEAR classes
|
||||
// directly in the future.
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
function executeWithBackup($file, $callback)
|
||||
{
|
||||
$exception = null;
|
||||
$backup = "$file.backup";
|
||||
|
||||
copy($file, $backup);
|
||||
|
||||
try {
|
||||
call_user_func($callback, $file);
|
||||
} catch (Exception $exception) {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
unlink($file);
|
||||
rename($backup, $file);
|
||||
|
||||
if ($exception) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
function parseAuthor($string)
|
||||
{
|
||||
$author = array();
|
||||
|
||||
if (preg_match('/^\s*(.+?)\s*(?:"(\S+)"\s*)?<(\S+)>\s*$/x', $string , $regs)) {
|
||||
if (count($regs) == 4) {
|
||||
list($_,$name,$user,$email) = $regs;
|
||||
$author['name'] = $name;
|
||||
$author['user'] = $user;
|
||||
$author['email'] = $email;
|
||||
} elseif (count($regs) == 3) {
|
||||
list($_,$name,$email) = $regs;
|
||||
$author['name'] = $name;
|
||||
$author['email'] = $email;
|
||||
}
|
||||
} else {
|
||||
$author['name'] = $string;
|
||||
}
|
||||
|
||||
return $author;
|
||||
}
|
||||
|
||||
function parseVersion($string)
|
||||
{
|
||||
$version_pattern = '([0-9.]+)';
|
||||
|
||||
if (preg_match("/^\s*$version_pattern\s*\$/x", $string, $regs)) {
|
||||
return array('min' => $regs[1] ?: '0.0.0');
|
||||
} elseif (preg_match("/^\s*[>=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
|
||||
return array('min' => $regs[1] ?: '0.0.0');
|
||||
} elseif (preg_match("/^\s*[<=]+\s*$version_pattern\s*\$/x", $string, $regs)) {
|
||||
return array('max' => $regs[1]);
|
||||
} elseif (preg_match("/^\s*$version_pattern\s*<=>\s*$version_pattern\s*\$/x", $string, $regs)) {
|
||||
return array(
|
||||
'min' => $regs[1] ?: '0.0.0',
|
||||
'max' => $regs[2],
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function addRolePath($pkg, $path, $role)
|
||||
{
|
||||
if (is_dir($path)) {
|
||||
$dirRoot = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$dirTree = new RecursiveIteratorIterator($dirRoot, RecursiveIteratorIterator::CHILD_FIRST);
|
||||
|
||||
foreach ($dirTree as $fileinfo) {
|
||||
if ($fileinfo->isFile()) {
|
||||
addPackageFile($pkg, $fileinfo, $role, $path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach (glob($path) as $filename) {
|
||||
addPackageFile($pkg, new SplFileInfo($filename), $role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addPackageFile($pkg, $fileinfo, $role, $baseDir = '')
|
||||
{
|
||||
$fileNode = $pkg->contents->dir->addChild('file');
|
||||
$fileNode->addAttribute('name', $filepath = $fileinfo->getPathname());
|
||||
$fileNode->addAttribute('role', $role);
|
||||
$fileNode->addAttribute('md5sum', md5_file($filepath));
|
||||
|
||||
$installNode = $pkg->phprelease->filelist->addChild('install');
|
||||
$installNode->addAttribute('name', $filepath);
|
||||
$installNode->addAttribute('as', !$baseDir ? basename($filepath) : substr($filepath, strlen($baseDir) + 1));
|
||||
}
|
||||
|
||||
function generatePackageXml($packageINI)
|
||||
{
|
||||
$XML = <<<XML
|
||||
<?xml version="1.0"?>
|
||||
<package packagerversion="1.4.10" version="2.0"
|
||||
xmlns="http://pear.php.net/dtd/package-2.0"
|
||||
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
|
||||
http://pear.php.net/dtd/tasks-1.0.xsd
|
||||
http://pear.php.net/dtd/package-2.0
|
||||
http://pear.php.net/dtd/package-2.0.xsd" />
|
||||
XML;
|
||||
|
||||
$cfg = parse_ini_file($packageINI, true);
|
||||
$pkg = new SimpleXMLElement($XML);
|
||||
|
||||
$pkg->name = $cfg['package']['name'];
|
||||
$pkg->channel = $cfg['package']['channel'];
|
||||
$pkg->summary = $cfg['package']['desc'];
|
||||
$pkg->description = $cfg['package']['desc'];
|
||||
|
||||
$author = parseAuthor($cfg['package']['author']);
|
||||
$pkg->addChild('lead');
|
||||
$pkg->lead->name = $author['name'];
|
||||
$pkg->lead->user = $author['user'];
|
||||
$pkg->lead->email = $author['email'];
|
||||
$pkg->lead->active = 'yes';
|
||||
|
||||
$datetime = new DateTime('now');
|
||||
$pkg->date = $datetime->format('Y-m-d');
|
||||
$pkg->time = $datetime->format('H:i:s');
|
||||
|
||||
$pkg->addChild('version');
|
||||
$pkg->version->release = $cfg['package']['version'];
|
||||
$pkg->version->api = $cfg['package']['version'];
|
||||
|
||||
$pkg->addChild('stability');
|
||||
$pkg->stability->release = $cfg['package']['stability'];
|
||||
$pkg->stability->api = $cfg['package']['stability'];
|
||||
|
||||
$pkg->license = $cfg['package']['license'];
|
||||
$pkg->notes = '-';
|
||||
|
||||
$pkg->addChild('contents')->addChild('dir')->addAttribute('name', '/');
|
||||
|
||||
$pkg->addChild('dependencies')->addChild('required');
|
||||
foreach ($cfg['require'] as $required => $version) {
|
||||
$version = parseVersion($version);
|
||||
$pkg->dependencies->required->addChild($required);
|
||||
|
||||
if (isset($version['min'])) {
|
||||
$pkg->dependencies->required->$required->min = $version['min'];
|
||||
}
|
||||
if (isset($version['max'])) {
|
||||
$pkg->dependencies->required->$required->min = $version['max'];
|
||||
}
|
||||
}
|
||||
|
||||
$pkg->addChild('phprelease')->addChild('filelist');
|
||||
|
||||
$pathToRole = array(
|
||||
'doc' => 'doc', 'docs' => 'doc', 'examples' => 'doc',
|
||||
'lib' => 'php', 'src' => 'php',
|
||||
'test' => 'test', 'tests' => 'test',
|
||||
);
|
||||
|
||||
foreach (array_merge($pathToRole, $cfg['roles'] ?: array()) as $path => $role) {
|
||||
addRolePath($pkg, $path, $role);
|
||||
}
|
||||
|
||||
return $pkg;
|
||||
}
|
||||
|
||||
function rewritePackageInstallAs($pkg)
|
||||
{
|
||||
foreach ($pkg->phprelease->filelist->install as $file) {
|
||||
if (preg_match('/^src\//', $file['name'])) {
|
||||
$file['as'] = "Predis/{$file['as']}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function savePackageXml($xml)
|
||||
{
|
||||
$dom = new DOMDocument("1.0");
|
||||
$dom->preserveWhiteSpace = false;
|
||||
$dom->formatOutput = true;
|
||||
$dom->loadXML($xml->asXML());
|
||||
|
||||
file_put_contents('package.xml', $dom->saveXML());
|
||||
}
|
||||
|
||||
function buildPackage()
|
||||
{
|
||||
passthru('pear -q package && rm package.xml');
|
||||
}
|
||||
|
||||
function modifyPhpunitXml($file)
|
||||
{
|
||||
$cfg = new SimpleXMLElement($file, null, true);
|
||||
|
||||
$cfg[0]['bootstrap'] = str_replace('tests/', '', $cfg[0]['bootstrap']);
|
||||
$cfg->testsuites->testsuite->directory = str_replace('tests/', '', $cfg->testsuites->testsuite->directory);
|
||||
|
||||
$cfg->saveXml($file);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
executeWithBackup(__DIR__.'/../phpunit.xml.dist', function ($file) {
|
||||
modifyPhpunitXml($file);
|
||||
|
||||
$pkg = generatePackageXml('package.ini');
|
||||
rewritePackageInstallAs($pkg);
|
||||
savePackageXml($pkg);
|
||||
|
||||
buildPackage();
|
||||
});
|
||||
Executable
+71
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
// In order to be able to execute this script to create a Phar archive of Predis,
|
||||
// the Phar module must be loaded and the "phar.readonly" directive php.ini must
|
||||
// be set to "off". You can change the values in the $options array to customize
|
||||
// the creation of the Phar archive to better suit your needs.
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
$options = array(
|
||||
'name' => 'predis',
|
||||
'project_path' => __DIR__ . '/../src',
|
||||
'compression' => Phar::NONE,
|
||||
'append_version' => true,
|
||||
);
|
||||
|
||||
function getPharFilename($options)
|
||||
{
|
||||
$filename = $options['name'];
|
||||
|
||||
// NOTE: do not consider "append_version" with Phar compression do to a bug in
|
||||
// Phar::compress() when renaming phar archives containing dots in their name.
|
||||
if ($options['append_version'] && $options['compression'] === Phar::NONE) {
|
||||
$versionFile = @fopen(__DIR__ . '/../VERSION', 'r');
|
||||
|
||||
if ($versionFile === false) {
|
||||
throw new Exception("Could not locate the VERSION file.");
|
||||
}
|
||||
|
||||
$version = trim(fgets($versionFile));
|
||||
fclose($versionFile);
|
||||
$filename .= "_$version";
|
||||
}
|
||||
|
||||
return "$filename.phar";
|
||||
}
|
||||
|
||||
function getPharStub($options)
|
||||
{
|
||||
return <<<EOSTUB
|
||||
<?php
|
||||
Phar::mapPhar('predis.phar');
|
||||
spl_autoload_register(function (\$class) {
|
||||
if (strpos(\$class, 'Predis\\\\') === 0) {
|
||||
\$file = 'phar://predis.phar/'.strtr(substr(\$class, 7), '\\\', '/').'.php';
|
||||
if (file_exists(\$file)) {
|
||||
require \$file;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
__HALT_COMPILER();
|
||||
EOSTUB;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
$phar = new Phar(getPharFilename($options));
|
||||
$phar->compress($options['compression']);
|
||||
$phar->setStub(getPharStub($options));
|
||||
$phar->buildFromDirectory($options['project_path']);
|
||||
Executable
+662
@@ -0,0 +1,662 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
// This script can be used to automatically glue all the .php files of Predis
|
||||
// into a single monolithic script file that can be used without an autoloader,
|
||||
// just like the other previous versions of the library.
|
||||
//
|
||||
// Much of its complexity is due to the fact that we cannot simply join PHP
|
||||
// files, but namespaces and classes definitions must follow a precise order
|
||||
// when dealing with subclassing and inheritance.
|
||||
//
|
||||
// The current implementation is pretty naïve, but it should do for now.
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
class CommandLine
|
||||
{
|
||||
public static function getOptions()
|
||||
{
|
||||
$parameters = array(
|
||||
's:' => 'source:',
|
||||
'o:' => 'output:',
|
||||
'e:' => 'exclude:',
|
||||
'E:' => 'exclude-classes:',
|
||||
);
|
||||
|
||||
$getops = getopt(implode(array_keys($parameters)), $parameters);
|
||||
|
||||
$options = array(
|
||||
'source' => __DIR__ . "/../src",
|
||||
'output' => PredisFile::NS_ROOT . '.php',
|
||||
'exclude' => array(),
|
||||
);
|
||||
|
||||
foreach ($getops as $option => $value) {
|
||||
switch ($option) {
|
||||
case 's':
|
||||
case 'source':
|
||||
$options['source'] = $value;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
case 'output':
|
||||
$options['output'] = $value;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
case 'exclude-classes':
|
||||
$options['exclude'] = @file($value, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: $value;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'exclude':
|
||||
$options['exclude'] = is_array($value) ? $value : array($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
|
||||
class PredisFile
|
||||
{
|
||||
const NS_ROOT = 'Predis';
|
||||
|
||||
private $namespaces;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->namespaces = array();
|
||||
}
|
||||
|
||||
public static function from($libraryPath, array $exclude = array())
|
||||
{
|
||||
$predisFile = new PredisFile();
|
||||
$libIterator = new RecursiveDirectoryIterator($libraryPath);
|
||||
|
||||
foreach (new RecursiveIteratorIterator($libIterator) as $classFile)
|
||||
{
|
||||
if (!$classFile->isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$namespace = self::NS_ROOT.strtr(str_replace($libraryPath, '', $classFile->getPath()), '/', '\\');
|
||||
|
||||
if (in_array(sprintf('%s\\%s', $namespace, $classFile->getBasename('.php')), $exclude)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phpNamespace = $predisFile->getNamespace($namespace);
|
||||
|
||||
if ($phpNamespace === false) {
|
||||
$phpNamespace = new PhpNamespace($namespace);
|
||||
$predisFile->addNamespace($phpNamespace);
|
||||
}
|
||||
|
||||
$phpClass = new PhpClass($phpNamespace, $classFile);
|
||||
}
|
||||
|
||||
return $predisFile;
|
||||
}
|
||||
|
||||
public function addNamespace(PhpNamespace $namespace)
|
||||
{
|
||||
if (isset($this->namespaces[(string)$namespace])) {
|
||||
throw new InvalidArgumentException("Duplicated namespace");
|
||||
}
|
||||
$this->namespaces[(string)$namespace] = $namespace;
|
||||
}
|
||||
|
||||
public function getNamespaces()
|
||||
{
|
||||
return $this->namespaces;
|
||||
}
|
||||
|
||||
public function getNamespace($namespace)
|
||||
{
|
||||
if (!isset($this->namespaces[$namespace])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->namespaces[$namespace];
|
||||
}
|
||||
|
||||
public function getClassByFQN($classFqn)
|
||||
{
|
||||
if (($nsLastPos = strrpos($classFqn, '\\')) !== false) {
|
||||
$namespace = $this->getNamespace(substr($classFqn, 0, $nsLastPos));
|
||||
if ($namespace === false) {
|
||||
return null;
|
||||
}
|
||||
$className = substr($classFqn, $nsLastPos + 1);
|
||||
|
||||
return $namespace->getClass($className);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function calculateDependencyScores(&$classes, $fqn)
|
||||
{
|
||||
if (!isset($classes[$fqn])) {
|
||||
$classes[$fqn] = 0;
|
||||
}
|
||||
|
||||
$classes[$fqn] += 1;
|
||||
|
||||
if (($phpClass = $this->getClassByFQN($fqn)) === null) {
|
||||
throw new RuntimeException(
|
||||
"Cannot found the class $fqn which is required by other subclasses. Are you missing a file?"
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($phpClass->getDependencies() as $fqn) {
|
||||
$this->calculateDependencyScores($classes, $fqn);
|
||||
}
|
||||
}
|
||||
|
||||
private function getDependencyScores()
|
||||
{
|
||||
$classes = array();
|
||||
|
||||
foreach ($this->getNamespaces() as $phpNamespace) {
|
||||
foreach ($phpNamespace->getClasses() as $phpClass) {
|
||||
$this->calculateDependencyScores($classes, $phpClass->getFQN());
|
||||
}
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
private function getOrderedNamespaces($dependencyScores)
|
||||
{
|
||||
$namespaces = array_fill_keys(array_unique(
|
||||
array_map(
|
||||
function ($fqn) { return PhpNamespace::extractName($fqn); },
|
||||
array_keys($dependencyScores)
|
||||
)
|
||||
), 0);
|
||||
|
||||
foreach ($dependencyScores as $classFqn => $score) {
|
||||
$namespaces[PhpNamespace::extractName($classFqn)] += $score;
|
||||
}
|
||||
|
||||
arsort($namespaces);
|
||||
|
||||
return array_keys($namespaces);
|
||||
}
|
||||
|
||||
private function getOrderedClasses(PhpNamespace $phpNamespace, $classes)
|
||||
{
|
||||
$nsClassesFQNs = array_map(function ($cl) { return $cl->getFQN(); }, $phpNamespace->getClasses());
|
||||
$nsOrderedClasses = array();
|
||||
|
||||
foreach ($nsClassesFQNs as $nsClassFQN) {
|
||||
$nsOrderedClasses[$nsClassFQN] = $classes[$nsClassFQN];
|
||||
}
|
||||
|
||||
arsort($nsOrderedClasses);
|
||||
|
||||
return array_keys($nsOrderedClasses);
|
||||
}
|
||||
|
||||
public function getPhpCode()
|
||||
{
|
||||
$buffer = array("<?php\n\n", PhpClass::LICENSE_HEADER, "\n\n");
|
||||
$classes = $this->getDependencyScores();
|
||||
$namespaces = $this->getOrderedNamespaces($classes);
|
||||
|
||||
foreach ($namespaces as $namespace) {
|
||||
$phpNamespace = $this->getNamespace($namespace);
|
||||
|
||||
// generate namespace directive
|
||||
$buffer[] = $phpNamespace->getPhpCode();
|
||||
$buffer[] = "\n";
|
||||
|
||||
// generate use directives
|
||||
$useDirectives = $phpNamespace->getUseDirectives();
|
||||
if (count($useDirectives) > 0) {
|
||||
$buffer[] = $useDirectives->getPhpCode();
|
||||
$buffer[] = "\n";
|
||||
}
|
||||
|
||||
// generate classes bodies
|
||||
$nsClasses = $this->getOrderedClasses($phpNamespace, $classes);
|
||||
foreach ($nsClasses as $classFQN) {
|
||||
$buffer[] = $this->getClassByFQN($classFQN)->getPhpCode();
|
||||
$buffer[] = "\n\n";
|
||||
}
|
||||
|
||||
$buffer[] = "/* " . str_repeat("-", 75) . " */";
|
||||
$buffer[] = "\n\n";
|
||||
}
|
||||
|
||||
return implode($buffer);
|
||||
}
|
||||
|
||||
public function saveTo($outputFile)
|
||||
{
|
||||
// TODO: add more sanity checks
|
||||
if ($outputFile === null || $outputFile === '') {
|
||||
throw new InvalidArgumentException('You must specify a valid output file');
|
||||
}
|
||||
file_put_contents($outputFile, $this->getPhpCode());
|
||||
}
|
||||
}
|
||||
|
||||
class PhpNamespace implements IteratorAggregate
|
||||
{
|
||||
private $namespace;
|
||||
private $classes;
|
||||
|
||||
public function __construct($namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->classes = array();
|
||||
$this->useDirectives = new PhpUseDirectives($this);
|
||||
}
|
||||
|
||||
public static function extractName($fqn)
|
||||
{
|
||||
$nsSepLast = strrpos($fqn, '\\');
|
||||
if ($nsSepLast === false) {
|
||||
return $fqn;
|
||||
}
|
||||
$ns = substr($fqn, 0, $nsSepLast);
|
||||
|
||||
return $ns !== '' ? $ns : null;
|
||||
}
|
||||
|
||||
public function addClass(PhpClass $class)
|
||||
{
|
||||
$this->classes[$class->getName()] = $class;
|
||||
}
|
||||
|
||||
public function getClass($className)
|
||||
{
|
||||
if (isset($this->classes[$className])) {
|
||||
return $this->classes[$className];
|
||||
}
|
||||
}
|
||||
|
||||
public function getClasses()
|
||||
{
|
||||
return array_values($this->classes);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->getClasses());
|
||||
}
|
||||
|
||||
public function getUseDirectives()
|
||||
{
|
||||
return $this->useDirectives;
|
||||
}
|
||||
|
||||
public function getPhpCode()
|
||||
{
|
||||
return "namespace $this->namespace;\n";
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
}
|
||||
|
||||
class PhpUseDirectives implements Countable, IteratorAggregate
|
||||
{
|
||||
private $use;
|
||||
private $aliases;
|
||||
private $reverseAliases;
|
||||
private $namespace;
|
||||
|
||||
public function __construct(PhpNamespace $namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->use = array();
|
||||
$this->aliases = array();
|
||||
$this->reverseAliases = array();
|
||||
}
|
||||
|
||||
public function add($use, $as = null)
|
||||
{
|
||||
if (in_array($use, $this->use)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rename = null;
|
||||
$this->use[] = $use;
|
||||
$aliasedClassName = $as ?: PhpClass::extractName($use);
|
||||
|
||||
if (isset($this->aliases[$aliasedClassName])) {
|
||||
$parentNs = $this->getParentNamespace();
|
||||
|
||||
if ($parentNs && false !== $pos = strrpos($parentNs, '\\')) {
|
||||
$parentNs = substr($parentNs, $pos);
|
||||
}
|
||||
|
||||
$newAlias = "{$parentNs}_{$aliasedClassName}";
|
||||
$rename = (object) array(
|
||||
'namespace' => $this->namespace,
|
||||
'from' => $aliasedClassName,
|
||||
'to' => $newAlias,
|
||||
);
|
||||
|
||||
$this->aliases[$newAlias] = $use;
|
||||
$as = $newAlias;
|
||||
} else {
|
||||
$this->aliases[$aliasedClassName] = $use;
|
||||
}
|
||||
|
||||
if ($as !== null) {
|
||||
$this->reverseAliases[$use] = $as;
|
||||
}
|
||||
|
||||
return $rename;
|
||||
}
|
||||
|
||||
public function getList()
|
||||
{
|
||||
return $this->use;
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->getList());
|
||||
}
|
||||
|
||||
public function getPhpCode()
|
||||
{
|
||||
$reverseAliases = $this->reverseAliases;
|
||||
|
||||
$reducer = function ($str, $use) use ($reverseAliases) {
|
||||
if (isset($reverseAliases[$use])) {
|
||||
return $str .= "use $use as {$reverseAliases[$use]};\n";
|
||||
} else {
|
||||
return $str .= "use $use;\n";
|
||||
}
|
||||
};
|
||||
|
||||
return array_reduce($this->getList(), $reducer, '');
|
||||
}
|
||||
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
public function getParentNamespace()
|
||||
{
|
||||
if (false !== $pos = strrpos($this->namespace, '\\')) {
|
||||
return substr($this->namespace, 0, $pos);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getFQN($className)
|
||||
{
|
||||
if (($nsSepFirst = strpos($className, '\\')) === false) {
|
||||
if (isset($this->aliases[$className])) {
|
||||
return $this->aliases[$className];
|
||||
}
|
||||
|
||||
return (string)$this->getNamespace() . "\\$className";
|
||||
}
|
||||
|
||||
if ($nsSepFirst != 0) {
|
||||
throw new InvalidArgumentException("Partially qualified names are not supported");
|
||||
}
|
||||
|
||||
return $className;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->use);
|
||||
}
|
||||
}
|
||||
|
||||
class PhpClass
|
||||
{
|
||||
const LICENSE_HEADER = <<<LICENSE
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
LICENSE;
|
||||
|
||||
private $namespace;
|
||||
private $file;
|
||||
private $body;
|
||||
private $implements;
|
||||
private $extends;
|
||||
private $name;
|
||||
|
||||
public function __construct(PhpNamespace $namespace, SplFileInfo $classFile)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
$this->file = $classFile;
|
||||
$this->implements = array();
|
||||
$this->extends = array();
|
||||
|
||||
$this->extractData();
|
||||
$namespace->addClass($this);
|
||||
}
|
||||
|
||||
public static function extractName($fqn)
|
||||
{
|
||||
$nsSepLast = strrpos($fqn, '\\');
|
||||
if ($nsSepLast === false) {
|
||||
return $fqn;
|
||||
}
|
||||
|
||||
return substr($fqn, $nsSepLast + 1);
|
||||
}
|
||||
|
||||
private function extractData()
|
||||
{
|
||||
$renames = array();
|
||||
$useDirectives = $this->getNamespace()->getUseDirectives();
|
||||
|
||||
$useExtractor = function ($m) use ($useDirectives, &$renames) {
|
||||
array_shift($m);
|
||||
|
||||
if (isset($m[1])) {
|
||||
$m[1] = str_replace(" as ", '', $m[1]);
|
||||
}
|
||||
|
||||
if ($rename = call_user_func_array(array($useDirectives, 'add'), $m)) {
|
||||
$renames[] = $rename;
|
||||
}
|
||||
};
|
||||
|
||||
$classBuffer = stream_get_contents(fopen($this->getFile()->getPathname(), 'r'));
|
||||
|
||||
$classBuffer = str_replace(self::LICENSE_HEADER, '', $classBuffer);
|
||||
|
||||
$classBuffer = preg_replace('/<\?php\s?\\n\s?/', '', $classBuffer);
|
||||
$classBuffer = preg_replace('/\s?\?>\n?/ms', '', $classBuffer);
|
||||
$classBuffer = preg_replace('/namespace\s+[\w\d_\\\\]+;\s?/', '', $classBuffer);
|
||||
$classBuffer = preg_replace_callback('/use\s+([\w\d_\\\\]+)(\s+as\s+.*)?;\s?\n?/', $useExtractor, $classBuffer);
|
||||
|
||||
foreach ($renames as $rename) {
|
||||
$classBuffer = str_replace($rename->from, $rename->to, $classBuffer);
|
||||
}
|
||||
|
||||
$this->body = trim($classBuffer);
|
||||
|
||||
$this->extractHierarchy();
|
||||
}
|
||||
|
||||
private function extractHierarchy()
|
||||
{
|
||||
$implements = array();
|
||||
$extends = array();
|
||||
|
||||
$extractor = function ($iterator, $callback) {
|
||||
$className = '';
|
||||
$iterator->seek($iterator->key() + 1);
|
||||
|
||||
while ($iterator->valid()) {
|
||||
$token = $iterator->current();
|
||||
|
||||
if (is_string($token)) {
|
||||
if (preg_match('/\s?,\s?/', $token)) {
|
||||
$callback(trim($className));
|
||||
$className = '';
|
||||
} else if ($token == '{') {
|
||||
$callback(trim($className));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($token[0]) {
|
||||
case T_NS_SEPARATOR:
|
||||
$className .= '\\';
|
||||
break;
|
||||
|
||||
case T_STRING:
|
||||
$className .= $token[1];
|
||||
break;
|
||||
|
||||
case T_IMPLEMENTS:
|
||||
case T_EXTENDS:
|
||||
$callback(trim($className));
|
||||
$iterator->seek($iterator->key() - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
$iterator->next();
|
||||
}
|
||||
};
|
||||
|
||||
$tokens = token_get_all("<?php\n" . trim($this->getPhpCode()));
|
||||
$iterator = new ArrayIterator($tokens);
|
||||
|
||||
while ($iterator->valid()) {
|
||||
$token = $iterator->current();
|
||||
if (is_string($token)) {
|
||||
$iterator->next();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($token[0]) {
|
||||
case T_CLASS:
|
||||
case T_INTERFACE:
|
||||
$iterator->seek($iterator->key() + 2);
|
||||
$tk = $iterator->current();
|
||||
$this->name = $tk[1];
|
||||
break;
|
||||
|
||||
case T_IMPLEMENTS:
|
||||
$extractor($iterator, function ($fqn) use (&$implements) {
|
||||
$implements[] = $fqn;
|
||||
});
|
||||
break;
|
||||
|
||||
case T_EXTENDS:
|
||||
$extractor($iterator, function ($fqn) use (&$extends) {
|
||||
$extends[] = $fqn;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
$iterator->next();
|
||||
}
|
||||
|
||||
$this->implements = $this->guessFQN($implements);
|
||||
$this->extends = $this->guessFQN($extends);
|
||||
}
|
||||
|
||||
public function guessFQN($classes)
|
||||
{
|
||||
$useDirectives = $this->getNamespace()->getUseDirectives();
|
||||
return array_map(array($useDirectives, 'getFQN'), $classes);
|
||||
}
|
||||
|
||||
public function getImplementedInterfaces($all = false)
|
||||
{
|
||||
if ($all) {
|
||||
return $this->implements;
|
||||
}
|
||||
|
||||
return array_filter(
|
||||
$this->implements,
|
||||
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
|
||||
);
|
||||
}
|
||||
|
||||
public function getExtendedClasses($all = false)
|
||||
{
|
||||
if ($all) {
|
||||
return $this->extemds;
|
||||
}
|
||||
|
||||
return array_filter(
|
||||
$this->extends,
|
||||
function ($cn) { return strpos($cn, 'Predis\\') === 0; }
|
||||
);
|
||||
}
|
||||
|
||||
public function getDependencies($all = false)
|
||||
{
|
||||
return array_merge(
|
||||
$this->getImplementedInterfaces($all),
|
||||
$this->getExtendedClasses($all)
|
||||
);
|
||||
}
|
||||
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
public function getFile()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getFQN()
|
||||
{
|
||||
return (string)$this->getNamespace() . '\\' . $this->name;
|
||||
}
|
||||
|
||||
public function getPhpCode()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return "class " . $this->getName() . '{ ... }';
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
$options = CommandLine::getOptions();
|
||||
$predisFile = PredisFile::from($options['source'], $options['exclude']);
|
||||
$predisFile->saveTo($options['output']);
|
||||
+12
-34
@@ -1,53 +1,31 @@
|
||||
{
|
||||
"name": "predis/predis",
|
||||
"type": "library",
|
||||
"description": "A flexible and feature-complete Redis/Valkey client for PHP.",
|
||||
"description": "Flexible and feature-complete PHP client library for Redis",
|
||||
"keywords": ["nosql", "redis", "predis"],
|
||||
"homepage": "http://github.com/predis/predis",
|
||||
"homepage": "http://github.com/nrk/predis",
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
"issues": "https://github.com/predis/predis/issues"
|
||||
"issues": "https://github.com/nrk/predis/issues"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Till Krüss",
|
||||
"homepage": "https://till.im",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tillkruss"
|
||||
"name": "Daniele Alessandri",
|
||||
"email": "suppakilla@gmail.com",
|
||||
"homepage": "http://clorophilla.net"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0",
|
||||
"psr/http-message": "^1.0|^2.0"
|
||||
"php": ">=5.3.2"
|
||||
},
|
||||
"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"
|
||||
"phpunit/phpunit": "~4.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"
|
||||
"ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol",
|
||||
"ext-curl": "Allows access to Webdis when paired with phpiredis"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Predis\\": "src/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"preferred-install": "dist"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
"psr-4": {"Predis\\": "src/"}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
@@ -3,14 +3,13 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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
|
||||
@@ -19,7 +18,7 @@ require __DIR__ . '/shared.php';
|
||||
use Predis\Cluster\Distributor\DistributorInterface;
|
||||
use Predis\Cluster\Hash\HashGeneratorInterface;
|
||||
use Predis\Cluster\PredisStrategy;
|
||||
use Predis\Connection\Cluster\PredisCluster;
|
||||
use Predis\Connection\Aggregate\PredisCluster;
|
||||
|
||||
class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
|
||||
{
|
||||
@@ -28,7 +27,7 @@ class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->nodes = [];
|
||||
$this->nodes = array();
|
||||
$this->nodesCount = 0;
|
||||
}
|
||||
|
||||
@@ -54,7 +53,7 @@ class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
|
||||
|
||||
public function getBySlot($slot)
|
||||
{
|
||||
return $this->nodes[$slot] ?? null;
|
||||
return isset($this->nodes[$slot]) ? $this->nodes[$slot] : null;
|
||||
}
|
||||
|
||||
public function getByHash($hash)
|
||||
@@ -88,7 +87,7 @@ class NaiveDistributor implements DistributorInterface, HashGeneratorInterface
|
||||
}
|
||||
}
|
||||
|
||||
$options = [
|
||||
$options = array(
|
||||
'cluster' => function () {
|
||||
$distributor = new NaiveDistributor();
|
||||
$strategy = new PredisStrategy($distributor);
|
||||
@@ -96,7 +95,7 @@ $options = [
|
||||
|
||||
return $cluster;
|
||||
},
|
||||
];
|
||||
);
|
||||
|
||||
$client = new Predis\Client($multiple_servers, $options);
|
||||
|
||||
@@ -105,8 +104,8 @@ for ($i = 0; $i < 100; ++$i) {
|
||||
$client->get("key:$i");
|
||||
}
|
||||
|
||||
$server1 = $client->getClientBy('alias', 'first')->info();
|
||||
$server2 = $client->getClientBy('alias', 'second')->info();
|
||||
$server1 = $client->getClientFor('first')->info();
|
||||
$server2 = $client->getClientFor('second')->info();
|
||||
|
||||
if (isset($server1['Keyspace'], $server2['Keyspace'])) {
|
||||
$server1 = $server1['Keyspace'];
|
||||
|
||||
@@ -3,18 +3,17 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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
|
||||
// useful as a starting poing to understand how your application interacts with
|
||||
// Redis.
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
@@ -23,7 +22,7 @@ use Predis\Connection\StreamConnection;
|
||||
class SimpleDebuggableConnection extends StreamConnection
|
||||
{
|
||||
private $tstart = 0;
|
||||
private $debugBuffer = [];
|
||||
private $debugBuffer = array();
|
||||
|
||||
public function connect()
|
||||
{
|
||||
@@ -37,7 +36,7 @@ class SimpleDebuggableConnection extends StreamConnection
|
||||
$firtsArg = $command->getArgument(0);
|
||||
$timestamp = round(microtime(true) - $this->tstart, 4);
|
||||
|
||||
$debug = $command->getId();
|
||||
$debug = $command->getId();
|
||||
$debug .= isset($firtsArg) ? " $firtsArg " : ' ';
|
||||
$debug .= "$direction $this";
|
||||
$debug .= " [{$timestamp}s]";
|
||||
@@ -66,11 +65,11 @@ class SimpleDebuggableConnection extends StreamConnection
|
||||
}
|
||||
}
|
||||
|
||||
$options = [
|
||||
'connections' => [
|
||||
$options = array(
|
||||
'connections' => array(
|
||||
'tcp' => 'SimpleDebuggableConnection',
|
||||
],
|
||||
];
|
||||
),
|
||||
);
|
||||
|
||||
$client = new Predis\Client($single_server, $options);
|
||||
$client->set('foo', 'bar');
|
||||
@@ -81,13 +80,13 @@ 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]',
|
||||
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]',
|
||||
)
|
||||
*/
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
require __DIR__.'/shared.php';
|
||||
|
||||
// This is a basic example on how to use the Predis\DispatcherLoop class.
|
||||
//
|
||||
@@ -24,13 +23,13 @@ require __DIR__ . '/shared.php';
|
||||
// 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]);
|
||||
$client = new Predis\Client($single_server + array('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);
|
||||
$dispatcher = new Predis\PubSub\DispatcherLoop($pubsub);
|
||||
|
||||
// Demonstrate how to use a callable class as a callback for the dispatcher loop.
|
||||
class EventsListener implements Countable
|
||||
@@ -39,7 +38,7 @@ class EventsListener implements Countable
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->events = [];
|
||||
$this->events = array();
|
||||
}
|
||||
|
||||
public function count()
|
||||
@@ -52,17 +51,17 @@ class EventsListener implements Countable
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
public function __invoke($payload, $dispatcher)
|
||||
public function __invoke($payload)
|
||||
{
|
||||
$this->events[] = $payload;
|
||||
}
|
||||
}
|
||||
|
||||
// Attach our callable class to the dispatcher.
|
||||
$dispatcher->attachCallback('events', $events = new EventsListener());
|
||||
$dispatcher->attachCallback('events', ($events = new EventsListener()));
|
||||
|
||||
// Attach a function to control the dispatcher loop termination with a message.
|
||||
$dispatcher->attachCallback('control', function ($payload, $dispatcher) {
|
||||
$dispatcher->attachCallback('control', function ($payload) use ($dispatcher) {
|
||||
if ($payload === 'terminate_dispatcher') {
|
||||
$dispatcher->stop();
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
require __DIR__.'/shared.php';
|
||||
|
||||
$client = new Predis\Client($single_server);
|
||||
|
||||
@@ -18,46 +17,41 @@ $client = new Predis\Client($single_server);
|
||||
$client->set('library', 'predis');
|
||||
$response = $client->get('library');
|
||||
|
||||
var_export($response);
|
||||
echo PHP_EOL;
|
||||
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 = [
|
||||
$mkv = array(
|
||||
'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;
|
||||
var_export($response); echo PHP_EOL;
|
||||
/* OUTPUT:
|
||||
array (
|
||||
0 => '1st user',
|
||||
1 => '2nd user',
|
||||
2 => '3rd user',
|
||||
)
|
||||
*/
|
||||
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([
|
||||
$response = $client->executeRaw(array(
|
||||
'MGET', 'uid:0001', 'uid:0002', 'uid:0003',
|
||||
]);
|
||||
));
|
||||
|
||||
var_export($response);
|
||||
echo PHP_EOL;
|
||||
var_export($response); echo PHP_EOL;
|
||||
/* OUTPUT:
|
||||
array (
|
||||
0 => '1st user',
|
||||
1 => '2nd user',
|
||||
2 => '3rd user',
|
||||
)
|
||||
*/
|
||||
0 => '1st user',
|
||||
1 => '2nd user',
|
||||
2 => '3rd user',
|
||||
) */
|
||||
|
||||
@@ -3,35 +3,34 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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 = new Predis\Client($single_server, array('prefix' => 'nrk:'));
|
||||
|
||||
$client->mset(['foo' => 'bar', 'lol' => 'wut']);
|
||||
$client->mset(array('foo' => 'bar', 'lol' => 'wut'));
|
||||
var_export($client->mget('foo', 'lol'));
|
||||
/*
|
||||
array (
|
||||
0 => 'bar',
|
||||
1 => 'wut',
|
||||
0 => 'bar',
|
||||
1 => 'wut',
|
||||
)
|
||||
*/
|
||||
|
||||
var_export($client->keys('*'));
|
||||
/*
|
||||
array (
|
||||
0 => 'nrk:foo',
|
||||
1 => 'nrk:lol',
|
||||
0 => 'nrk:foo',
|
||||
1 => 'nrk:lol',
|
||||
)
|
||||
*/
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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:
|
||||
// Additionally to the EVAL command defined in the current development profile,
|
||||
// the Predis\Command\ScriptCommand class can be used to build an higher level
|
||||
// abstraction for "scriptable" commands so that they will appear just like any
|
||||
// other command on the client-side. This is a quick example used to implement
|
||||
// INCREX.
|
||||
|
||||
use Predis\Command\ScriptCommand;
|
||||
|
||||
@@ -37,11 +37,11 @@ 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
|
||||
if cmd('exists', key) == 1 then
|
||||
insert(results, idx, cmd('incrby', key, increment))
|
||||
else
|
||||
insert(results, idx, false)
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
@@ -49,11 +49,14 @@ LUA;
|
||||
}
|
||||
}
|
||||
|
||||
$client = new Predis\Client($single_server, [
|
||||
'commands' => [
|
||||
'increxby' => 'IncrementExistingKeysBy',
|
||||
],
|
||||
]);
|
||||
$client = new Predis\Client($single_server, array(
|
||||
'profile' => function ($options) {
|
||||
$profile = $options->getDefault('profile');
|
||||
$profile->defineCommand('increxby', 'IncrementExistingKeysBy');
|
||||
|
||||
return $profile;
|
||||
},
|
||||
));
|
||||
|
||||
$client->mset('foo', 10, 'foobar', 100);
|
||||
|
||||
@@ -61,8 +64,8 @@ var_export($client->increxby('foo', 'foofoo', 'foobar', 50));
|
||||
|
||||
/*
|
||||
array (
|
||||
0 => 60,
|
||||
1 => NULL,
|
||||
2 => 150,
|
||||
0 => 60,
|
||||
1 => NULL,
|
||||
2 => 150,
|
||||
)
|
||||
*/
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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
|
||||
@@ -18,7 +17,7 @@ require __DIR__ . '/shared.php';
|
||||
// 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]);
|
||||
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
|
||||
|
||||
// Use only one instance of DateTime, we will update the timestamp later.
|
||||
$timestamp = new DateTime();
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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
|
||||
@@ -31,16 +30,16 @@ 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',
|
||||
),
|
||||
0 => Predis\Response\Status::__set_state(array(
|
||||
'payload' => 'OK',
|
||||
)),
|
||||
1 => 10,
|
||||
2 => 40,
|
||||
3 => true,
|
||||
4 => '40',
|
||||
5 => array (
|
||||
0 => NULL,
|
||||
1 => '40',
|
||||
),
|
||||
)
|
||||
*/
|
||||
|
||||
@@ -3,20 +3,19 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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]);
|
||||
$client = new Predis\Client($single_server + array('read_write_timeout' => 0));
|
||||
|
||||
// Initialize a new pubsub consumer.
|
||||
$pubsub = $client->pubSubLoop();
|
||||
@@ -24,10 +23,10 @@ $pubsub = $client->pubSubLoop();
|
||||
// Subscribe to your channels
|
||||
$pubsub->subscribe('control_channel', 'notifications');
|
||||
|
||||
// Start processing the pubsub messages. Open a terminal and use redis-cli
|
||||
// Start processing the pubsup 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
|
||||
// ./redis-cli PUBLISH notifications "this is a test"
|
||||
// ./redis-cli PUBLISH control_channel quit_loop
|
||||
foreach ($pubsub as $message) {
|
||||
switch ($message->kind) {
|
||||
case 'subscribe':
|
||||
@@ -44,7 +43,7 @@ foreach ($pubsub as $message) {
|
||||
}
|
||||
} else {
|
||||
echo "Received the following message from {$message->channel}:",
|
||||
PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL;
|
||||
PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -3,18 +3,17 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
require __DIR__.'/shared.php';
|
||||
|
||||
use Predis\Collection\Iterator;
|
||||
|
||||
// Starting with Redis 2.8, clients can iterate incrementally over collections
|
||||
// Starting from 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:
|
||||
//
|
||||
@@ -30,7 +29,8 @@ use Predis\Collection\Iterator;
|
||||
// See http://redis.io/commands/scan for more details.
|
||||
//
|
||||
|
||||
$client = new Predis\Client($single_server);
|
||||
// Create a client using `2.8` as a server profile (needs Redis 2.8!)
|
||||
$client = new Predis\Client($single_server, array('profile' => '2.8'));
|
||||
|
||||
// Prepare some keys for our example
|
||||
$client->del('predis:set', 'predis:zset', 'predis:hash');
|
||||
@@ -48,9 +48,9 @@ foreach (new Iterator\Keyspace($client, 'predis:*') as $key) {
|
||||
|
||||
/* OUTPUT
|
||||
Scan the keyspace matching only our prefixed keys:
|
||||
- predis:zset
|
||||
- predis:set
|
||||
- predis:hash
|
||||
- predis:zset
|
||||
- predis:set
|
||||
- predis:hash
|
||||
*/
|
||||
|
||||
// === Set iterator based on SSCAN ===
|
||||
@@ -61,11 +61,11 @@ foreach (new Iterator\SetKey($client, 'predis:set') as $member) {
|
||||
|
||||
/* OUTPUT
|
||||
Scan members of `predis:set`:
|
||||
- member:1
|
||||
- member:4
|
||||
- member:0
|
||||
- member:3
|
||||
- member:2
|
||||
- member:1
|
||||
- member:4
|
||||
- member:0
|
||||
- member:3
|
||||
- member:2
|
||||
*/
|
||||
|
||||
// === Sorted set iterator based on ZSCAN ===
|
||||
@@ -76,11 +76,11 @@ foreach (new Iterator\SortedSetKey($client, 'predis:zset') as $member => $rank)
|
||||
|
||||
/* 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]
|
||||
- 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 ===
|
||||
@@ -91,9 +91,9 @@ foreach (new Iterator\HashKey($client, 'predis:hash') as $field => $value) {
|
||||
|
||||
/* 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
|
||||
- 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;
|
||||
@@ -3,24 +3,23 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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.
|
||||
// more complex configuration that injects a new script command in the server
|
||||
// profile used by the new client instance and marks it marks it as a read-only
|
||||
// operation for replication so that it will be executed on slaves.
|
||||
|
||||
use Predis\Command\ScriptCommand;
|
||||
use Predis\Connection\Replication\MasterSlaveReplication;
|
||||
use Predis\Connection\Aggregate\MasterSlaveReplication;
|
||||
use Predis\Replication\ReplicationStrategy;
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
@@ -30,7 +29,7 @@ use Predis\Replication\ReplicationStrategy;
|
||||
|
||||
class HashMultipleGetAll extends ScriptCommand
|
||||
{
|
||||
public const BODY = <<<LUA
|
||||
const BODY = <<<LUA
|
||||
local hashes = {}
|
||||
for _, key in pairs(KEYS) do
|
||||
table.insert(hashes, key)
|
||||
@@ -47,15 +46,18 @@ LUA;
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
$parameters = [
|
||||
'tcp://127.0.0.1:6381?role=master&database=15',
|
||||
'tcp://127.0.0.1:6382?role=slave&alias=slave-01&database=15',
|
||||
];
|
||||
$parameters = array(
|
||||
'tcp://127.0.0.1:6379/?alias=master',
|
||||
'tcp://127.0.0.1:6380/?alias=slave',
|
||||
);
|
||||
|
||||
$options = [
|
||||
'commands' => [
|
||||
'hmgetall' => 'HashMultipleGetAll',
|
||||
],
|
||||
$options = array(
|
||||
'profile' => function ($options, $option) {
|
||||
$profile = $options->getDefault($option);
|
||||
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
|
||||
|
||||
return $profile;
|
||||
},
|
||||
'replication' => function () {
|
||||
$strategy = new ReplicationStrategy();
|
||||
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
|
||||
@@ -64,7 +66,7 @@ $options = [
|
||||
|
||||
return $replication;
|
||||
},
|
||||
];
|
||||
);
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
@@ -77,7 +79,7 @@ $client = new Predis\Client($parameters, $options);
|
||||
$hashes = $client->hmgetall('metavars', 'servers');
|
||||
|
||||
$replication = $client->getConnection();
|
||||
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionByAlias('slave-01');
|
||||
$stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave');
|
||||
|
||||
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'.
|
||||
*/
|
||||
@@ -3,14 +3,13 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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
|
||||
@@ -22,29 +21,29 @@ require __DIR__ . '/shared.php';
|
||||
// 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',
|
||||
];
|
||||
$parameters = array(
|
||||
'tcp://127.0.0.1:6379?database=15&alias=master',
|
||||
'tcp://127.0.0.1:6380?database=15&alias=slave',
|
||||
);
|
||||
|
||||
$options = ['replication' => 'predis'];
|
||||
$options = array('replication' => true);
|
||||
|
||||
$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;
|
||||
echo "Does 'foo' exist on {$current->alias}? $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;
|
||||
echo "Now 'foo' has been set to 'bar' on {$current->alias}!", 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;
|
||||
echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL;
|
||||
|
||||
/* OUTPUT:
|
||||
Does 'foo' exist on slave? yes.
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
require __DIR__.'/shared.php';
|
||||
|
||||
// This example demonstrates how to use Predis to save PHP sessions on Redis.
|
||||
//
|
||||
@@ -26,16 +25,16 @@ require __DIR__ . '/shared.php';
|
||||
//
|
||||
|
||||
if (!interface_exists('SessionHandlerInterface')) {
|
||||
exit('ATTENTION: the session handler implemented by Predis requires PHP >= 5.4.0 ' .
|
||||
die('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:']);
|
||||
$client = new Predis\Client($single_server, array('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]);
|
||||
$handler = new Predis\Session\Handler($client, array('gc_maxlifetime' => 5));
|
||||
|
||||
// Register the session handler.
|
||||
$handler->register();
|
||||
|
||||
@@ -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;
|
||||
+20
-25
@@ -3,18 +3,13 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
exit('Example scripts are meant to be executed locally via CLI.');
|
||||
}
|
||||
|
||||
require __DIR__ . '/../autoload.php';
|
||||
require __DIR__.'/../autoload.php';
|
||||
|
||||
function redis_version($info)
|
||||
{
|
||||
@@ -27,23 +22,23 @@ function redis_version($info)
|
||||
}
|
||||
}
|
||||
|
||||
$single_server = [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
$single_server = array(
|
||||
'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',
|
||||
],
|
||||
];
|
||||
$multiple_servers = array(
|
||||
array(
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'database' => 15,
|
||||
'alias' => 'first',
|
||||
),
|
||||
array(
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6380,
|
||||
'database' => 15,
|
||||
'alias' => 'second',
|
||||
),
|
||||
);
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) 2009-2020 Daniele Alessandri
|
||||
* (c) 2021-2025 Till Krüss
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/shared.php';
|
||||
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
|
||||
@@ -28,15 +27,15 @@ require __DIR__ . '/shared.php';
|
||||
function zpop($client, $key)
|
||||
{
|
||||
$element = null;
|
||||
$options = [
|
||||
'cas' => true, // Initialize with support for CAS operations
|
||||
$options = array(
|
||||
'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.
|
||||
];
|
||||
// which the client bails out with an exception.
|
||||
);
|
||||
|
||||
$client->transaction($options, function ($tx) use ($key, &$element) {
|
||||
@[$element] = $tx->zrange($key, 0, 0);
|
||||
@list($element) = $tx->zrange($key, 0, 0);
|
||||
|
||||
if (isset($element)) {
|
||||
$tx->multi(); // With CAS, MULTI *must* be explicitly invoked.
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
; This file is meant to be used with Onion http://c9s.github.com/Onion/
|
||||
; For instructions on how to build a PEAR package of Predis please follow
|
||||
; the instructions at this URL:
|
||||
;
|
||||
; https://github.com/c9s/Onion#a-quick-tutorial-for-building-pear-package
|
||||
;
|
||||
|
||||
[package]
|
||||
name = "Predis"
|
||||
desc = "Flexible and feature-complete PHP client library for Redis"
|
||||
homepage = "http://github.com/nrk/predis"
|
||||
license = "MIT"
|
||||
version = "1.0.3"
|
||||
stability = "stable"
|
||||
channel = "pear.nrk.io"
|
||||
|
||||
author = "Daniele Alessandri \"nrk\" <suppakilla@gmail.com>"
|
||||
|
||||
[require]
|
||||
php = ">= 5.3.2"
|
||||
pearinstaller = "1.4.1"
|
||||
|
||||
[roles]
|
||||
*.xml.dist = test
|
||||
*.md = doc
|
||||
LICENSE = doc
|
||||
|
||||
[optional phpiredis]
|
||||
hint = "Add support for faster protocol handling with phpiredis"
|
||||
extensions[] = socket
|
||||
extensions[] = phpiredis
|
||||
|
||||
[optional webdis]
|
||||
hint = "Add support for Webdis"
|
||||
extensions[] = curl
|
||||
extensions[] = phpiredis
|
||||
@@ -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>
|
||||
+13
-31
@@ -1,15 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
bootstrap="tests/bootstrap.php"
|
||||
|
||||
<phpunit bootstrap="tests/bootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="true"
|
||||
>
|
||||
beStrictAboutTestSize="true"
|
||||
checkForUnintentionallyCoveredCode="true"
|
||||
beStrictAboutTestsThatDoNotTestAnything="true">
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Predis Test Suite">
|
||||
@@ -17,19 +12,11 @@
|
||||
</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-phpiredis</group>
|
||||
<group>ext-curl</group>
|
||||
<group>cluster</group>
|
||||
<group>gears</group>
|
||||
<group>gears-cluster</group>
|
||||
<group>unprotected</group>
|
||||
<group>realm-webdis</group>
|
||||
<!-- <group>connected</group> -->
|
||||
<!-- <group>disconnected</group> -->
|
||||
<!-- <group>commands</group> -->
|
||||
@@ -44,19 +31,14 @@
|
||||
</filter>
|
||||
|
||||
<php>
|
||||
<!-- Redis -->
|
||||
<const name="REDIS_SERVER_VERSION" value="2.8" />
|
||||
<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" />
|
||||
<const name="REDIS_SERVER_DBNUM" value="15" />
|
||||
|
||||
<!-- 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"
|
||||
/>
|
||||
<!-- Webdis -->
|
||||
<const name="WEBDIS_SERVER_HOST" value="127.0.0.1" />
|
||||
<const name="WEBDIS_SERVER_PORT" value="7379" />
|
||||
</php>
|
||||
</phpunit>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit bootstrap="tests/bootstrap.php"
|
||||
colors="true"
|
||||
beStrictAboutTestSize="true"
|
||||
checkForUnintentionallyCoveredCode="true"
|
||||
beStrictAboutTestsThatDoNotTestAnything="true">
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Predis Test Suite">
|
||||
<directory>tests/Predis/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<groups>
|
||||
<exclude>
|
||||
<group>ext-phpiredis</group>
|
||||
<group>ext-curl</group>
|
||||
<group>realm-webdis</group>
|
||||
<!-- <group>connected</group> -->
|
||||
<!-- <group>disconnected</group> -->
|
||||
<!-- <group>commands</group> -->
|
||||
<!-- <group>slow</group> -->
|
||||
</exclude>
|
||||
</groups>
|
||||
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
<logging>
|
||||
<log type="coverage-text" target="php://stdout" />
|
||||
</logging>
|
||||
|
||||
<php>
|
||||
<!-- Redis -->
|
||||
<const name="REDIS_SERVER_VERSION" value="2.8" />
|
||||
<const name="REDIS_SERVER_HOST" value="127.0.0.1" />
|
||||
<const name="REDIS_SERVER_PORT" value="6379" />
|
||||
<const name="REDIS_SERVER_DBNUM" value="15" />
|
||||
|
||||
<!-- Webdis -->
|
||||
<const name="WEBDIS_SERVER_HOST" value="127.0.0.1" />
|
||||
<const name="WEBDIS_SERVER_PORT" value="7379" />
|
||||
</php>
|
||||
</phpunit>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user