mirror of
https://github.com/predis/predis.git
synced 2026-08-22 01:40:57 +00:00
Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ead747663 | |||
| 39cf6abf82 | |||
| dbae384a64 | |||
| d9ea0ce14f | |||
| 55f940e400 | |||
| 648be824d3 | |||
| c4e0f2f037 | |||
| 62f5ce6baf | |||
| 9cd4ed9e1e | |||
| 8491b1d880 | |||
| 8c8261432f | |||
| 5eccd6ee3c | |||
| 3d9588ec1a | |||
| e96f7f748a | |||
| dd0edf761a | |||
| 8cbbd073cb | |||
| 1e521b56cd | |||
| fcacd65576 | |||
| d5488f81f7 | |||
| 188dc6bf62 | |||
| 0bd57ab744 | |||
| b996b005cf | |||
| 82116b0366 | |||
| 5c1ddc9cfa | |||
| ea2fd9700d | |||
| b2ec264ff9 | |||
| bf7a70d6f0 | |||
| 4ecc44e1ee | |||
| 52d5b23db7 | |||
| d31a4c71ba | |||
| c92e305852 | |||
| b075564278 | |||
| f9e817f14c | |||
| 9e568396c6 | |||
| 2a91ebaba9 | |||
| ce15bd22b9 | |||
| b0439e503e | |||
| ffc1c25baa | |||
| ac165194f5 | |||
| 18ae294864 | |||
| 47de667aea | |||
| 588940c5cb | |||
| a1fe4013e1 | |||
| 3328ad76be | |||
| 17b717a8f5 | |||
| 95dbc2bb34 | |||
| 9bb5a1c71f | |||
| 8eb4804a5c | |||
| 5d99914939 | |||
| c45bb15767 | |||
| e0329d6c74 | |||
| 2175d87a96 | |||
| ce85fdadd1 | |||
| b15c0a02de | |||
| d2fbcc6b8c | |||
| 6596460a57 | |||
| 9ab0e31a75 | |||
| 11adccdaeb | |||
| f465792b27 | |||
| 1399d3863c | |||
| 1f1c9cf1a4 | |||
| dae2df0ae9 | |||
| 9338eaf573 | |||
| f378faaf22 | |||
| 8b150fcbf5 | |||
| 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 |
+3
-2
@@ -1,9 +1,10 @@
|
||||
* text=auto
|
||||
|
||||
/tests export-ignore
|
||||
/.github export-ignore
|
||||
/.editorconfig export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.php_cs.dist export-ignore
|
||||
/.php_cs export-ignore
|
||||
/.travis.yml export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/phpunit.xml.travisci export-ignore
|
||||
|
||||
@@ -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,52 +0,0 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
|
||||
predis:
|
||||
name: PHP ${{ matrix.php-versions }} (Redis ${{ matrix.redis-versions }})
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis:${{ matrix.redis-versions }}
|
||||
ports:
|
||||
- 6379:6379
|
||||
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1']
|
||||
redis-versions: ['3', '4', '5', '6', '7']
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP with Composer and extensions
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
uses: shivammathur/setup-php@v2
|
||||
|
||||
- name: Get Composer cache directory
|
||||
id: composercache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: Cache Composer dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composercache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: ${{ runner.os }}-composer-
|
||||
|
||||
- name: Install Composer dependencies
|
||||
env:
|
||||
PHP_VERSION: ${{ matrix.php-versions }}
|
||||
run: composer install --no-progress --prefer-dist --optimize-autoloader $(if [ "$PHP_VERSION" == "8.0" ]; then echo "--ignore-platform-reqs"; fi;)
|
||||
|
||||
- name: Test with PHPUnit
|
||||
run: vendor/bin/phpunit
|
||||
+6
-3
@@ -1,6 +1,9 @@
|
||||
/vendor
|
||||
*.tgz
|
||||
*.phar
|
||||
.php-version
|
||||
.php_cs.cache
|
||||
.phpunit.result.cache
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
package.xml
|
||||
composer.lock
|
||||
experiments/
|
||||
vendor/
|
||||
|
||||
@@ -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')
|
||||
);
|
||||
@@ -1,33 +0,0 @@
|
||||
<?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;
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setUsingCache(true)
|
||||
->setRules(array(
|
||||
'@Symfony' => true,
|
||||
'header_comment' => array(
|
||||
'header' => $PREDIS_HEADER,
|
||||
),
|
||||
'ordered_imports' => true,
|
||||
'phpdoc_order' => true,
|
||||
'binary_operator_spaces' => array(
|
||||
'align_double_arrow' => false,
|
||||
'align_equals' => false,
|
||||
),
|
||||
'array_syntax' => array('syntax' => 'long'),
|
||||
))
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->in(__DIR__.'/bin')
|
||||
->in(__DIR__.'/src')
|
||||
->in(__DIR__.'/tests')
|
||||
->in(__DIR__.'/examples')
|
||||
);
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
language: php
|
||||
sudo: false
|
||||
php:
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- hhvm
|
||||
branches:
|
||||
except:
|
||||
- v0.5
|
||||
- v0.6
|
||||
- v0.6-PHP_5.2
|
||||
- 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:
|
||||
fast_finish: true
|
||||
+863
-100
@@ -1,129 +1,892 @@
|
||||
## Changelog
|
||||
v1.0.4 (2016-05-30)
|
||||
================================================================================
|
||||
|
||||
## v2.0.0 (2022-06-08)
|
||||
- Added new profile for Redis 3.2 with its new commands: `HSTRLEN`, `BITFIELD`,
|
||||
`GEOADD`, `GEOHASH`, `GEOPOS`, `GEODIST`, `GEORADIUS`, `GEORADIUSBYMEMBER`.
|
||||
The default server profile for Predis is still the one for Redis 3.0 you must
|
||||
set the `profile` client option to `3.2` when initializing the client in order
|
||||
to be able to use them when connecting to Redis 3.2.
|
||||
|
||||
- Dropped support for PHP 7.1 and older
|
||||
- Various improvements in the handling of redis-cluster:
|
||||
|
||||
- Accepted values for some client options have changed, this is the new list of
|
||||
accepted values:
|
||||
- If the connection to a specific node fails when executing a command, the
|
||||
client tries to connect to another node in order to refresh the slots map
|
||||
and perform a new attempt to execute the command.
|
||||
|
||||
- `aggregate`: callable returning an aggregate connection.
|
||||
- `cluster`: string value (`predis`, `redis`), callable returning an aggregate
|
||||
connection.
|
||||
- `replication`: string value (`predis`, `sentinel`), callable returning an
|
||||
aggregate connection.
|
||||
- `commands`: command factory, named array mapping command IDs to PHP classes,
|
||||
callable returning a command factory or a named array.
|
||||
- `connections`: connection factory, callable object returning a connection
|
||||
factory, named array mapping URI schemes to PHP classes, string identifying
|
||||
a supported combination of configurations for the connection factory.
|
||||
- `prefix`: string value, command processor, callable.
|
||||
- `exceptions`: boolean.
|
||||
- Connections to nodes can be preassigned to non-contiguous slot ranges via
|
||||
the `slots` parameter using a comma separator. This is how it looks like
|
||||
in practice: `tcp://127.0.0.1:6379?slots=0-5460,5500-5600,11000`.
|
||||
|
||||
Note that both the `cluster` and `replication` options now return a closure
|
||||
acting as initializer instead of an aggregate connection instance.
|
||||
- __FIX__: broken values returned by `Predis\Collection\Iterator\HashKey` when
|
||||
iterating hash keys containing integer fields (PR #330, ISSUE #331).
|
||||
|
||||
- The `connections` client option now accepts certain string values identifying
|
||||
certain combinations of configurations for the connection factory. Currenlty
|
||||
this is used to provide a short way to configure Predis to load our phpiredis
|
||||
based connection backends simply, accepted values are:
|
||||
- __FIX__: prevent failures when `Predis\Connection\StreamConnection` serializes
|
||||
commands with holes in their arguments (e.g. `[0 => 'key:0', 2 => 'key:2']`).
|
||||
The same fix has been applied to `Predis\Protocol\Text\RequestSerializer`.
|
||||
(ISSUE #316).
|
||||
|
||||
- `phpiredis-stream` maps `Phpiredis\Connection\PhpiredisStreamConnection` to
|
||||
`tcp`, `redis`, `unix` URI schemes.
|
||||
- `phpiredis-socket` maps `Phpiredis\Connection\PhpiredisSocketConnection` to
|
||||
`tcp`, `redis`, `unix` URI schemes.
|
||||
- `phpiredis-stream` is simply an alias of `phpiredis-stream`.
|
||||
|
||||
- Added the new `Predis\Cluster\Hash\PhpiredisCRC16` class using ext-phpiredis
|
||||
to speed-up the generation of the CRC16 hash of keys for redis-cluster. Predis
|
||||
automatically uses this class when ext-phpiredis is loaded, but it is possible
|
||||
to configure the hash generator using the new `crc16` client option (accepted
|
||||
values `predis`, `phpiredis` or an hash generator instance).
|
||||
v1.0.3 (2015-07-30)
|
||||
================================================================================
|
||||
|
||||
- Replication backends now use the `role` parameter instead of `alias` in order
|
||||
to distinguish the role of a connection. Accepted values are `master`, `slave`
|
||||
and, for redis-sentinel, `sentinel`. This led to a redesign of how connections
|
||||
can be retrieved from replication backends: the method getConnectionById() now
|
||||
retrieves a connection only by its ID (ip:port pair), to get a connection by
|
||||
its alias there is the new method getConnectionByAlias(). This method is not
|
||||
supported by the redis-sentinel backend due to its dynamic nature (connections
|
||||
are retrieved and initialized at runtime from sentinels) but it is possible to
|
||||
get a single connection from the pool by using its ID. It is also possible to
|
||||
retrive a connection by role using the method getConnectionByRole().
|
||||
- __FIX__: the previous release introduced a severe regression on HHVM that made
|
||||
the library unable to connect to Redis when using IPv4 addresses. Code running
|
||||
on the standard PHP interpreter is not affected.
|
||||
|
||||
- The concept of connection ID (ip:port pair) and connection alias (the `alias`
|
||||
parameter) in `Predis\Connection\Cluster\PredisCluster` has been separated.
|
||||
This change does not affect distribution and it is safe for existing clusters.
|
||||
|
||||
- Client option classes now live in the `Predis\Configuration\Option` namespace.
|
||||
v1.0.2 (2015-07-30)
|
||||
================================================================================
|
||||
|
||||
- Classes for Redis commands have been moved into the new `Predis\Command\Redis`
|
||||
namespace and each class name mirrors the respective Redis command ID.
|
||||
- IPv6 is now fully supported.
|
||||
|
||||
- The concept of server profiles is gone, the library now uses a single command
|
||||
factory to create instances of commands classes. The `profile` option has been
|
||||
replaced by the `commands` option accepting `Predis\Command\FactoryInterface`
|
||||
to customize the underlying command factory. The default command factory class
|
||||
used by Predis is `Predis\Command\RedisFactory` and it still allows developers
|
||||
to define or override commands with their own implementations. In addition to
|
||||
that, `Predis\Command\RedisFactory` relies on a convention-over-configuration
|
||||
approach by looking for a suitable class with the same name as the command ID
|
||||
in the `Predis\Command\Redis` when the internal class map does not contain a
|
||||
class associated.
|
||||
- Added `redis` as an accepted scheme for connection parameters. When using this
|
||||
scheme, the rules used to parse URI strings match the provisional registration
|
||||
[published by IANA](http://www.iana.org/assignments/uri-schemes/prov/redis).
|
||||
|
||||
- The method `Predis\Client::getClientFor($connectionID)` has been replaced by
|
||||
`getClientBy($selector, $value, $callable = null)` which is more flexible as
|
||||
it is not limited to picking a connection from the underlying replication or
|
||||
cluster backend by ID, but allows users to specify a `$selector` that can be
|
||||
either `id` (the old behavior), `key`, `slot` or `command`. The client uses
|
||||
duck-typing instead of type-checking to verify that the underlying connection
|
||||
implements a method that matches the specified selector which means that some
|
||||
selectors may not be available to all kinds of connection backends.
|
||||
- Added new or missing commands: `HSTRLEN` (>= 3.2), `ZREVRANGEBYLEX` (>= 2.8)
|
||||
and `MIGRATE` (>= 2.6).
|
||||
|
||||
- The method `Predis\Client::getConnectionById($connectionID)` has been removed.
|
||||
- Implemented support for the `ZADD` modifiers `NX|XX`, `CH`, `INCR` (Redis >=
|
||||
3.0.2) using the simplified signature where scores and members are passed as
|
||||
a named array.
|
||||
|
||||
- Changed the signature for the constructor of `Predis\Command\RawCommand`.
|
||||
- __FIX__: `Predis\Configuration\Options` must not trigger the autoloader when
|
||||
option values are strings (ISSUE #257).
|
||||
|
||||
- The `Predis\Connection\Aggregate` namespace has been split into two separate
|
||||
namespaces for cluster backends (`Predis\Connection\Cluster`) and replication
|
||||
backends (`Predis\Connection\Replication`).
|
||||
- __FIX__: `BITPOS` was not defined in the key-prefix processor (ISSUE #265) and
|
||||
in the replication strategy.
|
||||
|
||||
- The method `Predis\Connection\AggregateConnectionInterface::getConnection()`
|
||||
has been renamed to `getConnectionByCommand()`.
|
||||
|
||||
- The methods `switchToMaster()` and `switchToSlave()` have been promoted to be
|
||||
part of `Predis\Connection\Replication\ReplicationInterface` while the method
|
||||
`switchTo($connection)` has been removed from it.
|
||||
v1.0.1 (2015-01-02)
|
||||
================================================================================
|
||||
|
||||
- The method `Predis\Connection\Cluster\PredisCluster::executeCommandOnNodes()`
|
||||
has been removed as it is possible to achieve the same by iterating over the
|
||||
connection or, even better, over the client instance in order to execute the
|
||||
same command against all of the registered connections.
|
||||
- Added `BITPOS` to the server profile for Redis 2.8.
|
||||
|
||||
- The class `Predis\CommunicationException` now uses the correct default types
|
||||
for the `$message` (string) and `$code` (integer) parameters.
|
||||
- Connection timeout for read/write operations can now be set for UNIX sockets
|
||||
where the underlying connection uses PHP's stream.
|
||||
|
||||
- The method `onConnectionError()` in `Predis\Connection\AbstractConnection`
|
||||
class now passes the second argument as an integer value `0` as its default
|
||||
value instead of `null`.
|
||||
- __FIX__: broken values returned by `Predis\Collection\Iterator\SortedSetKey`
|
||||
when iterating sorted set containing integer members (ISSUE #216).
|
||||
|
||||
- Support Pub/Sub and Pipelines when using replication
|
||||
- __FIX__: applied a minor workaround for a bug in old versions of PHP < 5.3.9
|
||||
affecting inheritance.
|
||||
|
||||
- The class `Predis\Transaction\AbortedMultiExecException` now uses the correct
|
||||
default types for the `$code` (integer) parameter.
|
||||
- __FIX__: prevent E_NOTICE warnings when using INFO [section] returns an empty
|
||||
response due to an unsupported specific set of information requested to Redis.
|
||||
|
||||
- __FIX__: using `strval` in `getScanOptions()` method, part of
|
||||
`Predis\Collection\Iterator\CursorBasedIterator` to make sure we retrieve the
|
||||
string value of `$this->match` and not passing `null` to `strlen()` function.
|
||||
|
||||
- __FIX__: the value returned from `getArgument()` in `isReadOperation()` method,
|
||||
part of `Predis\Replication\ReplicationStrategy` class, is checked to not pass
|
||||
`null` to `sha1` function.
|
||||
v1.0.0 (2014-08-01)
|
||||
================================================================================
|
||||
|
||||
- __FIX__: the value returned from `getArgument()` in `parseResponse()`method,
|
||||
part of `Predis\Command\Redis\SENTINEL` class, is checked to not pass `null`
|
||||
to `strtolower()` function.
|
||||
- Switched to PSR-4 for autoloading.
|
||||
|
||||
## v2.0.0-beta.1 (2022-05-26)
|
||||
- The default server profile for Redis is `3.0`.
|
||||
|
||||
Same as v2.0.0
|
||||
- Removed server profile for Redis 1.2.
|
||||
|
||||
- Added `SENTINEL` to the profile for Redis 2.6 and `PUBSUB` to the profile for
|
||||
Redis 2.8.
|
||||
|
||||
- `Predis\Client` can now send raw commands using `Predis\Client::executeRaw()`.
|
||||
|
||||
- Status responses are returned as instances of `Predis\Response\Status`, for
|
||||
example +OK is not returned as boolean TRUE anymore which is a breaking change
|
||||
for those using strict comparisons. Status responses can be casted to string
|
||||
values carrying the original payload, so one can do `$response == 'OK'` which
|
||||
is also more akin to how Redis replies to clients.
|
||||
|
||||
- Commands `ZRANGE`, `ZRANGEBYSCORE`, `ZREVRANGE` and `ZREVRANGEBYSCORE` using
|
||||
`WITHSCORE` return a named array of member => score instead of using an array
|
||||
of [member, score] elements. Insertion order is preserved anyway due to how
|
||||
PHP works internally.
|
||||
|
||||
- The command `ZSCAN` returns a named array of member => score instead of using
|
||||
an array of [member, score] elements. Insertion order is preserved anyway due
|
||||
to how PHP works internally.
|
||||
|
||||
- The rules for redis-cluster are now leveraged for empty key tags when using
|
||||
client-side sharding, which means that when one or the first occurrence of {}
|
||||
is found in a key it will most likely produce a different hash than previous
|
||||
versions of Predis thus leading to a different partitioning in these cases.
|
||||
|
||||
- Invoking `Predis\Client::connect()` when the underlying connection has been
|
||||
already established does not throw any exception anymore, now the connection
|
||||
simply does not attempt to perform any operation.
|
||||
|
||||
- Added the `aggregate` client option, useful to fully customize how the client
|
||||
should aggregate multiple connections when an array of connection parameters
|
||||
is passed to `Predis\Client::__construct()`.
|
||||
|
||||
- Dropped support for streamable multibulk responses. Actually we still ship the
|
||||
iterator response classes just in case anyone would want to build custom stuff
|
||||
at a level lower than the client abstraction (our standard and composable text
|
||||
protocol processors still handle them and can be used as an example).
|
||||
|
||||
- Simplified the implementation of connection parameters by removing method used
|
||||
to cast to int / bool / float certain parameters supplied by users. Casting
|
||||
values, if deemed necessary, should be done by the consumer or you can just
|
||||
subclass `Predis\Connection\Parameters` and override the `filter()` method.
|
||||
|
||||
- Changed a couple of options for our transaction abstraction:
|
||||
|
||||
- `exceptions`: overrides the value of the client option with the same name.
|
||||
Please note that it does not affect all the transaction control commands
|
||||
such as `MULTI`, `EXEC`, `DISCARD`, `WATCH` and `UNWATCH`.
|
||||
- `on_retry`: this option has been removed.
|
||||
|
||||
- Removed pipeline executors, now command pipelines can be easily customized by
|
||||
extending the standard `Predis\Pipeline\Pipeline` class. Accepted options when
|
||||
creating a pipeline using `Predis\Client::pipeline()` are:
|
||||
|
||||
- `atomic`: returns a pipeline wrapped in a MULTI / EXEC transaction
|
||||
(class: `Predis\Pipeline\Atomic`).
|
||||
- `fire-and-forget`: returns a pipeline that does not read back responses
|
||||
(class: `Predis\Pipeline\FireAndForget`).
|
||||
|
||||
- Renamed the two base abstract command classes:
|
||||
|
||||
- `Predis\Command\AbstractCommand` is now `Predis\Command\Command`
|
||||
- `Predis\Command\ScriptedCommand` is now `Predis\Command\ScriptCommand`
|
||||
|
||||
- Dropped `Predis\Command\Command::__toString()` (see issue #151).
|
||||
|
||||
- The key prefixing logic has been moved from command classes to the key prefix
|
||||
processor. Developers can define or override handlers used to prefix keys, but
|
||||
they can also define the needed logic in their command classes by implementing
|
||||
`Predis\Command\PrefixableCommandInterface` just like before.
|
||||
|
||||
- `Predis\PubSub\DispatcherLoop` now takes a `Predis\PubSub\Consumer` instance
|
||||
as the sole argument of its constructor instead of `Predis\ClientInterface`.
|
||||
|
||||
- All of the interfaces and classes related to translated Redis response types
|
||||
have been moved in the new `Predis\Response` namespace and most of them have
|
||||
been renamed to make their fully-qualified name less redundant. Now the base
|
||||
response interface is `Predis\Response\ResponseInterface`.
|
||||
|
||||
- Renamed interface `Predis\Command\Processor\CommandProcessorInterface` to a
|
||||
shorter `Predis\Command\Processor\ProcessorInterface`. Also removed interface
|
||||
for chain processors since it is basically useless.
|
||||
|
||||
- Renamed `Predis\ExecutableContextInterface` to `Predis\ClientContextInterface`
|
||||
and augmented it with a couple of required methods since this interface is no
|
||||
more comparable to a basic client as it could be misleading.
|
||||
|
||||
- The `Predis\Option` namespace is now known as `Predis\Configuration` and have
|
||||
a fully-reworked `Options` class with the ability to lazily initialize values
|
||||
using objects that responds to `__invoke()` (not all the kinds of callables)
|
||||
even for custom options defined by the user.
|
||||
|
||||
- Renamed `Predis\Connection\ConnectionInterface::writeCommand()` into
|
||||
`writeRequest()` for consistency with its counterpart, `readResponse()`.
|
||||
|
||||
- Renamed `Predis\Connection\SingleConnectionInterface::pushInitCommand()` into
|
||||
`addConnectCommand()` which is more obvious.
|
||||
|
||||
- Renamed the connection class based on both ext-phpiredis and ext-socket into
|
||||
`Predis\Connection\PhpiredisSocketConnection`. The one based on PHP's streams
|
||||
is still named `Predis\Connection\PhpiredisStreamConnection`.
|
||||
|
||||
- Renamed the connection factory class to `Predis\Connection\Factory`. Now its
|
||||
constructor does not require anymore a profile instance to create `AUTH` and
|
||||
`SELECT` commands when parameters contain both `password` and `database`. Raw
|
||||
commands will be used instead.
|
||||
|
||||
- Renamed the connection parameters class to `Predis\Connection\Parameters`. Now
|
||||
its constructor accepts only named arrays, but instances can still be created
|
||||
using both URIs or arrays using the static method `Parameters::create()`.
|
||||
|
||||
- The profile factory code has been extracted from the abstract Redis profile
|
||||
class and now lives in `Predis\Profile\Factory`.
|
||||
|
||||
- The `Predis\Connection` namespace has been completely reorganized by renaming
|
||||
a few classes and interfaces and adding some sub-namespaces.
|
||||
|
||||
- Most classes and interfaces in the `Predis\Protocol` namespace have been moved
|
||||
or renamed while rationalizing the whole API for external protocol processors.
|
||||
|
||||
|
||||
v0.8.7 (2014-08-01)
|
||||
================================================================================
|
||||
|
||||
- Added `3.0` in the server profiles aliases list for Redis 3.0. `2.8` is still
|
||||
the default server profile and `dev` still targets Redis 3.0.
|
||||
|
||||
- Added `COMMAND` to the server profile for Redis 2.8.
|
||||
|
||||
- Switched internally to the `CLUSTER SLOTS` command instead of `CLUSTER NODES`
|
||||
to fetch the updated slots map from redis-cluster. This change requires users
|
||||
to upgrade Redis nodes to >= 3.0.0b7.
|
||||
|
||||
- The updated slots map is now fetched automatically from redis-cluster upon the
|
||||
first `-MOVED` response by default. This change makes it possible to feed the
|
||||
client constructor with only a few nodes of the actual cluster composition,
|
||||
without needing a more complex configuration.
|
||||
|
||||
- Implemented support for `PING` in PUB/SUB loop for Redis >= 3.0.0b8.
|
||||
|
||||
- The default client-side sharding strategy and the one for redis-cluster now
|
||||
share the same implementations as they follow the same rules. One difference,
|
||||
aside from the different hashing function used to calculate distribution, is
|
||||
in how empty hash tags like {} are treated by redis-cluster.
|
||||
|
||||
- __FIX__: the patch applied to fix #180 introduced a regression affecting read/
|
||||
write timeouts in `Predis\Connection\PhpiredisStreamConnection`. Unfortunately
|
||||
the only possible solution requires PHP 5.4+. On PHP 5.3, read/write timeouts
|
||||
will be ignored from now on.
|
||||
|
||||
|
||||
v0.8.6 (2014-07-15)
|
||||
================================================================================
|
||||
|
||||
- Redis 2.8 is now the default server profile as there are no changes that would
|
||||
break compatibility with previous releases.
|
||||
|
||||
- Added `PFADD`, `PFCOUNT`, `PFMERGE` to the server profile for Redis 2.8 for
|
||||
handling the HyperLogLog data structure introduced in Redis 2.8.9.
|
||||
|
||||
- Added `ZLEXCOUNT`, `ZRANGEBYLEX`, `ZREMRANGEBYLEX` to the server profile for
|
||||
Redis 2.8 for handling lexicographic operations on members of sorted sets.
|
||||
|
||||
- Added support for key hash tags when using redis-cluster (Redis 3.0.0b1).
|
||||
|
||||
- __FIX__: minor tweaks to make Predis compatible with HHVM >= 2.4.0.
|
||||
|
||||
- __FIX__: responses to `INFO` are now properly parsed and will not break when
|
||||
redis sentinel is being used (ISSUE #154).
|
||||
|
||||
- __FIX__: added missing support for `INCRBYFLOAT` in cluster and replication
|
||||
configurations (ISSUE #159).
|
||||
|
||||
- __FIX__: fix parsing of the output of `CLUSTER NODES` to fetch the slots map
|
||||
from a node when redis-cluster has slaves in its configuration (ISSUE #165).
|
||||
|
||||
- __FIX__: prevent a stack overflow when iterating over large Redis collections
|
||||
using our abstraction for cursor-based iterators (ISSUE #182).
|
||||
|
||||
- __FIX__: properly discards transactions when the server immediately returns an
|
||||
error response (e.g. -OOM or -ERR on invalid arguments for a command) instead
|
||||
of a +QUEUED response (ISSUE #187).
|
||||
|
||||
- Upgraded to PHPUnit 4.* for the test suite.
|
||||
|
||||
|
||||
v0.8.5 (2014-01-16)
|
||||
================================================================================
|
||||
|
||||
- Added `2.8` in the server profiles aliases list for Redis 2.8. `2.6` is still
|
||||
the default server profile and `dev` now targets Redis 3.0.
|
||||
|
||||
- Added `SCAN`, `SSCAN`, `ZSCAN`, `HSCAN` to the server profile for Redis 2.8.
|
||||
|
||||
- Implemented PHP iterators for incremental iterations over Redis collections:
|
||||
|
||||
- keyspace (cursor-based iterator using `SCAN`)
|
||||
- sets (cursor-based iterator using `SSCAN`)
|
||||
- sorted sets (cursor-based iterator using `ZSCAN`)
|
||||
- hashes (cursor-based iterator using `HSCAN`)
|
||||
- lists (plain iterator using `LRANGE`)
|
||||
|
||||
- It is now possible to execute "raw commands" using `Predis\Command\RawCommand`
|
||||
and a variable list of command arguments. Input arguments are not filtered and
|
||||
responses are not parsed, which means arguments must follow the signature of
|
||||
the command as defined by Redis and complex responses are left untouched.
|
||||
|
||||
- URI parsing for connection parameters has been improved and has slightly less
|
||||
overhead when the number of fields in the querystring grows. New features are:
|
||||
|
||||
- Parsing does not break when value of a field contains one or more "=".
|
||||
- Repeated fieldnames using [] produce an array of values.
|
||||
- Empty or incomplete "key=value" pairs result in an empty string for "key".
|
||||
|
||||
- Various improvements and fixes to the redis-cluster connection backend:
|
||||
|
||||
- __FIX__: the `ASKING` command is sent upon -ASK redirections.
|
||||
- An updated slots-map can be fetched from nodes using the `CLUSTER NODES`
|
||||
command. By default this is a manual operation but can be enabled to get
|
||||
automatically done upon -MOVED redirections.
|
||||
- It is possible to specify a common set of connection parameters that are
|
||||
applied to connections created on the fly upon redirections to nodes not
|
||||
part of the initial pool.
|
||||
|
||||
- List of deprecated methods:
|
||||
|
||||
- `Predis\Client::multiExec()`: superseded by `Predis\Client::transaction()`
|
||||
and to be removed in the next major release.
|
||||
- `Predis\Client::pubSub()`: superseded by `Predis\Client::pubSubLoop()` and
|
||||
to be removed in the next major release. This change was needed due to the
|
||||
recently introduced `PUBSUB` command in Redis 2.8.
|
||||
|
||||
|
||||
v0.8.4 (2013-07-27)
|
||||
================================================================================
|
||||
|
||||
- Added `DUMP` and `RESTORE` to the server profile for Redis 2.6.
|
||||
|
||||
- Connection exceptions now report basic host details in their messages.
|
||||
|
||||
- Allow `Predis\Connection\PhpiredisConnection` to use a random IP when a host
|
||||
actually has several IPs (ISSUE #116).
|
||||
|
||||
- __FIX__: allow `HMSET` when using a cluster of Redis nodes with client-side
|
||||
sharding or redis-cluster (ISSUE #106).
|
||||
|
||||
- __FIX__: set `WITHSCORES` modifer for `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`
|
||||
and `ZREVRANGEBYSCORE` only when the options array passed to these commands
|
||||
has `WITHSCORES` set to `true` (ISSUE #107).
|
||||
|
||||
- __FIX__: scripted commands falling back from `EVALSHA` to `EVAL` resulted in
|
||||
PHP errors when using a prefixed client (ISSUE #109).
|
||||
|
||||
- __FIX__: `Predis\PubSub\DispatcherLoop` now works properly when using key
|
||||
prefixing (ISSUE #114).
|
||||
|
||||
|
||||
v0.8.3 (2013-02-18)
|
||||
================================================================================
|
||||
|
||||
- Added `CLIENT SETNAME` and `CLIENT GETNAME` (ISSUE #102).
|
||||
|
||||
- Implemented the `Predis\Connection\PhpiredisStreamConnection` class using the
|
||||
`phpiredis` extension like `Predis\Connection\PhpiredisStreamConnection`, but
|
||||
without requiring the `socket` extension since it relies on PHP's streams.
|
||||
|
||||
- Added support for the TCP_NODELAY flag via the `tcp_nodelay` parameter for
|
||||
stream-based connections, namely `Predis\Connection\StreamConnection` and
|
||||
`Predis\Connection\PhpiredisStreamConnection` (requires PHP >= 5.4.0).
|
||||
|
||||
- Updated the aggregated connection class for redis-cluster to work with 16384
|
||||
hash slots instead of 4096 to reflect the recent change from redis unstable
|
||||
([see this commit](https://github.com/antirez/redis/commit/ebd666d)).
|
||||
|
||||
- The constructor of `Predis\Client` now accepts a callable as first argument
|
||||
returning `Predis\Connection\ConnectionInterface`. Users can create their
|
||||
own self-contained strategies to create and set up the underlying connection.
|
||||
|
||||
- Users should return `0` from `Predis\Command\ScriptedCommand::getKeysCount()`
|
||||
instead of `FALSE` to indicate that all of the arguments of a Lua script must
|
||||
be used to populate `ARGV[]`. This does not represent a breaking change.
|
||||
|
||||
- The `Predis\Helpers` class has been deprecated and it will be removed in
|
||||
future releases.
|
||||
|
||||
|
||||
v0.8.2 (2013-02-03)
|
||||
================================================================================
|
||||
|
||||
- Added `Predis\Session\SessionHandler` to make it easy to store PHP sessions
|
||||
on Redis using Predis. Please note that this class needs either PHP >= 5.4.0
|
||||
or a polyfill for PHP's `SessionHandlerInterface`.
|
||||
|
||||
- Added the ability to get the default value of a client option directly from
|
||||
`Predis\Option\ClientOption` using the `getDefault()` method by passing the
|
||||
option name or its instance.
|
||||
|
||||
- __FIX__: the standard pipeline executor was not using the response parser
|
||||
methods associated to commands to process raw responses (ISSUE #101).
|
||||
|
||||
|
||||
v0.8.1 (2013-01-19)
|
||||
================================================================================
|
||||
|
||||
- The `connections` client option can now accept a callable object returning
|
||||
an instance of `Predis\Connection\ConnectionFactoryInterface`.
|
||||
|
||||
- Client options accepting callable objects as factories now pass their actual
|
||||
instance to the callable as the second argument.
|
||||
|
||||
- `Predis\Command\Processor\KeyPrefixProcessor` can now be directly casted to
|
||||
string to obtain the current prefix, useful with string interpolation.
|
||||
|
||||
- Added an optional callable argument to `Predis\Cluster\Distribution\HashRing`
|
||||
and `Predis\Cluster\Distribution\KetamaPureRing` constructor that can be used
|
||||
to customize how the distributor should extract the connection hash when
|
||||
initializing the nodes distribution (ISSUE #36).
|
||||
|
||||
- Correctly handle `TTL` and `PTTL` returning -2 on non existing keys starting
|
||||
with Redis 2.8.
|
||||
|
||||
- __FIX__: a missing use directive in `Predis\Transaction\MultiExecContext`
|
||||
caused PHP errors when Redis did not return `+QUEUED` replies to commands
|
||||
when inside a MULTI / EXEC context.
|
||||
|
||||
- __FIX__: the `parseResponse()` method implemented for a scripted command was
|
||||
ignored when retrying to execute a Lua script by falling back to `EVAL` after
|
||||
a `-NOSCRIPT` error (ISSUE #94).
|
||||
|
||||
- __FIX__: when subclassing `Predis\Client` the `getClientFor()` method returns
|
||||
a new instance of the subclass instead of a new instance of `Predis\Client`.
|
||||
|
||||
|
||||
v0.8.0 (2012-10-23)
|
||||
================================================================================
|
||||
|
||||
- The default server profile for Redis is now `2.6`.
|
||||
|
||||
- Certain connection parameters have been renamed:
|
||||
|
||||
- `connection_async` is now `async_connect`
|
||||
- `connection_timeout` is now `timeout`
|
||||
- `connection_persistent` is now `persistent`
|
||||
|
||||
- The `throw_errors` connection parameter has been removed and replaced by the
|
||||
new `exceptions` client option since exceptions on `-ERR` replies returned by
|
||||
Redis are not generated by connection classes anymore but instead are thrown
|
||||
by the client class and other abstractions such as pipeline contexts.
|
||||
|
||||
- Added smart support for redis-cluster (Redis v3.0) in addition to the usual
|
||||
cluster implementation that uses client-side sharding.
|
||||
|
||||
- Various namespaces and classes have been renamed to follow rules inspired by
|
||||
the Symfony2 naming conventions.
|
||||
|
||||
- The second argument of the constructor of `Predis\Client` does not accept
|
||||
strings or instances of `Predis\Profile\ServerProfileInterface` anymore.
|
||||
To specify a server profile you must explicitly set `profile` in the array
|
||||
of client options.
|
||||
|
||||
- `Predis\Command\ScriptedCommand` internally relies on `EVALSHA` instead of
|
||||
`EVAL` thus avoiding to send Lua scripts bodies on each request. The client
|
||||
automatically resends the command falling back to `EVAL` when Redis returns a
|
||||
`-NOSCRIPT` error. Automatic fallback to `EVAL` does not work with pipelines,
|
||||
inside a `MULTI / EXEC` context or with plain `EVALSHA` commands.
|
||||
|
||||
- Complex responses are no more parsed by connection classes as they must be
|
||||
processed by consumer classes using the handler associated to the issued
|
||||
command. This means that executing commands directly on connections only
|
||||
returns simple Redis types, but nothing changes when using `Predis\Client`
|
||||
or the provided abstractions for pipelines and transactions.
|
||||
|
||||
- Iterators for multi-bulk replies now skip the response parsing method of the
|
||||
command that generated the response and are passed directly to user code.
|
||||
Pipeline and transaction objects still consume automatically iterators.
|
||||
|
||||
- Cluster and replication connections now extend a new common interface,
|
||||
`Predis\Connection\AggregatedConnectionInterface`.
|
||||
|
||||
- `Predis\Connection\MasterSlaveReplication` now uses an external strategy
|
||||
class to handle the logic for checking readable / writable commands and Lua
|
||||
scripts.
|
||||
|
||||
- Command pipelines have been optimized for both speed and code cleanness, but
|
||||
at the cost of bringing a breaking change in the signature of the interface
|
||||
for pipeline executors.
|
||||
|
||||
- Added a new pipeline executor that sends commands wrapped in a MULTI / EXEC
|
||||
context to make the execution atomic: if a pipeline fails at a certain point
|
||||
then the whole pipeline is discarded.
|
||||
|
||||
- The key-hashing mechanism for commands is now handled externally and is no
|
||||
more a competence of each command class. This change is neeeded to support
|
||||
both client-side sharding and Redis cluster.
|
||||
|
||||
- `Predis\Options\Option` is now abstract, see `Predis\Option\AbstractOption`.
|
||||
|
||||
|
||||
v0.7.3 (2012-06-01)
|
||||
================================================================================
|
||||
|
||||
- New commands available in the Redis v2.6 profile (dev): `BITOP`, `BITCOUNT`.
|
||||
|
||||
- When the number of keys `Predis\Commands\ScriptedCommand` is negative, Predis
|
||||
will count from the end of the arguments list to calculate the actual number
|
||||
of keys that will be interpreted as elements for `KEYS` by the underlying
|
||||
`EVAL` command.
|
||||
|
||||
- __FIX__: `examples\CustomDistributionStrategy.php` had a mistyped constructor
|
||||
call and produced a bad distribution due to an error as pointed in ISSUE #63.
|
||||
This bug is limited to the above mentioned example and does not affect the
|
||||
classes implemented in the `Predis\Distribution` namespace.
|
||||
|
||||
- __FIX__: `Predis\Commands\ServerEvalSHA::getScriptHash()` was calculating the
|
||||
hash while it just needs to return the first argument of the command.
|
||||
|
||||
- __FIX__: `Predis\Autoloader` has been modified to allow cascading autoloaders
|
||||
for the `Predis` namespace.
|
||||
|
||||
|
||||
v0.7.2 (2012-04-01)
|
||||
================================================================================
|
||||
|
||||
- Added `2.6` in the server profiles aliases list for the upcoming Redis 2.6.
|
||||
`2.4` is still the default server profile. `dev` now targets Redis 2.8.
|
||||
|
||||
- Connection instances can be serialized and unserialized using `serialize()`
|
||||
and `unserialize()`. This is handy in certain scenarios such as client-side
|
||||
clustering or replication to lower the overhead of initializing a connection
|
||||
object with many sub-connections since unserializing them can be up to 5x
|
||||
times faster.
|
||||
|
||||
- Reworked the default autoloader to make it faster. It is also possible to
|
||||
prepend it in PHP's autoload stack.
|
||||
|
||||
- __FIX__: fixed parsing of the payload returned by `MONITOR` with Redis 2.6.
|
||||
|
||||
|
||||
v0.7.1 (2011-12-27)
|
||||
================================================================================
|
||||
|
||||
- The PEAR channel on PearHub has been deprecated in favour of `pear.nrk.io`.
|
||||
|
||||
- Miscellaneous minor fixes.
|
||||
|
||||
- Added transparent support for master / slave replication configurations where
|
||||
write operations are performed on the master server and read operations are
|
||||
routed to one of the slaves. Please refer to ISSUE #21 for a bit of history
|
||||
and more details about replication support in Predis.
|
||||
|
||||
- The `profile` client option now accepts a callable object used to initialize
|
||||
a new instance of `Predis\Profiles\IServerProfile`.
|
||||
|
||||
- Exposed a method for MULTI / EXEC contexts that adds the ability to execute
|
||||
instances of Redis commands against transaction objects.
|
||||
|
||||
|
||||
v0.7.0 (2011-12-11)
|
||||
================================================================================
|
||||
|
||||
- Predis now adheres to the PSR-0 standard which means that there is no more a
|
||||
single file holding all the classes of the library, but multiple files (one
|
||||
for each class). You can use any PSR-0 compatible autoloader to load Predis
|
||||
or just leverage the default one shipped with the library by requiring the
|
||||
`Predis/Autoloader.php` and call `Predis\Autoloader::register()`.
|
||||
|
||||
- The default server profile for Redis is now 2.4. The `dev` profile supports
|
||||
all the features of Redis 2.6 (currently unstable) such as Lua scripting.
|
||||
|
||||
- Support for long aliases (method names) for Redis commands has been dropped.
|
||||
|
||||
- Redis 1.0 is no more supported. From now on Predis will use only the unified
|
||||
protocol to serialize commands.
|
||||
|
||||
- It is possible to prefix keys transparently on a client-level basis with the
|
||||
new `prefix` client option.
|
||||
|
||||
- An external connection factory is used to initialize new connection instances
|
||||
and developers can now register their own connection classes using the new
|
||||
`connections` client option.
|
||||
|
||||
- It is possible to connect locally to Redis using UNIX domain sockets. Just
|
||||
use `unix:///path/to/redis.sock` or a named array just like in the following
|
||||
example: `array('scheme' => 'unix', 'path' => '/path/to/redis.sock');`.
|
||||
|
||||
- If the `phpiredis` extension is loaded by PHP, it is now possible to use an
|
||||
alternative connection class that leverages it to make Predis faster on many
|
||||
cases, especially when dealing with big multibulk replies, with the the only
|
||||
downside that persistent connections are not supported. Please refer to the
|
||||
documentation to see how to activate this class using the new `connections`
|
||||
client option.
|
||||
|
||||
- Predis is capable to talk with Webdis, albeit with some limitations such as
|
||||
the lack of pipelining and transactions, just by using the `http` scheme in
|
||||
in the connection parameters. All is needed is PHP with the `curl` and the
|
||||
`phpiredis` extensions loaded.
|
||||
|
||||
- Way too many changes in the public API to make a list here, we just tried to
|
||||
make all the Redis commands compatible with previous releases of v0.6 so that
|
||||
you do not have to worry if you are simply using Predis as a client. Probably
|
||||
the only breaking changes that should be mentioned here are:
|
||||
|
||||
- `throw_on_error` has been renamed to `throw_errors` and it is a connection
|
||||
parameter instead of a client option, along with `iterable_multibulk`.
|
||||
|
||||
- `key_distribution` has been removed from the client options. To customize
|
||||
the distribution strategy you must provide a callable object to the new
|
||||
`cluster` client option to configure and then return a new instance of
|
||||
`Predis\Network\IConnectionCluster`.
|
||||
|
||||
- `Predis\Client::create()` has been removed. Just use the constructor to set
|
||||
up a new instance of `Predis\Client`.
|
||||
|
||||
- `Predis\Client::pipelineSafe()` was deprecated in Predis v0.6.1 and now has
|
||||
finally removed. Use `Predis\Client::pipeline(array('safe' => true))`.
|
||||
|
||||
- `Predis\Client::rawCommand()` has been removed due to inconsistencies with
|
||||
the underlying connection abstractions. You can still get the raw resource
|
||||
out of a connection with `Predis\Network\IConnectionSingle::getResource()`
|
||||
so that you can talk directly with Redis.
|
||||
|
||||
- The `Predis\MultiBulkCommand` class has been merged into `Predis\Command` and
|
||||
thus removed. Serialization of commands is now a competence of connections.
|
||||
|
||||
- The `Predis\IConnection` interface has been splitted into two new interfaces:
|
||||
`Predis\Network\IConnectionSingle` and `Predis\Network\IConnectionCluster`.
|
||||
|
||||
- The constructor of `Predis\Client` now accepts more type of arguments such as
|
||||
instances of `Predis\IConnectionParameters` and `Predis\Network\IConnection`.
|
||||
|
||||
|
||||
v0.6.6 (2011-04-01)
|
||||
================================================================================
|
||||
|
||||
- Switched to Redis 2.2 as the default server profile (there are no changes
|
||||
that would break compatibility with previous releases). Long command names
|
||||
are no more supported by default but if you need them you can still require
|
||||
`Predis_Compatibility.php` to avoid breaking compatibility.
|
||||
|
||||
- Added a `VERSION` constant to `Predis\Client`.
|
||||
|
||||
- Some performance improvements for multibulk replies (parsing them is about
|
||||
16% faster than the previous version). A few core classes have been heavily
|
||||
optimized to reduce overhead when creating new instances.
|
||||
|
||||
- Predis now uses by default a new protocol reader, more lightweight and
|
||||
faster than the default handler-based one. Users can revert to the old
|
||||
protocol reader with the `reader` client option set to `composable`.
|
||||
This client option can also accept custom reader classes implementing the
|
||||
new `Predis\IResponseReader` interface.
|
||||
|
||||
- Added support for connecting to Redis using UNIX domain sockets (ISSUE #25).
|
||||
|
||||
- The `read_write_timeout` connection parameter can now be set to 0 or false
|
||||
to disable read and write timeouts on connections. The old behaviour of -1
|
||||
is still intact.
|
||||
|
||||
- `ZUNIONSTORE` and `ZINTERSTORE` can accept an array to specify a list of the
|
||||
source keys to be used to populate the destination key.
|
||||
|
||||
- `MGET`, `SINTER`, `SUNION` and `SDIFF` can accept an array to specify a list
|
||||
of keys. `SINTERSTORE`, `SUNIONSTORE` and `SDIFFSTORE` can also accept an
|
||||
array to specify the list of source keys.
|
||||
|
||||
- `SUBSCRIBE` and `PSUBSCRIBE` can accept a list of channels for subscription.
|
||||
|
||||
- __FIX__: some client-side clean-ups for `MULTI/EXEC` were handled incorrectly
|
||||
in a couple of corner cases (ISSUE #27).
|
||||
|
||||
|
||||
v0.6.5 (2011-02-12)
|
||||
================================================================================
|
||||
|
||||
- __FIX__: due to an untested internal change introduced in v0.6.4, a wrong
|
||||
handling of bulk reads of zero-length values was producing protocol
|
||||
desynchronization errors (ISSUE #20).
|
||||
|
||||
|
||||
v0.6.4 (2011-02-12)
|
||||
================================================================================
|
||||
|
||||
- Various performance improvements (15% ~ 25%) especially when dealing with
|
||||
long multibulk replies or when using clustered connections.
|
||||
|
||||
- Added the `on_retry` option to `Predis\MultiExecBlock` that can be used to
|
||||
specify an external callback (or any callable object) that gets invoked
|
||||
whenever a transaction is aborted by the server.
|
||||
|
||||
- Added inline (p)subscribtion via options when initializing an instance of
|
||||
`Predis\PubSubContext`.
|
||||
|
||||
|
||||
v0.6.3 (2011-01-01)
|
||||
================================================================================
|
||||
|
||||
- New commands available in the Redis v2.2 profile (dev):
|
||||
- Strings: `SETRANGE`, `GETRANGE`, `SETBIT`, `GETBIT`
|
||||
- Lists : `BRPOPLPUSH`
|
||||
|
||||
- The abstraction for `MULTI/EXEC` transactions has been dramatically improved
|
||||
by providing support for check-and-set (CAS) operations when using Redis >=
|
||||
2.2. Aborted transactions can also be optionally replayed in automatic up
|
||||
to a user-defined number of times, after which a `Predis\AbortedMultiExec`
|
||||
exception is thrown.
|
||||
|
||||
|
||||
v0.6.2 (2010-11-28)
|
||||
================================================================================
|
||||
|
||||
- Minor internal improvements and clean ups.
|
||||
|
||||
- New commands available in the Redis v2.2 profile (dev):
|
||||
- Strings: `STRLEN`
|
||||
- Lists : `LINSERT`, `RPUSHX`, `LPUSHX`
|
||||
- ZSets : `ZREVRANGEBYSCORE`
|
||||
- Misc. : `PERSIST`
|
||||
|
||||
- WATCH also accepts a single array parameter with the keys that should be
|
||||
monitored during a transaction.
|
||||
|
||||
- Improved the behaviour of `Predis\MultiExecBlock` in certain corner cases.
|
||||
|
||||
- Improved parameters checking for the SORT command.
|
||||
|
||||
- __FIX__: the `STORE` parameter for the `SORT` command didn't work correctly
|
||||
when using `0` as the target key (ISSUE #13).
|
||||
|
||||
- __FIX__: the methods for `UNWATCH` and `DISCARD` do not break anymore method
|
||||
chaining with `Predis\MultiExecBlock`.
|
||||
|
||||
|
||||
v0.6.1 (2010-07-11)
|
||||
================================================================================
|
||||
|
||||
- Minor internal improvements and clean ups.
|
||||
|
||||
- New commands available in the Redis v2.2 profile (dev):
|
||||
- Misc. : `WATCH`, `UNWATCH`
|
||||
|
||||
- Optional modifiers for `ZRANGE`, `ZREVRANGE` and `ZRANGEBYSCORE` queries are
|
||||
supported using an associative array passed as the last argument of their
|
||||
respective methods.
|
||||
|
||||
- The `LIMIT` modifier for `ZRANGEBYSCORE` can be specified using either:
|
||||
- an indexed array: `array($offset, $count)`
|
||||
- an associative array: `array('offset' => $offset, 'count' => $count)`
|
||||
|
||||
- The method `Predis\Client::__construct()` now accepts also instances of
|
||||
`Predis\ConnectionParameters`.
|
||||
|
||||
- `Predis\MultiExecBlock` and `Predis\PubSubContext` now throw an exception
|
||||
when trying to create their instances using a profile that does not
|
||||
support the required Redis commands or when the client is connected to
|
||||
a cluster of connections.
|
||||
|
||||
- Various improvements to `Predis\MultiExecBlock`:
|
||||
- fixes and more consistent behaviour across various usage cases.
|
||||
- support for `WATCH` and `UNWATCH` when using the current development
|
||||
profile (Redis v2.2) and aborted transactions.
|
||||
|
||||
- New signature for `Predis\Client::multiExec()` which is now able to accept
|
||||
an array of options for the underlying instance of `Predis\MultiExecBlock`.
|
||||
Backwards compatibility with previous releases of Predis is ensured.
|
||||
|
||||
- New signature for `Predis\Client::pipeline()` which is now able to accept
|
||||
an array of options for the underlying instance of Predis\CommandPipeline.
|
||||
Backwards compatibility with previous releases of Predis is ensured.
|
||||
The method `Predis\Client::pipelineSafe()` is to be considered deprecated.
|
||||
|
||||
- __FIX__: The `WEIGHT` modifier for `ZUNIONSTORE` and `ZINTERSTORE` was
|
||||
handled incorrectly with more than two weights specified.
|
||||
|
||||
|
||||
v0.6.0 (2010-05-24)
|
||||
================================================================================
|
||||
|
||||
- Switched to the new multi-bulk request protocol for all of the commands
|
||||
in the Redis 1.2 and Redis 2.0 profiles. Inline and bulk requests are now
|
||||
deprecated as they will be removed in future releases of Redis.
|
||||
|
||||
- The default server profile is `2.0` (targeting Redis 2.0.x). If you are
|
||||
using older versions of Redis, it is highly recommended that you specify
|
||||
which server profile the client should use (e.g. `1.2` when connecting
|
||||
to instances of Redis 1.2.x).
|
||||
|
||||
- Support for Redis 1.0 is now optional and it is provided by requiring
|
||||
'Predis_Compatibility.php' before creating an instance of `Predis\Client`.
|
||||
|
||||
- New commands added to the Redis 2.0 profile since Predis 0.5.1:
|
||||
- Strings: `SETEX`, `APPEND`, `SUBSTR`
|
||||
- ZSets : `ZCOUNT`, `ZRANK`, `ZUNIONSTORE`, `ZINTERSTORE`, `ZREMBYRANK`,
|
||||
`ZREVRANK`
|
||||
- Hashes : `HSET`, `HSETNX`, `HMSET`, `HINCRBY`, `HGET`, `HMGET`, `HDEL`,
|
||||
`HEXISTS`, `HLEN`, `HKEYS`, `HVALS`, `HGETALL`
|
||||
- PubSub : `PUBLISH`, `SUBSCRIBE`, `UNSUBSCRIBE`
|
||||
- Misc. : `DISCARD`, `CONFIG`
|
||||
|
||||
- Introduced client-level options with the new `Predis\ClientOptions` class.
|
||||
Options can be passed to the constructor of `Predis\Client` in its second
|
||||
argument as an array or an instance of `Predis\ClientOptions`. For brevity's
|
||||
sake and compatibility with older versions, the constructor still accepts
|
||||
an instance of `Predis\RedisServerProfile` in its second argument. The
|
||||
currently supported client options are:
|
||||
|
||||
- `profile` [default: `2.0` as of Predis 0.6.0]: specifies which server
|
||||
profile to use when connecting to Redis. This option accepts an instance
|
||||
of `Predis\RedisServerProfile` or a string that indicates the version.
|
||||
|
||||
- `key_distribution` [default: `Predis\Distribution\HashRing`]: specifies
|
||||
which key distribution strategy to use to distribute keys among the
|
||||
servers that compose a cluster. This option accepts an instance of
|
||||
`Predis\Distribution\IDistributionStrategy` so that users can implement
|
||||
their own key distribution strategy. `Predis\Distribution\KetamaPureRing`
|
||||
is an alternative distribution strategy providing a pure-PHP implementation
|
||||
of the same algorithm used by libketama.
|
||||
|
||||
- `throw_on_error` [default: `TRUE`]: server errors can optionally be handled
|
||||
"silently": instead of throwing an exception, the client returns an error
|
||||
response type.
|
||||
|
||||
- `iterable_multibulk` [EXPERIMENTAL - default: `FALSE`]: in addition to the
|
||||
classic way of fetching a whole multibulk reply into an array, the client
|
||||
can now optionally stream a multibulk reply down to the user code by using
|
||||
PHP iterators. It is just a little bit slower, but it can save a lot of
|
||||
memory in certain scenarios.
|
||||
|
||||
- New parameters for connections:
|
||||
|
||||
- `alias` [default: not set]: every connection can now be identified by an
|
||||
alias that is useful to get a specific connections when connected to a
|
||||
cluster of Redis servers.
|
||||
- `weight` [default: not set]: allows to balance keys asymmetrically across
|
||||
multiple servers. This is useful when you have servers with different
|
||||
amounts of memory to distribute the load of your keys accordingly.
|
||||
- `connection_async` [default: `FALSE`]: estabilish connections to servers
|
||||
in a non-blocking way, so that the client is not blocked while the socket
|
||||
resource performs the actual connection.
|
||||
- `connection_persistent` [default: `FALSE`]: the underlying socket resource
|
||||
is left open when a script ends its lifecycle. Persistent connections can
|
||||
lead to unpredictable or strange behaviours, so they should be used with
|
||||
extreme care.
|
||||
|
||||
- Introduced the `Predis\Pipeline\IPipelineExecutor` interface. Classes that
|
||||
implements this interface are used internally by the `Predis\CommandPipeline`
|
||||
class to change the behaviour of the pipeline when writing/reading commands
|
||||
from one or multiple servers. Here is the list of the default executors:
|
||||
|
||||
- `Predis\Pipeline\StandardExecutor`: exceptions generated by server errors
|
||||
might be thrown depending on the options passed to the client (see the
|
||||
`throw_on_error` client option). Instead, protocol or network errors always
|
||||
throw exceptions. This is the default executor for single and clustered
|
||||
connections and shares the same behaviour of Predis 0.5.x.
|
||||
- `Predis\Pipeline\SafeExecutor`: exceptions generated by server, protocol
|
||||
or network errors are not thrown but returned in the response array as
|
||||
instances of `Predis\ResponseError` or `Predis\CommunicationException`.
|
||||
- `Predis\Pipeline\SafeClusterExecutor`: this executor shares the same
|
||||
behaviour of `Predis\Pipeline\SafeExecutor` but it is geared towards
|
||||
clustered connections.
|
||||
|
||||
- Support for PUB/SUB is handled by the new `Predis\PubSubContext` class, which
|
||||
could also be used to build a callback dispatcher for PUB/SUB scenarios.
|
||||
|
||||
- When connected to a cluster of connections, it is now possible to get a
|
||||
new `Predis\Client` instance for a single connection of the cluster by
|
||||
passing its alias/index to the new `Predis\Client::getClientFor()` method.
|
||||
|
||||
- `Predis\CommandPipeline` and `Predis\MultiExecBlock` return their instances
|
||||
when invokink commands, thus allowing method chaining in pipelines and
|
||||
multi-exec blocks.
|
||||
|
||||
- `Predis\MultiExecBlock` can handle the new `DISCARD` command.
|
||||
|
||||
- Connections now support float values for the `connection_timeout` parameter
|
||||
to express timeouts with a microsecond resolution.
|
||||
|
||||
- __FIX__: TCP connections now respect the read/write timeout parameter when
|
||||
reading the payload of server responses. Previously, `stream_get_contents()`
|
||||
was being used internally to read data from a connection but it looks like
|
||||
PHP does not honour the specified timeout for socket streams when inside
|
||||
this function.
|
||||
|
||||
- __FIX__: The `GET` parameter for the `SORT` command now accepts also multiple
|
||||
key patterns by passing an array of strings. (ISSUE #1).
|
||||
|
||||
* __FIX__: Replies to the `DEL` command return the number of elements deleted
|
||||
by the server and not 0 or 1 interpreted as a boolean response. (ISSUE #4).
|
||||
|
||||
|
||||
v0.5.1 (2010-01-23)
|
||||
================================================================================
|
||||
|
||||
* `RPOPLPUSH` has been changed from bulk command to inline command in Redis
|
||||
1.2.1, so `ListPopLastPushHead` now extends `InlineCommand`. The old behavior
|
||||
is still available via the `ListPopLastPushHeadBulk` class so that you can
|
||||
override the server profile if you need the old (and uncorrect) behaviour
|
||||
when connecting to a Redis 1.2.0 instance.
|
||||
|
||||
* Added missing support for `BGREWRITEAOF` for Redis >= 1.2.0.
|
||||
|
||||
* Implemented a factory method for the `RedisServerProfile` class to ease the
|
||||
creation of new server profile instances based on a version string.
|
||||
|
||||
|
||||
v0.5.0 (2010-01-09)
|
||||
================================================================================
|
||||
* First versioned release of Predis
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
## Filing bug reports ##
|
||||
|
||||
Bugs or feature requests can be posted on the [GitHub issues](http://github.com/predis/predis/issues)
|
||||
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
|
||||
|
||||
@@ -18,14 +18,6 @@ least to some degree).
|
||||
Yes. Obviously persistent connections actually work only when using PHP configured as a persistent
|
||||
process reused by the web server (see [PHP-FPM](http://php-fpm.org)).
|
||||
|
||||
### Does Predis support SSL-encrypted connections? ###
|
||||
|
||||
Yes. Encrypted connections are mostly useful when connecting to Redis instances exposed by various
|
||||
cloud hosting providers without the need to configure an SSL proxy, but you should also take into
|
||||
account the general performances degradation especially during the connect() operation when the TLS
|
||||
handshake must be performed to secure the connection. Persistent SSL-encrypted connections may help
|
||||
in that respect, but they are supported only when running on PHP >= 7.0.0.
|
||||
|
||||
### Does Predis support transparent (de)serialization of values? ###
|
||||
|
||||
No and it will not ever do that by default. The reason behind this decision is that serialization is
|
||||
@@ -33,7 +25,7 @@ usually something that developers prefer to customize depending on their needs a
|
||||
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).
|
||||
do that [on this issue](http://github.com/nrk/predis/issues/29#issuecomment-1202624).
|
||||
|
||||
### How can I force Predis to connect to Redis before sending any command? ###
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2021 Daniele Alessandri
|
||||
Copyright (c) 2009-2016 Daniele Alessandri
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -1,39 +1,46 @@
|
||||
# Predis #
|
||||
|
||||
[![Software license][ico-license]](LICENSE)
|
||||
[![Latest stable][ico-version-stable]][link-packagist]
|
||||
[![Latest development][ico-version-dev]][link-packagist]
|
||||
[![Software license][ico-license]](LICENSE)
|
||||
[![Monthly installs][ico-downloads-monthly]][link-downloads]
|
||||
[![Build status][ico-build]][link-actions]
|
||||
[![Build status][ico-travis]][link-travis]
|
||||
[![HHVM support][ico-hhvm]][link-hhvm]
|
||||
|
||||
A flexible and feature-complete [Redis](http://redis.io) client for PHP 7.2 and newer.
|
||||
Flexible and feature-complete [Redis](http://redis.io) client for PHP >= 5.3 and HHVM >= 2.3.0.
|
||||
|
||||
Predis does not require any additional C extension by default, but it can be optionally paired with
|
||||
[phpiredis](https://github.com/nrk/phpiredis) to lower the overhead of the serialization and parsing
|
||||
of the [Redis RESP Protocol](http://redis.io/topics/protocol). For an __experimental__ asynchronous
|
||||
implementation of the client you can refer to [Predis\Async](https://github.com/nrk/predis-async).
|
||||
|
||||
More details about this project can be found on the [frequently asked questions](FAQ.md).
|
||||
|
||||
|
||||
## Main features ##
|
||||
|
||||
- Support for Redis from __3.0__ to __7.0__.
|
||||
- Support for different versions of Redis (from __2.0__ to __3.2__) using profiles.
|
||||
- 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).
|
||||
- Support for standalone master-slave replication setups.
|
||||
- Transparent key prefixing of keys using a customizable prefix strategy.
|
||||
- Command pipelining on both single nodes and clusters (client-side sharding only).
|
||||
- Abstraction for Redis transactions (Redis >= 2.0) and CAS operations (Redis >= 2.2).
|
||||
- Abstraction for Lua scripting (Redis >= 2.6) and automatic switching between `EVALSHA` or `EVAL`.
|
||||
- Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators.
|
||||
- Connections are established lazily by the client upon the first command and can be persisted.
|
||||
- Connections can be established via TCP/IP (also TLS/SSL-encrypted) or UNIX domain sockets.
|
||||
- Connections can be established via TCP/IP or UNIX domain sockets.
|
||||
- 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 ##
|
||||
|
||||
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).
|
||||
management of projects dependencies using [Composer](http://packagist.org/about-composer) or on our
|
||||
[own PEAR channel](http://pear.nrk.io) for a more traditional installation using PEAR. Ultimately,
|
||||
compressed archives of each release are [available on GitHub](https://github.com/nrk/predis/tags).
|
||||
|
||||
|
||||
### Loading the library ###
|
||||
@@ -50,6 +57,10 @@ require 'Predis/Autoloader.php';
|
||||
Predis\Autoloader::register();
|
||||
```
|
||||
|
||||
It is also possible to create a [phar](http://www.php.net/manual/en/intro.phar.php) archive directly
|
||||
from the repository by launching the `bin/create-phar` script. The generated phar already contains a
|
||||
stub defining its own autoloader, so you just need to `require()` it to start using the library.
|
||||
|
||||
|
||||
### Connecting to Redis ###
|
||||
|
||||
@@ -78,62 +89,34 @@ $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');
|
||||
$client = new Predis\Client('unix:///path/to/redis.sock');
|
||||
```
|
||||
|
||||
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.
|
||||
`tcp`) is also supported, with the difference that URI strings containing these schemes are parsed
|
||||
following the rules described on the IANA provisional registration.
|
||||
|
||||
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 ###
|
||||
|
||||
@@ -141,20 +124,19 @@ Many aspects and behaviors of the client can be configured by passing specific c
|
||||
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:
|
||||
|
||||
- `prefix`: prefix string applied to every key found in commands.
|
||||
- `profile`: specifies the profile to use to match a specific version of Redis.
|
||||
- `prefix`: prefix string 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.
|
||||
- `cluster`: specifies a cluster backend (`predis`, `redis` or callable object).
|
||||
- `replication`: specifies a replication backend (`TRUE` or callable object).
|
||||
- `aggregate`: overrides `cluster` and `replication` to provide a custom connections aggregator.
|
||||
|
||||
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.
|
||||
@@ -165,18 +147,18 @@ are stored in the options container for later use through the library.
|
||||
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.
|
||||
connection parameters 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:
|
||||
By default, when no specific client options are set and an array of connection parameters is passed
|
||||
to the client's constructor, Predis configures itself to work in clustering mode using a traditional
|
||||
client-side sharding approach to create a cluster of independent nodes and distribute the keyspace
|
||||
among them. This approach needs some form of external health monitoring of nodes and requires manual
|
||||
operations to rebalance the keyspace when changing its configuration by adding or removing nodes:
|
||||
|
||||
```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);
|
||||
```
|
||||
@@ -206,45 +188,16 @@ the value of a key. Instead of raising a connection error when a slave fails, th
|
||||
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):
|
||||
identified as the master (this can be done via connection parameters using the `alias` parameter set
|
||||
to `master`) and one or more servers acting as slaves:
|
||||
|
||||
```php
|
||||
$parameters = ['tcp://10.0.0.1?role=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||
$options = ['replication' => 'predis'];
|
||||
$parameters = ['tcp://10.0.0.1?alias=master', 'tcp://10.0.0.2', 'tcp://10.0.0.3'];
|
||||
$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,
|
||||
@@ -252,13 +205,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', 'tcp://10.0.0.3'];
|
||||
$options = ['replication' => function () {
|
||||
// Set scripts that won't trigger a switch from a slave to the master node.
|
||||
$strategy = new Predis\Replication\ReplicationStrategy();
|
||||
$strategy->setScriptReadOnly($LUA_SCRIPT);
|
||||
|
||||
return new Predis\Connection\Replication\MasterSlaveReplication($strategy);
|
||||
return new Predis\Connection\Aggregate\MasterSlaveReplication($strategy);
|
||||
}];
|
||||
|
||||
$client = new Predis\Client($parameters, $options);
|
||||
@@ -317,7 +270,7 @@ of a transaction using CAS you can see [the following example](examples/transact
|
||||
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:
|
||||
@@ -329,12 +282,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();
|
||||
```
|
||||
@@ -353,7 +303,7 @@ $response = $client->executeRaw(['SET', 'foo', 'bar']);
|
||||
While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using
|
||||
directly [`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha),
|
||||
Predis offers script commands as an higher level abstraction built upon them to make things simple.
|
||||
Script commands can be registered in the command factory used by the client and are accessible as if
|
||||
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)
|
||||
@@ -379,12 +329,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());
|
||||
```
|
||||
@@ -406,17 +353,6 @@ $client = new Predis\Client('tcp://127.0.0.1', [
|
||||
]);
|
||||
```
|
||||
|
||||
The client can also be configured to rely on a [phpiredis](https://github.com/nrk/phpiredis)-backend
|
||||
by specifying a descriptive string for the `connections` client option. Supported string values are:
|
||||
|
||||
- `phpiredis-stream` maps `tcp`, `redis` and `unix` to `Predis\Connection\PhpiredisStreamConnection`
|
||||
- `phpiredis-socket` maps `tcp`, `redis` and `unix` to `Predis\Connection\PhpiredisSocketConnection`
|
||||
- `phpiredis` is simply an alias of `phpiredis-stream`
|
||||
|
||||
```php
|
||||
$client = new Predis\Client('tcp://127.0.0.1', ['connections' => 'phpiredis']);
|
||||
```
|
||||
|
||||
Developers can create their own connection classes to support whole new network backends, extend
|
||||
existing classes or provide completely different implementations. Connection classes must implement
|
||||
`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`:
|
||||
@@ -453,14 +389,18 @@ stay consistent while working on the project.
|
||||
__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 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).
|
||||
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 ##
|
||||
@@ -468,27 +408,29 @@ found [on its actions page](https://github.com/predis/predis/actions).
|
||||
|
||||
### Project related links ###
|
||||
|
||||
- [Source code](https://github.com/predis/predis)
|
||||
- [Wiki](https://github.com/predis/predis/wiki)
|
||||
- [Issue tracker](https://github.com/predis/predis/issues)
|
||||
- [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))
|
||||
- [Till Krüss](https://till.im) ([Twitter](http://twitter.com/tillkruss))
|
||||
|
||||
|
||||
### 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-license]: https://img.shields.io/github/license/nrk/predis.svg?style=flat-square
|
||||
[ico-version-stable]: https://img.shields.io/packagist/v/predis/predis.svg?style=flat-square
|
||||
[ico-version-dev]: https://img.shields.io/packagist/vpre/predis/predis.svg?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/workflow/status/predis/predis/Tests/main?style=flat-square
|
||||
[ico-travis]: https://img.shields.io/travis/nrk/predis.svg?style=flat-square
|
||||
[ico-hhvm]: https://img.shields.io/hhvm/predis/predis.svg?style=flat-square
|
||||
|
||||
[link-packagist]: https://packagist.org/packages/predis/predis
|
||||
[link-actions]: https://github.com/predis/predis/actions
|
||||
[link-travis]: https://travis-ci.org/nrk/predis
|
||||
[link-downloads]: https://packagist.org/packages/predis/predis/stats
|
||||
[link-hhvm]: http://hhvm.h4cc.de/package/predis/predis
|
||||
|
||||
+21
-24
@@ -15,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
|
||||
//
|
||||
@@ -45,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;
|
||||
}
|
||||
|
||||
@@ -97,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)) {
|
||||
@@ -110,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);
|
||||
@@ -118,11 +109,18 @@ class CommandTestCaseGenerator
|
||||
|
||||
protected function getTestRealm()
|
||||
{
|
||||
if (empty($this->options['realm'])) {
|
||||
throw new RuntimeException('Invalid value for realm has been sepcified (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()
|
||||
@@ -132,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.");
|
||||
}
|
||||
@@ -176,7 +173,7 @@ class CommandTestCaseGenerator
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @group commands
|
||||
@@ -187,7 +184,7 @@ class $class extends PredisCommandTestCase
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedCommand(): string
|
||||
protected function getExpectedCommand()
|
||||
{
|
||||
return '$fqn';
|
||||
}
|
||||
@@ -195,7 +192,7 @@ class $class extends PredisCommandTestCase
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getExpectedId(): string
|
||||
protected function getExpectedId()
|
||||
{
|
||||
return '$id';
|
||||
}
|
||||
@@ -203,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.');
|
||||
|
||||
@@ -219,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.');
|
||||
|
||||
@@ -239,7 +236,7 @@ PHP;
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testPrefixKeys(): void
|
||||
public function testPrefixKeys()
|
||||
{
|
||||
\$this->markTestIncomplete('This test has not been implemented yet.');
|
||||
|
||||
@@ -255,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']);
|
||||
+7
-26
@@ -1,50 +1,31 @@
|
||||
{
|
||||
"name": "predis/predis",
|
||||
"type": "library",
|
||||
"description": "A flexible and feature-complete Redis client for PHP.",
|
||||
"description": "Flexible and feature-complete Redis client for PHP and HHVM",
|
||||
"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": "Daniele Alessandri",
|
||||
"email": "suppakilla@gmail.com",
|
||||
"homepage": "http://clorophilla.net",
|
||||
"role": "Creator & Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Till Krüss",
|
||||
"homepage": "https://till.im",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tillkruss"
|
||||
"homepage": "http://clorophilla.net"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
"php": ">=5.3.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.0 || ~9.4.4"
|
||||
"phpunit/phpunit": "~4.8"
|
||||
},
|
||||
"suggest": {
|
||||
"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/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.0-dev"
|
||||
}
|
||||
"psr-4": {"Predis\\": "src/"}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,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
|
||||
{
|
||||
@@ -104,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'];
|
||||
|
||||
@@ -36,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]";
|
||||
|
||||
@@ -51,7 +51,7 @@ class EventsListener implements Countable
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
public function __invoke($payload, $dispatcher)
|
||||
public function __invoke($payload)
|
||||
{
|
||||
$this->events[] = $payload;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ class EventsListener implements Countable
|
||||
$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();
|
||||
}
|
||||
|
||||
@@ -13,10 +13,11 @@ 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;
|
||||
|
||||
@@ -49,9 +50,12 @@ LUA;
|
||||
}
|
||||
|
||||
$client = new Predis\Client($single_server, array(
|
||||
'commands' => array(
|
||||
'increxby' => 'IncrementExistingKeysBy',
|
||||
),
|
||||
'profile' => function ($options) {
|
||||
$profile = $options->getDefault('profile');
|
||||
$profile->defineCommand('increxby', 'IncrementExistingKeysBy');
|
||||
|
||||
return $profile;
|
||||
},
|
||||
));
|
||||
|
||||
$client->mset('foo', 10, 'foobar', 100);
|
||||
|
||||
@@ -13,7 +13,7 @@ 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:
|
||||
//
|
||||
@@ -29,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');
|
||||
|
||||
@@ -14,12 +14,12 @@ 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;
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
@@ -47,14 +47,17 @@ LUA;
|
||||
// ------------------------------------------------------------------------- //
|
||||
|
||||
$parameters = array(
|
||||
'tcp://127.0.0.1:6381?role=master&database=15',
|
||||
'tcp://127.0.0.1:6382?role=slave&alias=slave-01&database=15',
|
||||
'tcp://127.0.0.1:6379/?alias=master',
|
||||
'tcp://127.0.0.1:6380/?alias=slave',
|
||||
);
|
||||
|
||||
$options = array(
|
||||
'commands' => array(
|
||||
'hmgetall' => 'HashMultipleGetAll',
|
||||
),
|
||||
'profile' => function ($options, $option) {
|
||||
$profile = $options->getDefault($option);
|
||||
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
|
||||
|
||||
return $profile;
|
||||
},
|
||||
'replication' => function () {
|
||||
$strategy = new ReplicationStrategy();
|
||||
$strategy->setScriptReadOnly(HashMultipleGetAll::BODY);
|
||||
@@ -76,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,58 +0,0 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
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 = array(
|
||||
'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, array(
|
||||
'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'.
|
||||
*/
|
||||
@@ -22,28 +22,28 @@ require __DIR__.'/shared.php';
|
||||
//
|
||||
|
||||
$parameters = array(
|
||||
'tcp://127.0.0.1:6381?role=master&database=15',
|
||||
'tcp://127.0.0.1:6382?role=slave&database=15',
|
||||
'tcp://127.0.0.1:6379?database=15&alias=master',
|
||||
'tcp://127.0.0.1:6380?database=15&alias=slave',
|
||||
);
|
||||
|
||||
$options = array('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.
|
||||
|
||||
+8
-12
@@ -9,10 +9,6 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
if (PHP_SAPI !== 'cli') {
|
||||
die("Example scripts are meant to be executed locally via CLI.");
|
||||
}
|
||||
|
||||
require __DIR__.'/../autoload.php';
|
||||
|
||||
function redis_version($info)
|
||||
@@ -27,22 +23,22 @@ function redis_version($info)
|
||||
}
|
||||
|
||||
$single_server = array(
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'database' => 15,
|
||||
);
|
||||
|
||||
$multiple_servers = array(
|
||||
array(
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'database' => 15,
|
||||
'alias' => 'first',
|
||||
'alias' => 'first',
|
||||
),
|
||||
array(
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6380,
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6380,
|
||||
'database' => 15,
|
||||
'alias' => 'second',
|
||||
'alias' => 'second',
|
||||
),
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ function zpop($client, $key)
|
||||
{
|
||||
$element = null;
|
||||
$options = array(
|
||||
'cas' => true, // Initialize with support for CAS operations
|
||||
'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.
|
||||
|
||||
+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 Redis client for PHP and HHVM"
|
||||
homepage = "http://github.com/nrk/predis"
|
||||
license = "MIT"
|
||||
version = "1.0.4"
|
||||
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
|
||||
@@ -8,6 +8,8 @@
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="true"
|
||||
beStrictAboutTestSize="true"
|
||||
beStrictAboutTestsThatDoNotTestAnything="true">
|
||||
|
||||
<testsuites>
|
||||
@@ -36,6 +38,7 @@
|
||||
|
||||
<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" />
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?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"
|
||||
syntaxCheck="true"
|
||||
beStrictAboutTestSize="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>
|
||||
@@ -16,7 +16,6 @@ namespace Predis;
|
||||
*
|
||||
* @author Eric Naeseth <eric@thumbtack.com>
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Autoloader
|
||||
{
|
||||
|
||||
+117
-128
@@ -16,8 +16,8 @@ use Predis\Command\RawCommand;
|
||||
use Predis\Command\ScriptCommand;
|
||||
use Predis\Configuration\Options;
|
||||
use Predis\Configuration\OptionsInterface;
|
||||
use Predis\Connection\AggregateConnectionInterface;
|
||||
use Predis\Connection\ConnectionInterface;
|
||||
use Predis\Connection\Parameters;
|
||||
use Predis\Connection\ParametersInterface;
|
||||
use Predis\Monitor\Consumer as MonitorConsumer;
|
||||
use Predis\Pipeline\Pipeline;
|
||||
@@ -38,18 +38,13 @@ use Predis\Transaction\MultiExec as MultiExecTransaction;
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class Client implements ClientInterface, \IteratorAggregate
|
||||
class Client implements ClientInterface
|
||||
{
|
||||
const VERSION = '2.0.0';
|
||||
const VERSION = '1.0.4';
|
||||
|
||||
/** @var OptionsInterface */
|
||||
private $options;
|
||||
|
||||
/** @var ConnectionInterface */
|
||||
private $connection;
|
||||
|
||||
/** @var Command\FactoryInterface */
|
||||
private $commands;
|
||||
protected $connection;
|
||||
protected $options;
|
||||
private $profile;
|
||||
|
||||
/**
|
||||
* @param mixed $parameters Connection parameters for one or more servers.
|
||||
@@ -57,101 +52,126 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
*/
|
||||
public function __construct($parameters = null, $options = null)
|
||||
{
|
||||
$this->options = static::createOptions($options ?? new Options);
|
||||
$this->connection = static::createConnection($this->options, $parameters ?? new Parameters);
|
||||
$this->commands = $this->options->commands;
|
||||
$this->options = $this->createOptions($options ?: array());
|
||||
$this->connection = $this->createConnection($parameters ?: array());
|
||||
$this->profile = $this->options->profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new set of client options for the client.
|
||||
* Creates a new instance of Predis\Configuration\Options from different
|
||||
* types of arguments or simply returns the passed argument if it is an
|
||||
* instance of Predis\Configuration\OptionsInterface.
|
||||
*
|
||||
* @param array|OptionsInterface $options Set of client options
|
||||
* @param mixed $options Client options.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return OptionsInterface
|
||||
*/
|
||||
protected static function createOptions($options)
|
||||
protected function createOptions($options)
|
||||
{
|
||||
if (is_array($options)) {
|
||||
return new Options($options);
|
||||
} elseif ($options instanceof OptionsInterface) {
|
||||
return $options;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Invalid type for client options');
|
||||
}
|
||||
|
||||
if ($options instanceof OptionsInterface) {
|
||||
return $options;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Invalid type for client options.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates single or aggregate connections from supplied arguments.
|
||||
* Creates single or aggregate connections from different types of arguments
|
||||
* (string, array) or returns the passed argument if it is an instance of a
|
||||
* class implementing Predis\Connection\ConnectionInterface.
|
||||
*
|
||||
* This method accepts the following types to create a connection instance:
|
||||
* Accepted types for connection parameters are:
|
||||
*
|
||||
* - Array (dictionary: single connection, indexed: aggregate connections)
|
||||
* - String (URI for a single connection)
|
||||
* - Callable (connection initializer callback)
|
||||
* - Instance of Predis\Connection\ParametersInterface (used as-is)
|
||||
* - Instance of Predis\Connection\ConnectionInterface (returned as-is)
|
||||
* - Instance of Predis\Connection\ConnectionInterface.
|
||||
* - Instance of Predis\Connection\ParametersInterface.
|
||||
* - Array
|
||||
* - String
|
||||
* - Callable
|
||||
*
|
||||
* When a callable is passed, it receives the original set of client options
|
||||
* and must return an instance of Predis\Connection\ConnectionInterface.
|
||||
*
|
||||
* Connections are created using the connection factory (in case of single
|
||||
* connections) or a specialized aggregate connection initializer (in case
|
||||
* of cluster and replication) retrieved from the supplied client options.
|
||||
*
|
||||
* @param OptionsInterface $options Client options container
|
||||
* @param mixed $parameters Connection parameters
|
||||
* @param mixed $parameters Connection parameters or connection instance.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
protected static function createConnection(OptionsInterface $options, $parameters)
|
||||
protected function createConnection($parameters)
|
||||
{
|
||||
if ($parameters instanceof ConnectionInterface) {
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
if ($parameters instanceof ParametersInterface || is_string($parameters)) {
|
||||
return $options->connections->create($parameters);
|
||||
return $this->options->connections->create($parameters);
|
||||
}
|
||||
|
||||
if (is_array($parameters)) {
|
||||
if (!isset($parameters[0])) {
|
||||
return $options->connections->create($parameters);
|
||||
} elseif ($options->defined('cluster') && $initializer = $options->cluster) {
|
||||
return $initializer($parameters, true);
|
||||
} elseif ($options->defined('replication') && $initializer = $options->replication) {
|
||||
return $initializer($parameters, true);
|
||||
} elseif ($options->defined('aggregate') && $initializer = $options->aggregate) {
|
||||
return $initializer($parameters, false);
|
||||
} else {
|
||||
throw new \InvalidArgumentException(
|
||||
'Array of connection parameters requires `cluster`, `replication` or `aggregate` client option'
|
||||
);
|
||||
return $this->options->connections->create($parameters);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_callable($parameters)) {
|
||||
$connection = call_user_func($parameters, $options);
|
||||
$options = $this->options;
|
||||
|
||||
if (!$connection instanceof ConnectionInterface) {
|
||||
throw new \InvalidArgumentException('Callable parameters must return a valid connection');
|
||||
if ($options->defined('aggregate')) {
|
||||
$initializer = $this->getConnectionInitializerWrapper($options->aggregate);
|
||||
$connection = $initializer($parameters, $options);
|
||||
} else {
|
||||
if ($options->defined('replication') && $replication = $options->replication) {
|
||||
$connection = $replication;
|
||||
} else {
|
||||
$connection = $options->cluster;
|
||||
}
|
||||
|
||||
$options->connections->aggregate($connection, $parameters);
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Invalid type for connection parameters');
|
||||
if (is_callable($parameters)) {
|
||||
$initializer = $this->getConnectionInitializerWrapper($parameters);
|
||||
$connection = $initializer($this->options);
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Invalid type for connection parameters.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a callable to make sure that its returned value represents a valid
|
||||
* connection type.
|
||||
*
|
||||
* @param mixed $callable
|
||||
*
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function getConnectionInitializerWrapper($callable)
|
||||
{
|
||||
return function () use ($callable) {
|
||||
$connection = call_user_func_array($callable, func_get_args());
|
||||
|
||||
if (!$connection instanceof ConnectionInterface) {
|
||||
throw new \UnexpectedValueException(
|
||||
'The callable connection initializer returned an invalid type.'
|
||||
);
|
||||
}
|
||||
|
||||
return $connection;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCommandFactory()
|
||||
public function getProfile()
|
||||
{
|
||||
return $this->commands;
|
||||
return $this->profile;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,53 +183,23 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new client using a specific underlying connection.
|
||||
* Creates a new client instance for the specified connection ID or alias,
|
||||
* only when working with an aggregate connection (cluster, replication).
|
||||
* The new client instances uses the same options of the original one.
|
||||
*
|
||||
* This method allows to create a new client instance by picking a specific
|
||||
* connection out of an aggregate one, with the same options of the original
|
||||
* client instance.
|
||||
* @param string $connectionID Identifier of a connection.
|
||||
*
|
||||
* The specified selector defines which logic to use to look for a suitable
|
||||
* connection by the specified value. Supported selectors are:
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* - `id`
|
||||
* - `key`
|
||||
* - `slot`
|
||||
* - `command`
|
||||
* - `alias`
|
||||
* - `role`
|
||||
*
|
||||
* Internally the client relies on duck-typing and follows this convention:
|
||||
*
|
||||
* $selector string => getConnectionBy$selector($value) method
|
||||
*
|
||||
* This means that support for specific selectors may vary depending on the
|
||||
* actual logic implemented by connection classes and there is no interface
|
||||
* binding a connection class to implement any of these.
|
||||
*
|
||||
* @param string $selector Type of selector.
|
||||
* @param mixed $value Value to be used by the selector.
|
||||
*
|
||||
* @return ClientInterface
|
||||
* @return Client
|
||||
*/
|
||||
public function getClientBy($selector, $value)
|
||||
public function getClientFor($connectionID)
|
||||
{
|
||||
$selector = strtolower($selector);
|
||||
|
||||
if (!in_array($selector, array('id', 'key', 'slot', 'role', 'alias', 'command'))) {
|
||||
throw new \InvalidArgumentException("Invalid selector type: `$selector`");
|
||||
if (!$connection = $this->getConnectionById($connectionID)) {
|
||||
throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
|
||||
}
|
||||
|
||||
if (!method_exists($this->connection, $method = "getConnectionBy$selector")) {
|
||||
$class = get_class($this->connection);
|
||||
throw new \InvalidArgumentException("Selecting connection by $selector is not supported by $class");
|
||||
}
|
||||
|
||||
if (!$connection = $this->connection->$method($value)) {
|
||||
throw new \InvalidArgumentException("Cannot find a connection by $selector matching `$value`");
|
||||
}
|
||||
|
||||
return new static($connection, $this->getOptions());
|
||||
return new static($connection, $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,12 +247,33 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the specified connection from the aggregate connection when the
|
||||
* client is in cluster or replication mode.
|
||||
*
|
||||
* @param string $connectionID Index or alias of the single connection.
|
||||
*
|
||||
* @throws NotSupportedException
|
||||
*
|
||||
* @return Connection\NodeConnectionInterface
|
||||
*/
|
||||
public function getConnectionById($connectionID)
|
||||
{
|
||||
if (!$this->connection instanceof AggregateConnectionInterface) {
|
||||
throw new NotSupportedException(
|
||||
'Retrieving connections by ID is supported only by aggregate connections.'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->connection->getConnectionById($connectionID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a command without filtering its arguments, parsing the response,
|
||||
* applying any prefix to keys or throwing exceptions on Redis errors even
|
||||
* regardless of client options.
|
||||
*
|
||||
* It is possible to identify Redis error responses from normal responses
|
||||
* It is possibile to indentify Redis error responses from normal responses
|
||||
* using the second optional argument which is populated by reference.
|
||||
*
|
||||
* @param array $arguments Command arguments as defined by the command signature.
|
||||
@@ -273,10 +284,9 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
public function executeRaw(array $arguments, &$error = null)
|
||||
{
|
||||
$error = false;
|
||||
$commandID = array_shift($arguments);
|
||||
|
||||
$response = $this->connection->executeCommand(
|
||||
new RawCommand($commandID, $arguments)
|
||||
new RawCommand($arguments)
|
||||
);
|
||||
|
||||
if ($response instanceof ResponseInterface) {
|
||||
@@ -305,7 +315,7 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
*/
|
||||
public function createCommand($commandID, $arguments = array())
|
||||
{
|
||||
return $this->commands->create($commandID, $arguments);
|
||||
return $this->profile->createCommand($commandID, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,7 +349,10 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
|
||||
{
|
||||
if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
|
||||
$response = $this->executeCommand($command->getEvalCommand());
|
||||
$eval = $this->createCommand('EVAL');
|
||||
$eval->setRawArguments($command->getEvalArguments());
|
||||
|
||||
$response = $this->executeCommand($eval);
|
||||
|
||||
if (!$response instanceof ResponseInterface) {
|
||||
$response = $command->parseResponse($response);
|
||||
@@ -382,11 +395,9 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
|
||||
return $this->$initializer($arg0, $arg1);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
default:
|
||||
return $this->$initializer($this, $argv);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,7 +476,7 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new publish/subscribe context and returns it, or starts its loop
|
||||
* Creates a new publis/subscribe context and returns it, or starts its loop
|
||||
* inside the optionally provided callable object.
|
||||
*
|
||||
* @param mixed ... Array of options, a callable for execution, or both.
|
||||
@@ -509,26 +520,4 @@ class Client implements ClientInterface, \IteratorAggregate
|
||||
{
|
||||
return new MonitorConsumer($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
$clients = array();
|
||||
$connection = $this->getConnection();
|
||||
|
||||
if (!$connection instanceof \Traversable) {
|
||||
return new \ArrayIterator(array(
|
||||
(string) $connection => new static($connection, $this->getOptions())
|
||||
));
|
||||
}
|
||||
|
||||
foreach ($connection as $node) {
|
||||
$clients[(string) $node] = new static($node, $this->getOptions());
|
||||
}
|
||||
|
||||
return new \ArrayIterator($clients);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use Predis\Command\CommandInterface;
|
||||
/**
|
||||
* Interface defining a client-side context such as a pipeline or transaction.
|
||||
*
|
||||
* @method $this del(array|string $keys)
|
||||
* @method $this del(array $keys)
|
||||
* @method $this dump($key)
|
||||
* @method $this exists($key)
|
||||
* @method $this expire($key, $seconds)
|
||||
@@ -38,8 +38,7 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this append($key, $value)
|
||||
* @method $this bitcount($key, $start = null, $end = null)
|
||||
* @method $this bitop($operation, $destkey, $key)
|
||||
* @method $this bitfield($key, $subcommand, ...$subcommandArg)
|
||||
* @method $this bitpos($key, $bit, $start = null, $end = null)
|
||||
* @method $this bitfield($key, ...)
|
||||
* @method $this decr($key)
|
||||
* @method $this decrby($key, $decrement)
|
||||
* @method $this get($key)
|
||||
@@ -74,15 +73,15 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this hsetnx($key, $field, $value)
|
||||
* @method $this hvals($key)
|
||||
* @method $this hstrlen($key, $field)
|
||||
* @method $this blpop(array|string $keys, $timeout)
|
||||
* @method $this brpop(array|string $keys, $timeout)
|
||||
* @method $this blpop(array $keys, $timeout)
|
||||
* @method $this brpop(array $keys, $timeout)
|
||||
* @method $this brpoplpush($source, $destination, $timeout)
|
||||
* @method $this lindex($key, $index)
|
||||
* @method $this linsert($key, $whence, $pivot, $value)
|
||||
* @method $this llen($key)
|
||||
* @method $this lpop($key)
|
||||
* @method $this lpush($key, array $values)
|
||||
* @method $this lpushx($key, array $values)
|
||||
* @method $this lpushx($key, $value)
|
||||
* @method $this lrange($key, $start, $stop)
|
||||
* @method $this lrem($key, $count, $value)
|
||||
* @method $this lset($key, $index, $value)
|
||||
@@ -90,13 +89,13 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this rpop($key)
|
||||
* @method $this rpoplpush($source, $destination)
|
||||
* @method $this rpush($key, array $values)
|
||||
* @method $this rpushx($key, array $values)
|
||||
* @method $this rpushx($key, $value)
|
||||
* @method $this sadd($key, array $members)
|
||||
* @method $this scard($key)
|
||||
* @method $this sdiff(array|string $keys)
|
||||
* @method $this sdiffstore($destination, array|string $keys)
|
||||
* @method $this sinter(array|string $keys)
|
||||
* @method $this sinterstore($destination, array|string $keys)
|
||||
* @method $this sdiff(array $keys)
|
||||
* @method $this sdiffstore($destination, array $keys)
|
||||
* @method $this sinter(array $keys)
|
||||
* @method $this sinterstore($destination, array $keys)
|
||||
* @method $this sismember($key, $member)
|
||||
* @method $this smembers($key)
|
||||
* @method $this smove($source, $destination, $member)
|
||||
@@ -104,13 +103,13 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this srandmember($key, $count = null)
|
||||
* @method $this srem($key, $member)
|
||||
* @method $this sscan($key, $cursor, array $options = null)
|
||||
* @method $this sunion(array|string $keys)
|
||||
* @method $this sunionstore($destination, array|string $keys)
|
||||
* @method $this sunion(array $keys)
|
||||
* @method $this sunionstore($destination, array $keys)
|
||||
* @method $this zadd($key, array $membersAndScoresDictionary)
|
||||
* @method $this zcard($key)
|
||||
* @method $this zcount($key, $min, $max)
|
||||
* @method $this zincrby($key, $increment, $member)
|
||||
* @method $this zinterstore($destination, array|string $keys, array $options = null)
|
||||
* @method $this zinterstore($destination, array $keys, array $options = null)
|
||||
* @method $this zrange($key, $start, $stop, array $options = null)
|
||||
* @method $this zrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method $this zrank($key, $member)
|
||||
@@ -118,9 +117,9 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this zremrangebyrank($key, $start, $stop)
|
||||
* @method $this zremrangebyscore($key, $min, $max)
|
||||
* @method $this zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method $this zrevrangebyscore($key, $max, $min, array $options = null)
|
||||
* @method $this zrevrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method $this zrevrank($key, $member)
|
||||
* @method $this zunionstore($destination, array|string $keys, array $options = null)
|
||||
* @method $this zunionstore($destination, array $keys, array $options = null)
|
||||
* @method $this zscore($key, $member)
|
||||
* @method $this zscan($key, $cursor, array $options = null)
|
||||
* @method $this zrangebylex($key, $start, $stop, array $options = null)
|
||||
@@ -128,8 +127,8 @@ use Predis\Command\CommandInterface;
|
||||
* @method $this zremrangebylex($key, $min, $max)
|
||||
* @method $this zlexcount($key, $min, $max)
|
||||
* @method $this pfadd($key, array $elements)
|
||||
* @method $this pfmerge($destinationKey, array|string $sourceKeys)
|
||||
* @method $this pfcount(array|string $keys)
|
||||
* @method $this pfmerge($destinationKey, array $sourceKeys)
|
||||
* @method $this pfcount(array $keys)
|
||||
* @method $this pubsub($subcommand, $argument)
|
||||
* @method $this publish($channel, $message)
|
||||
* @method $this discard()
|
||||
|
||||
+151
-156
@@ -12,10 +12,9 @@
|
||||
namespace Predis;
|
||||
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Command\FactoryInterface;
|
||||
use Predis\Configuration\OptionsInterface;
|
||||
use Predis\Connection\ConnectionInterface;
|
||||
use Predis\Response\Status;
|
||||
use Predis\Profile\ProfileInterface;
|
||||
|
||||
/**
|
||||
* Interface defining a client able to execute commands against Redis.
|
||||
@@ -25,168 +24,164 @@ use Predis\Response\Status;
|
||||
* and more friendly interface to ease programming which is described in the
|
||||
* following list of methods:
|
||||
*
|
||||
* @method int del(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method string|null dump(string $key)
|
||||
* @method int exists(string $key)
|
||||
* @method int expire(string $key, int $seconds)
|
||||
* @method int expireat(string $key, int $timestamp)
|
||||
* @method array keys(string $pattern)
|
||||
* @method int move(string $key, int $db)
|
||||
* @method mixed object($subcommand, string $key)
|
||||
* @method int persist(string $key)
|
||||
* @method int pexpire(string $key, int $milliseconds)
|
||||
* @method int pexpireat(string $key, int $timestamp)
|
||||
* @method int pttl(string $key)
|
||||
* @method string|null randomkey()
|
||||
* @method mixed rename(string $key, string $target)
|
||||
* @method int renamenx(string $key, string $target)
|
||||
* @method array scan($cursor, array $options = null)
|
||||
* @method array sort(string $key, array $options = null)
|
||||
* @method int ttl(string $key)
|
||||
* @method mixed type(string $key)
|
||||
* @method int append(string $key, $value)
|
||||
* @method int bitcount(string $key, $start = null, $end = null)
|
||||
* @method int bitop($operation, $destkey, $key)
|
||||
* @method array|null bitfield(string $key, $subcommand, ...$subcommandArg)
|
||||
* @method int bitpos(string $key, $bit, $start = null, $end = null)
|
||||
* @method int decr(string $key)
|
||||
* @method int decrby(string $key, int $decrement)
|
||||
* @method string|null get(string $key)
|
||||
* @method int getbit(string $key, $offset)
|
||||
* @method string getrange(string $key, $start, $end)
|
||||
* @method string|null getset(string $key, $value)
|
||||
* @method int incr(string $key)
|
||||
* @method int incrby(string $key, int $increment)
|
||||
* @method string incrbyfloat(string $key, int|float $increment)
|
||||
* @method array mget(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method Status psetex(string $key, $milliseconds, $value)
|
||||
* @method Status set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit(string $key, $offset, $value)
|
||||
* @method Status setex(string $key, $seconds, $value)
|
||||
* @method int setnx(string $key, $value)
|
||||
* @method int setrange(string $key, $offset, $value)
|
||||
* @method int strlen(string $key)
|
||||
* @method int hdel(string $key, array $fields)
|
||||
* @method int hexists(string $key, string $field)
|
||||
* @method string|null hget(string $key, string $field)
|
||||
* @method array hgetall(string $key)
|
||||
* @method int hincrby(string $key, string $field, int $increment)
|
||||
* @method string hincrbyfloat(string $key, string $field, int|float $increment)
|
||||
* @method array hkeys(string $key)
|
||||
* @method int hlen(string $key)
|
||||
* @method array hmget(string $key, array $fields)
|
||||
* @method mixed hmset(string $key, array $dictionary)
|
||||
* @method array hscan(string $key, $cursor, array $options = null)
|
||||
* @method int hset(string $key, string $field, string $value)
|
||||
* @method int hsetnx(string $key, string $field, string $value)
|
||||
* @method array hvals(string $key)
|
||||
* @method int hstrlen(string $key, string $field)
|
||||
* @method array|null blpop(array|string $keys, int|float $timeout)
|
||||
* @method array|null brpop(array|string $keys, int|float $timeout)
|
||||
* @method string|null brpoplpush(string $source, string $destination, int|float $timeout)
|
||||
* @method string|null lindex(string $key, int $index)
|
||||
* @method int linsert(string $key, $whence, $pivot, $value)
|
||||
* @method int llen(string $key)
|
||||
* @method string|null lpop(string $key)
|
||||
* @method int lpush(string $key, array $values)
|
||||
* @method int lpushx(string $key, array $values)
|
||||
* @method string[] lrange(string $key, int $start, int $stop)
|
||||
* @method int lrem(string $key, int $count, string $value)
|
||||
* @method mixed lset(string $key, int $index, string $value)
|
||||
* @method mixed ltrim(string $key, int $start, int $stop)
|
||||
* @method string|null rpop(string $key)
|
||||
* @method string|null rpoplpush(string $source, string $destination)
|
||||
* @method int rpush(string $key, array $values)
|
||||
* @method int rpushx(string $key, array $values)
|
||||
* @method int sadd(string $key, array $members)
|
||||
* @method int scard(string $key)
|
||||
* @method string[] sdiff(array|string $keys)
|
||||
* @method int sdiffstore(string $destination, array|string $keys)
|
||||
* @method string[] sinter(array|string $keys)
|
||||
* @method int sinterstore(string $destination, array|string $keys)
|
||||
* @method int sismember(string $key, string $member)
|
||||
* @method string[] smembers(string $key)
|
||||
* @method int smove(string $source, string $destination, string $member)
|
||||
* @method string|null spop(string $key, int $count = null)
|
||||
* @method string|null srandmember(string $key, int $count = null)
|
||||
* @method int srem(string $key, string $member)
|
||||
* @method array sscan(string $key, int $cursor, array $options = null)
|
||||
* @method string[] sunion(array|string $keys)
|
||||
* @method int sunionstore(string $destination, array|string $keys)
|
||||
* @method int touch(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method int zadd(string $key, array $membersAndScoresDictionary)
|
||||
* @method int zcard(string $key)
|
||||
* @method string zcount(string $key, int|string $min, int|string $max)
|
||||
* @method string zincrby(string $key, int $increment, string $member)
|
||||
* @method int zinterstore(string $destination, array|string $keys, array $options = null)
|
||||
* @method array zpopmin(string $key, int $count = 1)
|
||||
* @method array zpopmax(string $key, int $count = 1)
|
||||
* @method array zrange(string $key, int|string $start, int|string $stop, array $options = null)
|
||||
* @method array zrangebyscore(string $key, int|string $min, int|string $max, array $options = null)
|
||||
* @method int|null zrank(string $key, string $member)
|
||||
* @method int zrem(string $key, string ...$member)
|
||||
* @method int zremrangebyrank(string $key, int|string $start, int|string $stop)
|
||||
* @method int zremrangebyscore(string $key, int|string $min, int|string $max)
|
||||
* @method array zrevrange(string $key, int|string $start, int|string $stop, array $options = null)
|
||||
* @method array zrevrangebyscore(string $key, int|string $max, int|string $min, array $options = null)
|
||||
* @method int|null zrevrank(string $key, string $member)
|
||||
* @method int zunionstore(string $destination, array|string $keys, array $options = null)
|
||||
* @method string|null zscore(string $key, string $member)
|
||||
* @method array zscan(string $key, int $cursor, array $options = null)
|
||||
* @method array zrangebylex(string $key, string $start, string $stop, array $options = null)
|
||||
* @method array zrevrangebylex(string $key, string $start, string $stop, array $options = null)
|
||||
* @method int zremrangebylex(string $key, string $min, string $max)
|
||||
* @method int zlexcount(string $key, string $min, string $max)
|
||||
* @method int pfadd(string $key, array $elements)
|
||||
* @method mixed pfmerge(string $destinationKey, array|string $sourceKeys)
|
||||
* @method int pfcount(string[]|string $keyOrKeys, string ...$keys = null)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed discard()
|
||||
* @method array|null exec()
|
||||
* @method mixed multi()
|
||||
* @method mixed unwatch()
|
||||
* @method mixed watch(string $key)
|
||||
* @method mixed eval(string $script, int $numkeys, string ...$keyOrArg = null)
|
||||
* @method mixed evalsha(string $script, int $numkeys, string ...$keyOrArg = null)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method mixed auth(string $password)
|
||||
* @method string echo(string $message)
|
||||
* @method mixed ping(string $message = null)
|
||||
* @method mixed select(int $database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof(string $host, int $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
* @method int geoadd(string $key, $longitude, $latitude, $member)
|
||||
* @method array geohash(string $key, array $members)
|
||||
* @method array geopos(string $key, array $members)
|
||||
* @method string|null geodist(string $key, $member1, $member2, $unit = null)
|
||||
* @method array georadius(string $key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method array georadiusbymember(string $key, $member, $radius, $unit, array $options = null)
|
||||
* @method int del(array $keys)
|
||||
* @method string dump($key)
|
||||
* @method int exists($key)
|
||||
* @method int expire($key, $seconds)
|
||||
* @method int expireat($key, $timestamp)
|
||||
* @method array keys($pattern)
|
||||
* @method int move($key, $db)
|
||||
* @method mixed object($subcommand, $key)
|
||||
* @method int persist($key)
|
||||
* @method int pexpire($key, $milliseconds)
|
||||
* @method int pexpireat($key, $timestamp)
|
||||
* @method int pttl($key)
|
||||
* @method string randomkey()
|
||||
* @method mixed rename($key, $target)
|
||||
* @method int renamenx($key, $target)
|
||||
* @method array scan($cursor, array $options = null)
|
||||
* @method array sort($key, array $options = null)
|
||||
* @method int ttl($key)
|
||||
* @method mixed type($key)
|
||||
* @method int append($key, $value)
|
||||
* @method int bitcount($key, $start = null, $end = null)
|
||||
* @method int bitop($operation, $destkey, $key)
|
||||
* @method array bitfield($key, ...)
|
||||
* @method int decr($key)
|
||||
* @method int decrby($key, $decrement)
|
||||
* @method string get($key)
|
||||
* @method int getbit($key, $offset)
|
||||
* @method string getrange($key, $start, $end)
|
||||
* @method string getset($key, $value)
|
||||
* @method int incr($key)
|
||||
* @method int incrby($key, $increment)
|
||||
* @method string incrbyfloat($key, $increment)
|
||||
* @method array mget(array $keys)
|
||||
* @method mixed mset(array $dictionary)
|
||||
* @method int msetnx(array $dictionary)
|
||||
* @method mixed psetex($key, $milliseconds, $value)
|
||||
* @method mixed set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
|
||||
* @method int setbit($key, $offset, $value)
|
||||
* @method int setex($key, $seconds, $value)
|
||||
* @method int setnx($key, $value)
|
||||
* @method int setrange($key, $offset, $value)
|
||||
* @method int strlen($key)
|
||||
* @method int hdel($key, array $fields)
|
||||
* @method int hexists($key, $field)
|
||||
* @method string hget($key, $field)
|
||||
* @method array hgetall($key)
|
||||
* @method int hincrby($key, $field, $increment)
|
||||
* @method string hincrbyfloat($key, $field, $increment)
|
||||
* @method array hkeys($key)
|
||||
* @method int hlen($key)
|
||||
* @method array hmget($key, array $fields)
|
||||
* @method mixed hmset($key, array $dictionary)
|
||||
* @method array hscan($key, $cursor, array $options = null)
|
||||
* @method int hset($key, $field, $value)
|
||||
* @method int hsetnx($key, $field, $value)
|
||||
* @method array hvals($key)
|
||||
* @method int hstrlen($key, $field)
|
||||
* @method array blpop(array $keys, $timeout)
|
||||
* @method array brpop(array $keys, $timeout)
|
||||
* @method array brpoplpush($source, $destination, $timeout)
|
||||
* @method string lindex($key, $index)
|
||||
* @method int linsert($key, $whence, $pivot, $value)
|
||||
* @method int llen($key)
|
||||
* @method string lpop($key)
|
||||
* @method int lpush($key, array $values)
|
||||
* @method int lpushx($key, $value)
|
||||
* @method array lrange($key, $start, $stop)
|
||||
* @method int lrem($key, $count, $value)
|
||||
* @method mixed lset($key, $index, $value)
|
||||
* @method mixed ltrim($key, $start, $stop)
|
||||
* @method string rpop($key)
|
||||
* @method string rpoplpush($source, $destination)
|
||||
* @method int rpush($key, array $values)
|
||||
* @method int rpushx($key, $value)
|
||||
* @method int sadd($key, array $members)
|
||||
* @method int scard($key)
|
||||
* @method array sdiff(array $keys)
|
||||
* @method int sdiffstore($destination, array $keys)
|
||||
* @method array sinter(array $keys)
|
||||
* @method int sinterstore($destination, array $keys)
|
||||
* @method int sismember($key, $member)
|
||||
* @method array smembers($key)
|
||||
* @method int smove($source, $destination, $member)
|
||||
* @method string spop($key, $count = null)
|
||||
* @method string srandmember($key, $count = null)
|
||||
* @method int srem($key, $member)
|
||||
* @method array sscan($key, $cursor, array $options = null)
|
||||
* @method array sunion(array $keys)
|
||||
* @method int sunionstore($destination, array $keys)
|
||||
* @method int zadd($key, array $membersAndScoresDictionary)
|
||||
* @method int zcard($key)
|
||||
* @method string zcount($key, $min, $max)
|
||||
* @method string zincrby($key, $increment, $member)
|
||||
* @method int zinterstore($destination, array $keys, array $options = null)
|
||||
* @method array zrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrangebyscore($key, $min, $max, array $options = null)
|
||||
* @method int zrank($key, $member)
|
||||
* @method int zrem($key, $member)
|
||||
* @method int zremrangebyrank($key, $start, $stop)
|
||||
* @method int zremrangebyscore($key, $min, $max)
|
||||
* @method array zrevrange($key, $start, $stop, array $options = null)
|
||||
* @method array zrevrangebyscore($key, $max, $min, array $options = null)
|
||||
* @method int zrevrank($key, $member)
|
||||
* @method int zunionstore($destination, array $keys, array $options = null)
|
||||
* @method string zscore($key, $member)
|
||||
* @method array zscan($key, $cursor, array $options = null)
|
||||
* @method array zrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method array zrevrangebylex($key, $start, $stop, array $options = null)
|
||||
* @method int zremrangebylex($key, $min, $max)
|
||||
* @method int zlexcount($key, $min, $max)
|
||||
* @method int pfadd($key, array $elements)
|
||||
* @method mixed pfmerge($destinationKey, array $sourceKeys)
|
||||
* @method int pfcount(array $keys)
|
||||
* @method mixed pubsub($subcommand, $argument)
|
||||
* @method int publish($channel, $message)
|
||||
* @method mixed discard()
|
||||
* @method array exec()
|
||||
* @method mixed multi()
|
||||
* @method mixed unwatch()
|
||||
* @method mixed watch($key)
|
||||
* @method mixed eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
|
||||
* @method mixed script($subcommand, $argument = null)
|
||||
* @method mixed auth($password)
|
||||
* @method string echo($message)
|
||||
* @method mixed ping($message = null)
|
||||
* @method mixed select($database)
|
||||
* @method mixed bgrewriteaof()
|
||||
* @method mixed bgsave()
|
||||
* @method mixed client($subcommand, $argument = null)
|
||||
* @method mixed config($subcommand, $argument = null)
|
||||
* @method int dbsize()
|
||||
* @method mixed flushall()
|
||||
* @method mixed flushdb()
|
||||
* @method array info($section = null)
|
||||
* @method int lastsave()
|
||||
* @method mixed save()
|
||||
* @method mixed slaveof($host, $port)
|
||||
* @method mixed slowlog($subcommand, $argument = null)
|
||||
* @method array time()
|
||||
* @method array command()
|
||||
* @method int geoadd($key, $longitude, $latitude, $member)
|
||||
* @method array geohash($key, array $members)
|
||||
* @method array geopos($key, array $members)
|
||||
* @method string geodist($key, $member1, $member2, $unit = null)
|
||||
* @method array georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
|
||||
* @method array georadiusbymember($key, $member, $radius, $unit, array $options = null)
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface ClientInterface
|
||||
{
|
||||
/**
|
||||
* Returns the command factory used by the client.
|
||||
* Returns the server profile used by the client.
|
||||
*
|
||||
* @return FactoryInterface
|
||||
* @return ProfileInterface
|
||||
*/
|
||||
public function getCommandFactory();
|
||||
public function getProfile();
|
||||
|
||||
/**
|
||||
* Returns the client options specified upon initialization.
|
||||
|
||||
@@ -161,7 +161,7 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
|
||||
$replicas = (int) round($weightRatio * $totalNodes * $replicas);
|
||||
|
||||
for ($i = 0; $i < $replicas; ++$i) {
|
||||
$key = $this->hash("$nodeHash:$i");
|
||||
$key = crc32("$nodeHash:$i");
|
||||
$ring[$key] = $nodeObject;
|
||||
}
|
||||
}
|
||||
@@ -239,8 +239,9 @@ class HashRing implements DistributorInterface, HashGeneratorInterface
|
||||
public function get($value)
|
||||
{
|
||||
$hash = $this->hash($value);
|
||||
$node = $this->getByHash($hash);
|
||||
|
||||
return $this->getByHash($hash);
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,8 +61,6 @@ class CRC16 implements HashGeneratorInterface
|
||||
// CRC-CCITT-16 algorithm
|
||||
$crc = 0;
|
||||
$CCITT_16 = self::$CCITT_16;
|
||||
|
||||
$value = (string) $value;
|
||||
$strlen = strlen($value);
|
||||
|
||||
for ($i = 0; $i < $strlen; ++$i) {
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster\Hash;
|
||||
|
||||
use Predis\NotSupportedException;
|
||||
|
||||
/**
|
||||
* Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PhpiredisCRC16 implements HashGeneratorInterface
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!function_exists('phpiredis_utils_crc16')) {
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new NotSupportedException(
|
||||
'This hash generator requires a compatible version of ext-phpiredis'
|
||||
);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hash($value)
|
||||
{
|
||||
return phpiredis_utils_crc16($value);
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,9 @@ class PredisStrategy extends ClusterStrategy
|
||||
{
|
||||
$key = $this->extractKeyTag($key);
|
||||
$hash = $this->distributor->hash($key);
|
||||
$slot = $this->distributor->getSlot($hash);
|
||||
|
||||
return $this->distributor->getSlot($hash);
|
||||
return $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,8 +41,9 @@ class RedisStrategy extends ClusterStrategy
|
||||
public function getSlotByKey($key)
|
||||
{
|
||||
$key = $this->extractKeyTag($key);
|
||||
$slot = $this->hashGenerator->hash($key) & 0x3FFF;
|
||||
|
||||
return $this->hashGenerator->hash($key) & 0x3FFF;
|
||||
return $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +51,8 @@ class RedisStrategy extends ClusterStrategy
|
||||
*/
|
||||
public function getDistributor()
|
||||
{
|
||||
$class = get_class($this);
|
||||
throw new NotSupportedException("$class does not provide an external distributor");
|
||||
throw new NotSupportedException(
|
||||
'This cluster strategy does not provide an external distributor'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Predis\Cluster;
|
||||
|
||||
/**
|
||||
* Slot map for redis-cluster.
|
||||
*/
|
||||
class SlotMap implements \ArrayAccess, \IteratorAggregate, \Countable
|
||||
{
|
||||
private $slots = array();
|
||||
|
||||
/**
|
||||
* Checks if the given slot is valid.
|
||||
*
|
||||
* @param int $first Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($slot)
|
||||
{
|
||||
return $slot >= 0x0000 && $slot <= 0x3FFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given slot range is valid.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValidRange($first, $last)
|
||||
{
|
||||
return $first >= 0x0000 && $first <= 0x3FFF && $last >= 0x0000 && $last <= 0x3FFF && $first <= $last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the slot map.
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->slots = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the slot map is empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return empty($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current slot map as a dictionary of $slot => $node.
|
||||
*
|
||||
* The order of the slots in the dictionary is not guaranteed.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of unique nodes in the slot map.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getNodes()
|
||||
{
|
||||
return array_keys(array_flip($this->slots));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot range to a node.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @throws \OutOfBoundsException
|
||||
*/
|
||||
public function setSlots($first, $last, $connection)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new \OutOfBoundsException("Invalid slot range $first-$last for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots += array_fill($first, $last - $first + 1, (string) $connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified slot range.
|
||||
*
|
||||
* @param int $first Initial slot of the range.
|
||||
* @param int $last Last slot of the range.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSlots($first, $last)
|
||||
{
|
||||
if (!static::isValidRange($first, $last)) {
|
||||
throw new \OutOfBoundsException("Invalid slot range $first-$last");
|
||||
}
|
||||
|
||||
return array_intersect_key($this->slots, array_fill($first, $last - $first + 1, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified slot is assigned.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($slot)
|
||||
{
|
||||
return isset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($slot)
|
||||
{
|
||||
if (isset($this->slots[$slot])) {
|
||||
return $this->slots[$slot];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the specified slot to a node.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
* @param NodeConnectionInterface|string $connection ID or connection instance.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($slot, $connection)
|
||||
{
|
||||
if (!static::isValid($slot)) {
|
||||
throw new \OutOfBoundsException("Invalid slot $slot for `$connection`");
|
||||
}
|
||||
|
||||
$this->slots[(int) $slot] = (string) $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node assigned to the specified slot.
|
||||
*
|
||||
* @param int $slot Slot index.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($slot)
|
||||
{
|
||||
unset($this->slots[$slot]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current number of assigned slots.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return count($this->slots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the slot map.
|
||||
*
|
||||
* @return \ArrayIterator
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->slots);
|
||||
}
|
||||
}
|
||||
@@ -65,8 +65,8 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
*/
|
||||
protected function requiredCommand(ClientInterface $client, $commandID)
|
||||
{
|
||||
if (!$client->getCommandFactory()->supports($commandID)) {
|
||||
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
|
||||
if (!$client->getProfile()->supportsCommand($commandID)) {
|
||||
throw new NotSupportedException("The current profile does not support '$commandID'.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
{
|
||||
$options = array();
|
||||
|
||||
if (strlen(strval($this->match)) > 0) {
|
||||
if (strlen($this->match) > 0) {
|
||||
$options['MATCH'] = $this->match;
|
||||
}
|
||||
|
||||
@@ -139,7 +139,6 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
$this->reset();
|
||||
@@ -149,7 +148,6 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->current;
|
||||
@@ -158,7 +156,6 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
@@ -167,7 +164,6 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
tryFetch: {
|
||||
@@ -188,7 +184,6 @@ abstract class CursorBasedIterator implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->valid;
|
||||
|
||||
@@ -50,9 +50,11 @@ class HashKey extends CursorBasedIterator
|
||||
*/
|
||||
protected function extractNext()
|
||||
{
|
||||
$this->position = key($this->elements);
|
||||
$this->current = current($this->elements);
|
||||
if ($kv = each($this->elements)) {
|
||||
$this->position = $kv[0];
|
||||
$this->current = $kv[1];
|
||||
|
||||
unset($this->elements[$this->position]);
|
||||
unset($this->elements[$this->position]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,8 @@ class ListKey implements \Iterator
|
||||
*/
|
||||
protected function requiredCommand(ClientInterface $client, $commandID)
|
||||
{
|
||||
if (!$client->getCommandFactory()->supports($commandID)) {
|
||||
throw new NotSupportedException("'$commandID' is not supported by the current command factory.");
|
||||
if (!$client->getProfile()->supportsCommand($commandID)) {
|
||||
throw new NotSupportedException("The current profile does not support '$commandID'.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,6 @@ class ListKey implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function rewind()
|
||||
{
|
||||
$this->reset();
|
||||
@@ -138,7 +137,6 @@ class ListKey implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return $this->current;
|
||||
@@ -147,7 +145,6 @@ class ListKey implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function key()
|
||||
{
|
||||
return $this->position;
|
||||
@@ -156,7 +153,6 @@ class ListKey implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function next()
|
||||
{
|
||||
if (!$this->elements && $this->fetchmore) {
|
||||
@@ -173,7 +169,6 @@ class ListKey implements \Iterator
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function valid()
|
||||
{
|
||||
return $this->valid;
|
||||
|
||||
@@ -50,9 +50,11 @@ class SortedSetKey extends CursorBasedIterator
|
||||
*/
|
||||
protected function extractNext()
|
||||
{
|
||||
$this->position = key($this->elements);
|
||||
$this->current = current($this->elements);
|
||||
if ($kv = each($this->elements)) {
|
||||
$this->position = $kv[0];
|
||||
$this->current = $kv[1];
|
||||
|
||||
unset($this->elements[$this->position]);
|
||||
unset($this->elements[$this->position]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-2
@@ -21,12 +21,24 @@ abstract class Command implements CommandInterface
|
||||
private $slot;
|
||||
private $arguments = array();
|
||||
|
||||
/**
|
||||
* Returns a filtered array of the arguments.
|
||||
*
|
||||
* @param array $arguments List of arguments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
$this->arguments = $this->filterArguments($arguments);
|
||||
unset($this->slot);
|
||||
}
|
||||
|
||||
@@ -92,7 +104,7 @@ abstract class Command implements CommandInterface
|
||||
*/
|
||||
public static function normalizeArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 1 && isset($arguments[0]) && is_array($arguments[0])) {
|
||||
if (count($arguments) === 1 && is_array($arguments[0])) {
|
||||
return $arguments[0];
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ interface CommandInterface
|
||||
/**
|
||||
* Parses a raw response and returns a PHP object.
|
||||
*
|
||||
* @param string|array|null $data Binary string containing the whole response.
|
||||
* @param string $data Binary string containing the whole response.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/auth
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class AUTH extends RedisCommand
|
||||
class ConnectionAuth extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/echo
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class ECHO_ extends RedisCommand
|
||||
class ConnectionEcho extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/ping
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PING extends RedisCommand
|
||||
class ConnectionPing extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/quit
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class QUIT extends RedisCommand
|
||||
class ConnectionQuit extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/select
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class SELECT extends RedisCommand
|
||||
class ConnectionSelect extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -1,145 +0,0 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
use Predis\ClientException;
|
||||
use Predis\Command\Processor\ProcessorInterface;
|
||||
|
||||
/**
|
||||
* Base command factory class.
|
||||
*
|
||||
* This class provides all of the common functionalities required for a command
|
||||
* factory to create new instances of Redis commands objects. It also allows to
|
||||
* define or undefine command handler classes for each command ID.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
abstract class Factory implements FactoryInterface
|
||||
{
|
||||
protected $commands = [];
|
||||
protected $processor;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(string ...$commandIDs): bool
|
||||
{
|
||||
foreach ($commandIDs as $commandID) {
|
||||
if ($this->getCommandClass($commandID) === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FQCN of a class that represents the specified command ID.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param string $commandID Command ID
|
||||
*
|
||||
* @return ?string
|
||||
*/
|
||||
public function getCommandClass(string $commandID): ?string
|
||||
{
|
||||
if (isset($this->commands[$commandID = strtoupper($commandID)])) {
|
||||
return $this->commands[$commandID];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create(string $commandID, array $arguments = []): CommandInterface
|
||||
{
|
||||
if (!$commandClass = $this->getCommandClass($commandID)) {
|
||||
$commandID = strtoupper($commandID);
|
||||
|
||||
throw new ClientException("Command `$commandID` is not a registered Redis command.");
|
||||
}
|
||||
|
||||
$command = new $commandClass();
|
||||
$command->setArguments($arguments);
|
||||
|
||||
if (isset($this->processor)) {
|
||||
$this->processor->process($command);
|
||||
}
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a command in the factory.
|
||||
*
|
||||
* Only classes implementing Predis\Command\CommandInterface are allowed to
|
||||
* handle a command. If the command specified by its ID is already handled
|
||||
* by the factory, the underlying command class is replaced by the new one.
|
||||
*
|
||||
* @param string $commandID Command ID
|
||||
* @param string $commandClass FQCN of a class implementing Predis\Command\CommandInterface
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function define(string $commandID, string $commandClass): void
|
||||
{
|
||||
if (!is_a($commandClass, 'Predis\Command\CommandInterface', true)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"Class $commandClass must implement Predis\Command\CommandInterface"
|
||||
);
|
||||
}
|
||||
|
||||
$this->commands[strtoupper($commandID)] = $commandClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undefines a command in the factory.
|
||||
*
|
||||
* When the factory already has a class handler associated to the specified
|
||||
* command ID it is removed from the map of known commands. Nothing happens
|
||||
* when the command is not handled by the factory.
|
||||
*
|
||||
* @param string $commandID Command ID
|
||||
*/
|
||||
public function undefine(string $commandID): void
|
||||
{
|
||||
unset($this->commands[strtoupper($commandID)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a command processor for processing command arguments.
|
||||
*
|
||||
* Command processors are used to process and transform arguments of Redis
|
||||
* commands before their newly created instances are returned to the caller
|
||||
* of "create()".
|
||||
*
|
||||
* A NULL value can be used to effectively unset any processor if previously
|
||||
* set for the command factory.
|
||||
*
|
||||
* @param ProcessorInterface|null $processor Command processor or NULL value.
|
||||
*/
|
||||
public function setProcessor(?ProcessorInterface $processor): void
|
||||
{
|
||||
$this->processor = $processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current command processor.
|
||||
*
|
||||
* @return ?ProcessorInterface
|
||||
*/
|
||||
public function getProcessor(): ?ProcessorInterface
|
||||
{
|
||||
return $this->processor;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* Command factory interface.
|
||||
*
|
||||
* A command factory is used through the library to create instances of commands
|
||||
* classes implementing Predis\Command\CommandInterface mapped to Redis commands
|
||||
* by their command ID string (SET, GET, etc...).
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
interface FactoryInterface
|
||||
{
|
||||
/**
|
||||
* Checks if the command factory supports the specified list of commands.
|
||||
*
|
||||
* @param array $commandIDs List of command IDs
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports(string ...$commandIDs): bool;
|
||||
|
||||
/**
|
||||
* Creates a new command instance.
|
||||
*
|
||||
* @param string $commandID Command ID
|
||||
* @param array $arguments Arguments for the command
|
||||
*
|
||||
* @return CommandInterface
|
||||
*/
|
||||
public function create(string $commandID, array $arguments = array()): CommandInterface;
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/geoadd
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class GEOADD extends RedisCommand
|
||||
class GeospatialGeoAdd extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,7 +29,7 @@ class GEOADD extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 2 && is_array($arguments[1])) {
|
||||
foreach (array_pop($arguments) as $item) {
|
||||
@@ -39,6 +37,6 @@ class GEOADD extends RedisCommand
|
||||
}
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/geodist
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class GEODIST extends RedisCommand
|
||||
class GeospatialGeoDist extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/geohash
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class GEOHASH extends RedisCommand
|
||||
class GeospatialGeoHash extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,13 +29,13 @@ class GEOHASH extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 2 && is_array($arguments[1])) {
|
||||
$members = array_pop($arguments);
|
||||
$arguments = array_merge($arguments, $members);
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/geopos
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class GEOPOS extends RedisCommand
|
||||
class GeospatialGeoPos extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,13 +29,13 @@ class GEOPOS extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 2 && is_array($arguments[1])) {
|
||||
$members = array_pop($arguments);
|
||||
$arguments = array_merge($arguments, $members);
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/georadius
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class GEORADIUS extends RedisCommand
|
||||
class GeospatialGeoRadius extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,7 +29,7 @@ class GEORADIUS extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
if ($arguments && is_array(end($arguments))) {
|
||||
$options = array_change_key_case(array_pop($arguments), CASE_UPPER);
|
||||
@@ -68,6 +66,6 @@ class GEORADIUS extends RedisCommand
|
||||
}
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -9,14 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/georadiusbymember
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class GEORADIUSBYMEMBER extends GEORADIUS
|
||||
class GeospatialGeoRadiusByMember extends GeospatialGeoRadius
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hdel
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HDEL extends RedisCommand
|
||||
class HashDelete extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,10 +29,8 @@ class HDEL extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
$arguments = self::normalizeVariadic($arguments);
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return self::normalizeVariadic($arguments);
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hexists
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HEXISTS extends RedisCommand
|
||||
class HashExists extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -27,4 +25,12 @@ class HEXISTS extends RedisCommand
|
||||
{
|
||||
return 'HEXISTS';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hget
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HGET extends RedisCommand
|
||||
class HashGet extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hgetall
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HGETALL extends RedisCommand
|
||||
class HashGetAll extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hmget
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HMGET extends RedisCommand
|
||||
class HashGetMultiple extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,10 +29,8 @@ class HMGET extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
$arguments = self::normalizeVariadic($arguments);
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return self::normalizeVariadic($arguments);
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hincrby
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HINCRBY extends RedisCommand
|
||||
class HashIncrementBy extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hincrbyfloat
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HINCRBYFLOAT extends RedisCommand
|
||||
class HashIncrementByFloat extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hkeys
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HKEYS extends RedisCommand
|
||||
class HashKeys extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hlen
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HLEN extends RedisCommand
|
||||
class HashLength extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hscan
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HSCAN extends RedisCommand
|
||||
class HashScan extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,14 +29,14 @@ class HSCAN extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 3 && is_array($arguments[2])) {
|
||||
$options = $this->prepareOptions(array_pop($arguments));
|
||||
$arguments = array_merge($arguments, $options);
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hset
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HSET extends RedisCommand
|
||||
class HashSet extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -27,4 +25,12 @@ class HSET extends RedisCommand
|
||||
{
|
||||
return 'HSET';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hmset
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HMSET extends RedisCommand
|
||||
class HashSetMultiple extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,7 +29,7 @@ class HMSET extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 2 && is_array($arguments[1])) {
|
||||
$flattenedKVs = array($arguments[0]);
|
||||
@@ -42,9 +40,9 @@ class HMSET extends RedisCommand
|
||||
$flattenedKVs[] = $v;
|
||||
}
|
||||
|
||||
$arguments = $flattenedKVs;
|
||||
return $flattenedKVs;
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hsetnx
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HSETNX extends RedisCommand
|
||||
class HashSetPreserve extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -27,4 +25,12 @@ class HSETNX extends RedisCommand
|
||||
{
|
||||
return 'HSETNX';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hstrlen
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HSTRLEN extends RedisCommand
|
||||
class HashStringLength extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/hvals
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class HVALS extends RedisCommand
|
||||
class HashValues extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pfadd
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PFADD extends RedisCommand
|
||||
class HyperLogLogAdd extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,10 +29,16 @@ class PFADD extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
$arguments = self::normalizeVariadic($arguments);
|
||||
return self::normalizeVariadic($arguments);
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pfcount
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PFCOUNT extends RedisCommand
|
||||
class HyperLogLogCount extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,10 +29,8 @@ class PFCOUNT extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
$arguments = self::normalizeArguments($arguments);
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return self::normalizeArguments($arguments);
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pfmerge
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PFMERGE extends RedisCommand
|
||||
class HyperLogLogMerge extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,10 +29,8 @@ class PFMERGE extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
$arguments = self::normalizeArguments($arguments);
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return self::normalizeArguments($arguments);
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/del
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class DEL extends RedisCommand
|
||||
class KeyDelete extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,10 +29,8 @@ class DEL extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
$arguments = self::normalizeArguments($arguments);
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return self::normalizeArguments($arguments);
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/dump
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class DUMP extends RedisCommand
|
||||
class KeyDump extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/exists
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class EXISTS extends RedisCommand
|
||||
class KeyExists extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -27,4 +25,12 @@ class EXISTS extends RedisCommand
|
||||
{
|
||||
return 'EXISTS';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/expire
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class EXPIRE extends RedisCommand
|
||||
class KeyExpire extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -27,4 +25,12 @@ class EXPIRE extends RedisCommand
|
||||
{
|
||||
return 'EXPIRE';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/expireat
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class EXPIREAT extends RedisCommand
|
||||
class KeyExpireAt extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -27,4 +25,12 @@ class EXPIREAT extends RedisCommand
|
||||
{
|
||||
return 'EXPIREAT';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/keys
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class KEYS extends RedisCommand
|
||||
class KeyKeys extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/migrate
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class MIGRATE extends RedisCommand
|
||||
class KeyMigrate extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,7 +29,7 @@ class MIGRATE extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
if (is_array(end($arguments))) {
|
||||
foreach (array_pop($arguments) as $modifier => $value) {
|
||||
@@ -47,6 +45,6 @@ class MIGRATE extends RedisCommand
|
||||
}
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return $arguments;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/move
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class MOVE extends RedisCommand
|
||||
class KeyMove extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -27,4 +25,12 @@ class MOVE extends RedisCommand
|
||||
{
|
||||
return 'MOVE';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/persist
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PERSIST extends RedisCommand
|
||||
class KeyPersist extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -27,4 +25,12 @@ class PERSIST extends RedisCommand
|
||||
{
|
||||
return 'PERSIST';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pexpire
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PEXPIRE extends RedisCommand
|
||||
class KeyPreciseExpire extends KeyExpire
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pexpireat
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PEXPIREAT extends RedisCommand
|
||||
class KeyPreciseExpireAt extends KeyExpireAt
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/pttl
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class PTTL extends RedisCommand
|
||||
class KeyPreciseTimeToLive extends KeyTimeToLive
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/randomkey
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class RANDOMKEY extends RedisCommand
|
||||
class KeyRandom extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/rename
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class RENAME extends RedisCommand
|
||||
class KeyRename extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/renamenx
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class RENAMENX extends RedisCommand
|
||||
class KeyRenamePreserve extends KeyRename
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -27,4 +25,12 @@ class RENAMENX extends RedisCommand
|
||||
{
|
||||
return 'RENAMENX';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parseResponse($data)
|
||||
{
|
||||
return (bool) $data;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/restore
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class RESTORE extends RedisCommand
|
||||
class KeyRestore extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/scan
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class SCAN extends RedisCommand
|
||||
class KeyScan extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,14 +29,14 @@ class SCAN extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 2 && is_array($arguments[1])) {
|
||||
$options = $this->prepareOptions(array_pop($arguments));
|
||||
$arguments = array_merge($arguments, $options);
|
||||
}
|
||||
|
||||
parent::setArguments($arguments);
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/sort
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class SORT extends RedisCommand
|
||||
class KeySort extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
@@ -31,12 +29,10 @@ class SORT extends RedisCommand
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setArguments(array $arguments)
|
||||
protected function filterArguments(array $arguments)
|
||||
{
|
||||
if (count($arguments) === 1) {
|
||||
parent::setArguments($arguments);
|
||||
|
||||
return;
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
$query = array($arguments[0]);
|
||||
@@ -82,6 +78,6 @@ class SORT extends RedisCommand
|
||||
$query[] = $sortParams['STORE'];
|
||||
}
|
||||
|
||||
parent::setArguments($query);
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -9,16 +9,14 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Command\Redis;
|
||||
|
||||
use Predis\Command\Command as RedisCommand;
|
||||
namespace Predis\Command;
|
||||
|
||||
/**
|
||||
* @link http://redis.io/commands/ttl
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class TTL extends RedisCommand
|
||||
class KeyTimeToLive extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user