diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-05-01 20:06:01 +0300 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-05-01 20:06:01 +0300 |
commit | c603cf71406ae2ff7c3771290c26a954846c1190 (patch) | |
tree | 8d0357fca42efaf954bf13603765472df8969aa8 /vendor | |
parent | 32aaf015a943b6ce52e5cbec6e26b85870c89cf6 (diff) |
Rate limit and various fixes
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'vendor')
300 files changed, 23546 insertions, 0 deletions
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 6e9b6ebc..da997c97 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -7,6 +7,8 @@ $baseDir = dirname($vendorDir); return array( 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Predis\\' => array($vendorDir . '/predis/predis/src'), + 'Perimeter\\RateLimiter\\' => array($vendorDir . '/perimeter/rate-limiter-php'), 'MaxMind\\WebService\\' => array($vendorDir . '/maxmind/web-service-common/src/WebService'), 'MaxMind\\Exception\\' => array($vendorDir . '/maxmind/web-service-common/src/Exception'), 'MaxMind\\Db\\' => array($vendorDir . '/maxmind-db/reader/src/MaxMind/Db'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 1595c4c5..0037c20d 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -10,6 +10,8 @@ class ComposerStaticInit8fa0ec799546a9f5452392a519c87a0d 'P' => array ( 'Psr\\Log\\' => 8, + 'Predis\\' => 7, + 'Perimeter\\RateLimiter\\' => 22, ), 'M' => array ( @@ -33,6 +35,14 @@ class ComposerStaticInit8fa0ec799546a9f5452392a519c87a0d array ( 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', ), + 'Predis\\' => + array ( + 0 => __DIR__ . '/..' . '/predis/predis/src', + ), + 'Perimeter\\RateLimiter\\' => + array ( + 0 => __DIR__ . '/..' . '/perimeter/rate-limiter-php', + ), 'MaxMind\\WebService\\' => array ( 0 => __DIR__ . '/..' . '/maxmind/web-service-common/src/WebService', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 7a39dfa1..88db5999 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -305,6 +305,120 @@ "homepage": "https://github.com/maxmind/web-service-common-php" }, { + "name": "perimeter/rate-limiter-php", + "version": "dev-develop", + "version_normalized": "dev-develop", + "source": { + "type": "git", + "url": "https://github.com/perimeter/rate-limiter-php.git", + "reference": "121ba37284dd701afc2f546c4f49d1954564a24a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/perimeter/rate-limiter-php/zipball/121ba37284dd701afc2f546c4f49d1954564a24a", + "reference": "121ba37284dd701afc2f546c4f49d1954564a24a", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "predis/predis": "1.0.*" + }, + "require-dev": { + "doctrine/orm": "2.4.*" + }, + "suggest": { + "perimeter/cache-bundle": "Required to use the Cache storage engine", + "perimeter/rate-limit-bundle": "allows you to integrate with symfony2" + }, + "time": "2015-12-03T00:42:23+00:00", + "type": "library", + "installation-source": "source", + "autoload": { + "psr-4": { + "Perimeter\\RateLimiter\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brent Shaffer", + "email": "bshafs@gmail.com" + }, + { + "name": "Gary Rogers", + "email": "garogers@adobe.com" + }, + { + "name": "Courtney Ferguson", + "email": "cofergus@adobe.com" + } + ], + "description": "A very simple Rate Limiter for PHP", + "homepage": "http://github.com/perimeter/rate-limiter-php", + "keywords": [ + "api", + "api-gateway", + "perimeter", + "ratelimit", + "redis" + ] + }, + { + "name": "predis/predis", + "version": "v1.0.4", + "version_normalized": "1.0.4.0", + "source": { + "type": "git", + "url": "https://github.com/nrk/predis.git", + "reference": "9ead747663bb1b1ae017dfa0d152aca87792b42f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nrk/predis/zipball/9ead747663bb1b1ae017dfa0d152aca87792b42f", + "reference": "9ead747663bb1b1ae017dfa0d152aca87792b42f", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.8" + }, + "suggest": { + "ext-curl": "Allows access to Webdis when paired with phpiredis", + "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol" + }, + "time": "2016-05-30T15:25:52+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Predis\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net" + } + ], + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "homepage": "http://github.com/nrk/predis", + "keywords": [ + "nosql", + "predis", + "redis" + ] + }, + { "name": "psr/log", "version": "1.0.2", "version_normalized": "1.0.2.0", diff --git a/vendor/perimeter/rate-limiter-php b/vendor/perimeter/rate-limiter-php new file mode 160000 +Subproject 121ba37284dd701afc2f546c4f49d1954564a24 diff --git a/vendor/predis/predis/CHANGELOG.md b/vendor/predis/predis/CHANGELOG.md new file mode 100644 index 00000000..2f3606a2 --- /dev/null +++ b/vendor/predis/predis/CHANGELOG.md @@ -0,0 +1,892 @@ +v1.0.4 (2016-05-30) +================================================================================ + +- 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. + +- Various improvements in the handling of redis-cluster: + + - 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. + + - 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`. + +- __FIX__: broken values returned by `Predis\Collection\Iterator\HashKey` when + iterating hash keys containing integer fields (PR #330, ISSUE #331). + +- __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). + + +v1.0.3 (2015-07-30) +================================================================================ + +- __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. + + +v1.0.2 (2015-07-30) +================================================================================ + +- IPv6 is now fully supported. + +- 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). + +- Added new or missing commands: `HSTRLEN` (>= 3.2), `ZREVRANGEBYLEX` (>= 2.8) + and `MIGRATE` (>= 2.6). + +- 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. + +- __FIX__: `Predis\Configuration\Options` must not trigger the autoloader when + option values are strings (ISSUE #257). + +- __FIX__: `BITPOS` was not defined in the key-prefix processor (ISSUE #265) and + in the replication strategy. + + +v1.0.1 (2015-01-02) +================================================================================ + +- Added `BITPOS` to the server profile for Redis 2.8. + +- Connection timeout for read/write operations can now be set for UNIX sockets + where the underlying connection uses PHP's stream. + +- __FIX__: broken values returned by `Predis\Collection\Iterator\SortedSetKey` + when iterating sorted set containing integer members (ISSUE #216). + +- __FIX__: applied a minor workaround for a bug in old versions of PHP < 5.3.9 + affecting inheritance. + +- __FIX__: prevent E_NOTICE warnings when using INFO [section] returns an empty + response due to an unsupported specific set of information requested to Redis. + + +v1.0.0 (2014-08-01) +================================================================================ + +- Switched to PSR-4 for autoloading. + +- The default server profile for Redis is `3.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 diff --git a/vendor/predis/predis/CONTRIBUTING.md b/vendor/predis/predis/CONTRIBUTING.md new file mode 100644 index 00000000..8da3339d --- /dev/null +++ b/vendor/predis/predis/CONTRIBUTING.md @@ -0,0 +1,44 @@ +## Filing bug reports ## + +Bugs or feature requests can be posted on the [GitHub issues](http://github.com/nrk/predis/issues) +section of the project. + +When reporting bugs, in addition to the obvious description of your issue you __must__ always provide +some essential information about your environment such as: + + 1. version of Predis (check the `VERSION` file or the `Predis\Client::VERSION` constant). + 2. version of Redis (check `redis_version` returned by [`INFO`](http://redis.io/commands/info)). + 3. version of PHP. + 4. name and version of the operating system. + 5. when possible, a small snippet of code that reproduces the issue. + +__Think about it__: we do not have a crystal ball and cannot predict things or peer into the unknown +so please provide as much details as possible to help us isolating issues and fix them. + +__Never__ use GitHub issues to post generic questions about Predis! When you have questions about +how Predis works or how it can be used, please just hop me an email and I will get back to you as +soon as possible. + + +## Contributing code ## + +If you want to work on Predis, it is highly recommended that you first run the test suite in order +to check that everything is OK and report strange behaviours or bugs. When modifying Predis please +make sure that no warnings or notices are emitted by PHP running the interpreter in your development +environment with the `error_reporting` variable set to `E_ALL | E_STRICT`. + +The recommended way to contribute to Predis is to fork the project on GitHub, create topic branches +on your newly created repository to fix bugs or add new features (possibly with tests covering your +modifications) and then open a pull request with a description of the applied changes. Obviously you +can use any other Git hosting provider of your preference. + +We always aim for consistency in our code base so you should follow basic coding rules as defined by +[PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md) +and [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) +and stick with the conventions used in Predis to name classes and interfaces. Indentation should be +done with 4 spaces and code should be wrapped at 100 columns (please try to stay within this limit +even if the above mentioned official coding guidelines set the soft limit to 120 columns). + +Please follow these [commit guidelines](http://git-scm.com/book/ch5-2.html#Commit-Guidelines) when +committing your code to Git and always write a meaningful (not necessarily extended) description of +your changes before opening pull requests. diff --git a/vendor/predis/predis/FAQ.md b/vendor/predis/predis/FAQ.md new file mode 100644 index 00000000..07ec1f0b --- /dev/null +++ b/vendor/predis/predis/FAQ.md @@ -0,0 +1,169 @@ +# Some frequently asked questions about Predis # +________________________________________________ + +### What is the point of Predis? ### + +The main point of Predis is about offering a highly customizable and extensible client for Redis, +that can be easily extended by developers while still being reasonabily fast. With Predis you can +swap almost any class with your own custom implementation: you can have custom connection classes, +new distribution strategies for client-side sharding, or handlers to replace or add Redis commands. +All of this can be achieved without messing with the source code of the library and directly in your +own application. Given the fast pace at which Redis is developed and adds new features, this can be +a great asset since it allows developers to add new and still missing features or commands or change +the standard behaviour of the library without the need to break dependencies in production code (at +least to some degree). + +### Does Predis support UNIX domain sockets and persistent connections? ### + +Yes. Obviously persistent connections actually work only when using PHP configured as a persistent +process reused by the web server (see [PHP-FPM](http://php-fpm.org)). + +### Does Predis support transparent (de)serialization of values? ### + +No and it will not ever do that by default. The reason behind this decision is that serialization is +usually something that developers prefer to customize depending on their needs and can not be easily +generalized when using Redis because of the many possible access patterns for your data. This does +not mean that it is impossible to have such a feature since you can leverage the extensibility of +this library to define your own serialization-aware commands. You can find more details about how to +do that [on this issue](http://github.com/nrk/predis/issues/29#issuecomment-1202624). + +### How can I force Predis to connect to Redis before sending any command? ### + +Explicitly connecting to Redis is usually not needed since the client initializes connections lazily +only when they are needed. Admittedly, this behavior can be inconvenient in certain scenarios when +you absolutely need to perform an upfront check to determine if the server is up and running and +eventually catch exceptions on failures. Forcing the client to open the underlying connection can be +done by invoking `Predis\Client::connect()`: + +```php +$client = new Predis\Client(); + +try { + $client->connect(); +} catch (Predis\Connection\ConnectionException $exception) { + // We could not connect to Redis! Your handling code goes here. +} + +$client->info(); +``` + +### How Predis abstracts Redis commands? ### + +The approach used to implement Redis commands is quite simple: by default each command follows the +same signature as defined on the [Redis documentation](http://redis.io/commands) which makes things +pretty easy if you already know how Redis works or you need to look up how to use certain commands. +Alternatively, variadic commands can accept an array for keys or values (depending on the command) +instead of a list of arguments. Commands such as [`RPUSH`](http://redis.io/commands/rpush) and +[`HMSET`](http://redis.io/commands/hmset) are great examples: + +```php +$client->rpush('my:list', 'value1', 'value2', 'value3'); // plain method arguments +$client->rpush('my:list', ['value1', 'value2', 'value3']); // single argument array + +$client->hmset('my:hash', 'field1', 'value1', 'field2', 'value2'); // plain method arguments +$client->hmset('my:hash', ['field1'=>'value1', 'field2'=>'value2']); // single named array +``` + +An exception to this rule is [`SORT`](http://redis.io/commands/sort) for which modifiers are passed +[using a named array](tests/Predis/Command/KeySortTest.php#L54-L75). + + +# Speaking about performances... # +_________________________________________________ + + +### Predis is a pure-PHP implementation: it can not be fast enough! ### + +It really depends, but most of the times the answer is: _yes, it is fast enough_. I will give you a +couple of easy numbers with a simple test that uses a single client and is executed by PHP 5.5.6 +against a local instance of Redis 2.8 that runs under Ubuntu 13.10 on a Intel Q6600: + +``` +21000 SET/sec using 12 bytes for both key and value. +21000 GET/sec while retrieving the very same values. +0.130 seconds to fetch 30000 keys using _KEYS *_. +``` + +How does it compare with [__phpredis__](http://github.com/nicolasff/phpredis), a nice C extension +providing an efficient client for Redis? + +``` +30100 SET/sec using 12 bytes for both key and value +29400 GET/sec while retrieving the very same values +0.035 seconds to fetch 30000 keys using "KEYS *"". +``` + +Wow __phpredis__ seems much faster! Well, we are comparing a C extension with a pure-PHP library so +lower numbers are quite expected but there is a fundamental flaw in them: is this really how you are +going to use Redis in your application? Are you really going to send thousands of commands using a +for-loop on each page request using a single client instance? If so... well I guess you are probably +doing something wrong. Also, if you need to `SET` or `GET` multiple keys you should definitely use +commands such as `MSET` and `MGET`. You can also use pipelining to get more performances when this +technique can be used. + +There is one more thing: we have tested the overhead of Predis by connecting on a localhost instance +of Redis but how these numbers change when we hit the physical network by connecting to remote Redis +instances? + +``` +Using Predis: +3200 SET/sec using 12 bytes for both key and value +3200 GET/sec while retrieving the very same values +0.132 seconds to fetch 30000 keys using "KEYS *". + +Using phpredis: +3500 SET/sec using 12 bytes for both key and value +3500 GET/sec while retrieving the very same values +0.045 seconds to fetch 30000 keys using "KEYS *". +``` + +There you go, you get almost the same average numbers and the reason is simple: network latency is a +real performance killer and you cannot do (almost) anything about that. As a disclaimer, remember +that we are measuring the overhead of client libraries implementations and the effects of network +round-trip times, so we are not really measuring how fast Redis is. Redis shines best with thousands +of concurrent clients doing requests! Also, actual performances should be measured according to how +your application will use Redis. + +### I am convinced, but performances for multi-bulk responses are still worse ### + +Fair enough, but there is an option available if you need even more speed and consists on installing +__[phpiredis](http://github.com/nrk/phpiredis)__ (note the additional _i_ in the name) and let the +client use it. __phpiredis__ is another C extension that wraps __hiredis__ (the official C client +library for Redis) with a thin layer exposing its features to PHP. You can then choose between two +different connection classes: + + - `Predis\Connection\PhpiredisStreamConnection` (using native PHP streams). + - `Predis\Connection\PhpiredisSocketConnection` (requires `ext-socket`). + +You will now get the benefits of a faster protocol serializer and parser just by adding a couple of +lines of code: + +```php +$client = new Predis\Client('tcp://127.0.0.1', array( + 'connections' => array( + 'tcp' => 'Predis\Connection\PhpiredisStreamConnection', + 'unix' => 'Predis\Connection\PhpiredisSocketConnection', + ), +)); +``` + +Dead simple. Nothing changes in the way you use the library in your application. So how fast is it +our basic benchmark script now? There are not much improvements for inline or short bulk responses +like the ones returned by `SET` and `GET`, but the speed for parsing multi-bulk responses is now on +par with phpredis: + +``` +Fatching 30000 keys with _KEYS *_ using Predis paired with phpiredis:: + +0.035 seconds from a local Redis instance +0.047 seconds from a remote Redis instance +``` + +### If I need an extension to get better performances, why not using phpredis? ### + +Good question. Generically speaking if you need absolute uber-speed using Redis on the localhost and +you do not care about abstractions built around some Redis features such as MULTI / EXEC, or if you +do not need any kind of extensibility or guaranteed backwards compatibility with different versions +of Redis (Predis currently supports from 1.2 up to 2.8 and the current development version), then +using __phpredis__ makes absolutely sense. Otherwise, Predis is perfect for the job and by adding +__phpiredis__ you can get a nice speed bump almost for free. diff --git a/vendor/predis/predis/LICENSE b/vendor/predis/predis/LICENSE new file mode 100644 index 00000000..c35b6574 --- /dev/null +++ b/vendor/predis/predis/LICENSE @@ -0,0 +1,22 @@ +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 +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/predis/predis/README.md b/vendor/predis/predis/README.md new file mode 100644 index 00000000..f7dcfc94 --- /dev/null +++ b/vendor/predis/predis/README.md @@ -0,0 +1,436 @@ +# Predis # + +[![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-travis]][link-travis] +[![HHVM support][ico-hhvm]][link-hhvm] + +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 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 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 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 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) 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 ### + +Predis relies on the autoloading features of PHP to load its files when needed and complies with the +[PSR-4 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md). +Autoloading is handled automatically when dependencies are managed through Composer, but it is also +possible to leverage its own autoloader in projects or scripts lacking any autoload facility: + +```php +// Prepend a base path if Predis is not available in your "include_path". +require 'Predis/Autoloader.php'; + +Predis\Autoloader::register(); +``` + +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 ### + +When creating a client instance without passing any connection parameter, Predis assumes `127.0.0.1` +and `6379` as default host and port. The default timeout for the `connect()` operation is 5 seconds: + +```php +$client = new Predis\Client(); +$client->set('foo', 'bar'); +$value = $client->get('foo'); +``` + +Connection parameters can be supplied either in the form of URI strings or named arrays. The latter +is the preferred way to supply parameters, but URI strings can be useful when parameters are read +from non-structured or partially-structured sources: + +```php +// Parameters passed using a named array: +$client = new Predis\Client([ + 'scheme' => 'tcp', + 'host' => '10.0.0.1', + 'port' => 6379, +]); + +// Same set of parameters, passed using an URI string: +$client = new Predis\Client('tcp://10.0.0.1:6379'); +``` + +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 connection schemes [`redis`](http://www.iana.org/assignments/uri-schemes/prov/redis) (alias of +`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. + +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'], +]); +``` + +See the [aggregate connections](#aggregate-connections) section of this document for more details. + + +### Client configuration ### + +Many aspects and behaviors of the client can be configured by passing specific client options to the +second argument of `Predis\Client::__construct()`: + +```php +$client = new Predis\Client($parameters, ['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: + + - `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 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. + + +### Aggregate connections ### + +Aggregate connections are the foundation upon which Predis implements clustering and replication and +they are used to group multiple connections to single Redis nodes and hide the specific logic needed +to handle them properly depending on the context. Aggregate connections usually require an array of +connection parameters when creating a new client instance. + +#### Cluster #### + +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']; + +$client = new Predis\Client($parameters); +``` + +Along with Redis 3.0, a new supervised and coordinated type of clustering was introduced in the form +of [redis-cluster](http://redis.io/topics/cluster-tutorial). This kind of approach uses a different +algorithm to distribute the keyspaces, with Redis nodes coordinating themselves by communicating via +a gossip protocol to handle health status, rebalancing, nodes discovery and request redirection. In +order to connect to a cluster managed by redis-cluster, the client requires a list of its nodes (not +necessarily complete since it will automatically discover new nodes if necessary) and the `cluster` +client options set to `redis`: + +```php +$parameters = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3']; +$options = ['cluster' => 'redis']; + +$client = new Predis\Client($parameters, $options); +``` + +#### Replication #### + +The client can be configured to operate in a single master / multiple slaves setup to provide better +service availability. When using replication, Predis recognizes read-only commands and sends them to +a random slave in order to provide some sort of load-balancing and switches to the master as soon as +it detects a command that performs any kind of operation that would end up modifying the keyspace or +the value of a key. Instead of raising a connection error when a slave fails, the client attempts to +fall back to a different slave among the ones provided in the configuration. + +The basic configuration needed to use the client in replication mode requires one Redis server to be +identified as the master (this can be done via connection parameters using the `alias` parameter set +to `master`) and one or more servers acting as slaves: + +```php +$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); +``` + +While Predis is able to distinguish commands performing write and read-only operations, `EVAL` and +`EVALSHA` represent a corner case in which the client switches to the master node because it cannot +tell when a Lua script is safe to be executed on slaves. While this is indeed the default behavior, +when certain Lua scripts do not perform write operations it is possible to provide an hint to tell +the client to stick with slaves for their execution: + +```php +$parameters = ['tcp://10.0.0.1?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\Aggregate\MasterSlaveReplication($strategy); +}]; + +$client = new Predis\Client($parameters, $options); +$client->eval($LUA_SCRIPT, 0); // Sticks to slave using `eval`... +$client->evalsha(sha1($LUA_SCRIPT), 0); // ... and `evalsha`, too. +``` + +The [`examples`](examples/) directory contains a few scripts that demonstrate how the client can be +configured and used to leverage replication in both basic and complex scenarios. + + +### Command pipelines ### + +Pipelining can help with performances when many commands need to be sent to a server by reducing the +latency introduced by network round-trip timings. Pipelining also works with aggregate connections. +The client can execute the pipeline inside a callable block or return a pipeline instance with the +ability to chain commands thanks to its fluent interface: + +```php +// Executes a pipeline inside the given callable block: +$responses = $client->pipeline(function ($pipe) { + for ($i = 0; $i < 1000; $i++) { + $pipe->set("key:$i", str_pad($i, 4, '0', 0)); + $pipe->get("key:$i"); + } +}); + +// Returns a pipeline that can be chained thanks to its fluent interface: +$responses = $client->pipeline()->set('foo', 'bar')->get('foo')->execute(); +``` + + +### Transactions ### + +The client provides an abstraction for Redis transactions based on `MULTI` and `EXEC` with a similar +interface to command pipelines: + +```php +// Executes a transaction inside the given callable block: +$responses = $client->transaction(function ($tx) { + $tx->set('foo', 'bar'); + $tx->get('foo'); +}); + +// Returns a transaction that can be chained thanks to its fluent interface: +$responses = $client->transaction()->set('foo', 'bar')->get('foo')->execute(); +``` + +This abstraction can perform check-and-set operations thanks to `WATCH` and `UNWATCH` and provides +automatic retries of transactions aborted by Redis when `WATCH`ed keys are touched. For an example +of a transaction using CAS you can see [the following example](examples/transaction_using_cas.php). + + +### Adding new commands ### + +While we try to update Predis to stay up to date with all the commands available in Redis, you might +prefer to stick with an old version of the library or provide a different way to filter arguments or +parse responses for specific commands. To achieve that, Predis provides the ability to implement new +command classes to define or override commands in the default server profiles used by the client: + +```php +// Define a new command by extending Predis\Command\Command: +class BrandNewRedisCommand extends Predis\Command\Command +{ + public function getId() + { + return 'NEWCMD'; + } +} + +// Inject your command in the current profile: +$client = new Predis\Client(); +$client->getProfile()->defineCommand('newcmd', 'BrandNewRedisCommand'); + +$response = $client->newcmd(); +``` + +There is also a method to send raw commands without filtering their arguments or parsing responses. +Users must provide the list of arguments for the command as an array, following the signatures as +defined by the [Redis documentation for commands](http://redis.io/commands): + +```php +$response = $client->executeRaw(['SET', 'foo', 'bar']); +``` + + +### Script commands ### + +While it is possible to leverage [Lua scripting](http://redis.io/commands/eval) on Redis 2.6+ using +directly [`EVAL`](http://redis.io/commands/eval) and [`EVALSHA`](http://redis.io/commands/evalsha), +Predis offers script commands as an higher level abstraction built upon them to make things simple. +Script commands can be registered in the 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) +is used as a fall back when needed: + +```php +// Define a new script command by extending Predis\Command\ScriptCommand: +class ListPushRandomValue extends Predis\Command\ScriptCommand +{ + public function getKeysCount() + { + return 1; + } + + public function getScript() + { + return <<<LUA +math.randomseed(ARGV[1]) +local rnd = tostring(math.random()) +redis.call('lpush', KEYS[1], rnd) +return rnd +LUA; + } +} + +// Inject the script command in the current profile: +$client = new Predis\Client(); +$client->getProfile()->defineCommand('lpushrand', 'ListPushRandomValue'); + +$response = $client->lpushrand('random_values', $seed = mt_rand()); +``` + + +### Customizable connection backends ### + +Predis can use different connection backends to connect to Redis. Two of them leverage a third party +extension such as [phpiredis](https://github.com/nrk/phpiredis) resulting in major performance gains +especially when dealing with big multibulk responses. While one is based on PHP streams, the other +is based on socket resources provided by `ext-socket`. Both support TCP/IP and UNIX domain sockets: + +```php +$client = new Predis\Client('tcp://127.0.0.1', [ + 'connections' => [ + 'tcp' => 'Predis\Connection\PhpiredisStreamConnection', // PHP stream resources + 'unix' => 'Predis\Connection\PhpiredisSocketConnection', // ext-socket resources + ], +]); +``` + +Developers can create their own connection classes to support whole new network backends, extend +existing classes or provide completely different implementations. Connection classes must implement +`Predis\Connection\NodeConnectionInterface` or extend `Predis\Connection\AbstractConnection`: + +```php +class MyConnectionClass implements Predis\Connection\NodeConnectionInterface +{ + // Implementation goes here... +} + +// Use MyConnectionClass to handle connections for the `tcp` scheme: +$client = new Predis\Client('tcp://127.0.0.1', [ + 'connections' => ['tcp' => 'MyConnectionClass'], +]); +``` + +For a more in-depth insight on how to create new connection backends you can refer to the actual +implementation of the standard connection classes available in the `Predis\Connection` namespace. + + +## Development ## + + +### Reporting bugs and contributing code ### + +Contributions to Predis are highly appreciated either in the form of pull requests for new features, +bug fixes, or just bug reports. We only ask you to adhere to a [basic set of rules](CONTRIBUTING.md) +before submitting your changes or filing bugs on the issue tracker to make it easier for everyone to +stay consistent while working on the project. + + +### Test suite ### + +__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. This test suite performs +integration tests against a running instance of Redis (>= 2.4.0 is required) to verify the correct +behavior of the implementation of each command and automatically skips commands not defined in the +specified Redis profile. If you do not have Redis up and running, integration tests can be disabled. +By default the test suite is configured to execute integration tests using the profile for Redis 2.8 +(which is the current stable version of Redis) but can optionally target a Redis instance built from +the `unstable` branch by modifying `phpunit.xml` and setting `REDIS_SERVER_VERSION` to `dev` so that +the development server profile will be used. You can refer to [the tests README](tests/README.md) +for more detailed information about testing Predis. + +Predis uses Travis CI for continuous integration and the history for past and current builds can be +found [on its project page](http://travis-ci.org/nrk/predis). + + +## Other ## + + +### Project related links ### + +- [Source code](https://github.com/nrk/predis) +- [Wiki](https://wiki.github.com/nrk/predis) +- [Issue tracker](https://github.com/nrk/predis/issues) +- [PEAR channel](http://pear.nrk.io) + + +### Author ### + +- [Daniele Alessandri](mailto:suppakilla@gmail.com) ([twitter](http://twitter.com/JoL1hAHN)) + + +### 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/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-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-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 diff --git a/vendor/predis/predis/VERSION b/vendor/predis/predis/VERSION new file mode 100644 index 00000000..ee90284c --- /dev/null +++ b/vendor/predis/predis/VERSION @@ -0,0 +1 @@ +1.0.4 diff --git a/vendor/predis/predis/autoload.php b/vendor/predis/predis/autoload.php new file mode 100644 index 00000000..6b5a00b1 --- /dev/null +++ b/vendor/predis/predis/autoload.php @@ -0,0 +1,14 @@ +<?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__.'/src/Autoloader.php'; + +Predis\Autoloader::register(); diff --git a/vendor/predis/predis/bin/create-command-test b/vendor/predis/predis/bin/create-command-test new file mode 100755 index 00000000..930797df --- /dev/null +++ b/vendor/predis/predis/bin/create-command-test @@ -0,0 +1,275 @@ +#!/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 generate a file with the scheleton +// of a test case to test a Redis command by specifying the name of the class +// in the Predis\Command namespace (only classes in this namespace are valid). +// For example, to generate a test case for SET (which is represented by the +// Predis\Command\StringSet class): +// +// $ ./bin/generate-command-test --class=StringSet +// +// Here is a list of optional arguments: +// +// --realm: each command has its own realm (commands that operate on strings, +// lists, sets and such) but while this realm is usually inferred from the name +// of the specified class, sometimes it can be useful to override it with a +// custom one. +// +// --output: write the generated test case to the specified path instead of +// the default one. +// +// --overwrite: pre-existing test files are not overwritten unless this option +// is explicitly specified. +// -------------------------------------------------------------------------- // + +use Predis\Command\CommandInterface; +use Predis\Command\PrefixableCommandInterface; + +class CommandTestCaseGenerator +{ + private $options; + + public function __construct(array $options) + { + if (!isset($options['class'])) { + throw new RuntimeException("Missing 'class' option."); + } + $this->options = $options; + } + + public static function fromCommandLine() + { + $parameters = array( + 'c:' => 'class:', + 'r::' => 'realm::', + 'o::' => 'output::', + 'x::' => 'overwrite::' + ); + + $getops = getopt(implode(array_keys($parameters)), $parameters); + + $options = array( + 'overwrite' => false, + 'tests' => __DIR__.'/../tests/Predis', + ); + + foreach ($getops as $option => $value) { + switch ($option) { + case 'c': + case 'class': + $options['class'] = $value; + break; + + case 'r': + case 'realm': + $options['realm'] = $value; + break; + + case 'o': + case 'output': + $options['output'] = $value; + break; + + case 'x': + case 'overwrite': + $options['overwrite'] = true; + break; + } + } + + if (!isset($options['class'])) { + throw new RuntimeException("Missing 'class' option."); + } + + $options['fqn'] = "Predis\\Command\\{$options['class']}"; + $options['path'] = "Command/{$options['class']}.php"; + + $source = __DIR__.'/../src/'.$options['path']; + if (!file_exists($source)) { + throw new RuntimeException("Cannot find class file for {$options['fqn']} in $source."); + } + + if (!isset($options['output'])) { + $options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', 'Test.php', $options['path'])); + } + + return new self($options); + } + + protected function getTestRealm() + { + if (isset($this->options['realm'])) { + if (!$this->options['realm']) { + throw new RuntimeException('Invalid value for realm has been sepcified (empty).'); + } + 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() + { + $reflection = new ReflectionClass($class = $this->options['fqn']); + + if (!$reflection->isInstantiable()) { + throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed."); + } + if (!$reflection->implementsInterface('Predis\Command\CommandInterface')) { + throw new RuntimeException("Class $class must implement Predis\Command\CommandInterface."); + } + + /* + * @var CommandInterface + */ + $instance = $reflection->newInstance(); + + $buffer = $this->getTestCaseBuffer($instance); + + return $buffer; + } + + public function save() + { + $options = $this->options; + if (file_exists($options['output']) && !$options['overwrite']) { + throw new RuntimeException("File {$options['output']} already exist. Specify the --overwrite option to overwrite the existing file."); + } + file_put_contents($options['output'], $this->generate()); + } + + protected function getTestCaseBuffer(CommandInterface $instance) + { + $id = $instance->getId(); + $fqn = get_class($instance); + $fqnParts = explode('\\', $fqn); + $class = array_pop($fqnParts) . "Test"; + $realm = $this->getTestRealm(); + + $buffer =<<<PHP +<?php + +/* + * This file is part of the Predis package. + * + * (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; + +/** + * @group commands + * @group realm-$realm + */ +class $class extends PredisCommandTestCase +{ + /** + * {@inheritdoc} + */ + protected function getExpectedCommand() + { + return '$fqn'; + } + + /** + * {@inheritdoc} + */ + protected function getExpectedId() + { + return '$id'; + } + + /** + * @group disconnected + */ + public function testFilterArguments() + { + \$this->markTestIncomplete('This test has not been implemented yet.'); + + \$arguments = array(/* add arguments */); + \$expected = array(/* add arguments */); + + \$command = \$this->getCommand(); + \$command->setArguments(\$arguments); + + \$this->assertSame(\$expected, \$command->getArguments()); + } + + /** + * @group disconnected + */ + public function testParseResponse() + { + \$this->markTestIncomplete('This test has not been implemented yet.'); + + \$raw = null; + \$expected = null; + + \$command = \$this->getCommand(); + + \$this->assertSame(\$expected, \$command->parseResponse(\$raw)); + } + +PHP; + + if ($instance instanceof PrefixableCommandInterface) { + $buffer .=<<<PHP + + /** + * @group disconnected + */ + public function testPrefixKeys() + { + \$this->markTestIncomplete('This test has not been implemented yet.'); + + \$arguments = array(/* add arguments */); + \$expected = array(/* add arguments */); + + \$command = \$this->getCommandWithArgumentsArray(\$arguments); + \$command->prefixKeys('prefix:'); + + \$this->assertSame(\$expected, \$command->getArguments()); + } + + /** + * @group disconnected + */ + public function testPrefixKeysIgnoredOnEmptyArguments() + { + \$command = \$this->getCommand(); + \$command->prefixKeys('prefix:'); + + \$this->assertSame(array(), \$command->getArguments()); + } + +PHP; + } + + return "$buffer}\n"; + } +} + +// ------------------------------------------------------------------------- // + +require __DIR__.'/../autoload.php'; + +$generator = CommandTestCaseGenerator::fromCommandLine(); +$generator->save(); diff --git a/vendor/predis/predis/bin/create-pear b/vendor/predis/predis/bin/create-pear new file mode 100755 index 00000000..b4f92db4 --- /dev/null +++ b/vendor/predis/predis/bin/create-pear @@ -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(); +}); diff --git a/vendor/predis/predis/bin/create-phar b/vendor/predis/predis/bin/create-phar new file mode 100755 index 00000000..8501d4a7 --- /dev/null +++ b/vendor/predis/predis/bin/create-phar @@ -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']); diff --git a/vendor/predis/predis/bin/create-single-file b/vendor/predis/predis/bin/create-single-file new file mode 100755 index 00000000..ecb876b0 --- /dev/null +++ b/vendor/predis/predis/bin/create-single-file @@ -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']); diff --git a/vendor/predis/predis/composer.json b/vendor/predis/predis/composer.json new file mode 100644 index 00000000..e67ba577 --- /dev/null +++ b/vendor/predis/predis/composer.json @@ -0,0 +1,31 @@ +{ + "name": "predis/predis", + "type": "library", + "description": "Flexible and feature-complete Redis client for PHP and HHVM", + "keywords": ["nosql", "redis", "predis"], + "homepage": "http://github.com/nrk/predis", + "license": "MIT", + "support": { + "issues": "https://github.com/nrk/predis/issues" + }, + "authors": [ + { + "name": "Daniele Alessandri", + "email": "suppakilla@gmail.com", + "homepage": "http://clorophilla.net" + } + ], + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "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/"} + } +} diff --git a/vendor/predis/predis/examples/custom_cluster_distributor.php b/vendor/predis/predis/examples/custom_cluster_distributor.php new file mode 100644 index 00000000..0a3a421e --- /dev/null +++ b/vendor/predis/predis/examples/custom_cluster_distributor.php @@ -0,0 +1,117 @@ +<?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'; + +// Developers can implement Predis\Distribution\DistributorInterface to create +// their own distributors used by the client to distribute keys among a cluster +// of servers. + +use Predis\Cluster\Distributor\DistributorInterface; +use Predis\Cluster\Hash\HashGeneratorInterface; +use Predis\Cluster\PredisStrategy; +use Predis\Connection\Aggregate\PredisCluster; + +class NaiveDistributor implements DistributorInterface, HashGeneratorInterface +{ + private $nodes; + private $nodesCount; + + public function __construct() + { + $this->nodes = array(); + $this->nodesCount = 0; + } + + public function add($node, $weight = null) + { + $this->nodes[] = $node; + ++$this->nodesCount; + } + + public function remove($node) + { + $this->nodes = array_filter($this->nodes, function ($n) use ($node) { + return $n !== $node; + }); + + $this->nodesCount = count($this->nodes); + } + + public function getSlot($hash) + { + return $this->nodesCount > 1 ? abs($hash % $this->nodesCount) : 0; + } + + public function getBySlot($slot) + { + return isset($this->nodes[$slot]) ? $this->nodes[$slot] : null; + } + + public function getByHash($hash) + { + if (!$this->nodesCount) { + throw new RuntimeException('No connections.'); + } + + $slot = $this->getSlot($hash); + $node = $this->getBySlot($slot); + + return $node; + } + + public function get($value) + { + $hash = $this->hash($value); + $node = $this->getByHash($hash); + + return $node; + } + + public function hash($value) + { + return crc32($value); + } + + public function getHashGenerator() + { + return $this; + } +} + +$options = array( + 'cluster' => function () { + $distributor = new NaiveDistributor(); + $strategy = new PredisStrategy($distributor); + $cluster = new PredisCluster($strategy); + + return $cluster; + }, +); + +$client = new Predis\Client($multiple_servers, $options); + +for ($i = 0; $i < 100; ++$i) { + $client->set("key:$i", str_pad($i, 4, '0', 0)); + $client->get("key:$i"); +} + +$server1 = $client->getClientFor('first')->info(); +$server2 = $client->getClientFor('second')->info(); + +if (isset($server1['Keyspace'], $server2['Keyspace'])) { + $server1 = $server1['Keyspace']; + $server2 = $server2['Keyspace']; +} + +printf("Server '%s' has %d keys while server '%s' has %d keys.\n", + 'first', $server1['db15']['keys'], 'second', $server2['db15']['keys'] +); diff --git a/vendor/predis/predis/examples/debuggable_connection.php b/vendor/predis/predis/examples/debuggable_connection.php new file mode 100644 index 00000000..1a30b027 --- /dev/null +++ b/vendor/predis/predis/examples/debuggable_connection.php @@ -0,0 +1,92 @@ +<?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'; + +// This is an example of how you can easily extend an existing connection class +// and trace the execution of commands for debugging purposes. This can be quite +// useful as a starting poing to understand how your application interacts with +// Redis. + +use Predis\Command\CommandInterface; +use Predis\Connection\StreamConnection; + +class SimpleDebuggableConnection extends StreamConnection +{ + private $tstart = 0; + private $debugBuffer = array(); + + public function connect() + { + $this->tstart = microtime(true); + + parent::connect(); + } + + private function storeDebug(CommandInterface $command, $direction) + { + $firtsArg = $command->getArgument(0); + $timestamp = round(microtime(true) - $this->tstart, 4); + + $debug = $command->getId(); + $debug .= isset($firtsArg) ? " $firtsArg " : ' '; + $debug .= "$direction $this"; + $debug .= " [{$timestamp}s]"; + + $this->debugBuffer[] = $debug; + } + + public function writeRequest(CommandInterface $command) + { + parent::writeRequest($command); + + $this->storeDebug($command, '->'); + } + + public function readResponse(CommandInterface $command) + { + $response = parent::readResponse($command); + $this->storeDebug($command, '<-'); + + return $response; + } + + public function getDebugBuffer() + { + return $this->debugBuffer; + } +} + +$options = array( + 'connections' => array( + 'tcp' => 'SimpleDebuggableConnection', + ), +); + +$client = new Predis\Client($single_server, $options); +$client->set('foo', 'bar'); +$client->get('foo'); +$client->info(); + +var_export($client->getConnection()->getDebugBuffer()); + +/* OUTPUT: +array ( + 0 => 'SELECT 15 -> 127.0.0.1:6379 [0.0008s]', + 1 => 'SELECT 15 <- 127.0.0.1:6379 [0.001s]', + 2 => 'SET foo -> 127.0.0.1:6379 [0.001s]', + 3 => 'SET foo <- 127.0.0.1:6379 [0.0011s]', + 4 => 'GET foo -> 127.0.0.1:6379 [0.0013s]', + 5 => 'GET foo <- 127.0.0.1:6379 [0.0015s]', + 6 => 'INFO -> 127.0.0.1:6379 [0.0019s]', + 7 => 'INFO <- 127.0.0.1:6379 [0.0022s]', +) +*/ diff --git a/vendor/predis/predis/examples/dispatcher_loop.php b/vendor/predis/predis/examples/dispatcher_loop.php new file mode 100644 index 00000000..7123491b --- /dev/null +++ b/vendor/predis/predis/examples/dispatcher_loop.php @@ -0,0 +1,79 @@ +<?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'; + +// This is a basic example on how to use the Predis\DispatcherLoop class. +// +// To see this example in action you can just use redis-cli and publish some +// messages to the 'events' and 'control' channel, e.g.: + +// ./redis-cli +// PUBLISH events first +// PUBLISH events second +// PUBLISH events third +// PUBLISH control terminate_dispatcher + +// Create a client and disable r/w timeout on the socket +$client = new Predis\Client($single_server + array('read_write_timeout' => 0)); + +// Return an initialized PubSub consumer instance from the client. +$pubsub = $client->pubSubLoop(); + +// Create a dispatcher loop instance and attach a bunch of callbacks. +$dispatcher = new Predis\PubSub\DispatcherLoop($pubsub); + +// Demonstrate how to use a callable class as a callback for the dispatcher loop. +class EventsListener implements Countable +{ + private $events; + + public function __construct() + { + $this->events = array(); + } + + public function count() + { + return count($this->events); + } + + public function getEvents() + { + return $this->events; + } + + public function __invoke($payload) + { + $this->events[] = $payload; + } +} + +// Attach our callable class to the dispatcher. +$dispatcher->attachCallback('events', ($events = new EventsListener())); + +// Attach a function to control the dispatcher loop termination with a message. +$dispatcher->attachCallback('control', function ($payload) use ($dispatcher) { + if ($payload === 'terminate_dispatcher') { + $dispatcher->stop(); + } +}); + +// Run the dispatcher loop until the callback attached to the 'control' channel +// receives 'terminate_dispatcher' as a message. +$dispatcher->run(); + +// Display our achievements! +echo "We received {$events->count()} messages!", PHP_EOL; + +// Say goodbye :-) +$version = redis_version($client->info()); +echo "Goodbye from Redis $version!", PHP_EOL; diff --git a/vendor/predis/predis/examples/executing_redis_commands.php b/vendor/predis/predis/examples/executing_redis_commands.php new file mode 100644 index 00000000..6e567536 --- /dev/null +++ b/vendor/predis/predis/examples/executing_redis_commands.php @@ -0,0 +1,57 @@ +<?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'; + +$client = new Predis\Client($single_server); + +// Plain old SET and GET example... +$client->set('library', 'predis'); +$response = $client->get('library'); + +var_export($response); echo PHP_EOL; +/* OUTPUT: 'predis' */ + +// Redis has the MSET and MGET commands to set or get multiple keys in one go, +// cases like this Predis accepts arguments for variadic commands both as a list +// of arguments or an array containing all of the keys and/or values. +$mkv = array( + 'uid:0001' => '1st user', + 'uid:0002' => '2nd user', + 'uid:0003' => '3rd user', +); + +$client->mset($mkv); +$response = $client->mget(array_keys($mkv)); + +var_export($response); echo PHP_EOL; +/* OUTPUT: +array ( + 0 => '1st user', + 1 => '2nd user', + 2 => '3rd user', +) */ + +// Predis can also send "raw" commands to Redis. The difference between sending +// commands to Redis the usual way and the "raw" way is that in the latter case +// their arguments are not filtered nor responses coming from Redis are parsed. + +$response = $client->executeRaw(array( + 'MGET', 'uid:0001', 'uid:0002', 'uid:0003', +)); + +var_export($response); echo PHP_EOL; +/* OUTPUT: +array ( + 0 => '1st user', + 1 => '2nd user', + 2 => '3rd user', +) */ diff --git a/vendor/predis/predis/examples/key_prefixing.php b/vendor/predis/predis/examples/key_prefixing.php new file mode 100644 index 00000000..1486330d --- /dev/null +++ b/vendor/predis/predis/examples/key_prefixing.php @@ -0,0 +1,36 @@ +<?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 can prefix keys found in commands arguments before sending commands to +// Redis, even for complex commands such as SORT, ZUNIONSTORE and ZINTERSTORE. +// Prefixing keys can be useful to create user-level namespaces for you keyspace +// thus reducing the need for separate logical databases in certain scenarios. + +$client = new Predis\Client($single_server, array('prefix' => 'nrk:')); + +$client->mset(array('foo' => 'bar', 'lol' => 'wut')); +var_export($client->mget('foo', 'lol')); +/* +array ( + 0 => 'bar', + 1 => 'wut', +) +*/ + +var_export($client->keys('*')); +/* +array ( + 0 => 'nrk:foo', + 1 => 'nrk:lol', +) +*/ diff --git a/vendor/predis/predis/examples/lua_scripting_abstraction.php b/vendor/predis/predis/examples/lua_scripting_abstraction.php new file mode 100644 index 00000000..50c07bf4 --- /dev/null +++ b/vendor/predis/predis/examples/lua_scripting_abstraction.php @@ -0,0 +1,71 @@ +<?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'; + +// This example will not work with versions of Redis < 2.6. +// +// 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; + +class IncrementExistingKeysBy extends ScriptCommand +{ + public function getKeysCount() + { + // Tell Predis to use all the arguments but the last one as arguments + // for KEYS. The last one will be used to populate ARGV. + return -1; + } + + public function getScript() + { + return <<<LUA +local cmd, insert = redis.call, table.insert +local increment, results = ARGV[1], { } + +for idx, key in ipairs(KEYS) do + if cmd('exists', key) == 1 then + insert(results, idx, cmd('incrby', key, increment)) + else + insert(results, idx, false) + end +end + +return results +LUA; + } +} + +$client = new Predis\Client($single_server, array( + 'profile' => function ($options) { + $profile = $options->getDefault('profile'); + $profile->defineCommand('increxby', 'IncrementExistingKeysBy'); + + return $profile; + }, +)); + +$client->mset('foo', 10, 'foobar', 100); + +var_export($client->increxby('foo', 'foofoo', 'foobar', 50)); + +/* +array ( + 0 => 60, + 1 => NULL, + 2 => 150, +) +*/ diff --git a/vendor/predis/predis/examples/monitor_consumer.php b/vendor/predis/predis/examples/monitor_consumer.php new file mode 100644 index 00000000..d472049b --- /dev/null +++ b/vendor/predis/predis/examples/monitor_consumer.php @@ -0,0 +1,44 @@ +<?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'; + +// This is a basic example on how to use the Predis\Monitor\Consumer class. You +// can use redis-cli to send commands to the same Redis instance your client is +// connected to, and then type "ECHO QUIT_MONITOR" in redis-cli when you want to +// exit the monitor loop and terminate this script in a graceful way. + +// Create a client and disable r/w timeout on the socket. +$client = new Predis\Client($single_server + array('read_write_timeout' => 0)); + +// Use only one instance of DateTime, we will update the timestamp later. +$timestamp = new DateTime(); + +foreach (($monitor = $client->monitor()) as $event) { + $timestamp->setTimestamp((int) $event->timestamp); + + // If we notice a ECHO command with the message QUIT_MONITOR, we stop the + // monitor consumer and then break the loop. + if ($event->command === 'ECHO' && $event->arguments === '"QUIT_MONITOR"') { + echo 'Exiting the monitor loop...', PHP_EOL; + $monitor->stop(); + break; + } + + echo "* Received {$event->command} on DB {$event->database} at {$timestamp->format(DateTime::W3C)}", PHP_EOL; + if (isset($event->arguments)) { + echo " Arguments: {$event->arguments}", PHP_EOL; + } +} + +// Say goodbye :-) +$version = redis_version($client->info()); +echo "Goodbye from Redis $version!", PHP_EOL; diff --git a/vendor/predis/predis/examples/pipelining_commands.php b/vendor/predis/predis/examples/pipelining_commands.php new file mode 100644 index 00000000..632ef94c --- /dev/null +++ b/vendor/predis/predis/examples/pipelining_commands.php @@ -0,0 +1,45 @@ +<?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'; + +// When you have a whole set of consecutive commands to send to a redis server, +// you can use a pipeline to dramatically improve performances. Pipelines can +// greatly reduce the effects of network round-trips. + +$client = new Predis\Client($single_server); + +$responses = $client->pipeline(function ($pipe) { + $pipe->flushdb(); + $pipe->incrby('counter', 10); + $pipe->incrby('counter', 30); + $pipe->exists('counter'); + $pipe->get('counter'); + $pipe->mget('does_not_exist', 'counter'); +}); + +var_export($responses); + +/* OUTPUT: +array ( + 0 => Predis\Response\Status::__set_state(array( + 'payload' => 'OK', + )), + 1 => 10, + 2 => 40, + 3 => true, + 4 => '40', + 5 => array ( + 0 => NULL, + 1 => '40', + ), +) +*/ diff --git a/vendor/predis/predis/examples/pubsub_consumer.php b/vendor/predis/predis/examples/pubsub_consumer.php new file mode 100644 index 00000000..24c485cc --- /dev/null +++ b/vendor/predis/predis/examples/pubsub_consumer.php @@ -0,0 +1,59 @@ +<?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'; + +// Starting from Redis 2.0 clients can subscribe and listen for events published +// on certain channels using a Publish/Subscribe (PUB/SUB) approach. + +// Create a client and disable r/w timeout on the socket +$client = new Predis\Client($single_server + array('read_write_timeout' => 0)); + +// Initialize a new pubsub consumer. +$pubsub = $client->pubSubLoop(); + +// Subscribe to your channels +$pubsub->subscribe('control_channel', 'notifications'); + +// Start processing the pubsup messages. Open a terminal and use redis-cli +// to push messages to the channels. Examples: +// ./redis-cli PUBLISH notifications "this is a test" +// ./redis-cli PUBLISH control_channel quit_loop +foreach ($pubsub as $message) { + switch ($message->kind) { + case 'subscribe': + echo "Subscribed to {$message->channel}", PHP_EOL; + break; + + case 'message': + if ($message->channel == 'control_channel') { + if ($message->payload == 'quit_loop') { + echo 'Aborting pubsub loop...', PHP_EOL; + $pubsub->unsubscribe(); + } else { + echo "Received an unrecognized command: {$message->payload}.", PHP_EOL; + } + } else { + echo "Received the following message from {$message->channel}:", + PHP_EOL, " {$message->payload}", PHP_EOL, PHP_EOL; + } + break; + } +} + +// Always unset the pubsub consumer instance when you are done! The +// class destructor will take care of cleanups and prevent protocol +// desynchronizations between the client and the server. +unset($pubsub); + +// Say goodbye :-) +$version = redis_version($client->info()); +echo "Goodbye from Redis $version!", PHP_EOL; diff --git a/vendor/predis/predis/examples/redis_collections_iterators.php b/vendor/predis/predis/examples/redis_collections_iterators.php new file mode 100644 index 00000000..62755c9f --- /dev/null +++ b/vendor/predis/predis/examples/redis_collections_iterators.php @@ -0,0 +1,99 @@ +<?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'; + +use Predis\Collection\Iterator; + +// 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: +// +// - SCAN (iterates over the keyspace) +// - SSCAN (iterates over members of a set) +// - ZSCAN (iterates over members and ranks of a sorted set) +// - HSCAN (iterates over fields and values of an hash). + +// Predis provides a specialized abstraction for each command based on standard +// SPL iterators making it possible to easily consume SCAN-based iterations in +// your PHP code. +// +// See http://redis.io/commands/scan for more details. +// + +// 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'); +for ($i = 0; $i < 5; ++$i) { + $client->sadd('predis:set', "member:$i"); + $client->zadd('predis:zset', -$i, "member:$i"); + $client->hset('predis:hash', "field:$i", "value:$i"); +} + +// === Keyspace iterator based on SCAN === +echo 'Scan the keyspace matching only our prefixed keys:', PHP_EOL; +foreach (new Iterator\Keyspace($client, 'predis:*') as $key) { + echo " - $key", PHP_EOL; +} + +/* OUTPUT +Scan the keyspace matching only our prefixed keys: + - predis:zset + - predis:set + - predis:hash +*/ + +// === Set iterator based on SSCAN === +echo 'Scan members of `predis:set`:', PHP_EOL; +foreach (new Iterator\SetKey($client, 'predis:set') as $member) { + echo " - $member", PHP_EOL; +} + +/* OUTPUT +Scan members of `predis:set`: + - member:1 + - member:4 + - member:0 + - member:3 + - member:2 +*/ + +// === Sorted set iterator based on ZSCAN === +echo 'Scan members and ranks of `predis:zset`:', PHP_EOL; +foreach (new Iterator\SortedSetKey($client, 'predis:zset') as $member => $rank) { + echo " - $member [rank: $rank]", PHP_EOL; +} + +/* OUTPUT +Scan members and ranks of `predis:zset`: + - member:4 [rank: -4] + - member:3 [rank: -3] + - member:2 [rank: -2] + - member:1 [rank: -1] + - member:0 [rank: 0] +*/ + +// === Hash iterator based on HSCAN === +echo 'Scan fields and values of `predis:hash`:', PHP_EOL; +foreach (new Iterator\HashKey($client, 'predis:hash') as $field => $value) { + echo " - $field => $value", PHP_EOL; +} + +/* OUTPUT +Scan fields and values of `predis:hash`: + - field:0 => value:0 + - field:1 => value:1 + - field:2 => value:2 + - field:3 => value:3 + - field:4 => value:4 +*/ diff --git a/vendor/predis/predis/examples/replication_complex.php b/vendor/predis/predis/examples/replication_complex.php new file mode 100644 index 00000000..71193d23 --- /dev/null +++ b/vendor/predis/predis/examples/replication_complex.php @@ -0,0 +1,85 @@ +<?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 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 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\Aggregate\MasterSlaveReplication; +use Predis\Replication\ReplicationStrategy; + +// ------------------------------------------------------------------------- // + +// Define a new script command that returns all the fields of a variable number +// of hashes with a single roundtrip. + +class HashMultipleGetAll extends ScriptCommand +{ + const BODY = <<<LUA +local hashes = {} +for _, key in pairs(KEYS) do + table.insert(hashes, key) + table.insert(hashes, redis.call('hgetall', key)) +end +return hashes +LUA; + + public function getScript() + { + return self::BODY; + } +} + +// ------------------------------------------------------------------------- // + +$parameters = array( + 'tcp://127.0.0.1:6379/?alias=master', + 'tcp://127.0.0.1:6380/?alias=slave', +); + +$options = array( + 'profile' => function ($options, $option) { + $profile = $options->getDefault($option); + $profile->defineCommand('hmgetall', 'HashMultipleGetAll'); + + return $profile; + }, + 'replication' => function () { + $strategy = new ReplicationStrategy(); + $strategy->setScriptReadOnly(HashMultipleGetAll::BODY); + + $replication = new MasterSlaveReplication($strategy); + + return $replication; + }, +); + +// ------------------------------------------------------------------------- // + +$client = new Predis\Client($parameters, $options); + +// Execute the following commands on the master server using redis-cli: +// $ ./redis-cli HMSET metavars foo bar hoge piyo +// $ ./redis-cli HMSET servers master host1 slave host2 + +$hashes = $client->hmgetall('metavars', 'servers'); + +$replication = $client->getConnection(); +$stillOnSlave = $replication->getCurrent() === $replication->getConnectionById('slave'); + +echo 'Is still on slave? ', $stillOnSlave ? 'YES!' : 'NO!', PHP_EOL; +var_export($hashes); diff --git a/vendor/predis/predis/examples/replication_simple.php b/vendor/predis/predis/examples/replication_simple.php new file mode 100644 index 00000000..91b6db9c --- /dev/null +++ b/vendor/predis/predis/examples/replication_simple.php @@ -0,0 +1,52 @@ +<?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 master / slave replication scenarios where write operations +// are performed on the master server and read operations are executed against +// one of the slaves. The behavior of commands or EVAL scripts can be customized +// at will. As soon as a write operation is performed the client switches to the +// master server for all the subsequent requests (either reads and writes). +// +// This example must be executed using the second Redis server configured as the +// slave of the first one (see the "SLAVEOF" command). +// + +$parameters = array( + 'tcp://127.0.0.1:6379?database=15&alias=master', + 'tcp://127.0.0.1:6380?database=15&alias=slave', +); + +$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->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->alias}!", PHP_EOL; + +// Read operation. +$bar = $client->get('foo'); +$current = $client->getConnection()->getCurrent()->getParameters(); +echo "We fetched 'foo' from {$current->alias} and its value is '$bar'.", PHP_EOL; + +/* OUTPUT: +Does 'foo' exist on slave? yes. +Now 'foo' has been set to 'bar' on master! +We fetched 'foo' from master and its value is 'bar'. +*/ diff --git a/vendor/predis/predis/examples/session_handler.php b/vendor/predis/predis/examples/session_handler.php new file mode 100644 index 00000000..4868a4df --- /dev/null +++ b/vendor/predis/predis/examples/session_handler.php @@ -0,0 +1,52 @@ +<?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'; + +// This example demonstrates how to use Predis to save PHP sessions on Redis. +// +// The value of `session.gc_maxlifetime` in `php.ini` will be used by default as +// the TTL for keys holding session data but this value can be overridden when +// creating the session handler instance using the `gc_maxlifetime` option. +// +// NOTE: this class requires PHP >= 5.4 but can be used on PHP 5.3 if a polyfill +// for SessionHandlerInterface is provided either by you or an external package +// like `symfony/http-foundation`. +// +// See http://www.php.net/class.sessionhandlerinterface.php for more details. +// + +if (!interface_exists('SessionHandlerInterface')) { + die('ATTENTION: the session handler implemented by Predis requires PHP >= 5.4.0 '. + "or a polyfill for SessionHandlerInterface provided by an external package.\n"); +} + +// Instantiate a new client just like you would normally do. Using a prefix for +// keys will effectively prefix all session keys with the specified string. +$client = new Predis\Client($single_server, array('prefix' => 'sessions:')); + +// Set `gc_maxlifetime` to specify a time-to-live of 5 seconds for session keys. +$handler = new Predis\Session\Handler($client, array('gc_maxlifetime' => 5)); + +// Register the session handler. +$handler->register(); + +// We just set a fixed session ID only for the sake of our example. +session_id('example_session_id'); + +session_start(); + +if (isset($_SESSION['foo'])) { + echo "Session has `foo` set to {$_SESSION['foo']}", PHP_EOL; +} else { + $_SESSION['foo'] = $value = mt_rand(); + echo "Empty session, `foo` has been set with $value", PHP_EOL; +} diff --git a/vendor/predis/predis/examples/shared.php b/vendor/predis/predis/examples/shared.php new file mode 100644 index 00000000..00e3faf9 --- /dev/null +++ b/vendor/predis/predis/examples/shared.php @@ -0,0 +1,44 @@ +<?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__.'/../autoload.php'; + +function redis_version($info) +{ + if (isset($info['Server']['redis_version'])) { + return $info['Server']['redis_version']; + } elseif (isset($info['redis_version'])) { + return $info['redis_version']; + } else { + return 'unknown version'; + } +} + +$single_server = array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 15, +); + +$multiple_servers = array( + array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 15, + 'alias' => 'first', + ), + array( + 'host' => '127.0.0.1', + 'port' => 6380, + 'database' => 15, + 'alias' => 'second', + ), +); diff --git a/vendor/predis/predis/examples/transaction_using_cas.php b/vendor/predis/predis/examples/transaction_using_cas.php new file mode 100644 index 00000000..68bc4676 --- /dev/null +++ b/vendor/predis/predis/examples/transaction_using_cas.php @@ -0,0 +1,52 @@ +<?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'; + +// This is an implementation of an atomic client-side ZPOP using the support for +// check-and-set (CAS) operations with MULTI/EXEC transactions, as described in +// "WATCH explained" from http://redis.io/topics/transactions +// +// First, populate your database with a tiny sample data set: +// +// ./redis-cli +// SELECT 15 +// ZADD zset 1 a 2 b 3 c +// +// Then execute this script four times and see its output. +// + +function zpop($client, $key) +{ + $element = null; + $options = array( + 'cas' => true, // Initialize with support for CAS operations + 'watch' => $key, // Key that needs to be WATCHed to detect changes + 'retry' => 3, // Number of retries on aborted transactions, after + // which the client bails out with an exception. + ); + + $client->transaction($options, function ($tx) use ($key, &$element) { + @list($element) = $tx->zrange($key, 0, 0); + + if (isset($element)) { + $tx->multi(); // With CAS, MULTI *must* be explicitly invoked. + $tx->zrem($key, $element); + } + }); + + return $element; +} + +$client = new Predis\Client($single_server); +$zpopped = zpop($client, 'zset'); + +echo isset($zpopped) ? "ZPOPed $zpopped" : 'Nothing to ZPOP!', PHP_EOL; diff --git a/vendor/predis/predis/package.ini b/vendor/predis/predis/package.ini new file mode 100644 index 00000000..a38adf7c --- /dev/null +++ b/vendor/predis/predis/package.ini @@ -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 diff --git a/vendor/predis/predis/src/Autoloader.php b/vendor/predis/predis/src/Autoloader.php new file mode 100644 index 00000000..17ec2ff1 --- /dev/null +++ b/vendor/predis/predis/src/Autoloader.php @@ -0,0 +1,62 @@ +<?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; + +/** + * Implements a lightweight PSR-0 compliant autoloader for Predis. + * + * @author Eric Naeseth <eric@thumbtack.com> + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Autoloader +{ + private $directory; + private $prefix; + private $prefixLength; + + /** + * @param string $baseDirectory Base directory where the source files are located. + */ + public function __construct($baseDirectory = __DIR__) + { + $this->directory = $baseDirectory; + $this->prefix = __NAMESPACE__.'\\'; + $this->prefixLength = strlen($this->prefix); + } + + /** + * Registers the autoloader class with the PHP SPL autoloader. + * + * @param bool $prepend Prepend the autoloader on the stack instead of appending it. + */ + public static function register($prepend = false) + { + spl_autoload_register(array(new self(), 'autoload'), true, $prepend); + } + + /** + * Loads a class from a file using its fully qualified name. + * + * @param string $className Fully qualified name of a class. + */ + public function autoload($className) + { + if (0 === strpos($className, $this->prefix)) { + $parts = explode('\\', substr($className, $this->prefixLength)); + $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php'; + + if (is_file($filepath)) { + require $filepath; + } + } + } +} diff --git a/vendor/predis/predis/src/Client.php b/vendor/predis/predis/src/Client.php new file mode 100644 index 00000000..73fb8af6 --- /dev/null +++ b/vendor/predis/predis/src/Client.php @@ -0,0 +1,523 @@ +<?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; + +use Predis\Command\CommandInterface; +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\ParametersInterface; +use Predis\Monitor\Consumer as MonitorConsumer; +use Predis\Pipeline\Pipeline; +use Predis\PubSub\Consumer as PubSubConsumer; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ResponseInterface; +use Predis\Response\ServerException; +use Predis\Transaction\MultiExec as MultiExecTransaction; + +/** + * Client class used for connecting and executing commands on Redis. + * + * This is the main high-level abstraction of Predis upon which various other + * abstractions are built. Internally it aggregates various other classes each + * one with its own responsibility and scope. + * + * {@inheritdoc} + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Client implements ClientInterface +{ + const VERSION = '1.0.4'; + + protected $connection; + protected $options; + private $profile; + + /** + * @param mixed $parameters Connection parameters for one or more servers. + * @param mixed $options Options to configure some behaviours of the client. + */ + public function __construct($parameters = null, $options = null) + { + $this->options = $this->createOptions($options ?: array()); + $this->connection = $this->createConnection($parameters ?: array()); + $this->profile = $this->options->profile; + } + + /** + * 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 mixed $options Client options. + * + * @throws \InvalidArgumentException + * + * @return OptionsInterface + */ + protected function createOptions($options) + { + if (is_array($options)) { + return new Options($options); + } + + if ($options instanceof OptionsInterface) { + return $options; + } + + throw new \InvalidArgumentException('Invalid type for client options.'); + } + + /** + * 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. + * + * Accepted types for connection parameters are: + * + * - Instance of Predis\Connection\ConnectionInterface. + * - Instance of Predis\Connection\ParametersInterface. + * - Array + * - String + * - Callable + * + * @param mixed $parameters Connection parameters or connection instance. + * + * @throws \InvalidArgumentException + * + * @return ConnectionInterface + */ + protected function createConnection($parameters) + { + if ($parameters instanceof ConnectionInterface) { + return $parameters; + } + + if ($parameters instanceof ParametersInterface || is_string($parameters)) { + return $this->options->connections->create($parameters); + } + + if (is_array($parameters)) { + if (!isset($parameters[0])) { + return $this->options->connections->create($parameters); + } + + $options = $this->options; + + 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; + } + + 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 getProfile() + { + return $this->profile; + } + + /** + * {@inheritdoc} + */ + public function getOptions() + { + return $this->options; + } + + /** + * 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. + * + * @param string $connectionID Identifier of a connection. + * + * @throws \InvalidArgumentException + * + * @return Client + */ + public function getClientFor($connectionID) + { + if (!$connection = $this->getConnectionById($connectionID)) { + throw new \InvalidArgumentException("Invalid connection ID: $connectionID."); + } + + return new static($connection, $this->options); + } + + /** + * Opens the underlying connection and connects to the server. + */ + public function connect() + { + $this->connection->connect(); + } + + /** + * Closes the underlying connection and disconnects from the server. + */ + public function disconnect() + { + $this->connection->disconnect(); + } + + /** + * Closes the underlying connection and disconnects from the server. + * + * This is the same as `Client::disconnect()` as it does not actually send + * the `QUIT` command to Redis, but simply closes the connection. + */ + public function quit() + { + $this->disconnect(); + } + + /** + * Returns the current state of the underlying connection. + * + * @return bool + */ + public function isConnected() + { + return $this->connection->isConnected(); + } + + /** + * {@inheritdoc} + */ + public function getConnection() + { + 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 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. + * @param bool $error Set to TRUE when Redis returned an error response. + * + * @return mixed + */ + public function executeRaw(array $arguments, &$error = null) + { + $error = false; + + $response = $this->connection->executeCommand( + new RawCommand($arguments) + ); + + if ($response instanceof ResponseInterface) { + if ($response instanceof ErrorResponseInterface) { + $error = true; + } + + return (string) $response; + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function __call($commandID, $arguments) + { + return $this->executeCommand( + $this->createCommand($commandID, $arguments) + ); + } + + /** + * {@inheritdoc} + */ + public function createCommand($commandID, $arguments = array()) + { + return $this->profile->createCommand($commandID, $arguments); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $response = $this->connection->executeCommand($command); + + if ($response instanceof ResponseInterface) { + if ($response instanceof ErrorResponseInterface) { + $response = $this->onErrorResponse($command, $response); + } + + return $response; + } + + return $command->parseResponse($response); + } + + /** + * Handles -ERR responses returned by Redis. + * + * @param CommandInterface $command Redis command that generated the error. + * @param ErrorResponseInterface $response Instance of the error response. + * + * @throws ServerException + * + * @return mixed + */ + protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response) + { + if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') { + $eval = $this->createCommand('EVAL'); + $eval->setRawArguments($command->getEvalArguments()); + + $response = $this->executeCommand($eval); + + if (!$response instanceof ResponseInterface) { + $response = $command->parseResponse($response); + } + + return $response; + } + + if ($this->options->exceptions) { + throw new ServerException($response->getMessage()); + } + + return $response; + } + + /** + * Executes the specified initializer method on `$this` by adjusting the + * actual invokation depending on the arity (0, 1 or 2 arguments). This is + * simply an utility method to create Redis contexts instances since they + * follow a common initialization path. + * + * @param string $initializer Method name. + * @param array $argv Arguments for the method. + * + * @return mixed + */ + private function sharedContextFactory($initializer, $argv = null) + { + switch (count($argv)) { + case 0: + return $this->$initializer(); + + case 1: + return is_array($argv[0]) + ? $this->$initializer($argv[0]) + : $this->$initializer(null, $argv[0]); + + case 2: + list($arg0, $arg1) = $argv; + + return $this->$initializer($arg0, $arg1); + + default: + return $this->$initializer($this, $argv); + } + } + + /** + * Creates a new pipeline context and returns it, or returns the results of + * a pipeline executed inside the optionally provided callable object. + * + * @param mixed ... Array of options, a callable for execution, or both. + * + * @return Pipeline|array + */ + public function pipeline(/* arguments */) + { + return $this->sharedContextFactory('createPipeline', func_get_args()); + } + + /** + * Actual pipeline context initializer method. + * + * @param array $options Options for the context. + * @param mixed $callable Optional callable used to execute the context. + * + * @return Pipeline|array + */ + protected function createPipeline(array $options = null, $callable = null) + { + if (isset($options['atomic']) && $options['atomic']) { + $class = 'Predis\Pipeline\Atomic'; + } elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) { + $class = 'Predis\Pipeline\FireAndForget'; + } else { + $class = 'Predis\Pipeline\Pipeline'; + } + + /* + * @var ClientContextInterface + */ + $pipeline = new $class($this); + + if (isset($callable)) { + return $pipeline->execute($callable); + } + + return $pipeline; + } + + /** + * Creates a new transaction context and returns it, or returns the results + * of a transaction executed inside the optionally provided callable object. + * + * @param mixed ... Array of options, a callable for execution, or both. + * + * @return MultiExecTransaction|array + */ + public function transaction(/* arguments */) + { + return $this->sharedContextFactory('createTransaction', func_get_args()); + } + + /** + * Actual transaction context initializer method. + * + * @param array $options Options for the context. + * @param mixed $callable Optional callable used to execute the context. + * + * @return MultiExecTransaction|array + */ + protected function createTransaction(array $options = null, $callable = null) + { + $transaction = new MultiExecTransaction($this, $options); + + if (isset($callable)) { + return $transaction->execute($callable); + } + + return $transaction; + } + + /** + * 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. + * + * @return PubSubConsumer|null + */ + public function pubSubLoop(/* arguments */) + { + return $this->sharedContextFactory('createPubSub', func_get_args()); + } + + /** + * Actual publish/subscribe context initializer method. + * + * @param array $options Options for the context. + * @param mixed $callable Optional callable used to execute the context. + * + * @return PubSubConsumer|null + */ + protected function createPubSub(array $options = null, $callable = null) + { + $pubsub = new PubSubConsumer($this, $options); + + if (!isset($callable)) { + return $pubsub; + } + + foreach ($pubsub as $message) { + if (call_user_func($callable, $pubsub, $message) === false) { + $pubsub->stop(); + } + } + } + + /** + * Creates a new monitor consumer and returns it. + * + * @return MonitorConsumer + */ + public function monitor() + { + return new MonitorConsumer($this); + } +} diff --git a/vendor/predis/predis/src/ClientContextInterface.php b/vendor/predis/predis/src/ClientContextInterface.php new file mode 100644 index 00000000..a4100d4b --- /dev/null +++ b/vendor/predis/predis/src/ClientContextInterface.php @@ -0,0 +1,198 @@ +<?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; + +use Predis\Command\CommandInterface; + +/** + * Interface defining a client-side context such as a pipeline or transaction. + * + * @method $this del(array $keys) + * @method $this dump($key) + * @method $this exists($key) + * @method $this expire($key, $seconds) + * @method $this expireat($key, $timestamp) + * @method $this keys($pattern) + * @method $this move($key, $db) + * @method $this object($subcommand, $key) + * @method $this persist($key) + * @method $this pexpire($key, $milliseconds) + * @method $this pexpireat($key, $timestamp) + * @method $this pttl($key) + * @method $this randomkey() + * @method $this rename($key, $target) + * @method $this renamenx($key, $target) + * @method $this scan($cursor, array $options = null) + * @method $this sort($key, array $options = null) + * @method $this ttl($key) + * @method $this type($key) + * @method $this append($key, $value) + * @method $this bitcount($key, $start = null, $end = null) + * @method $this bitop($operation, $destkey, $key) + * @method $this bitfield($key, ...) + * @method $this decr($key) + * @method $this decrby($key, $decrement) + * @method $this get($key) + * @method $this getbit($key, $offset) + * @method $this getrange($key, $start, $end) + * @method $this getset($key, $value) + * @method $this incr($key) + * @method $this incrby($key, $increment) + * @method $this incrbyfloat($key, $increment) + * @method $this mget(array $keys) + * @method $this mset(array $dictionary) + * @method $this msetnx(array $dictionary) + * @method $this psetex($key, $milliseconds, $value) + * @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null) + * @method $this setbit($key, $offset, $value) + * @method $this setex($key, $seconds, $value) + * @method $this setnx($key, $value) + * @method $this setrange($key, $offset, $value) + * @method $this strlen($key) + * @method $this hdel($key, array $fields) + * @method $this hexists($key, $field) + * @method $this hget($key, $field) + * @method $this hgetall($key) + * @method $this hincrby($key, $field, $increment) + * @method $this hincrbyfloat($key, $field, $increment) + * @method $this hkeys($key) + * @method $this hlen($key) + * @method $this hmget($key, array $fields) + * @method $this hmset($key, array $dictionary) + * @method $this hscan($key, $cursor, array $options = null) + * @method $this hset($key, $field, $value) + * @method $this hsetnx($key, $field, $value) + * @method $this hvals($key) + * @method $this hstrlen($key, $field) + * @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, $value) + * @method $this lrange($key, $start, $stop) + * @method $this lrem($key, $count, $value) + * @method $this lset($key, $index, $value) + * @method $this ltrim($key, $start, $stop) + * @method $this rpop($key) + * @method $this rpoplpush($source, $destination) + * @method $this rpush($key, array $values) + * @method $this rpushx($key, $value) + * @method $this sadd($key, array $members) + * @method $this scard($key) + * @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) + * @method $this spop($key, $count = null) + * @method $this srandmember($key, $count = null) + * @method $this srem($key, $member) + * @method $this sscan($key, $cursor, array $options = null) + * @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 $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) + * @method $this zrem($key, $member) + * @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, $min, $max, array $options = null) + * @method $this zrevrank($key, $member) + * @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) + * @method $this zrevrangebylex($key, $start, $stop, array $options = null) + * @method $this zremrangebylex($key, $min, $max) + * @method $this zlexcount($key, $min, $max) + * @method $this pfadd($key, array $elements) + * @method $this pfmerge($destinationKey, array $sourceKeys) + * @method $this pfcount(array $keys) + * @method $this pubsub($subcommand, $argument) + * @method $this publish($channel, $message) + * @method $this discard() + * @method $this exec() + * @method $this multi() + * @method $this unwatch() + * @method $this watch($key) + * @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) + * @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null) + * @method $this script($subcommand, $argument = null) + * @method $this auth($password) + * @method $this echo($message) + * @method $this ping($message = null) + * @method $this select($database) + * @method $this bgrewriteaof() + * @method $this bgsave() + * @method $this client($subcommand, $argument = null) + * @method $this config($subcommand, $argument = null) + * @method $this dbsize() + * @method $this flushall() + * @method $this flushdb() + * @method $this info($section = null) + * @method $this lastsave() + * @method $this save() + * @method $this slaveof($host, $port) + * @method $this slowlog($subcommand, $argument = null) + * @method $this time() + * @method $this command() + * @method $this geoadd($key, $longitude, $latitude, $member) + * @method $this geohash($key, array $members) + * @method $this geopos($key, array $members) + * @method $this geodist($key, $member1, $member2, $unit = null) + * @method $this georadius($key, $longitude, $latitude, $radius, $unit, array $options = null) + * @method $this georadiusbymember($key, $member, $radius, $unit, array $options = null) + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ClientContextInterface +{ + /** + * Sends the specified command instance to Redis. + * + * @param CommandInterface $command Command instance. + * + * @return mixed + */ + public function executeCommand(CommandInterface $command); + + /** + * Sends the specified command with its arguments to Redis. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return mixed + */ + public function __call($method, $arguments); + + /** + * Starts the execution of the context. + * + * @param mixed $callable Optional callback for execution. + * + * @return array + */ + public function execute($callable = null); +} diff --git a/vendor/predis/predis/src/ClientException.php b/vendor/predis/predis/src/ClientException.php new file mode 100644 index 00000000..6c07aaf0 --- /dev/null +++ b/vendor/predis/predis/src/ClientException.php @@ -0,0 +1,21 @@ +<?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; + +/** + * Exception class that identifies client-side errors. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ClientException extends PredisException +{ +} diff --git a/vendor/predis/predis/src/ClientInterface.php b/vendor/predis/predis/src/ClientInterface.php new file mode 100644 index 00000000..440d6daf --- /dev/null +++ b/vendor/predis/predis/src/ClientInterface.php @@ -0,0 +1,239 @@ +<?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; + +use Predis\Command\CommandInterface; +use Predis\Configuration\OptionsInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Profile\ProfileInterface; + +/** + * Interface defining a client able to execute commands against Redis. + * + * All the commands exposed by the client generally have the same signature as + * described by the Redis documentation, but some of them offer an additional + * and more friendly interface to ease programming which is described in the + * following list of methods: + * + * @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 server profile used by the client. + * + * @return ProfileInterface + */ + public function getProfile(); + + /** + * Returns the client options specified upon initialization. + * + * @return OptionsInterface + */ + public function getOptions(); + + /** + * Opens the underlying connection to the server. + */ + public function connect(); + + /** + * Closes the underlying connection from the server. + */ + public function disconnect(); + + /** + * Returns the underlying connection instance. + * + * @return ConnectionInterface + */ + public function getConnection(); + + /** + * Creates a new instance of the specified Redis command. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return CommandInterface + */ + public function createCommand($method, $arguments = array()); + + /** + * Executes the specified Redis command. + * + * @param CommandInterface $command Command instance. + * + * @return mixed + */ + public function executeCommand(CommandInterface $command); + + /** + * Creates a Redis command with the specified arguments and sends a request + * to the server. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return mixed + */ + public function __call($method, $arguments); +} diff --git a/vendor/predis/predis/src/Cluster/ClusterStrategy.php b/vendor/predis/predis/src/Cluster/ClusterStrategy.php new file mode 100644 index 00000000..1891907f --- /dev/null +++ b/vendor/predis/predis/src/Cluster/ClusterStrategy.php @@ -0,0 +1,469 @@ +<?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; + +use Predis\Command\CommandInterface; +use Predis\Command\ScriptCommand; + +/** + * Common class implementing the logic needed to support clustering strategies. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +abstract class ClusterStrategy implements StrategyInterface +{ + protected $commands; + + /** + * + */ + public function __construct() + { + $this->commands = $this->getDefaultCommands(); + } + + /** + * Returns the default map of supported commands with their handlers. + * + * @return array + */ + protected function getDefaultCommands() + { + $getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument'); + $getKeyFromAllArguments = array($this, 'getKeyFromAllArguments'); + + return array( + /* commands operating on the key space */ + 'EXISTS' => $getKeyFromAllArguments, + 'DEL' => $getKeyFromAllArguments, + 'TYPE' => $getKeyFromFirstArgument, + 'EXPIRE' => $getKeyFromFirstArgument, + 'EXPIREAT' => $getKeyFromFirstArgument, + 'PERSIST' => $getKeyFromFirstArgument, + 'PEXPIRE' => $getKeyFromFirstArgument, + 'PEXPIREAT' => $getKeyFromFirstArgument, + 'TTL' => $getKeyFromFirstArgument, + 'PTTL' => $getKeyFromFirstArgument, + 'SORT' => array($this, 'getKeyFromSortCommand'), + 'DUMP' => $getKeyFromFirstArgument, + 'RESTORE' => $getKeyFromFirstArgument, + + /* commands operating on string values */ + 'APPEND' => $getKeyFromFirstArgument, + 'DECR' => $getKeyFromFirstArgument, + 'DECRBY' => $getKeyFromFirstArgument, + 'GET' => $getKeyFromFirstArgument, + 'GETBIT' => $getKeyFromFirstArgument, + 'MGET' => $getKeyFromAllArguments, + 'SET' => $getKeyFromFirstArgument, + 'GETRANGE' => $getKeyFromFirstArgument, + 'GETSET' => $getKeyFromFirstArgument, + 'INCR' => $getKeyFromFirstArgument, + 'INCRBY' => $getKeyFromFirstArgument, + 'INCRBYFLOAT' => $getKeyFromFirstArgument, + 'SETBIT' => $getKeyFromFirstArgument, + 'SETEX' => $getKeyFromFirstArgument, + 'MSET' => array($this, 'getKeyFromInterleavedArguments'), + 'MSETNX' => array($this, 'getKeyFromInterleavedArguments'), + 'SETNX' => $getKeyFromFirstArgument, + 'SETRANGE' => $getKeyFromFirstArgument, + 'STRLEN' => $getKeyFromFirstArgument, + 'SUBSTR' => $getKeyFromFirstArgument, + 'BITOP' => array($this, 'getKeyFromBitOp'), + 'BITCOUNT' => $getKeyFromFirstArgument, + 'BITFIELD' => $getKeyFromFirstArgument, + + /* commands operating on lists */ + 'LINSERT' => $getKeyFromFirstArgument, + 'LINDEX' => $getKeyFromFirstArgument, + 'LLEN' => $getKeyFromFirstArgument, + 'LPOP' => $getKeyFromFirstArgument, + 'RPOP' => $getKeyFromFirstArgument, + 'RPOPLPUSH' => $getKeyFromAllArguments, + 'BLPOP' => array($this, 'getKeyFromBlockingListCommands'), + 'BRPOP' => array($this, 'getKeyFromBlockingListCommands'), + 'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'), + 'LPUSH' => $getKeyFromFirstArgument, + 'LPUSHX' => $getKeyFromFirstArgument, + 'RPUSH' => $getKeyFromFirstArgument, + 'RPUSHX' => $getKeyFromFirstArgument, + 'LRANGE' => $getKeyFromFirstArgument, + 'LREM' => $getKeyFromFirstArgument, + 'LSET' => $getKeyFromFirstArgument, + 'LTRIM' => $getKeyFromFirstArgument, + + /* commands operating on sets */ + 'SADD' => $getKeyFromFirstArgument, + 'SCARD' => $getKeyFromFirstArgument, + 'SDIFF' => $getKeyFromAllArguments, + 'SDIFFSTORE' => $getKeyFromAllArguments, + 'SINTER' => $getKeyFromAllArguments, + 'SINTERSTORE' => $getKeyFromAllArguments, + 'SUNION' => $getKeyFromAllArguments, + 'SUNIONSTORE' => $getKeyFromAllArguments, + 'SISMEMBER' => $getKeyFromFirstArgument, + 'SMEMBERS' => $getKeyFromFirstArgument, + 'SSCAN' => $getKeyFromFirstArgument, + 'SPOP' => $getKeyFromFirstArgument, + 'SRANDMEMBER' => $getKeyFromFirstArgument, + 'SREM' => $getKeyFromFirstArgument, + + /* commands operating on sorted sets */ + 'ZADD' => $getKeyFromFirstArgument, + 'ZCARD' => $getKeyFromFirstArgument, + 'ZCOUNT' => $getKeyFromFirstArgument, + 'ZINCRBY' => $getKeyFromFirstArgument, + 'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), + 'ZRANGE' => $getKeyFromFirstArgument, + 'ZRANGEBYSCORE' => $getKeyFromFirstArgument, + 'ZRANK' => $getKeyFromFirstArgument, + 'ZREM' => $getKeyFromFirstArgument, + 'ZREMRANGEBYRANK' => $getKeyFromFirstArgument, + 'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument, + 'ZREVRANGE' => $getKeyFromFirstArgument, + 'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument, + 'ZREVRANK' => $getKeyFromFirstArgument, + 'ZSCORE' => $getKeyFromFirstArgument, + 'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'), + 'ZSCAN' => $getKeyFromFirstArgument, + 'ZLEXCOUNT' => $getKeyFromFirstArgument, + 'ZRANGEBYLEX' => $getKeyFromFirstArgument, + 'ZREMRANGEBYLEX' => $getKeyFromFirstArgument, + 'ZREVRANGEBYLEX' => $getKeyFromFirstArgument, + + /* commands operating on hashes */ + 'HDEL' => $getKeyFromFirstArgument, + 'HEXISTS' => $getKeyFromFirstArgument, + 'HGET' => $getKeyFromFirstArgument, + 'HGETALL' => $getKeyFromFirstArgument, + 'HMGET' => $getKeyFromFirstArgument, + 'HMSET' => $getKeyFromFirstArgument, + 'HINCRBY' => $getKeyFromFirstArgument, + 'HINCRBYFLOAT' => $getKeyFromFirstArgument, + 'HKEYS' => $getKeyFromFirstArgument, + 'HLEN' => $getKeyFromFirstArgument, + 'HSET' => $getKeyFromFirstArgument, + 'HSETNX' => $getKeyFromFirstArgument, + 'HVALS' => $getKeyFromFirstArgument, + 'HSCAN' => $getKeyFromFirstArgument, + 'HSTRLEN' => $getKeyFromFirstArgument, + + /* commands operating on HyperLogLog */ + 'PFADD' => $getKeyFromFirstArgument, + 'PFCOUNT' => $getKeyFromAllArguments, + 'PFMERGE' => $getKeyFromAllArguments, + + /* scripting */ + 'EVAL' => array($this, 'getKeyFromScriptingCommands'), + 'EVALSHA' => array($this, 'getKeyFromScriptingCommands'), + + /* commands performing geospatial operations */ + 'GEOADD' => $getKeyFromFirstArgument, + 'GEOHASH' => $getKeyFromFirstArgument, + 'GEOPOS' => $getKeyFromFirstArgument, + 'GEODIST' => $getKeyFromFirstArgument, + 'GEORADIUS' => array($this, 'getKeyFromGeoradiusCommands'), + 'GEORADIUSBYMEMBER' => array($this, 'getKeyFromGeoradiusCommands'), + ); + } + + /** + * Returns the list of IDs for the supported commands. + * + * @return array + */ + public function getSupportedCommands() + { + return array_keys($this->commands); + } + + /** + * Sets an handler for the specified command ID. + * + * The signature of the callback must have a single parameter of type + * Predis\Command\CommandInterface. + * + * When the callback argument is omitted or NULL, the previously associated + * handler for the specified command ID is removed. + * + * @param string $commandID Command ID. + * @param mixed $callback A valid callable object, or NULL to unset the handler. + * + * @throws \InvalidArgumentException + */ + public function setCommandHandler($commandID, $callback = null) + { + $commandID = strtoupper($commandID); + + if (!isset($callback)) { + unset($this->commands[$commandID]); + + return; + } + + if (!is_callable($callback)) { + throw new \InvalidArgumentException( + 'The argument must be a callable object or NULL.' + ); + } + + $this->commands[$commandID] = $callback; + } + + /** + * Extracts the key from the first argument of a command instance. + * + * @param CommandInterface $command Command instance. + * + * @return string + */ + protected function getKeyFromFirstArgument(CommandInterface $command) + { + return $command->getArgument(0); + } + + /** + * Extracts the key from a command with multiple keys only when all keys in + * the arguments array produce the same hash. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromAllArguments(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if ($this->checkSameSlotForKeys($arguments)) { + return $arguments[0]; + } + } + + /** + * Extracts the key from a command with multiple keys only when all keys in + * the arguments array produce the same hash. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromInterleavedArguments(CommandInterface $command) + { + $arguments = $command->getArguments(); + $keys = array(); + + for ($i = 0; $i < count($arguments); $i += 2) { + $keys[] = $arguments[$i]; + } + + if ($this->checkSameSlotForKeys($keys)) { + return $arguments[0]; + } + } + + /** + * Extracts the key from SORT command. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromSortCommand(CommandInterface $command) + { + $arguments = $command->getArguments(); + $firstKey = $arguments[0]; + + if (1 === $argc = count($arguments)) { + return $firstKey; + } + + $keys = array($firstKey); + + for ($i = 1; $i < $argc; ++$i) { + if (strtoupper($arguments[$i]) === 'STORE') { + $keys[] = $arguments[++$i]; + } + } + + if ($this->checkSameSlotForKeys($keys)) { + return $firstKey; + } + } + + /** + * Extracts the key from BLPOP and BRPOP commands. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromBlockingListCommands(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if ($this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) { + return $arguments[0]; + } + } + + /** + * Extracts the key from BITOP command. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromBitOp(CommandInterface $command) + { + $arguments = $command->getArguments(); + + if ($this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) { + return $arguments[1]; + } + } + + /** + * Extracts the key from GEORADIUS and GEORADIUSBYMEMBER commands. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromGeoradiusCommands(CommandInterface $command) + { + $arguments = $command->getArguments(); + $argc = count($arguments); + $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; + + if ($argc > $startIndex) { + $keys = array($arguments[0]); + + for ($i = $startIndex; $i < $argc; ++$i) { + $argument = strtoupper($arguments[$i]); + if ($argument === 'STORE' || $argument === 'STOREDIST') { + $keys[] = $arguments[++$i]; + } + } + + if ($this->checkSameSlotForKeys($keys)) { + return $arguments[0]; + } else { + return; + } + } + + return $arguments[0]; + } + + /** + * Extracts the key from ZINTERSTORE and ZUNIONSTORE commands. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromZsetAggregationCommands(CommandInterface $command) + { + $arguments = $command->getArguments(); + $keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1])); + + if ($this->checkSameSlotForKeys($keys)) { + return $arguments[0]; + } + } + + /** + * Extracts the key from EVAL and EVALSHA commands. + * + * @param CommandInterface $command Command instance. + * + * @return string|null + */ + protected function getKeyFromScriptingCommands(CommandInterface $command) + { + if ($command instanceof ScriptCommand) { + $keys = $command->getKeys(); + } else { + $keys = array_slice($args = $command->getArguments(), 2, $args[1]); + } + + if ($keys && $this->checkSameSlotForKeys($keys)) { + return $keys[0]; + } + } + + /** + * {@inheritdoc} + */ + public function getSlot(CommandInterface $command) + { + $slot = $command->getSlot(); + + if (!isset($slot) && isset($this->commands[$cmdID = $command->getId()])) { + $key = call_user_func($this->commands[$cmdID], $command); + + if (isset($key)) { + $slot = $this->getSlotByKey($key); + $command->setSlot($slot); + } + } + + return $slot; + } + + /** + * Checks if the specified array of keys will generate the same hash. + * + * @param array $keys Array of keys. + * + * @return bool + */ + protected function checkSameSlotForKeys(array $keys) + { + if (!$count = count($keys)) { + return false; + } + + $currentSlot = $this->getSlotByKey($keys[0]); + + for ($i = 1; $i < $count; ++$i) { + $nextSlot = $this->getSlotByKey($keys[$i]); + + if ($currentSlot !== $nextSlot) { + return false; + } + + $currentSlot = $nextSlot; + } + + return true; + } + + /** + * Returns only the hashable part of a key (delimited by "{...}"), or the + * whole key if a key tag is not found in the string. + * + * @param string $key A key. + * + * @return string + */ + protected function extractKeyTag($key) + { + if (false !== $start = strpos($key, '{')) { + if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) { + $key = substr($key, $start, $end - $start); + } + } + + return $key; + } +} diff --git a/vendor/predis/predis/src/Cluster/Distributor/DistributorInterface.php b/vendor/predis/predis/src/Cluster/Distributor/DistributorInterface.php new file mode 100644 index 00000000..831f52c5 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Distributor/DistributorInterface.php @@ -0,0 +1,82 @@ +<?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\Distributor; + +use Predis\Cluster\Hash\HashGeneratorInterface; + +/** + * A distributor implements the logic to automatically distribute keys among + * several nodes for client-side sharding. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface DistributorInterface +{ + /** + * Adds a node to the distributor with an optional weight. + * + * @param mixed $node Node object. + * @param int $weight Weight for the node. + */ + public function add($node, $weight = null); + + /** + * Removes a node from the distributor. + * + * @param mixed $node Node object. + */ + public function remove($node); + + /** + * Returns the corresponding slot of a node from the distributor using the + * computed hash of a key. + * + * @param mixed $hash + * + * @return mixed + */ + public function getSlot($hash); + + /** + * Returns a node from the distributor using its assigned slot ID. + * + * @param mixed $slot + * + * @return mixed|null + */ + public function getBySlot($slot); + + /** + * Returns a node from the distributor using the computed hash of a key. + * + * @param mixed $hash + * + * @return mixed + */ + public function getByHash($hash); + + /** + * Returns a node from the distributor mapping to the specified value. + * + * @param string $value + * + * @return mixed + */ + public function get($value); + + /** + * Returns the underlying hash generator instance. + * + * @return HashGeneratorInterface + */ + public function getHashGenerator(); +} diff --git a/vendor/predis/predis/src/Cluster/Distributor/EmptyRingException.php b/vendor/predis/predis/src/Cluster/Distributor/EmptyRingException.php new file mode 100644 index 00000000..039f2f2e --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Distributor/EmptyRingException.php @@ -0,0 +1,21 @@ +<?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\Distributor; + +/** + * Exception class that identifies empty rings. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class EmptyRingException extends \Exception +{ +} diff --git a/vendor/predis/predis/src/Cluster/Distributor/HashRing.php b/vendor/predis/predis/src/Cluster/Distributor/HashRing.php new file mode 100644 index 00000000..db864d91 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Distributor/HashRing.php @@ -0,0 +1,270 @@ +<?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\Distributor; + +use Predis\Cluster\Hash\HashGeneratorInterface; + +/** + * This class implements an hashring-based distributor that uses the same + * algorithm of memcache to distribute keys in a cluster using client-side + * sharding. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + * @author Lorenzo Castelli <lcastelli@gmail.com> + */ +class HashRing implements DistributorInterface, HashGeneratorInterface +{ + const DEFAULT_REPLICAS = 128; + const DEFAULT_WEIGHT = 100; + + private $ring; + private $ringKeys; + private $ringKeysCount; + private $replicas; + private $nodeHashCallback; + private $nodes = array(); + + /** + * @param int $replicas Number of replicas in the ring. + * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes. + */ + public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null) + { + $this->replicas = $replicas; + $this->nodeHashCallback = $nodeHashCallback; + } + + /** + * Adds a node to the ring with an optional weight. + * + * @param mixed $node Node object. + * @param int $weight Weight for the node. + */ + public function add($node, $weight = null) + { + // In case of collisions in the hashes of the nodes, the node added + // last wins, thus the order in which nodes are added is significant. + $this->nodes[] = array( + 'object' => $node, + 'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT, + ); + + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function remove($node) + { + // A node is removed by resetting the ring so that it's recreated from + // scratch, in order to reassign possible hashes with collisions to the + // right node according to the order in which they were added in the + // first place. + for ($i = 0; $i < count($this->nodes); ++$i) { + if ($this->nodes[$i]['object'] === $node) { + array_splice($this->nodes, $i, 1); + $this->reset(); + + break; + } + } + } + + /** + * Resets the distributor. + */ + private function reset() + { + unset( + $this->ring, + $this->ringKeys, + $this->ringKeysCount + ); + } + + /** + * Returns the initialization status of the distributor. + * + * @return bool + */ + private function isInitialized() + { + return isset($this->ringKeys); + } + + /** + * Calculates the total weight of all the nodes in the distributor. + * + * @return int + */ + private function computeTotalWeight() + { + $totalWeight = 0; + + foreach ($this->nodes as $node) { + $totalWeight += $node['weight']; + } + + return $totalWeight; + } + + /** + * Initializes the distributor. + */ + private function initialize() + { + if ($this->isInitialized()) { + return; + } + + if (!$this->nodes) { + throw new EmptyRingException('Cannot initialize an empty hashring.'); + } + + $this->ring = array(); + $totalWeight = $this->computeTotalWeight(); + $nodesCount = count($this->nodes); + + foreach ($this->nodes as $node) { + $weightRatio = $node['weight'] / $totalWeight; + $this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio); + } + + ksort($this->ring, SORT_NUMERIC); + $this->ringKeys = array_keys($this->ring); + $this->ringKeysCount = count($this->ringKeys); + } + + /** + * Implements the logic needed to add a node to the hashring. + * + * @param array $ring Source hashring. + * @param mixed $node Node object to be added. + * @param int $totalNodes Total number of nodes. + * @param int $replicas Number of replicas in the ring. + * @param float $weightRatio Weight ratio for the node. + */ + protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio) + { + $nodeObject = $node['object']; + $nodeHash = $this->getNodeHash($nodeObject); + $replicas = (int) round($weightRatio * $totalNodes * $replicas); + + for ($i = 0; $i < $replicas; ++$i) { + $key = crc32("$nodeHash:$i"); + $ring[$key] = $nodeObject; + } + } + + /** + * {@inheritdoc} + */ + protected function getNodeHash($nodeObject) + { + if (!isset($this->nodeHashCallback)) { + return (string) $nodeObject; + } + + return call_user_func($this->nodeHashCallback, $nodeObject); + } + + /** + * {@inheritdoc} + */ + public function hash($value) + { + return crc32($value); + } + + /** + * {@inheritdoc} + */ + public function getByHash($hash) + { + return $this->ring[$this->getSlot($hash)]; + } + + /** + * {@inheritdoc} + */ + public function getBySlot($slot) + { + $this->initialize(); + + if (isset($this->ring[$slot])) { + return $this->ring[$slot]; + } + } + + /** + * {@inheritdoc} + */ + public function getSlot($hash) + { + $this->initialize(); + + $ringKeys = $this->ringKeys; + $upper = $this->ringKeysCount - 1; + $lower = 0; + + while ($lower <= $upper) { + $index = ($lower + $upper) >> 1; + $item = $ringKeys[$index]; + + if ($item > $hash) { + $upper = $index - 1; + } elseif ($item < $hash) { + $lower = $index + 1; + } else { + return $item; + } + } + + return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)]; + } + + /** + * {@inheritdoc} + */ + public function get($value) + { + $hash = $this->hash($value); + $node = $this->getByHash($hash); + + return $node; + } + + /** + * Implements a strategy to deal with wrap-around errors during binary searches. + * + * @param int $upper + * @param int $lower + * @param int $ringKeysCount + * + * @return int + */ + protected function wrapAroundStrategy($upper, $lower, $ringKeysCount) + { + // Binary search for the last item in ringkeys with a value less or + // equal to the key. If no such item exists, return the last item. + return $upper >= 0 ? $upper : $ringKeysCount - 1; + } + + /** + * {@inheritdoc} + */ + public function getHashGenerator() + { + return $this; + } +} diff --git a/vendor/predis/predis/src/Cluster/Distributor/KetamaRing.php b/vendor/predis/predis/src/Cluster/Distributor/KetamaRing.php new file mode 100644 index 00000000..dc77f320 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Distributor/KetamaRing.php @@ -0,0 +1,71 @@ +<?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\Distributor; + +/** + * This class implements an hashring-based distributor that uses the same + * algorithm of libketama to distribute keys in a cluster using client-side + * sharding. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + * @author Lorenzo Castelli <lcastelli@gmail.com> + */ +class KetamaRing extends HashRing +{ + const DEFAULT_REPLICAS = 160; + + /** + * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes. + */ + public function __construct($nodeHashCallback = null) + { + parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback); + } + + /** + * {@inheritdoc} + */ + protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio) + { + $nodeObject = $node['object']; + $nodeHash = $this->getNodeHash($nodeObject); + $replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4)); + + for ($i = 0; $i < $replicas; ++$i) { + $unpackedDigest = unpack('V4', md5("$nodeHash-$i", true)); + + foreach ($unpackedDigest as $key) { + $ring[$key] = $nodeObject; + } + } + } + + /** + * {@inheritdoc} + */ + public function hash($value) + { + $hash = unpack('V', md5($value, true)); + + return $hash[1]; + } + + /** + * {@inheritdoc} + */ + protected function wrapAroundStrategy($upper, $lower, $ringKeysCount) + { + // Binary search for the first item in ringkeys with a value greater + // or equal to the key. If no such item exists, return the first item. + return $lower < $ringKeysCount ? $lower : 0; + } +} diff --git a/vendor/predis/predis/src/Cluster/Hash/CRC16.php b/vendor/predis/predis/src/Cluster/Hash/CRC16.php new file mode 100644 index 00000000..3add0cef --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Hash/CRC16.php @@ -0,0 +1,72 @@ +<?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; + +/** + * Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class CRC16 implements HashGeneratorInterface +{ + private static $CCITT_16 = array( + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, + ); + + /** + * {@inheritdoc} + */ + public function hash($value) + { + // CRC-CCITT-16 algorithm + $crc = 0; + $CCITT_16 = self::$CCITT_16; + $strlen = strlen($value); + + for ($i = 0; $i < $strlen; ++$i) { + $crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF; + } + + return $crc; + } +} diff --git a/vendor/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php b/vendor/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php new file mode 100644 index 00000000..271b9e72 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php @@ -0,0 +1,30 @@ +<?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; + +/** + * An hash generator implements the logic used to calculate the hash of a key to + * distribute operations among Redis nodes. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface HashGeneratorInterface +{ + /** + * Generates an hash from a string to be used for distribution. + * + * @param string $value String value. + * + * @return int + */ + public function hash($value); +} diff --git a/vendor/predis/predis/src/Cluster/PredisStrategy.php b/vendor/predis/predis/src/Cluster/PredisStrategy.php new file mode 100644 index 00000000..20668427 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/PredisStrategy.php @@ -0,0 +1,79 @@ +<?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; + +use Predis\Cluster\Distributor\DistributorInterface; +use Predis\Cluster\Distributor\HashRing; + +/** + * Default cluster strategy used by Predis to handle client-side sharding. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PredisStrategy extends ClusterStrategy +{ + protected $distributor; + + /** + * @param DistributorInterface $distributor Optional distributor instance. + */ + public function __construct(DistributorInterface $distributor = null) + { + parent::__construct(); + + $this->distributor = $distributor ?: new HashRing(); + } + + /** + * {@inheritdoc} + */ + public function getSlotByKey($key) + { + $key = $this->extractKeyTag($key); + $hash = $this->distributor->hash($key); + $slot = $this->distributor->getSlot($hash); + + return $slot; + } + + /** + * {@inheritdoc} + */ + protected function checkSameSlotForKeys(array $keys) + { + if (!$count = count($keys)) { + return false; + } + + $currentKey = $this->extractKeyTag($keys[0]); + + for ($i = 1; $i < $count; ++$i) { + $nextKey = $this->extractKeyTag($keys[$i]); + + if ($currentKey !== $nextKey) { + return false; + } + + $currentKey = $nextKey; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getDistributor() + { + return $this->distributor; + } +} diff --git a/vendor/predis/predis/src/Cluster/RedisStrategy.php b/vendor/predis/predis/src/Cluster/RedisStrategy.php new file mode 100644 index 00000000..df0bdb49 --- /dev/null +++ b/vendor/predis/predis/src/Cluster/RedisStrategy.php @@ -0,0 +1,58 @@ +<?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; + +use Predis\Cluster\Hash\CRC16; +use Predis\Cluster\Hash\HashGeneratorInterface; +use Predis\NotSupportedException; + +/** + * Default class used by Predis to calculate hashes out of keys of + * commands supported by redis-cluster. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisStrategy extends ClusterStrategy +{ + protected $hashGenerator; + + /** + * @param HashGeneratorInterface $hashGenerator Hash generator instance. + */ + public function __construct(HashGeneratorInterface $hashGenerator = null) + { + parent::__construct(); + + $this->hashGenerator = $hashGenerator ?: new CRC16(); + } + + /** + * {@inheritdoc} + */ + public function getSlotByKey($key) + { + $key = $this->extractKeyTag($key); + $slot = $this->hashGenerator->hash($key) & 0x3FFF; + + return $slot; + } + + /** + * {@inheritdoc} + */ + public function getDistributor() + { + throw new NotSupportedException( + 'This cluster strategy does not provide an external distributor' + ); + } +} diff --git a/vendor/predis/predis/src/Cluster/StrategyInterface.php b/vendor/predis/predis/src/Cluster/StrategyInterface.php new file mode 100644 index 00000000..cdf7d09f --- /dev/null +++ b/vendor/predis/predis/src/Cluster/StrategyInterface.php @@ -0,0 +1,53 @@ +<?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; + +use Predis\Cluster\Distributor\DistributorInterface; +use Predis\Command\CommandInterface; + +/** + * Interface for classes defining the strategy used to calculate an hash out of + * keys extracted from supported commands. + * + * This is mostly useful to support clustering via client-side sharding. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface StrategyInterface +{ + /** + * Returns a slot for the given command used for clustering distribution or + * NULL when this is not possible. + * + * @param CommandInterface $command Command instance. + * + * @return int + */ + public function getSlot(CommandInterface $command); + + /** + * Returns a slot for the given key used for clustering distribution or NULL + * when this is not possible. + * + * @param string $key Key string. + * + * @return int + */ + public function getSlotByKey($key); + + /** + * Returns a distributor instance to be used by the cluster. + * + * @return DistributorInterface + */ + public function getDistributor(); +} diff --git a/vendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.php b/vendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.php new file mode 100644 index 00000000..922883f0 --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.php @@ -0,0 +1,191 @@ +<?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\Collection\Iterator; + +use Predis\ClientInterface; +use Predis\NotSupportedException; + +/** + * Provides the base implementation for a fully-rewindable PHP iterator that can + * incrementally iterate over cursor-based collections stored on Redis using the + * commands in the `SCAN` family. + * + * Given their incremental nature with multiple fetches, these kind of iterators + * offer limited guarantees about the returned elements because the collection + * can change several times during the iteration process. + * + * @see http://redis.io/commands/scan + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +abstract class CursorBasedIterator implements \Iterator +{ + protected $client; + protected $match; + protected $count; + + protected $valid; + protected $fetchmore; + protected $elements; + protected $cursor; + protected $position; + protected $current; + + /** + * @param ClientInterface $client Client connected to Redis. + * @param string $match Pattern to match during the server-side iteration. + * @param int $count Hint used by Redis to compute the number of results per iteration. + */ + public function __construct(ClientInterface $client, $match = null, $count = null) + { + $this->client = $client; + $this->match = $match; + $this->count = $count; + + $this->reset(); + } + + /** + * Ensures that the client supports the specified Redis command required to + * fetch elements from the server to perform the iteration. + * + * @param ClientInterface $client Client connected to Redis. + * @param string $commandID Command ID. + * + * @throws NotSupportedException + */ + protected function requiredCommand(ClientInterface $client, $commandID) + { + if (!$client->getProfile()->supportsCommand($commandID)) { + throw new NotSupportedException("The current profile does not support '$commandID'."); + } + } + + /** + * Resets the inner state of the iterator. + */ + protected function reset() + { + $this->valid = true; + $this->fetchmore = true; + $this->elements = array(); + $this->cursor = 0; + $this->position = -1; + $this->current = null; + } + + /** + * Returns an array of options for the `SCAN` command. + * + * @return array + */ + protected function getScanOptions() + { + $options = array(); + + if (strlen($this->match) > 0) { + $options['MATCH'] = $this->match; + } + + if ($this->count > 0) { + $options['COUNT'] = $this->count; + } + + return $options; + } + + /** + * Fetches a new set of elements from the remote collection, effectively + * advancing the iteration process. + * + * @return array + */ + abstract protected function executeCommand(); + + /** + * Populates the local buffer of elements fetched from the server during + * the iteration. + */ + protected function fetch() + { + list($cursor, $elements) = $this->executeCommand(); + + if (!$cursor) { + $this->fetchmore = false; + } + + $this->cursor = $cursor; + $this->elements = $elements; + } + + /** + * Extracts next values for key() and current(). + */ + protected function extractNext() + { + ++$this->position; + $this->current = array_shift($this->elements); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->reset(); + $this->next(); + } + + /** + * {@inheritdoc} + */ + public function current() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + tryFetch: { + if (!$this->elements && $this->fetchmore) { + $this->fetch(); + } + + if ($this->elements) { + $this->extractNext(); + } elseif ($this->cursor) { + goto tryFetch; + } else { + $this->valid = false; + } + } + } + + /** + * {@inheritdoc} + */ + public function valid() + { + return $this->valid; + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/HashKey.php b/vendor/predis/predis/src/Collection/Iterator/HashKey.php new file mode 100644 index 00000000..078625fd --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/HashKey.php @@ -0,0 +1,60 @@ +<?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\Collection\Iterator; + +use Predis\ClientInterface; + +/** + * Abstracts the iteration of fields and values of an hash by leveraging the + * HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + * + * @link http://redis.io/commands/scan + */ +class HashKey extends CursorBasedIterator +{ + protected $key; + + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client, $key, $match = null, $count = null) + { + $this->requiredCommand($client, 'HSCAN'); + + parent::__construct($client, $match, $count); + + $this->key = $key; + } + + /** + * {@inheritdoc} + */ + protected function executeCommand() + { + return $this->client->hscan($this->key, $this->cursor, $this->getScanOptions()); + } + + /** + * {@inheritdoc} + */ + protected function extractNext() + { + if ($kv = each($this->elements)) { + $this->position = $kv[0]; + $this->current = $kv[1]; + + unset($this->elements[$this->position]); + } + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/Keyspace.php b/vendor/predis/predis/src/Collection/Iterator/Keyspace.php new file mode 100644 index 00000000..5d985b9b --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/Keyspace.php @@ -0,0 +1,43 @@ +<?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\Collection\Iterator; + +use Predis\ClientInterface; + +/** + * Abstracts the iteration of the keyspace on a Redis instance by leveraging the + * SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + * + * @link http://redis.io/commands/scan + */ +class Keyspace extends CursorBasedIterator +{ + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client, $match = null, $count = null) + { + $this->requiredCommand($client, 'SCAN'); + + parent::__construct($client, $match, $count); + } + + /** + * {@inheritdoc} + */ + protected function executeCommand() + { + return $this->client->scan($this->cursor, $this->getScanOptions()); + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/ListKey.php b/vendor/predis/predis/src/Collection/Iterator/ListKey.php new file mode 100644 index 00000000..7a6eb479 --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/ListKey.php @@ -0,0 +1,176 @@ +<?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\Collection\Iterator; + +use Predis\ClientInterface; +use Predis\NotSupportedException; + +/** + * Abstracts the iteration of items stored in a list by leveraging the LRANGE + * command wrapped in a fully-rewindable PHP iterator. + * + * This iterator tries to emulate the behaviour of cursor-based iterators based + * on the SCAN-family of commands introduced in Redis <= 2.8, meaning that due + * to its incremental nature with multiple fetches it can only offer limited + * guarantees on the returned elements because the collection can change several + * times (trimmed, deleted, overwritten) during the iteration process. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + * + * @link http://redis.io/commands/lrange + */ +class ListKey implements \Iterator +{ + protected $client; + protected $count; + protected $key; + + protected $valid; + protected $fetchmore; + protected $elements; + protected $position; + protected $current; + + /** + * @param ClientInterface $client Client connected to Redis. + * @param string $key Redis list key. + * @param int $count Number of items retrieved on each fetch operation. + * + * @throws \InvalidArgumentException + */ + public function __construct(ClientInterface $client, $key, $count = 10) + { + $this->requiredCommand($client, 'LRANGE'); + + if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) { + throw new \InvalidArgumentException('The $count argument must be a positive integer.'); + } + + $this->client = $client; + $this->key = $key; + $this->count = $count; + + $this->reset(); + } + + /** + * Ensures that the client instance supports the specified Redis command + * required to fetch elements from the server to perform the iteration. + * + * @param ClientInterface $client Client connected to Redis. + * @param string $commandID Command ID. + * + * @throws NotSupportedException + */ + protected function requiredCommand(ClientInterface $client, $commandID) + { + if (!$client->getProfile()->supportsCommand($commandID)) { + throw new NotSupportedException("The current profile does not support '$commandID'."); + } + } + + /** + * Resets the inner state of the iterator. + */ + protected function reset() + { + $this->valid = true; + $this->fetchmore = true; + $this->elements = array(); + $this->position = -1; + $this->current = null; + } + + /** + * Fetches a new set of elements from the remote collection, effectively + * advancing the iteration process. + * + * @return array + */ + protected function executeCommand() + { + return $this->client->lrange($this->key, $this->position + 1, $this->position + $this->count); + } + + /** + * Populates the local buffer of elements fetched from the server during the + * iteration. + */ + protected function fetch() + { + $elements = $this->executeCommand(); + + if (count($elements) < $this->count) { + $this->fetchmore = false; + } + + $this->elements = $elements; + } + + /** + * Extracts next values for key() and current(). + */ + protected function extractNext() + { + ++$this->position; + $this->current = array_shift($this->elements); + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + $this->reset(); + $this->next(); + } + + /** + * {@inheritdoc} + */ + public function current() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + if (!$this->elements && $this->fetchmore) { + $this->fetch(); + } + + if ($this->elements) { + $this->extractNext(); + } else { + $this->valid = false; + } + } + + /** + * {@inheritdoc} + */ + public function valid() + { + return $this->valid; + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/SetKey.php b/vendor/predis/predis/src/Collection/Iterator/SetKey.php new file mode 100644 index 00000000..bf254397 --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/SetKey.php @@ -0,0 +1,47 @@ +<?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\Collection\Iterator; + +use Predis\ClientInterface; + +/** + * Abstracts the iteration of members stored in a set by leveraging the SSCAN + * command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + * + * @link http://redis.io/commands/scan + */ +class SetKey extends CursorBasedIterator +{ + protected $key; + + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client, $key, $match = null, $count = null) + { + $this->requiredCommand($client, 'SSCAN'); + + parent::__construct($client, $match, $count); + + $this->key = $key; + } + + /** + * {@inheritdoc} + */ + protected function executeCommand() + { + return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions()); + } +} diff --git a/vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php b/vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php new file mode 100644 index 00000000..e2f17892 --- /dev/null +++ b/vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php @@ -0,0 +1,60 @@ +<?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\Collection\Iterator; + +use Predis\ClientInterface; + +/** + * Abstracts the iteration of members stored in a sorted set by leveraging the + * ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + * + * @link http://redis.io/commands/scan + */ +class SortedSetKey extends CursorBasedIterator +{ + protected $key; + + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client, $key, $match = null, $count = null) + { + $this->requiredCommand($client, 'ZSCAN'); + + parent::__construct($client, $match, $count); + + $this->key = $key; + } + + /** + * {@inheritdoc} + */ + protected function executeCommand() + { + return $this->client->zscan($this->key, $this->cursor, $this->getScanOptions()); + } + + /** + * {@inheritdoc} + */ + protected function extractNext() + { + if ($kv = each($this->elements)) { + $this->position = $kv[0]; + $this->current = $kv[1]; + + unset($this->elements[$this->position]); + } + } +} diff --git a/vendor/predis/predis/src/Command/Command.php b/vendor/predis/predis/src/Command/Command.php new file mode 100644 index 00000000..bb538e7c --- /dev/null +++ b/vendor/predis/predis/src/Command/Command.php @@ -0,0 +1,129 @@ +<?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; + +/** + * Base class for Redis commands. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +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 = $this->filterArguments($arguments); + unset($this->slot); + } + + /** + * {@inheritdoc} + */ + public function setRawArguments(array $arguments) + { + $this->arguments = $arguments; + unset($this->slot); + } + + /** + * {@inheritdoc} + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * {@inheritdoc} + */ + public function getArgument($index) + { + if (isset($this->arguments[$index])) { + return $this->arguments[$index]; + } + } + + /** + * {@inheritdoc} + */ + public function setSlot($slot) + { + $this->slot = $slot; + } + + /** + * {@inheritdoc} + */ + public function getSlot() + { + if (isset($this->slot)) { + return $this->slot; + } + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data; + } + + /** + * Normalizes the arguments array passed to a Redis command. + * + * @param array $arguments Arguments for a command. + * + * @return array + */ + public static function normalizeArguments(array $arguments) + { + if (count($arguments) === 1 && is_array($arguments[0])) { + return $arguments[0]; + } + + return $arguments; + } + + /** + * Normalizes the arguments array passed to a variadic Redis command. + * + * @param array $arguments Arguments for a command. + * + * @return array + */ + public static function normalizeVariadic(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + return array_merge(array($arguments[0]), $arguments[1]); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/CommandInterface.php b/vendor/predis/predis/src/Command/CommandInterface.php new file mode 100644 index 00000000..9f349e1d --- /dev/null +++ b/vendor/predis/predis/src/Command/CommandInterface.php @@ -0,0 +1,81 @@ +<?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; + +/** + * Defines an abstraction representing a Redis command. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface CommandInterface +{ + /** + * Returns the ID of the Redis command. By convention, command identifiers + * must always be uppercase. + * + * @return string + */ + public function getId(); + + /** + * Assign the specified slot to the command for clustering distribution. + * + * @param int $slot Slot ID. + */ + public function setSlot($slot); + + /** + * Returns the assigned slot of the command for clustering distribution. + * + * @return int|null + */ + public function getSlot(); + + /** + * Sets the arguments for the command. + * + * @param array $arguments List of arguments. + */ + public function setArguments(array $arguments); + + /** + * Sets the raw arguments for the command without processing them. + * + * @param array $arguments List of arguments. + */ + public function setRawArguments(array $arguments); + + /** + * Gets the arguments of the command. + * + * @return array + */ + public function getArguments(); + + /** + * Gets the argument of the command at the specified index. + * + * @param int $index Index of the desired argument. + * + * @return mixed|null + */ + public function getArgument($index); + + /** + * Parses a raw response and returns a PHP object. + * + * @param string $data Binary string containing the whole response. + * + * @return mixed + */ + public function parseResponse($data); +} diff --git a/vendor/predis/predis/src/Command/ConnectionAuth.php b/vendor/predis/predis/src/Command/ConnectionAuth.php new file mode 100644 index 00000000..c8c9dedc --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionAuth.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/auth + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ConnectionAuth extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'AUTH'; + } +} diff --git a/vendor/predis/predis/src/Command/ConnectionEcho.php b/vendor/predis/predis/src/Command/ConnectionEcho.php new file mode 100644 index 00000000..fd496097 --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionEcho.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/echo + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ConnectionEcho extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ECHO'; + } +} diff --git a/vendor/predis/predis/src/Command/ConnectionPing.php b/vendor/predis/predis/src/Command/ConnectionPing.php new file mode 100644 index 00000000..fa9d7346 --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionPing.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/ping + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ConnectionPing extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PING'; + } +} diff --git a/vendor/predis/predis/src/Command/ConnectionQuit.php b/vendor/predis/predis/src/Command/ConnectionQuit.php new file mode 100644 index 00000000..e59e31e3 --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionQuit.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/quit + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ConnectionQuit extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'QUIT'; + } +} diff --git a/vendor/predis/predis/src/Command/ConnectionSelect.php b/vendor/predis/predis/src/Command/ConnectionSelect.php new file mode 100644 index 00000000..1da82567 --- /dev/null +++ b/vendor/predis/predis/src/Command/ConnectionSelect.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/select + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ConnectionSelect extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SELECT'; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoAdd.php b/vendor/predis/predis/src/Command/GeospatialGeoAdd.php new file mode 100644 index 00000000..adca2ca5 --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoAdd.php @@ -0,0 +1,42 @@ +<?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; + +/** + * @link http://redis.io/commands/geoadd + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class GeospatialGeoAdd extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEOADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + foreach (array_pop($arguments) as $item) { + $arguments = array_merge($arguments, $item); + } + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoDist.php b/vendor/predis/predis/src/Command/GeospatialGeoDist.php new file mode 100644 index 00000000..17c5f549 --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoDist.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/geodist + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class GeospatialGeoDist extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEODIST'; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoHash.php b/vendor/predis/predis/src/Command/GeospatialGeoHash.php new file mode 100644 index 00000000..2eccaf4f --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoHash.php @@ -0,0 +1,41 @@ +<?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; + +/** + * @link http://redis.io/commands/geohash + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class GeospatialGeoHash extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEOHASH'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $members = array_pop($arguments); + $arguments = array_merge($arguments, $members); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoPos.php b/vendor/predis/predis/src/Command/GeospatialGeoPos.php new file mode 100644 index 00000000..6b7a9a3d --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoPos.php @@ -0,0 +1,41 @@ +<?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; + +/** + * @link http://redis.io/commands/geopos + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class GeospatialGeoPos extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEOPOS'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $members = array_pop($arguments); + $arguments = array_merge($arguments, $members); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoRadius.php b/vendor/predis/predis/src/Command/GeospatialGeoRadius.php new file mode 100644 index 00000000..f2052148 --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoRadius.php @@ -0,0 +1,71 @@ +<?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; + +/** + * @link http://redis.io/commands/georadius + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class GeospatialGeoRadius extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEORADIUS'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if ($arguments && is_array(end($arguments))) { + $options = array_change_key_case(array_pop($arguments), CASE_UPPER); + + if (isset($options['WITHCOORD']) && $options['WITHCOORD'] == true) { + $arguments[] = 'WITHCOORD'; + } + + if (isset($options['WITHDIST']) && $options['WITHDIST'] == true) { + $arguments[] = 'WITHDIST'; + } + + if (isset($options['WITHHASH']) && $options['WITHHASH'] == true) { + $arguments[] = 'WITHHASH'; + } + + if (isset($options['COUNT'])) { + $arguments[] = 'COUNT'; + $arguments[] = $options['COUNT']; + } + + if (isset($options['SORT'])) { + $arguments[] = strtoupper($options['SORT']); + } + + if (isset($options['STORE'])) { + $arguments[] = 'STORE'; + $arguments[] = $options['STORE']; + } + + if (isset($options['STOREDIST'])) { + $arguments[] = 'STOREDIST'; + $arguments[] = $options['STOREDIST']; + } + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/GeospatialGeoRadiusByMember.php b/vendor/predis/predis/src/Command/GeospatialGeoRadiusByMember.php new file mode 100644 index 00000000..abfff7b4 --- /dev/null +++ b/vendor/predis/predis/src/Command/GeospatialGeoRadiusByMember.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/georadiusbymember + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class GeospatialGeoRadiusByMember extends GeospatialGeoRadius +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GEORADIUSBYMEMBER'; + } +} diff --git a/vendor/predis/predis/src/Command/HashDelete.php b/vendor/predis/predis/src/Command/HashDelete.php new file mode 100644 index 00000000..d5d4c38c --- /dev/null +++ b/vendor/predis/predis/src/Command/HashDelete.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/hdel + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashDelete extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HDEL'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/HashExists.php b/vendor/predis/predis/src/Command/HashExists.php new file mode 100644 index 00000000..a2c69b90 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashExists.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/hexists + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashExists extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HEXISTS'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/HashGet.php b/vendor/predis/predis/src/Command/HashGet.php new file mode 100644 index 00000000..20f33da5 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashGet.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/hget + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashGet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HGET'; + } +} diff --git a/vendor/predis/predis/src/Command/HashGetAll.php b/vendor/predis/predis/src/Command/HashGetAll.php new file mode 100644 index 00000000..d6986752 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashGetAll.php @@ -0,0 +1,42 @@ +<?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; + +/** + * @link http://redis.io/commands/hgetall + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashGetAll extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HGETALL'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $result = array(); + + for ($i = 0; $i < count($data); ++$i) { + $result[$data[$i]] = $data[++$i]; + } + + return $result; + } +} diff --git a/vendor/predis/predis/src/Command/HashGetMultiple.php b/vendor/predis/predis/src/Command/HashGetMultiple.php new file mode 100644 index 00000000..820ce958 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashGetMultiple.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/hmget + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashGetMultiple extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HMGET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/HashIncrementBy.php b/vendor/predis/predis/src/Command/HashIncrementBy.php new file mode 100644 index 00000000..a37359ff --- /dev/null +++ b/vendor/predis/predis/src/Command/HashIncrementBy.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/hincrby + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashIncrementBy extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HINCRBY'; + } +} diff --git a/vendor/predis/predis/src/Command/HashIncrementByFloat.php b/vendor/predis/predis/src/Command/HashIncrementByFloat.php new file mode 100644 index 00000000..bce9714f --- /dev/null +++ b/vendor/predis/predis/src/Command/HashIncrementByFloat.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/hincrbyfloat + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashIncrementByFloat extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HINCRBYFLOAT'; + } +} diff --git a/vendor/predis/predis/src/Command/HashKeys.php b/vendor/predis/predis/src/Command/HashKeys.php new file mode 100644 index 00000000..28266020 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashKeys.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/hkeys + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashKeys extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HKEYS'; + } +} diff --git a/vendor/predis/predis/src/Command/HashLength.php b/vendor/predis/predis/src/Command/HashLength.php new file mode 100644 index 00000000..d70926f1 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashLength.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/hlen + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashLength extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HLEN'; + } +} diff --git a/vendor/predis/predis/src/Command/HashScan.php b/vendor/predis/predis/src/Command/HashScan.php new file mode 100644 index 00000000..afde74eb --- /dev/null +++ b/vendor/predis/predis/src/Command/HashScan.php @@ -0,0 +1,85 @@ +<?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; + +/** + * @link http://redis.io/commands/hscan + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashScan extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSCAN'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 3 && is_array($arguments[2])) { + $options = $this->prepareOptions(array_pop($arguments)); + $arguments = array_merge($arguments, $options); + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $options = array_change_key_case($options, CASE_UPPER); + $normalized = array(); + + if (!empty($options['MATCH'])) { + $normalized[] = 'MATCH'; + $normalized[] = $options['MATCH']; + } + + if (!empty($options['COUNT'])) { + $normalized[] = 'COUNT'; + $normalized[] = $options['COUNT']; + } + + return $normalized; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $fields = $data[1]; + $result = array(); + + for ($i = 0; $i < count($fields); ++$i) { + $result[$fields[$i]] = $fields[++$i]; + } + + $data[1] = $result; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/HashSet.php b/vendor/predis/predis/src/Command/HashSet.php new file mode 100644 index 00000000..d3154a9b --- /dev/null +++ b/vendor/predis/predis/src/Command/HashSet.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/hset + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashSet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSET'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/HashSetMultiple.php b/vendor/predis/predis/src/Command/HashSetMultiple.php new file mode 100644 index 00000000..6069e2ad --- /dev/null +++ b/vendor/predis/predis/src/Command/HashSetMultiple.php @@ -0,0 +1,48 @@ +<?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; + +/** + * @link http://redis.io/commands/hmset + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashSetMultiple extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HMSET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $flattenedKVs = array($arguments[0]); + $args = $arguments[1]; + + foreach ($args as $k => $v) { + $flattenedKVs[] = $k; + $flattenedKVs[] = $v; + } + + return $flattenedKVs; + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/HashSetPreserve.php b/vendor/predis/predis/src/Command/HashSetPreserve.php new file mode 100644 index 00000000..582100d4 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashSetPreserve.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/hsetnx + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashSetPreserve extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSETNX'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/HashStringLength.php b/vendor/predis/predis/src/Command/HashStringLength.php new file mode 100644 index 00000000..7cfda80d --- /dev/null +++ b/vendor/predis/predis/src/Command/HashStringLength.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/hstrlen + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashStringLength extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HSTRLEN'; + } +} diff --git a/vendor/predis/predis/src/Command/HashValues.php b/vendor/predis/predis/src/Command/HashValues.php new file mode 100644 index 00000000..0a5ea5f6 --- /dev/null +++ b/vendor/predis/predis/src/Command/HashValues.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/hvals + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HashValues extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'HVALS'; + } +} diff --git a/vendor/predis/predis/src/Command/HyperLogLogAdd.php b/vendor/predis/predis/src/Command/HyperLogLogAdd.php new file mode 100644 index 00000000..18d2bd7b --- /dev/null +++ b/vendor/predis/predis/src/Command/HyperLogLogAdd.php @@ -0,0 +1,44 @@ +<?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; + +/** + * @link http://redis.io/commands/pfadd + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HyperLogLogAdd extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PFADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/HyperLogLogCount.php b/vendor/predis/predis/src/Command/HyperLogLogCount.php new file mode 100644 index 00000000..0afe5427 --- /dev/null +++ b/vendor/predis/predis/src/Command/HyperLogLogCount.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/pfcount + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HyperLogLogCount extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PFCOUNT'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/HyperLogLogMerge.php b/vendor/predis/predis/src/Command/HyperLogLogMerge.php new file mode 100644 index 00000000..c160be5b --- /dev/null +++ b/vendor/predis/predis/src/Command/HyperLogLogMerge.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/pfmerge + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class HyperLogLogMerge extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PFMERGE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/KeyDelete.php b/vendor/predis/predis/src/Command/KeyDelete.php new file mode 100644 index 00000000..89bdfdb7 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyDelete.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/del + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyDelete extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DEL'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/KeyDump.php b/vendor/predis/predis/src/Command/KeyDump.php new file mode 100644 index 00000000..6d9c4880 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyDump.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/dump + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyDump extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DUMP'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyExists.php b/vendor/predis/predis/src/Command/KeyExists.php new file mode 100644 index 00000000..5196ca16 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyExists.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/exists + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyExists extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXISTS'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/KeyExpire.php b/vendor/predis/predis/src/Command/KeyExpire.php new file mode 100644 index 00000000..fd7c9c80 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyExpire.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/expire + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyExpire extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXPIRE'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/KeyExpireAt.php b/vendor/predis/predis/src/Command/KeyExpireAt.php new file mode 100644 index 00000000..e2fe7aeb --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyExpireAt.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/expireat + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyExpireAt extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXPIREAT'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/KeyKeys.php b/vendor/predis/predis/src/Command/KeyKeys.php new file mode 100644 index 00000000..6d74c40d --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyKeys.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/keys + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyKeys extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'KEYS'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyMigrate.php b/vendor/predis/predis/src/Command/KeyMigrate.php new file mode 100644 index 00000000..3324ef94 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyMigrate.php @@ -0,0 +1,50 @@ +<?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; + +/** + * @link http://redis.io/commands/migrate + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyMigrate extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MIGRATE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (is_array(end($arguments))) { + foreach (array_pop($arguments) as $modifier => $value) { + $modifier = strtoupper($modifier); + + if ($modifier === 'COPY' && $value == true) { + $arguments[] = $modifier; + } + + if ($modifier === 'REPLACE' && $value == true) { + $arguments[] = $modifier; + } + } + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/KeyMove.php b/vendor/predis/predis/src/Command/KeyMove.php new file mode 100644 index 00000000..8f1ab2a7 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyMove.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/move + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyMove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MOVE'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/KeyPersist.php b/vendor/predis/predis/src/Command/KeyPersist.php new file mode 100644 index 00000000..e7729553 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyPersist.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/persist + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyPersist extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PERSIST'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/KeyPreciseExpire.php b/vendor/predis/predis/src/Command/KeyPreciseExpire.php new file mode 100644 index 00000000..258ec476 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyPreciseExpire.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/pexpire + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyPreciseExpire extends KeyExpire +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PEXPIRE'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyPreciseExpireAt.php b/vendor/predis/predis/src/Command/KeyPreciseExpireAt.php new file mode 100644 index 00000000..e4192187 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyPreciseExpireAt.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/pexpireat + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyPreciseExpireAt extends KeyExpireAt +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PEXPIREAT'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyPreciseTimeToLive.php b/vendor/predis/predis/src/Command/KeyPreciseTimeToLive.php new file mode 100644 index 00000000..bdcd34b9 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyPreciseTimeToLive.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/pttl + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyPreciseTimeToLive extends KeyTimeToLive +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PTTL'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyRandom.php b/vendor/predis/predis/src/Command/KeyRandom.php new file mode 100644 index 00000000..b208b2db --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyRandom.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/randomkey + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyRandom extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RANDOMKEY'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data !== '' ? $data : null; + } +} diff --git a/vendor/predis/predis/src/Command/KeyRename.php b/vendor/predis/predis/src/Command/KeyRename.php new file mode 100644 index 00000000..82e44fb2 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyRename.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/rename + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyRename extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RENAME'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyRenamePreserve.php b/vendor/predis/predis/src/Command/KeyRenamePreserve.php new file mode 100644 index 00000000..773ece6d --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyRenamePreserve.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/renamenx + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyRenamePreserve extends KeyRename +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RENAMENX'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/KeyRestore.php b/vendor/predis/predis/src/Command/KeyRestore.php new file mode 100644 index 00000000..a5b0b2db --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyRestore.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/restore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyRestore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RESTORE'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyScan.php b/vendor/predis/predis/src/Command/KeyScan.php new file mode 100644 index 00000000..05f5bb3a --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyScan.php @@ -0,0 +1,66 @@ +<?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; + +/** + * @link http://redis.io/commands/scan + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyScan extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SCAN'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + $options = $this->prepareOptions(array_pop($arguments)); + $arguments = array_merge($arguments, $options); + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $options = array_change_key_case($options, CASE_UPPER); + $normalized = array(); + + if (!empty($options['MATCH'])) { + $normalized[] = 'MATCH'; + $normalized[] = $options['MATCH']; + } + + if (!empty($options['COUNT'])) { + $normalized[] = 'COUNT'; + $normalized[] = $options['COUNT']; + } + + return $normalized; + } +} diff --git a/vendor/predis/predis/src/Command/KeySort.php b/vendor/predis/predis/src/Command/KeySort.php new file mode 100644 index 00000000..fd449f13 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeySort.php @@ -0,0 +1,83 @@ +<?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; + +/** + * @link http://redis.io/commands/sort + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeySort extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SORT'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 1) { + return $arguments; + } + + $query = array($arguments[0]); + $sortParams = array_change_key_case($arguments[1], CASE_UPPER); + + if (isset($sortParams['BY'])) { + $query[] = 'BY'; + $query[] = $sortParams['BY']; + } + + if (isset($sortParams['GET'])) { + $getargs = $sortParams['GET']; + + if (is_array($getargs)) { + foreach ($getargs as $getarg) { + $query[] = 'GET'; + $query[] = $getarg; + } + } else { + $query[] = 'GET'; + $query[] = $getargs; + } + } + + if (isset($sortParams['LIMIT']) && + is_array($sortParams['LIMIT']) && + count($sortParams['LIMIT']) == 2) { + $query[] = 'LIMIT'; + $query[] = $sortParams['LIMIT'][0]; + $query[] = $sortParams['LIMIT'][1]; + } + + if (isset($sortParams['SORT'])) { + $query[] = strtoupper($sortParams['SORT']); + } + + if (isset($sortParams['ALPHA']) && $sortParams['ALPHA'] == true) { + $query[] = 'ALPHA'; + } + + if (isset($sortParams['STORE'])) { + $query[] = 'STORE'; + $query[] = $sortParams['STORE']; + } + + return $query; + } +} diff --git a/vendor/predis/predis/src/Command/KeyTimeToLive.php b/vendor/predis/predis/src/Command/KeyTimeToLive.php new file mode 100644 index 00000000..67697a6f --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyTimeToLive.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/ttl + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyTimeToLive extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'TTL'; + } +} diff --git a/vendor/predis/predis/src/Command/KeyType.php b/vendor/predis/predis/src/Command/KeyType.php new file mode 100644 index 00000000..f4f06e45 --- /dev/null +++ b/vendor/predis/predis/src/Command/KeyType.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/type + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyType extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'TYPE'; + } +} diff --git a/vendor/predis/predis/src/Command/ListIndex.php b/vendor/predis/predis/src/Command/ListIndex.php new file mode 100644 index 00000000..27c64be7 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListIndex.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/lindex + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListIndex extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LINDEX'; + } +} diff --git a/vendor/predis/predis/src/Command/ListInsert.php b/vendor/predis/predis/src/Command/ListInsert.php new file mode 100644 index 00000000..7d53d11b --- /dev/null +++ b/vendor/predis/predis/src/Command/ListInsert.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/linsert + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListInsert extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LINSERT'; + } +} diff --git a/vendor/predis/predis/src/Command/ListLength.php b/vendor/predis/predis/src/Command/ListLength.php new file mode 100644 index 00000000..6495beb7 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListLength.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/llen + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListLength extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LLEN'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopFirst.php b/vendor/predis/predis/src/Command/ListPopFirst.php new file mode 100644 index 00000000..84d5d673 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopFirst.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/lpop + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPopFirst extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LPOP'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopFirstBlocking.php b/vendor/predis/predis/src/Command/ListPopFirstBlocking.php new file mode 100644 index 00000000..7dc7c000 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopFirstBlocking.php @@ -0,0 +1,41 @@ +<?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; + +/** + * @link http://redis.io/commands/blpop + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPopFirstBlocking extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BLPOP'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[0])) { + list($arguments, $timeout) = $arguments; + array_push($arguments, $timeout); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopLast.php b/vendor/predis/predis/src/Command/ListPopLast.php new file mode 100644 index 00000000..9e92db5f --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopLast.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/rpop + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPopLast extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPOP'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopLastBlocking.php b/vendor/predis/predis/src/Command/ListPopLastBlocking.php new file mode 100644 index 00000000..781eb919 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopLastBlocking.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/brpop + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPopLastBlocking extends ListPopFirstBlocking +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BRPOP'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopLastPushHead.php b/vendor/predis/predis/src/Command/ListPopLastPushHead.php new file mode 100644 index 00000000..f430eb22 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopLastPushHead.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/rpoplpush + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPopLastPushHead extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPOPLPUSH'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPopLastPushHeadBlocking.php b/vendor/predis/predis/src/Command/ListPopLastPushHeadBlocking.php new file mode 100644 index 00000000..ee9c93c8 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPopLastPushHeadBlocking.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/brpoplpush + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPopLastPushHeadBlocking extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BRPOPLPUSH'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPushHead.php b/vendor/predis/predis/src/Command/ListPushHead.php new file mode 100644 index 00000000..74bf7c49 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPushHead.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/lpush + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPushHead extends ListPushTail +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LPUSH'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPushHeadX.php b/vendor/predis/predis/src/Command/ListPushHeadX.php new file mode 100644 index 00000000..8e136b88 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPushHeadX.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/lpushx + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPushHeadX extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LPUSHX'; + } +} diff --git a/vendor/predis/predis/src/Command/ListPushTail.php b/vendor/predis/predis/src/Command/ListPushTail.php new file mode 100644 index 00000000..f2a057c0 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPushTail.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/rpush + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPushTail extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPUSH'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/ListPushTailX.php b/vendor/predis/predis/src/Command/ListPushTailX.php new file mode 100644 index 00000000..1af3645b --- /dev/null +++ b/vendor/predis/predis/src/Command/ListPushTailX.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/rpushx + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListPushTailX extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'RPUSHX'; + } +} diff --git a/vendor/predis/predis/src/Command/ListRange.php b/vendor/predis/predis/src/Command/ListRange.php new file mode 100644 index 00000000..32a21a6e --- /dev/null +++ b/vendor/predis/predis/src/Command/ListRange.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/lrange + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListRange extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LRANGE'; + } +} diff --git a/vendor/predis/predis/src/Command/ListRemove.php b/vendor/predis/predis/src/Command/ListRemove.php new file mode 100644 index 00000000..c5800899 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListRemove.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/lrem + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListRemove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LREM'; + } +} diff --git a/vendor/predis/predis/src/Command/ListSet.php b/vendor/predis/predis/src/Command/ListSet.php new file mode 100644 index 00000000..5e59864d --- /dev/null +++ b/vendor/predis/predis/src/Command/ListSet.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/lset + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListSet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LSET'; + } +} diff --git a/vendor/predis/predis/src/Command/ListTrim.php b/vendor/predis/predis/src/Command/ListTrim.php new file mode 100644 index 00000000..19314180 --- /dev/null +++ b/vendor/predis/predis/src/Command/ListTrim.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/ltrim + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ListTrim extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LTRIM'; + } +} diff --git a/vendor/predis/predis/src/Command/PrefixableCommandInterface.php b/vendor/predis/predis/src/Command/PrefixableCommandInterface.php new file mode 100644 index 00000000..6d54554f --- /dev/null +++ b/vendor/predis/predis/src/Command/PrefixableCommandInterface.php @@ -0,0 +1,27 @@ +<?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; + +/** + * Defines a command whose keys can be prefixed. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface PrefixableCommandInterface extends CommandInterface +{ + /** + * Prefixes all the keys found in the arguments of the command. + * + * @param string $prefix String used to prefix the keys. + */ + public function prefixKeys($prefix); +} diff --git a/vendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.php b/vendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.php new file mode 100644 index 00000000..75201276 --- /dev/null +++ b/vendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.php @@ -0,0 +1,450 @@ +<?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\Processor; + +use Predis\Command\CommandInterface; +use Predis\Command\PrefixableCommandInterface; + +/** + * Command processor capable of prefixing keys stored in the arguments of Redis + * commands supported. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class KeyPrefixProcessor implements ProcessorInterface +{ + private $prefix; + private $commands; + + /** + * @param string $prefix Prefix for the keys. + */ + public function __construct($prefix) + { + $this->prefix = $prefix; + $this->commands = array( + /* ---------------- Redis 1.2 ---------------- */ + 'EXISTS' => 'static::all', + 'DEL' => 'static::all', + 'TYPE' => 'static::first', + 'KEYS' => 'static::first', + 'RENAME' => 'static::all', + 'RENAMENX' => 'static::all', + 'EXPIRE' => 'static::first', + 'EXPIREAT' => 'static::first', + 'TTL' => 'static::first', + 'MOVE' => 'static::first', + 'SORT' => 'static::sort', + 'DUMP' => 'static::first', + 'RESTORE' => 'static::first', + 'SET' => 'static::first', + 'SETNX' => 'static::first', + 'MSET' => 'static::interleaved', + 'MSETNX' => 'static::interleaved', + 'GET' => 'static::first', + 'MGET' => 'static::all', + 'GETSET' => 'static::first', + 'INCR' => 'static::first', + 'INCRBY' => 'static::first', + 'DECR' => 'static::first', + 'DECRBY' => 'static::first', + 'RPUSH' => 'static::first', + 'LPUSH' => 'static::first', + 'LLEN' => 'static::first', + 'LRANGE' => 'static::first', + 'LTRIM' => 'static::first', + 'LINDEX' => 'static::first', + 'LSET' => 'static::first', + 'LREM' => 'static::first', + 'LPOP' => 'static::first', + 'RPOP' => 'static::first', + 'RPOPLPUSH' => 'static::all', + 'SADD' => 'static::first', + 'SREM' => 'static::first', + 'SPOP' => 'static::first', + 'SMOVE' => 'static::skipLast', + 'SCARD' => 'static::first', + 'SISMEMBER' => 'static::first', + 'SINTER' => 'static::all', + 'SINTERSTORE' => 'static::all', + 'SUNION' => 'static::all', + 'SUNIONSTORE' => 'static::all', + 'SDIFF' => 'static::all', + 'SDIFFSTORE' => 'static::all', + 'SMEMBERS' => 'static::first', + 'SRANDMEMBER' => 'static::first', + 'ZADD' => 'static::first', + 'ZINCRBY' => 'static::first', + 'ZREM' => 'static::first', + 'ZRANGE' => 'static::first', + 'ZREVRANGE' => 'static::first', + 'ZRANGEBYSCORE' => 'static::first', + 'ZCARD' => 'static::first', + 'ZSCORE' => 'static::first', + 'ZREMRANGEBYSCORE' => 'static::first', + /* ---------------- Redis 2.0 ---------------- */ + 'SETEX' => 'static::first', + 'APPEND' => 'static::first', + 'SUBSTR' => 'static::first', + 'BLPOP' => 'static::skipLast', + 'BRPOP' => 'static::skipLast', + 'ZUNIONSTORE' => 'static::zsetStore', + 'ZINTERSTORE' => 'static::zsetStore', + 'ZCOUNT' => 'static::first', + 'ZRANK' => 'static::first', + 'ZREVRANK' => 'static::first', + 'ZREMRANGEBYRANK' => 'static::first', + 'HSET' => 'static::first', + 'HSETNX' => 'static::first', + 'HMSET' => 'static::first', + 'HINCRBY' => 'static::first', + 'HGET' => 'static::first', + 'HMGET' => 'static::first', + 'HDEL' => 'static::first', + 'HEXISTS' => 'static::first', + 'HLEN' => 'static::first', + 'HKEYS' => 'static::first', + 'HVALS' => 'static::first', + 'HGETALL' => 'static::first', + 'SUBSCRIBE' => 'static::all', + 'UNSUBSCRIBE' => 'static::all', + 'PSUBSCRIBE' => 'static::all', + 'PUNSUBSCRIBE' => 'static::all', + 'PUBLISH' => 'static::first', + /* ---------------- Redis 2.2 ---------------- */ + 'PERSIST' => 'static::first', + 'STRLEN' => 'static::first', + 'SETRANGE' => 'static::first', + 'GETRANGE' => 'static::first', + 'SETBIT' => 'static::first', + 'GETBIT' => 'static::first', + 'RPUSHX' => 'static::first', + 'LPUSHX' => 'static::first', + 'LINSERT' => 'static::first', + 'BRPOPLPUSH' => 'static::skipLast', + 'ZREVRANGEBYSCORE' => 'static::first', + 'WATCH' => 'static::all', + /* ---------------- Redis 2.6 ---------------- */ + 'PTTL' => 'static::first', + 'PEXPIRE' => 'static::first', + 'PEXPIREAT' => 'static::first', + 'PSETEX' => 'static::first', + 'INCRBYFLOAT' => 'static::first', + 'BITOP' => 'static::skipFirst', + 'BITCOUNT' => 'static::first', + 'HINCRBYFLOAT' => 'static::first', + 'EVAL' => 'static::evalKeys', + 'EVALSHA' => 'static::evalKeys', + 'MIGRATE' => 'static::migrate', + /* ---------------- Redis 2.8 ---------------- */ + 'SSCAN' => 'static::first', + 'ZSCAN' => 'static::first', + 'HSCAN' => 'static::first', + 'PFADD' => 'static::first', + 'PFCOUNT' => 'static::all', + 'PFMERGE' => 'static::all', + 'ZLEXCOUNT' => 'static::first', + 'ZRANGEBYLEX' => 'static::first', + 'ZREMRANGEBYLEX' => 'static::first', + 'ZREVRANGEBYLEX' => 'static::first', + 'BITPOS' => 'static::first', + /* ---------------- Redis 3.2 ---------------- */ + 'HSTRLEN' => 'static::first', + 'BITFIELD' => 'static::first', + 'GEOADD' => 'static::first', + 'GEOHASH' => 'static::first', + 'GEOPOS' => 'static::first', + 'GEODIST' => 'static::first', + 'GEORADIUS' => 'static::georadius', + 'GEORADIUSBYMEMBER' => 'static::georadius', + ); + } + + /** + * Sets a prefix that is applied to all the keys. + * + * @param string $prefix Prefix for the keys. + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * Gets the current prefix. + * + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + /** + * {@inheritdoc} + */ + public function process(CommandInterface $command) + { + if ($command instanceof PrefixableCommandInterface) { + $command->prefixKeys($this->prefix); + } elseif (isset($this->commands[$commandID = strtoupper($command->getId())])) { + call_user_func($this->commands[$commandID], $command, $this->prefix); + } + } + + /** + * Sets an handler for the specified command ID. + * + * The callback signature must have 2 parameters of the following types: + * + * - Predis\Command\CommandInterface (command instance) + * - String (prefix) + * + * When the callback argument is omitted or NULL, the previously + * associated handler for the specified command ID is removed. + * + * @param string $commandID The ID of the command to be handled. + * @param mixed $callback A valid callable object or NULL. + * + * @throws \InvalidArgumentException + */ + public function setCommandHandler($commandID, $callback = null) + { + $commandID = strtoupper($commandID); + + if (!isset($callback)) { + unset($this->commands[$commandID]); + + return; + } + + if (!is_callable($callback)) { + throw new \InvalidArgumentException( + 'Callback must be a valid callable object or NULL' + ); + } + + $this->commands[$commandID] = $callback; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->getPrefix(); + } + + /** + * Applies the specified prefix only the first argument. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function first(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to all the arguments. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function all(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + foreach ($arguments as &$key) { + $key = "$prefix$key"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix only to even arguments in the list. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function interleaved(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $length = count($arguments); + + for ($i = 0; $i < $length; $i += 2) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to all the arguments but the first one. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function skipFirst(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $length = count($arguments); + + for ($i = 1; $i < $length; ++$i) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to all the arguments but the last one. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function skipLast(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $length = count($arguments); + + for ($i = 0; $i < $length - 1; ++$i) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the keys of a SORT command. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function sort(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + + if (($count = count($arguments)) > 1) { + for ($i = 1; $i < $count; ++$i) { + switch (strtoupper($arguments[$i])) { + case 'BY': + case 'STORE': + $arguments[$i] = "$prefix{$arguments[++$i]}"; + break; + + case 'GET': + $value = $arguments[++$i]; + if ($value !== '#') { + $arguments[$i] = "$prefix$value"; + } + break; + + case 'LIMIT'; + $i += 2; + break; + } + } + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the keys of an EVAL-based command. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function evalKeys(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + for ($i = 2; $i < $arguments[1] + 2; ++$i) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the keys of Z[INTERSECTION|UNION]STORE. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function zsetStore(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + $length = ((int) $arguments[1]) + 2; + + for ($i = 2; $i < $length; ++$i) { + $arguments[$i] = "$prefix{$arguments[$i]}"; + } + + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the key of a MIGRATE command. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function migrate(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[2] = "$prefix{$arguments[2]}"; + $command->setRawArguments($arguments); + } + } + + /** + * Applies the specified prefix to the key of a GEORADIUS command. + * + * @param CommandInterface $command Command instance. + * @param string $prefix Prefix string. + */ + public static function georadius(CommandInterface $command, $prefix) + { + if ($arguments = $command->getArguments()) { + $arguments[0] = "$prefix{$arguments[0]}"; + $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; + + if (($count = count($arguments)) > $startIndex) { + for ($i = $startIndex; $i < $count; ++$i) { + switch (strtoupper($arguments[$i])) { + case 'STORE': + case 'STOREDIST': + $arguments[$i] = "$prefix{$arguments[++$i]}"; + break; + + } + } + } + + $command->setRawArguments($arguments); + } + } +} diff --git a/vendor/predis/predis/src/Command/Processor/ProcessorChain.php b/vendor/predis/predis/src/Command/Processor/ProcessorChain.php new file mode 100644 index 00000000..0a4768b0 --- /dev/null +++ b/vendor/predis/predis/src/Command/Processor/ProcessorChain.php @@ -0,0 +1,130 @@ +<?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\Processor; + +use Predis\Command\CommandInterface; + +/** + * Default implementation of a command processors chain. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ProcessorChain implements \ArrayAccess, ProcessorInterface +{ + private $processors = array(); + + /** + * @param array $processors List of instances of ProcessorInterface. + */ + public function __construct($processors = array()) + { + foreach ($processors as $processor) { + $this->add($processor); + } + } + + /** + * {@inheritdoc} + */ + public function add(ProcessorInterface $processor) + { + $this->processors[] = $processor; + } + + /** + * {@inheritdoc} + */ + public function remove(ProcessorInterface $processor) + { + if (false !== $index = array_search($processor, $this->processors, true)) { + unset($this[$index]); + } + } + + /** + * {@inheritdoc} + */ + public function process(CommandInterface $command) + { + for ($i = 0; $i < $count = count($this->processors); ++$i) { + $this->processors[$i]->process($command); + } + } + + /** + * {@inheritdoc} + */ + public function getProcessors() + { + return $this->processors; + } + + /** + * Returns an iterator over the list of command processor in the chain. + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->processors); + } + + /** + * Returns the number of command processors in the chain. + * + * @return int + */ + public function count() + { + return count($this->processors); + } + + /** + * {@inheritdoc} + */ + public function offsetExists($index) + { + return isset($this->processors[$index]); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($index) + { + return $this->processors[$index]; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($index, $processor) + { + if (!$processor instanceof ProcessorInterface) { + throw new \InvalidArgumentException( + 'A processor chain accepts only instances of '. + "'Predis\Command\Processor\ProcessorInterface'." + ); + } + + $this->processors[$index] = $processor; + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($index) + { + unset($this->processors[$index]); + $this->processors = array_values($this->processors); + } +} diff --git a/vendor/predis/predis/src/Command/Processor/ProcessorInterface.php b/vendor/predis/predis/src/Command/Processor/ProcessorInterface.php new file mode 100644 index 00000000..2f910580 --- /dev/null +++ b/vendor/predis/predis/src/Command/Processor/ProcessorInterface.php @@ -0,0 +1,29 @@ +<?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\Processor; + +use Predis\Command\CommandInterface; + +/** + * A command processor processes Redis commands before they are sent to Redis. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ProcessorInterface +{ + /** + * Processes the given Redis command. + * + * @param CommandInterface $command Command instance. + */ + public function process(CommandInterface $command); +} diff --git a/vendor/predis/predis/src/Command/PubSubPublish.php b/vendor/predis/predis/src/Command/PubSubPublish.php new file mode 100644 index 00000000..55508f8d --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubPublish.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/publish + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PubSubPublish extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PUBLISH'; + } +} diff --git a/vendor/predis/predis/src/Command/PubSubPubsub.php b/vendor/predis/predis/src/Command/PubSubPubsub.php new file mode 100644 index 00000000..8cf81297 --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubPubsub.php @@ -0,0 +1,61 @@ +<?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; + +/** + * @link http://redis.io/commands/pubsub + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PubSubPubsub extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PUBSUB'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + switch (strtolower($this->getArgument(0))) { + case 'numsub': + return self::processNumsub($data); + + default: + return $data; + } + } + + /** + * Returns the processed response to PUBSUB NUMSUB. + * + * @param array $channels List of channels + * + * @return array + */ + protected static function processNumsub(array $channels) + { + $processed = array(); + $count = count($channels); + + for ($i = 0; $i < $count; ++$i) { + $processed[$channels[$i]] = $channels[++$i]; + } + + return $processed; + } +} diff --git a/vendor/predis/predis/src/Command/PubSubSubscribe.php b/vendor/predis/predis/src/Command/PubSubSubscribe.php new file mode 100644 index 00000000..e477b313 --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubSubscribe.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/subscribe + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PubSubSubscribe extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUBSCRIBE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/PubSubSubscribeByPattern.php b/vendor/predis/predis/src/Command/PubSubSubscribeByPattern.php new file mode 100644 index 00000000..01182806 --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubSubscribeByPattern.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/psubscribe + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PubSubSubscribeByPattern extends PubSubSubscribe +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PSUBSCRIBE'; + } +} diff --git a/vendor/predis/predis/src/Command/PubSubUnsubscribe.php b/vendor/predis/predis/src/Command/PubSubUnsubscribe.php new file mode 100644 index 00000000..d57c3ac6 --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubUnsubscribe.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/unsubscribe + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PubSubUnsubscribe extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'UNSUBSCRIBE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/PubSubUnsubscribeByPattern.php b/vendor/predis/predis/src/Command/PubSubUnsubscribeByPattern.php new file mode 100644 index 00000000..4d76508b --- /dev/null +++ b/vendor/predis/predis/src/Command/PubSubUnsubscribeByPattern.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/punsubscribe + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PubSubUnsubscribeByPattern extends PubSubUnsubscribe +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PUNSUBSCRIBE'; + } +} diff --git a/vendor/predis/predis/src/Command/RawCommand.php b/vendor/predis/predis/src/Command/RawCommand.php new file mode 100644 index 00000000..2dd48ca1 --- /dev/null +++ b/vendor/predis/predis/src/Command/RawCommand.php @@ -0,0 +1,131 @@ +<?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; + +/** + * Class for generic "anonymous" Redis commands. + * + * This command class does not filter input arguments or parse responses, but + * can be used to leverage the standard Predis API to execute any command simply + * by providing the needed arguments following the command signature as defined + * by Redis in its documentation. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RawCommand implements CommandInterface +{ + private $slot; + private $commandID; + private $arguments; + + /** + * @param array $arguments Command ID and its arguments. + * + * @throws \InvalidArgumentException + */ + public function __construct(array $arguments) + { + if (!$arguments) { + throw new \InvalidArgumentException( + 'The arguments array must contain at least the command ID.' + ); + } + + $this->commandID = strtoupper(array_shift($arguments)); + $this->arguments = $arguments; + } + + /** + * Creates a new raw command using a variadic method. + * + * @param string $commandID Redis command ID. + * @param string ... Arguments list for the command. + * + * @return CommandInterface + */ + public static function create($commandID /* [ $arg, ... */) + { + $arguments = func_get_args(); + $command = new self($arguments); + + return $command; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->commandID; + } + + /** + * {@inheritdoc} + */ + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + unset($this->slot); + } + + /** + * {@inheritdoc} + */ + public function setRawArguments(array $arguments) + { + $this->setArguments($arguments); + } + + /** + * {@inheritdoc} + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * {@inheritdoc} + */ + public function getArgument($index) + { + if (isset($this->arguments[$index])) { + return $this->arguments[$index]; + } + } + + /** + * {@inheritdoc} + */ + public function setSlot($slot) + { + $this->slot = $slot; + } + + /** + * {@inheritdoc} + */ + public function getSlot() + { + if (isset($this->slot)) { + return $this->slot; + } + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ScriptCommand.php b/vendor/predis/predis/src/Command/ScriptCommand.php new file mode 100644 index 00000000..a30bc1d2 --- /dev/null +++ b/vendor/predis/predis/src/Command/ScriptCommand.php @@ -0,0 +1,77 @@ +<?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; + +/** + * Base class used to implement an higher level abstraction for commands based + * on Lua scripting with EVAL and EVALSHA. + * + * @link http://redis.io/commands/eval + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +abstract class ScriptCommand extends ServerEvalSHA +{ + /** + * Gets the body of a Lua script. + * + * @return string + */ + abstract public function getScript(); + + /** + * Specifies the number of arguments that should be considered as keys. + * + * The default behaviour for the base class is to return 0 to indicate that + * all the elements of the arguments array should be considered as keys, but + * subclasses can enforce a static number of keys. + * + * @return int + */ + protected function getKeysCount() + { + return 0; + } + + /** + * Returns the elements from the arguments that are identified as keys. + * + * @return array + */ + public function getKeys() + { + return array_slice($this->getArguments(), 2, $this->getKeysCount()); + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (($numkeys = $this->getKeysCount()) && $numkeys < 0) { + $numkeys = count($arguments) + $numkeys; + } + + return array_merge(array(sha1($this->getScript()), (int) $numkeys), $arguments); + } + + /** + * @return array + */ + public function getEvalArguments() + { + $arguments = $this->getArguments(); + $arguments[0] = $this->getScript(); + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ServerBackgroundRewriteAOF.php b/vendor/predis/predis/src/Command/ServerBackgroundRewriteAOF.php new file mode 100644 index 00000000..c66a294e --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerBackgroundRewriteAOF.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/bgrewriteaof + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerBackgroundRewriteAOF extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BGREWRITEAOF'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data == 'Background append only file rewriting started'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerBackgroundSave.php b/vendor/predis/predis/src/Command/ServerBackgroundSave.php new file mode 100644 index 00000000..4bf67ef3 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerBackgroundSave.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/bgsave + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerBackgroundSave extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BGSAVE'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return $data === 'Background saving started' ? true : $data; + } +} diff --git a/vendor/predis/predis/src/Command/ServerClient.php b/vendor/predis/predis/src/Command/ServerClient.php new file mode 100644 index 00000000..d00ebbff --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerClient.php @@ -0,0 +1,74 @@ +<?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; + +/** + * @link http://redis.io/commands/client-list + * @link http://redis.io/commands/client-kill + * @link http://redis.io/commands/client-getname + * @link http://redis.io/commands/client-setname + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerClient extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'CLIENT'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $args = array_change_key_case($this->getArguments(), CASE_UPPER); + + switch (strtoupper($args[0])) { + case 'LIST': + return $this->parseClientList($data); + case 'KILL': + case 'GETNAME': + case 'SETNAME': + default: + return $data; + } + } + + /** + * Parses the response to CLIENT LIST and returns a structured list. + * + * @param string $data Response buffer. + * + * @return array + */ + protected function parseClientList($data) + { + $clients = array(); + + foreach (explode("\n", $data, -1) as $clientData) { + $client = array(); + + foreach (explode(' ', $clientData) as $kv) { + @list($k, $v) = explode('=', $kv); + $client[$k] = $v; + } + + $clients[] = $client; + } + + return $clients; + } +} diff --git a/vendor/predis/predis/src/Command/ServerCommand.php b/vendor/predis/predis/src/Command/ServerCommand.php new file mode 100644 index 00000000..e9b3393c --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerCommand.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/command + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerCommand extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'COMMAND'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerConfig.php b/vendor/predis/predis/src/Command/ServerConfig.php new file mode 100644 index 00000000..81e497ae --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerConfig.php @@ -0,0 +1,49 @@ +<?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; + +/** + * @link http://redis.io/commands/config-set + * @link http://redis.io/commands/config-get + * @link http://redis.io/commands/config-resetstat + * @link http://redis.io/commands/config-rewrite + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerConfig extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'CONFIG'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $result = array(); + + for ($i = 0; $i < count($data); ++$i) { + $result[$data[$i]] = $data[++$i]; + } + + return $result; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ServerDatabaseSize.php b/vendor/predis/predis/src/Command/ServerDatabaseSize.php new file mode 100644 index 00000000..6bc89724 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerDatabaseSize.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/dbsize + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerDatabaseSize extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DBSIZE'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerEval.php b/vendor/predis/predis/src/Command/ServerEval.php new file mode 100644 index 00000000..f5eefd81 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerEval.php @@ -0,0 +1,38 @@ +<?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; + +/** + * @link http://redis.io/commands/eval + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerEval extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EVAL'; + } + + /** + * Calculates the SHA1 hash of the body of the script. + * + * @return string SHA1 hash. + */ + public function getScriptHash() + { + return sha1($this->getArgument(0)); + } +} diff --git a/vendor/predis/predis/src/Command/ServerEvalSHA.php b/vendor/predis/predis/src/Command/ServerEvalSHA.php new file mode 100644 index 00000000..520a8e98 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerEvalSHA.php @@ -0,0 +1,38 @@ +<?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; + +/** + * @link http://redis.io/commands/evalsha + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerEvalSHA extends ServerEval +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EVALSHA'; + } + + /** + * Returns the SHA1 hash of the body of the script. + * + * @return string SHA1 hash. + */ + public function getScriptHash() + { + return $this->getArgument(0); + } +} diff --git a/vendor/predis/predis/src/Command/ServerFlushAll.php b/vendor/predis/predis/src/Command/ServerFlushAll.php new file mode 100644 index 00000000..c35b2ad6 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerFlushAll.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/flushall + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerFlushAll extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'FLUSHALL'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerFlushDatabase.php b/vendor/predis/predis/src/Command/ServerFlushDatabase.php new file mode 100644 index 00000000..3da6b320 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerFlushDatabase.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/flushdb + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerFlushDatabase extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'FLUSHDB'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerInfo.php b/vendor/predis/predis/src/Command/ServerInfo.php new file mode 100644 index 00000000..96d6adad --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerInfo.php @@ -0,0 +1,111 @@ +<?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; + +/** + * @link http://redis.io/commands/info + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerInfo extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INFO'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + $info = array(); + $infoLines = preg_split('/\r?\n/', $data); + + foreach ($infoLines as $row) { + if (strpos($row, ':') === false) { + continue; + } + + list($k, $v) = $this->parseRow($row); + $info[$k] = $v; + } + + return $info; + } + + /** + * Parses a single row of the response and returns the key-value pair. + * + * @param string $row Single row of the response. + * + * @return array + */ + protected function parseRow($row) + { + list($k, $v) = explode(':', $row, 2); + + if (preg_match('/^db\d+$/', $k)) { + $v = $this->parseDatabaseStats($v); + } + + return array($k, $v); + } + + /** + * Extracts the statistics of each logical DB from the string buffer. + * + * @param string $str Response buffer. + * + * @return array + */ + protected function parseDatabaseStats($str) + { + $db = array(); + + foreach (explode(',', $str) as $dbvar) { + list($dbvk, $dbvv) = explode('=', $dbvar); + $db[trim($dbvk)] = $dbvv; + } + + return $db; + } + + /** + * Parses the response and extracts the allocation statistics. + * + * @param string $str Response buffer. + * + * @return array + */ + protected function parseAllocationStats($str) + { + $stats = array(); + + foreach (explode(',', $str) as $kv) { + @list($size, $objects, $extra) = explode('=', $kv); + + // hack to prevent incorrect values when parsing the >=256 key + if (isset($extra)) { + $size = ">=$objects"; + $objects = $extra; + } + + $stats[$size] = $objects; + } + + return $stats; + } +} diff --git a/vendor/predis/predis/src/Command/ServerInfoV26x.php b/vendor/predis/predis/src/Command/ServerInfoV26x.php new file mode 100644 index 00000000..90c9b716 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerInfoV26x.php @@ -0,0 +1,56 @@ +<?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; + +/** + * @link http://redis.io/commands/info + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerInfoV26x extends ServerInfo +{ + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if ($data === '') { + return array(); + } + + $info = array(); + + $current = null; + $infoLines = preg_split('/\r?\n/', $data); + + if (isset($infoLines[0]) && $infoLines[0][0] !== '#') { + return parent::parseResponse($data); + } + + foreach ($infoLines as $row) { + if ($row === '') { + continue; + } + + if (preg_match('/^# (\w+)$/', $row, $matches)) { + $info[$matches[1]] = array(); + $current = &$info[$matches[1]]; + continue; + } + + list($k, $v) = $this->parseRow($row); + $current[$k] = $v; + } + + return $info; + } +} diff --git a/vendor/predis/predis/src/Command/ServerLastSave.php b/vendor/predis/predis/src/Command/ServerLastSave.php new file mode 100644 index 00000000..feeb19a8 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerLastSave.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/lastsave + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerLastSave extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'LASTSAVE'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerMonitor.php b/vendor/predis/predis/src/Command/ServerMonitor.php new file mode 100644 index 00000000..1c3d3309 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerMonitor.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/monitor + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerMonitor extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MONITOR'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerObject.php b/vendor/predis/predis/src/Command/ServerObject.php new file mode 100644 index 00000000..f921701c --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerObject.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/object + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerObject extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'OBJECT'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerSave.php b/vendor/predis/predis/src/Command/ServerSave.php new file mode 100644 index 00000000..addefe20 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerSave.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/save + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerSave extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SAVE'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerScript.php b/vendor/predis/predis/src/Command/ServerScript.php new file mode 100644 index 00000000..7a01018d --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerScript.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/script + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerScript extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SCRIPT'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerSentinel.php b/vendor/predis/predis/src/Command/ServerSentinel.php new file mode 100644 index 00000000..c0962db3 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerSentinel.php @@ -0,0 +1,66 @@ +<?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; + +/** + * @link http://redis.io/topics/sentinel + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerSentinel extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SENTINEL'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + switch (strtolower($this->getArgument(0))) { + case 'masters': + case 'slaves': + return self::processMastersOrSlaves($data); + + default: + return $data; + } + } + + /** + * Returns a processed response to SENTINEL MASTERS or SENTINEL SLAVES. + * + * @param array $servers List of Redis servers. + * + * @return array + */ + protected static function processMastersOrSlaves(array $servers) + { + foreach ($servers as $idx => $node) { + $processed = array(); + $count = count($node); + + for ($i = 0; $i < $count; ++$i) { + $processed[$node[$i]] = $node[++$i]; + } + + $servers[$idx] = $processed; + } + + return $servers; + } +} diff --git a/vendor/predis/predis/src/Command/ServerShutdown.php b/vendor/predis/predis/src/Command/ServerShutdown.php new file mode 100644 index 00000000..f5b745a2 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerShutdown.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/shutdown + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerShutdown extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SHUTDOWN'; + } +} diff --git a/vendor/predis/predis/src/Command/ServerSlaveOf.php b/vendor/predis/predis/src/Command/ServerSlaveOf.php new file mode 100644 index 00000000..4ff44556 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerSlaveOf.php @@ -0,0 +1,40 @@ +<?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; + +/** + * @link http://redis.io/commands/slaveof + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerSlaveOf extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SLAVEOF'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 0 || $arguments[0] === 'NO ONE') { + return array('NO', 'ONE'); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ServerSlowlog.php b/vendor/predis/predis/src/Command/ServerSlowlog.php new file mode 100644 index 00000000..137ff59e --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerSlowlog.php @@ -0,0 +1,51 @@ +<?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; + +/** + * @link http://redis.io/commands/slowlog + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerSlowlog extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SLOWLOG'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $log = array(); + + foreach ($data as $index => $entry) { + $log[$index] = array( + 'id' => $entry[0], + 'timestamp' => $entry[1], + 'duration' => $entry[2], + 'command' => $entry[3], + ); + } + + return $log; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ServerTime.php b/vendor/predis/predis/src/Command/ServerTime.php new file mode 100644 index 00000000..589f92c5 --- /dev/null +++ b/vendor/predis/predis/src/Command/ServerTime.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/time + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerTime extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'TIME'; + } +} diff --git a/vendor/predis/predis/src/Command/SetAdd.php b/vendor/predis/predis/src/Command/SetAdd.php new file mode 100644 index 00000000..c1188181 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetAdd.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/sadd + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetAdd extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/SetCardinality.php b/vendor/predis/predis/src/Command/SetCardinality.php new file mode 100644 index 00000000..a9f959b7 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetCardinality.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/scard + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetCardinality extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SCARD'; + } +} diff --git a/vendor/predis/predis/src/Command/SetDifference.php b/vendor/predis/predis/src/Command/SetDifference.php new file mode 100644 index 00000000..35f23f98 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetDifference.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/sdiff + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetDifference extends SetIntersection +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SDIFF'; + } +} diff --git a/vendor/predis/predis/src/Command/SetDifferenceStore.php b/vendor/predis/predis/src/Command/SetDifferenceStore.php new file mode 100644 index 00000000..0cb78155 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetDifferenceStore.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/sdiffstore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetDifferenceStore extends SetIntersectionStore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SDIFFSTORE'; + } +} diff --git a/vendor/predis/predis/src/Command/SetIntersection.php b/vendor/predis/predis/src/Command/SetIntersection.php new file mode 100644 index 00000000..d18258fd --- /dev/null +++ b/vendor/predis/predis/src/Command/SetIntersection.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/sinter + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetIntersection extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SINTER'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/SetIntersectionStore.php b/vendor/predis/predis/src/Command/SetIntersectionStore.php new file mode 100644 index 00000000..b748618a --- /dev/null +++ b/vendor/predis/predis/src/Command/SetIntersectionStore.php @@ -0,0 +1,40 @@ +<?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; + +/** + * @link http://redis.io/commands/sinterstore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetIntersectionStore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SINTERSTORE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 2 && is_array($arguments[1])) { + return array_merge(array($arguments[0]), $arguments[1]); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/SetIsMember.php b/vendor/predis/predis/src/Command/SetIsMember.php new file mode 100644 index 00000000..1b484907 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetIsMember.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/sismember + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetIsMember extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SISMEMBER'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/SetMembers.php b/vendor/predis/predis/src/Command/SetMembers.php new file mode 100644 index 00000000..f4076ae8 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetMembers.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/smembers + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetMembers extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SMEMBERS'; + } +} diff --git a/vendor/predis/predis/src/Command/SetMove.php b/vendor/predis/predis/src/Command/SetMove.php new file mode 100644 index 00000000..72d514be --- /dev/null +++ b/vendor/predis/predis/src/Command/SetMove.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/smove + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetMove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SMOVE'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/SetPop.php b/vendor/predis/predis/src/Command/SetPop.php new file mode 100644 index 00000000..b78d3f33 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetPop.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/spop + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetPop extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SPOP'; + } +} diff --git a/vendor/predis/predis/src/Command/SetRandomMember.php b/vendor/predis/predis/src/Command/SetRandomMember.php new file mode 100644 index 00000000..2cb79a04 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetRandomMember.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/srandmember + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetRandomMember extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SRANDMEMBER'; + } +} diff --git a/vendor/predis/predis/src/Command/SetRemove.php b/vendor/predis/predis/src/Command/SetRemove.php new file mode 100644 index 00000000..b34710c6 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetRemove.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/srem + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetRemove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SREM'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/SetScan.php b/vendor/predis/predis/src/Command/SetScan.php new file mode 100644 index 00000000..d42b28df --- /dev/null +++ b/vendor/predis/predis/src/Command/SetScan.php @@ -0,0 +1,66 @@ +<?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; + +/** + * @link http://redis.io/commands/sscan + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetScan extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SSCAN'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 3 && is_array($arguments[2])) { + $options = $this->prepareOptions(array_pop($arguments)); + $arguments = array_merge($arguments, $options); + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $options = array_change_key_case($options, CASE_UPPER); + $normalized = array(); + + if (!empty($options['MATCH'])) { + $normalized[] = 'MATCH'; + $normalized[] = $options['MATCH']; + } + + if (!empty($options['COUNT'])) { + $normalized[] = 'COUNT'; + $normalized[] = $options['COUNT']; + } + + return $normalized; + } +} diff --git a/vendor/predis/predis/src/Command/SetUnion.php b/vendor/predis/predis/src/Command/SetUnion.php new file mode 100644 index 00000000..7da842b4 --- /dev/null +++ b/vendor/predis/predis/src/Command/SetUnion.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/sunion + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetUnion extends SetIntersection +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUNION'; + } +} diff --git a/vendor/predis/predis/src/Command/SetUnionStore.php b/vendor/predis/predis/src/Command/SetUnionStore.php new file mode 100644 index 00000000..eac821ad --- /dev/null +++ b/vendor/predis/predis/src/Command/SetUnionStore.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/sunionstore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class SetUnionStore extends SetIntersectionStore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUNIONSTORE'; + } +} diff --git a/vendor/predis/predis/src/Command/StringAppend.php b/vendor/predis/predis/src/Command/StringAppend.php new file mode 100644 index 00000000..dac8b847 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringAppend.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/append + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringAppend extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'APPEND'; + } +} diff --git a/vendor/predis/predis/src/Command/StringBitCount.php b/vendor/predis/predis/src/Command/StringBitCount.php new file mode 100644 index 00000000..193cce91 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringBitCount.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/bitcount + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringBitCount extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITCOUNT'; + } +} diff --git a/vendor/predis/predis/src/Command/StringBitField.php b/vendor/predis/predis/src/Command/StringBitField.php new file mode 100644 index 00000000..9f4deaa6 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringBitField.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/bitfield + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringBitField extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITFIELD'; + } +} diff --git a/vendor/predis/predis/src/Command/StringBitOp.php b/vendor/predis/predis/src/Command/StringBitOp.php new file mode 100644 index 00000000..e04ee79c --- /dev/null +++ b/vendor/predis/predis/src/Command/StringBitOp.php @@ -0,0 +1,42 @@ +<?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; + +/** + * @link http://redis.io/commands/bitop + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringBitOp extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITOP'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 3 && is_array($arguments[2])) { + list($operation, $destination) = $arguments; + $arguments = $arguments[2]; + array_unshift($arguments, $operation, $destination); + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/StringBitPos.php b/vendor/predis/predis/src/Command/StringBitPos.php new file mode 100644 index 00000000..42957665 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringBitPos.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/bitpos + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringBitPos extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'BITPOS'; + } +} diff --git a/vendor/predis/predis/src/Command/StringDecrement.php b/vendor/predis/predis/src/Command/StringDecrement.php new file mode 100644 index 00000000..aa5808cd --- /dev/null +++ b/vendor/predis/predis/src/Command/StringDecrement.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/decr + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringDecrement extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DECR'; + } +} diff --git a/vendor/predis/predis/src/Command/StringDecrementBy.php b/vendor/predis/predis/src/Command/StringDecrementBy.php new file mode 100644 index 00000000..cbf3e112 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringDecrementBy.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/decrby + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringDecrementBy extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DECRBY'; + } +} diff --git a/vendor/predis/predis/src/Command/StringGet.php b/vendor/predis/predis/src/Command/StringGet.php new file mode 100644 index 00000000..138e915c --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGet.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/get + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringGet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GET'; + } +} diff --git a/vendor/predis/predis/src/Command/StringGetBit.php b/vendor/predis/predis/src/Command/StringGetBit.php new file mode 100644 index 00000000..3c5b4f9b --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGetBit.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/getbit + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringGetBit extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GETBIT'; + } +} diff --git a/vendor/predis/predis/src/Command/StringGetMultiple.php b/vendor/predis/predis/src/Command/StringGetMultiple.php new file mode 100644 index 00000000..e340f9cf --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGetMultiple.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/mget + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringGetMultiple extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MGET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeArguments($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/StringGetRange.php b/vendor/predis/predis/src/Command/StringGetRange.php new file mode 100644 index 00000000..bb10565b --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGetRange.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/getrange + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringGetRange extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GETRANGE'; + } +} diff --git a/vendor/predis/predis/src/Command/StringGetSet.php b/vendor/predis/predis/src/Command/StringGetSet.php new file mode 100644 index 00000000..b68870d4 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringGetSet.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/getset + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringGetSet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'GETSET'; + } +} diff --git a/vendor/predis/predis/src/Command/StringIncrement.php b/vendor/predis/predis/src/Command/StringIncrement.php new file mode 100644 index 00000000..fa1846e2 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringIncrement.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/incr + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringIncrement extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INCR'; + } +} diff --git a/vendor/predis/predis/src/Command/StringIncrementBy.php b/vendor/predis/predis/src/Command/StringIncrementBy.php new file mode 100644 index 00000000..9d8241a2 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringIncrementBy.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/incrby + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringIncrementBy extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INCRBY'; + } +} diff --git a/vendor/predis/predis/src/Command/StringIncrementByFloat.php b/vendor/predis/predis/src/Command/StringIncrementByFloat.php new file mode 100644 index 00000000..164a0869 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringIncrementByFloat.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/incrbyfloat + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringIncrementByFloat extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'INCRBYFLOAT'; + } +} diff --git a/vendor/predis/predis/src/Command/StringPreciseSetExpire.php b/vendor/predis/predis/src/Command/StringPreciseSetExpire.php new file mode 100644 index 00000000..2faa954d --- /dev/null +++ b/vendor/predis/predis/src/Command/StringPreciseSetExpire.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/psetex + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringPreciseSetExpire extends StringSetExpire +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'PSETEX'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSet.php b/vendor/predis/predis/src/Command/StringSet.php new file mode 100644 index 00000000..b1469945 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSet.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/set + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringSet extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SET'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetBit.php b/vendor/predis/predis/src/Command/StringSetBit.php new file mode 100644 index 00000000..7933b6be --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetBit.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/setbit + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringSetBit extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETBIT'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetExpire.php b/vendor/predis/predis/src/Command/StringSetExpire.php new file mode 100644 index 00000000..f0881708 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetExpire.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/setex + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringSetExpire extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETEX'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetMultiple.php b/vendor/predis/predis/src/Command/StringSetMultiple.php new file mode 100644 index 00000000..a3c5324d --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetMultiple.php @@ -0,0 +1,48 @@ +<?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; + +/** + * @link http://redis.io/commands/mset + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringSetMultiple extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MSET'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 1 && is_array($arguments[0])) { + $flattenedKVs = array(); + $args = $arguments[0]; + + foreach ($args as $k => $v) { + $flattenedKVs[] = $k; + $flattenedKVs[] = $v; + } + + return $flattenedKVs; + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php b/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php new file mode 100644 index 00000000..f98f1f7c --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/msetnx + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringSetMultiplePreserve extends StringSetMultiple +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MSETNX'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetPreserve.php b/vendor/predis/predis/src/Command/StringSetPreserve.php new file mode 100644 index 00000000..726c35c8 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetPreserve.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/setnx + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringSetPreserve extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETNX'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + return (bool) $data; + } +} diff --git a/vendor/predis/predis/src/Command/StringSetRange.php b/vendor/predis/predis/src/Command/StringSetRange.php new file mode 100644 index 00000000..4d9389f4 --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSetRange.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/setrange + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringSetRange extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SETRANGE'; + } +} diff --git a/vendor/predis/predis/src/Command/StringStrlen.php b/vendor/predis/predis/src/Command/StringStrlen.php new file mode 100644 index 00000000..10f492fd --- /dev/null +++ b/vendor/predis/predis/src/Command/StringStrlen.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/strlen + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringStrlen extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'STRLEN'; + } +} diff --git a/vendor/predis/predis/src/Command/StringSubstr.php b/vendor/predis/predis/src/Command/StringSubstr.php new file mode 100644 index 00000000..3aab7ade --- /dev/null +++ b/vendor/predis/predis/src/Command/StringSubstr.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/substr + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StringSubstr extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'SUBSTR'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionDiscard.php b/vendor/predis/predis/src/Command/TransactionDiscard.php new file mode 100644 index 00000000..44aca2b1 --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionDiscard.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/discard + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class TransactionDiscard extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'DISCARD'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionExec.php b/vendor/predis/predis/src/Command/TransactionExec.php new file mode 100644 index 00000000..dbd81aae --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionExec.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/exec + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class TransactionExec extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'EXEC'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionMulti.php b/vendor/predis/predis/src/Command/TransactionMulti.php new file mode 100644 index 00000000..673bf55d --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionMulti.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/multi + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class TransactionMulti extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'MULTI'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionUnwatch.php b/vendor/predis/predis/src/Command/TransactionUnwatch.php new file mode 100644 index 00000000..79255544 --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionUnwatch.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/unwatch + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class TransactionUnwatch extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'UNWATCH'; + } +} diff --git a/vendor/predis/predis/src/Command/TransactionWatch.php b/vendor/predis/predis/src/Command/TransactionWatch.php new file mode 100644 index 00000000..d3607801 --- /dev/null +++ b/vendor/predis/predis/src/Command/TransactionWatch.php @@ -0,0 +1,40 @@ +<?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; + +/** + * @link http://redis.io/commands/watch + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class TransactionWatch extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'WATCH'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (isset($arguments[0]) && is_array($arguments[0])) { + return $arguments[0]; + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetAdd.php b/vendor/predis/predis/src/Command/ZSetAdd.php new file mode 100644 index 00000000..55e4729e --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetAdd.php @@ -0,0 +1,43 @@ +<?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; + +/** + * @link http://redis.io/commands/zadd + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetAdd extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZADD'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (is_array(end($arguments))) { + foreach (array_pop($arguments) as $member => $score) { + $arguments[] = $score; + $arguments[] = $member; + } + } + + return $arguments; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetCardinality.php b/vendor/predis/predis/src/Command/ZSetCardinality.php new file mode 100644 index 00000000..10332009 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetCardinality.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zcard + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetCardinality extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZCARD'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetCount.php b/vendor/predis/predis/src/Command/ZSetCount.php new file mode 100644 index 00000000..918bd2b8 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetCount.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zcount + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetCount extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZCOUNT'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetIncrementBy.php b/vendor/predis/predis/src/Command/ZSetIncrementBy.php new file mode 100644 index 00000000..245a8e0f --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetIncrementBy.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zincrby + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetIncrementBy extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZINCRBY'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetIntersectionStore.php b/vendor/predis/predis/src/Command/ZSetIntersectionStore.php new file mode 100644 index 00000000..572a7a32 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetIntersectionStore.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zinterstore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetIntersectionStore extends ZSetUnionStore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZINTERSTORE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetLexCount.php b/vendor/predis/predis/src/Command/ZSetLexCount.php new file mode 100644 index 00000000..447b8eb3 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetLexCount.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zlexcount + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetLexCount extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZLEXCOUNT'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRange.php b/vendor/predis/predis/src/Command/ZSetRange.php new file mode 100644 index 00000000..ce72c7c0 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRange.php @@ -0,0 +1,105 @@ +<?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; + +/** + * @link http://redis.io/commands/zrange + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetRange extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANGE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 4) { + $lastType = gettype($arguments[3]); + + if ($lastType === 'string' && strtoupper($arguments[3]) === 'WITHSCORES') { + // Used for compatibility with older versions + $arguments[3] = array('WITHSCORES' => true); + $lastType = 'array'; + } + + if ($lastType === 'array') { + $options = $this->prepareOptions(array_pop($arguments)); + + return array_merge($arguments, $options); + } + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (!empty($opts['WITHSCORES'])) { + $finalizedOpts[] = 'WITHSCORES'; + } + + return $finalizedOpts; + } + + /** + * Checks for the presence of the WITHSCORES modifier. + * + * @return bool + */ + protected function withScores() + { + $arguments = $this->getArguments(); + + if (count($arguments) < 4) { + return false; + } + + return strtoupper($arguments[3]) === 'WITHSCORES'; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if ($this->withScores()) { + $result = array(); + + for ($i = 0; $i < count($data); ++$i) { + $result[$data[$i]] = $data[++$i]; + } + + return $result; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRangeByLex.php b/vendor/predis/predis/src/Command/ZSetRangeByLex.php new file mode 100644 index 00000000..9b2991a8 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRangeByLex.php @@ -0,0 +1,55 @@ +<?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; + +/** + * @link http://redis.io/commands/zrangebylex + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetRangeByLex extends ZSetRange +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANGEBYLEX'; + } + + /** + * {@inheritdoc} + */ + protected function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) { + $limit = array_change_key_case($opts['LIMIT'], CASE_UPPER); + + $finalizedOpts[] = 'LIMIT'; + $finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0]; + $finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1]; + } + + return $finalizedOpts; + } + + /** + * {@inheritdoc} + */ + protected function withScores() + { + return false; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRangeByScore.php b/vendor/predis/predis/src/Command/ZSetRangeByScore.php new file mode 100644 index 00000000..961a5bc2 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRangeByScore.php @@ -0,0 +1,68 @@ +<?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; + +/** + * @link http://redis.io/commands/zrangebyscore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetRangeByScore extends ZSetRange +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANGEBYSCORE'; + } + + /** + * {@inheritdoc} + */ + protected function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (isset($opts['LIMIT']) && is_array($opts['LIMIT'])) { + $limit = array_change_key_case($opts['LIMIT'], CASE_UPPER); + + $finalizedOpts[] = 'LIMIT'; + $finalizedOpts[] = isset($limit['OFFSET']) ? $limit['OFFSET'] : $limit[0]; + $finalizedOpts[] = isset($limit['COUNT']) ? $limit['COUNT'] : $limit[1]; + } + + return array_merge($finalizedOpts, parent::prepareOptions($options)); + } + + /** + * {@inheritdoc} + */ + protected function withScores() + { + $arguments = $this->getArguments(); + + for ($i = 3; $i < count($arguments); ++$i) { + switch (strtoupper($arguments[$i])) { + case 'WITHSCORES': + return true; + + case 'LIMIT': + $i += 2; + break; + } + } + + return false; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRank.php b/vendor/predis/predis/src/Command/ZSetRank.php new file mode 100644 index 00000000..d0c9c536 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRank.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zrank + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetRank extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZRANK'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRemove.php b/vendor/predis/predis/src/Command/ZSetRemove.php new file mode 100644 index 00000000..cd8ada05 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRemove.php @@ -0,0 +1,36 @@ +<?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; + +/** + * @link http://redis.io/commands/zrem + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetRemove extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREM'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + return self::normalizeVariadic($arguments); + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRemoveRangeByLex.php b/vendor/predis/predis/src/Command/ZSetRemoveRangeByLex.php new file mode 100644 index 00000000..9ea2d9e5 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRemoveRangeByLex.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zremrangebylex + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetRemoveRangeByLex extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREMRANGEBYLEX'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRemoveRangeByRank.php b/vendor/predis/predis/src/Command/ZSetRemoveRangeByRank.php new file mode 100644 index 00000000..89cd5baf --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRemoveRangeByRank.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zremrangebyrank + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetRemoveRangeByRank extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREMRANGEBYRANK'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetRemoveRangeByScore.php b/vendor/predis/predis/src/Command/ZSetRemoveRangeByScore.php new file mode 100644 index 00000000..a7c30814 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetRemoveRangeByScore.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zremrangebyscore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetRemoveRangeByScore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREMRANGEBYSCORE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetReverseRange.php b/vendor/predis/predis/src/Command/ZSetReverseRange.php new file mode 100644 index 00000000..6a46a7a5 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetReverseRange.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zrevrange + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetReverseRange extends ZSetRange +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANGE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetReverseRangeByLex.php b/vendor/predis/predis/src/Command/ZSetReverseRangeByLex.php new file mode 100644 index 00000000..5dd611d9 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetReverseRangeByLex.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zrevrangebylex + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetReverseRangeByLex extends ZSetRangeByLex +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANGEBYLEX'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetReverseRangeByScore.php b/vendor/predis/predis/src/Command/ZSetReverseRangeByScore.php new file mode 100644 index 00000000..1078eb72 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetReverseRangeByScore.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zrevrangebyscore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetReverseRangeByScore extends ZSetRangeByScore +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANGEBYSCORE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetReverseRank.php b/vendor/predis/predis/src/Command/ZSetReverseRank.php new file mode 100644 index 00000000..33fb8158 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetReverseRank.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zrevrank + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetReverseRank extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZREVRANK'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetScan.php b/vendor/predis/predis/src/Command/ZSetScan.php new file mode 100644 index 00000000..1dc2352e --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetScan.php @@ -0,0 +1,85 @@ +<?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; + +/** + * @link http://redis.io/commands/zscan + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetScan extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZSCAN'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + if (count($arguments) === 3 && is_array($arguments[2])) { + $options = $this->prepareOptions(array_pop($arguments)); + $arguments = array_merge($arguments, $options); + } + + return $arguments; + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + protected function prepareOptions($options) + { + $options = array_change_key_case($options, CASE_UPPER); + $normalized = array(); + + if (!empty($options['MATCH'])) { + $normalized[] = 'MATCH'; + $normalized[] = $options['MATCH']; + } + + if (!empty($options['COUNT'])) { + $normalized[] = 'COUNT'; + $normalized[] = $options['COUNT']; + } + + return $normalized; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($data) + { + if (is_array($data)) { + $members = $data[1]; + $result = array(); + + for ($i = 0; $i < count($members); ++$i) { + $result[$members[$i]] = (float) $members[++$i]; + } + + $data[1] = $result; + } + + return $data; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetScore.php b/vendor/predis/predis/src/Command/ZSetScore.php new file mode 100644 index 00000000..2e7fce8e --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetScore.php @@ -0,0 +1,28 @@ +<?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; + +/** + * @link http://redis.io/commands/zscore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetScore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZSCORE'; + } +} diff --git a/vendor/predis/predis/src/Command/ZSetUnionStore.php b/vendor/predis/predis/src/Command/ZSetUnionStore.php new file mode 100644 index 00000000..befc5ce7 --- /dev/null +++ b/vendor/predis/predis/src/Command/ZSetUnionStore.php @@ -0,0 +1,78 @@ +<?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; + +/** + * @link http://redis.io/commands/zunionstore + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ZSetUnionStore extends Command +{ + /** + * {@inheritdoc} + */ + public function getId() + { + return 'ZUNIONSTORE'; + } + + /** + * {@inheritdoc} + */ + protected function filterArguments(array $arguments) + { + $options = array(); + $argc = count($arguments); + + if ($argc > 2 && is_array($arguments[$argc - 1])) { + $options = $this->prepareOptions(array_pop($arguments)); + } + + if (is_array($arguments[1])) { + $arguments = array_merge( + array($arguments[0], count($arguments[1])), + $arguments[1] + ); + } + + return array_merge($arguments, $options); + } + + /** + * Returns a list of options and modifiers compatible with Redis. + * + * @param array $options List of options. + * + * @return array + */ + private function prepareOptions($options) + { + $opts = array_change_key_case($options, CASE_UPPER); + $finalizedOpts = array(); + + if (isset($opts['WEIGHTS']) && is_array($opts['WEIGHTS'])) { + $finalizedOpts[] = 'WEIGHTS'; + + foreach ($opts['WEIGHTS'] as $weight) { + $finalizedOpts[] = $weight; + } + } + + if (isset($opts['AGGREGATE'])) { + $finalizedOpts[] = 'AGGREGATE'; + $finalizedOpts[] = $opts['AGGREGATE']; + } + + return $finalizedOpts; + } +} diff --git a/vendor/predis/predis/src/CommunicationException.php b/vendor/predis/predis/src/CommunicationException.php new file mode 100644 index 00000000..13fe357c --- /dev/null +++ b/vendor/predis/predis/src/CommunicationException.php @@ -0,0 +1,80 @@ +<?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; + +use Predis\Connection\NodeConnectionInterface; + +/** + * Base exception class for network-related errors. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +abstract class CommunicationException extends PredisException +{ + private $connection; + + /** + * @param NodeConnectionInterface $connection Connection that generated the exception. + * @param string $message Error message. + * @param int $code Error code. + * @param \Exception $innerException Inner exception for wrapping the original error. + */ + public function __construct( + NodeConnectionInterface $connection, + $message = null, + $code = null, + \Exception $innerException = null + ) { + parent::__construct($message, $code, $innerException); + $this->connection = $connection; + } + + /** + * Gets the connection that generated the exception. + * + * @return NodeConnectionInterface + */ + public function getConnection() + { + return $this->connection; + } + + /** + * Indicates if the receiver should reset the underlying connection. + * + * @return bool + */ + public function shouldResetConnection() + { + return true; + } + + /** + * Helper method to handle exceptions generated by a connection object. + * + * @param CommunicationException $exception Exception. + * + * @throws CommunicationException + */ + public static function handle(CommunicationException $exception) + { + if ($exception->shouldResetConnection()) { + $connection = $exception->getConnection(); + + if ($connection->isConnected()) { + $connection->disconnect(); + } + } + + throw $exception; + } +} diff --git a/vendor/predis/predis/src/Configuration/ClusterOption.php b/vendor/predis/predis/src/Configuration/ClusterOption.php new file mode 100644 index 00000000..69e36de7 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ClusterOption.php @@ -0,0 +1,76 @@ +<?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\Configuration; + +use Predis\Connection\Aggregate\ClusterInterface; +use Predis\Connection\Aggregate\PredisCluster; +use Predis\Connection\Aggregate\RedisCluster; + +/** + * Configures an aggregate connection used for clustering + * multiple Redis nodes using various implementations with + * different algorithms or strategies. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ClusterOption implements OptionInterface +{ + /** + * Creates a new cluster connection from on a known descriptive name. + * + * @param OptionsInterface $options Instance of the client options. + * @param string $id Descriptive identifier of the cluster type (`predis`, `redis-cluster`) + * + * @return ClusterInterface|null + */ + protected function createByDescription(OptionsInterface $options, $id) + { + switch ($id) { + case 'predis': + case 'predis-cluster': + return new PredisCluster(); + + case 'redis': + case 'redis-cluster': + return new RedisCluster($options->connections); + + default: + return; + } + } + + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if (is_string($value)) { + $value = $this->createByDescription($options, $value); + } + + if (!$value instanceof ClusterInterface) { + throw new \InvalidArgumentException( + "An instance of type 'Predis\Connection\Aggregate\ClusterInterface' was expected." + ); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + return new PredisCluster(); + } +} diff --git a/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php b/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php new file mode 100644 index 00000000..ba38df96 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php @@ -0,0 +1,54 @@ +<?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\Configuration; + +use Predis\Connection\Factory; +use Predis\Connection\FactoryInterface; + +/** + * Configures a connection factory used by the client to create new connection + * instances for single Redis nodes. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ConnectionFactoryOption implements OptionInterface +{ + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if ($value instanceof FactoryInterface) { + return $value; + } elseif (is_array($value)) { + $factory = $this->getDefault($options); + + foreach ($value as $scheme => $initializer) { + $factory->define($scheme, $initializer); + } + + return $factory; + } else { + throw new \InvalidArgumentException( + 'Invalid value provided for the connections option.' + ); + } + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + return new Factory(); + } +} diff --git a/vendor/predis/predis/src/Configuration/ExceptionsOption.php b/vendor/predis/predis/src/Configuration/ExceptionsOption.php new file mode 100644 index 00000000..337733e4 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ExceptionsOption.php @@ -0,0 +1,37 @@ +<?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\Configuration; + +/** + * Configures whether consumers (such as the client) should throw exceptions on + * Redis errors (-ERR responses) or just return instances of error responses. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ExceptionsOption implements OptionInterface +{ + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + return filter_var($value, FILTER_VALIDATE_BOOLEAN); + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + return true; + } +} diff --git a/vendor/predis/predis/src/Configuration/OptionInterface.php b/vendor/predis/predis/src/Configuration/OptionInterface.php new file mode 100644 index 00000000..b31e0c98 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/OptionInterface.php @@ -0,0 +1,40 @@ +<?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\Configuration; + +/** + * Defines an handler used by Predis\Configuration\Options to filter, validate + * or return default values for a given option. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface OptionInterface +{ + /** + * Filters and validates the passed value. + * + * @param OptionsInterface $options Options container. + * @param mixed $value Input value. + * + * @return mixed + */ + public function filter(OptionsInterface $options, $value); + + /** + * Returns the default value for the option. + * + * @param OptionsInterface $options Options container. + * + * @return mixed + */ + public function getDefault(OptionsInterface $options); +} diff --git a/vendor/predis/predis/src/Configuration/Options.php b/vendor/predis/predis/src/Configuration/Options.php new file mode 100644 index 00000000..c17dd546 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/Options.php @@ -0,0 +1,122 @@ +<?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\Configuration; + +/** + * Manages Predis options with filtering, conversion and lazy initialization of + * values using a mini-DI container approach. + * + * {@inheritdoc} + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Options implements OptionsInterface +{ + protected $input; + protected $options; + protected $handlers; + + /** + * @param array $options Array of options with their values + */ + public function __construct(array $options = array()) + { + $this->input = $options; + $this->options = array(); + $this->handlers = $this->getHandlers(); + } + + /** + * Ensures that the default options are initialized. + * + * @return array + */ + protected function getHandlers() + { + return array( + 'cluster' => 'Predis\Configuration\ClusterOption', + 'connections' => 'Predis\Configuration\ConnectionFactoryOption', + 'exceptions' => 'Predis\Configuration\ExceptionsOption', + 'prefix' => 'Predis\Configuration\PrefixOption', + 'profile' => 'Predis\Configuration\ProfileOption', + 'replication' => 'Predis\Configuration\ReplicationOption', + ); + } + + /** + * {@inheritdoc} + */ + public function getDefault($option) + { + if (isset($this->handlers[$option])) { + $handler = $this->handlers[$option]; + $handler = new $handler(); + + return $handler->getDefault($this); + } + } + + /** + * {@inheritdoc} + */ + public function defined($option) + { + return + array_key_exists($option, $this->options) || + array_key_exists($option, $this->input) + ; + } + + /** + * {@inheritdoc} + */ + public function __isset($option) + { + return ( + array_key_exists($option, $this->options) || + array_key_exists($option, $this->input) + ) && $this->__get($option) !== null; + } + + /** + * {@inheritdoc} + */ + public function __get($option) + { + if (isset($this->options[$option]) || array_key_exists($option, $this->options)) { + return $this->options[$option]; + } + + if (isset($this->input[$option]) || array_key_exists($option, $this->input)) { + $value = $this->input[$option]; + unset($this->input[$option]); + + if (is_object($value) && method_exists($value, '__invoke')) { + $value = $value($this, $option); + } + + if (isset($this->handlers[$option])) { + $handler = $this->handlers[$option]; + $handler = new $handler(); + $value = $handler->filter($this, $value); + } + + return $this->options[$option] = $value; + } + + if (isset($this->handlers[$option])) { + return $this->options[$option] = $this->getDefault($option); + } + + return; + } +} diff --git a/vendor/predis/predis/src/Configuration/OptionsInterface.php b/vendor/predis/predis/src/Configuration/OptionsInterface.php new file mode 100644 index 00000000..f8116470 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/OptionsInterface.php @@ -0,0 +1,64 @@ +<?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\Configuration; + +/** + * Interface defining a container for client options. + * + * @property-read mixed aggregate Custom connection aggregator. + * @property-read mixed cluster Aggregate connection for clustering. + * @property-read mixed connections Connection factory. + * @property-read mixed exceptions Toggles exceptions in client for -ERR responses. + * @property-read mixed prefix Key prefixing strategy using the given prefix. + * @property-read mixed profile Server profile. + * @property-read mixed replication Aggregate connection for replication. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface OptionsInterface +{ + /** + * Returns the default value for the given option. + * + * @param string $option Name of the option. + * + * @return mixed|null + */ + public function getDefault($option); + + /** + * Checks if the given option has been set by the user upon initialization. + * + * @param string $option Name of the option. + * + * @return bool + */ + public function defined($option); + + /** + * Checks if the given option has been set and does not evaluate to NULL. + * + * @param string $option Name of the option. + * + * @return bool + */ + public function __isset($option); + + /** + * Returns the value of the given option. + * + * @param string $option Name of the option. + * + * @return mixed|null + */ + public function __get($option); +} diff --git a/vendor/predis/predis/src/Configuration/PrefixOption.php b/vendor/predis/predis/src/Configuration/PrefixOption.php new file mode 100644 index 00000000..5827cdc3 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/PrefixOption.php @@ -0,0 +1,44 @@ +<?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\Configuration; + +use Predis\Command\Processor\KeyPrefixProcessor; +use Predis\Command\Processor\ProcessorInterface; + +/** + * Configures a command processor that apply the specified prefix string to a + * series of Redis commands considered prefixable. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PrefixOption implements OptionInterface +{ + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if ($value instanceof ProcessorInterface) { + return $value; + } + + return new KeyPrefixProcessor($value); + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + // NOOP + } +} diff --git a/vendor/predis/predis/src/Configuration/ProfileOption.php b/vendor/predis/predis/src/Configuration/ProfileOption.php new file mode 100644 index 00000000..864936e0 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ProfileOption.php @@ -0,0 +1,69 @@ +<?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\Configuration; + +use Predis\Profile\Factory; +use Predis\Profile\ProfileInterface; +use Predis\Profile\RedisProfile; + +/** + * Configures the server profile to be used by the client to create command + * instances depending on the specified version of the Redis server. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ProfileOption implements OptionInterface +{ + /** + * Sets the commands processors that need to be applied to the profile. + * + * @param OptionsInterface $options Client options. + * @param ProfileInterface $profile Server profile. + */ + protected function setProcessors(OptionsInterface $options, ProfileInterface $profile) + { + if (isset($options->prefix) && $profile instanceof RedisProfile) { + // NOTE: directly using __get('prefix') is actually a workaround for + // HHVM 2.3.0. It's correct and respects the options interface, it's + // just ugly. We will remove this hack when HHVM will fix re-entrant + // calls to __get() once and for all. + + $profile->setProcessor($options->__get('prefix')); + } + } + + /** + * {@inheritdoc} + */ + public function filter(OptionsInterface $options, $value) + { + if (is_string($value)) { + $value = Factory::get($value); + $this->setProcessors($options, $value); + } elseif (!$value instanceof ProfileInterface) { + throw new \InvalidArgumentException('Invalid value for the profile option.'); + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + $profile = Factory::getDefault(); + $this->setProcessors($options, $profile); + + return $profile; + } +} diff --git a/vendor/predis/predis/src/Configuration/ReplicationOption.php b/vendor/predis/predis/src/Configuration/ReplicationOption.php new file mode 100644 index 00000000..fd2c8108 --- /dev/null +++ b/vendor/predis/predis/src/Configuration/ReplicationOption.php @@ -0,0 +1,61 @@ +<?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\Configuration; + +use Predis\Connection\Aggregate\MasterSlaveReplication; +use Predis\Connection\Aggregate\ReplicationInterface; + +/** + * Configures an aggregate connection used for master/slave replication among + * multiple Redis nodes. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ReplicationOption implements OptionInterface +{ + /** + * {@inheritdoc} + * + * @todo There's more code than needed due to a bug in filter_var() as + * discussed here https://bugs.php.net/bug.php?id=49510 and different + * behaviours when encountering NULL values on PHP 5.3. + */ + public function filter(OptionsInterface $options, $value) + { + if ($value instanceof ReplicationInterface) { + return $value; + } + + if (is_bool($value) || $value === null) { + return $value ? $this->getDefault($options) : null; + } + + if ( + !is_object($value) && + null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) + ) { + return $asbool ? $this->getDefault($options) : null; + } + + throw new \InvalidArgumentException( + "An instance of type 'Predis\Connection\Aggregate\ReplicationInterface' was expected." + ); + } + + /** + * {@inheritdoc} + */ + public function getDefault(OptionsInterface $options) + { + return new MasterSlaveReplication(); + } +} diff --git a/vendor/predis/predis/src/Connection/AbstractConnection.php b/vendor/predis/predis/src/Connection/AbstractConnection.php new file mode 100644 index 00000000..029a337e --- /dev/null +++ b/vendor/predis/predis/src/Connection/AbstractConnection.php @@ -0,0 +1,239 @@ +<?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\Connection; + +use Predis\Command\CommandInterface; +use Predis\CommunicationException; +use Predis\Protocol\ProtocolException; + +/** + * Base class with the common logic used by connection classes to communicate + * with Redis. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +abstract class AbstractConnection implements NodeConnectionInterface +{ + private $resource; + private $cachedId; + + protected $parameters; + protected $initCommands = array(); + + /** + * @param ParametersInterface $parameters Initialization parameters for the connection. + */ + public function __construct(ParametersInterface $parameters) + { + $this->parameters = $this->assertParameters($parameters); + } + + /** + * Disconnects from the server and destroys the underlying resource when + * PHP's garbage collector kicks in. + */ + public function __destruct() + { + $this->disconnect(); + } + + /** + * Checks some of the parameters used to initialize the connection. + * + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @throws \InvalidArgumentException + * + * @return ParametersInterface + */ + protected function assertParameters(ParametersInterface $parameters) + { + switch ($parameters->scheme) { + case 'tcp': + case 'redis': + case 'unix': + break; + + default: + throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); + } + + return $parameters; + } + + /** + * Creates the underlying resource used to communicate with Redis. + * + * @return mixed + */ + abstract protected function createResource(); + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return isset($this->resource); + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if (!$this->isConnected()) { + $this->resource = $this->createResource(); + + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + unset($this->resource); + } + + /** + * {@inheritdoc} + */ + public function addConnectCommand(CommandInterface $command) + { + $this->initCommands[] = $command; + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $this->writeRequest($command); + + return $this->readResponse($command); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->read(); + } + + /** + * Helper method that returns an exception message augmented with useful + * details from the connection parameters. + * + * @param string $message Error message. + * + * @return string + */ + private function createExceptionMessage($message) + { + $parameters = $this->parameters; + + if ($parameters->scheme === 'unix') { + return "$message [$parameters->scheme:$parameters->path]"; + } + + if (filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + return "$message [$parameters->scheme://[$parameters->host]:$parameters->port]"; + } + + return "$message [$parameters->scheme://$parameters->host:$parameters->port]"; + } + + /** + * Helper method to handle connection errors. + * + * @param string $message Error message. + * @param int $code Error code. + */ + protected function onConnectionError($message, $code = null) + { + CommunicationException::handle( + new ConnectionException($this, static::createExceptionMessage($message), $code) + ); + } + + /** + * Helper method to handle protocol errors. + * + * @param string $message Error message. + */ + protected function onProtocolError($message) + { + CommunicationException::handle( + new ProtocolException($this, static::createExceptionMessage($message)) + ); + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + if (isset($this->resource)) { + return $this->resource; + } + + $this->connect(); + + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Gets an identifier for the connection. + * + * @return string + */ + protected function getIdentifier() + { + if ($this->parameters->scheme === 'unix') { + return $this->parameters->path; + } + + return "{$this->parameters->host}:{$this->parameters->port}"; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + if (!isset($this->cachedId)) { + $this->cachedId = $this->getIdentifier(); + } + + return $this->cachedId; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('parameters', 'initCommands'); + } +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/ClusterInterface.php b/vendor/predis/predis/src/Connection/Aggregate/ClusterInterface.php new file mode 100644 index 00000000..af0f5aab --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/ClusterInterface.php @@ -0,0 +1,24 @@ +<?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\Connection\Aggregate; + +use Predis\Connection\AggregateConnectionInterface; + +/** + * Defines a cluster of Redis servers formed by aggregating multiple connection + * instances to single Redis nodes. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ClusterInterface extends AggregateConnectionInterface +{ +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php b/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php new file mode 100644 index 00000000..ca99aa8e --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php @@ -0,0 +1,281 @@ +<?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\Connection\Aggregate; + +use Predis\Command\CommandInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\Replication\ReplicationStrategy; + +/** + * Aggregate connection handling replication of Redis nodes configured in a + * single master / multiple slaves setup. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class MasterSlaveReplication implements ReplicationInterface +{ + /** + * @var ReplicationStrategy + */ + protected $strategy; + + /** + * @var NodeConnectionInterface + */ + protected $master; + + /** + * @var NodeConnectionInterface[] + */ + protected $slaves = array(); + + /** + * @var NodeConnectionInterface + */ + protected $current; + + /** + * {@inheritdoc} + */ + public function __construct(ReplicationStrategy $strategy = null) + { + $this->strategy = $strategy ?: new ReplicationStrategy(); + } + + /** + * Checks if one master and at least one slave have been defined. + */ + protected function check() + { + if (!isset($this->master) || !$this->slaves) { + throw new \RuntimeException('Replication needs one master and at least one slave.'); + } + } + + /** + * Resets the connection state. + */ + protected function reset() + { + $this->current = null; + } + + /** + * {@inheritdoc} + */ + public function add(NodeConnectionInterface $connection) + { + $alias = $connection->getParameters()->alias; + + if ($alias === 'master') { + $this->master = $connection; + } else { + $this->slaves[$alias ?: count($this->slaves)] = $connection; + } + + $this->reset(); + } + + /** + * {@inheritdoc} + */ + public function remove(NodeConnectionInterface $connection) + { + if ($connection->getParameters()->alias === 'master') { + $this->master = null; + $this->reset(); + + return true; + } else { + if (($id = array_search($connection, $this->slaves, true)) !== false) { + unset($this->slaves[$id]); + $this->reset(); + + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + if ($this->current === null) { + $this->check(); + $this->current = $this->strategy->isReadOperation($command) + ? $this->pickSlave() + : $this->master; + + return $this->current; + } + + if ($this->current === $this->master) { + return $this->current; + } + + if (!$this->strategy->isReadOperation($command)) { + $this->current = $this->master; + } + + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionId) + { + if ($connectionId === 'master') { + return $this->master; + } + + if (isset($this->slaves[$connectionId])) { + return $this->slaves[$connectionId]; + } + + return; + } + + /** + * {@inheritdoc} + */ + public function switchTo($connection) + { + $this->check(); + + if (!$connection instanceof NodeConnectionInterface) { + $connection = $this->getConnectionById($connection); + } + + if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) { + throw new \InvalidArgumentException('Invalid connection or connection not found.'); + } + + $this->current = $connection; + } + + /** + * {@inheritdoc} + */ + public function getCurrent() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function getMaster() + { + return $this->master; + } + + /** + * {@inheritdoc} + */ + public function getSlaves() + { + return array_values($this->slaves); + } + + /** + * Returns the underlying replication strategy. + * + * @return ReplicationStrategy + */ + public function getReplicationStrategy() + { + return $this->strategy; + } + + /** + * Returns a random slave. + * + * @return NodeConnectionInterface + */ + protected function pickSlave() + { + if ($this->slaves) { + return $this->slaves[array_rand($this->slaves)]; + } + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return $this->current ? $this->current->isConnected() : false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if ($this->current === null) { + $this->check(); + $this->current = $this->pickSlave(); + } + + $this->current->connect(); + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->master) { + $this->master->disconnect(); + } + + foreach ($this->slaves as $connection) { + $connection->disconnect(); + } + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->getConnection($command)->writeRequest($command); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->getConnection($command)->readResponse($command); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + return $this->getConnection($command)->executeCommand($command); + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('master', 'slaves', 'strategy'); + } +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php b/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php new file mode 100644 index 00000000..33f98bf2 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php @@ -0,0 +1,235 @@ +<?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\Connection\Aggregate; + +use Predis\Cluster\PredisStrategy; +use Predis\Cluster\StrategyInterface; +use Predis\Command\CommandInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\NotSupportedException; + +/** + * Abstraction for a cluster of aggregate connections to various Redis servers + * implementing client-side sharding based on pluggable distribution strategies. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + * + * @todo Add the ability to remove connections from pool. + */ +class PredisCluster implements ClusterInterface, \IteratorAggregate, \Countable +{ + private $pool; + private $strategy; + private $distributor; + + /** + * @param StrategyInterface $strategy Optional cluster strategy. + */ + public function __construct(StrategyInterface $strategy = null) + { + $this->pool = array(); + $this->strategy = $strategy ?: new PredisStrategy(); + $this->distributor = $this->strategy->getDistributor(); + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + foreach ($this->pool as $connection) { + if ($connection->isConnected()) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + foreach ($this->pool as $connection) { + $connection->connect(); + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + foreach ($this->pool as $connection) { + $connection->disconnect(); + } + } + + /** + * {@inheritdoc} + */ + public function add(NodeConnectionInterface $connection) + { + $parameters = $connection->getParameters(); + + if (isset($parameters->alias)) { + $this->pool[$parameters->alias] = $connection; + } else { + $this->pool[] = $connection; + } + + $weight = isset($parameters->weight) ? $parameters->weight : null; + $this->distributor->add($connection, $weight); + } + + /** + * {@inheritdoc} + */ + public function remove(NodeConnectionInterface $connection) + { + if (($id = array_search($connection, $this->pool, true)) !== false) { + unset($this->pool[$id]); + $this->distributor->remove($connection); + + return true; + } + + return false; + } + + /** + * Removes a connection instance using its alias or index. + * + * @param string $connectionID Alias or index of a connection. + * + * @return bool Returns true if the connection was in the pool. + */ + public function removeById($connectionID) + { + if ($connection = $this->getConnectionById($connectionID)) { + return $this->remove($connection); + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + $slot = $this->strategy->getSlot($command); + + if (!isset($slot)) { + throw new NotSupportedException( + "Cannot use '{$command->getId()}' over clusters of connections." + ); + } + + $node = $this->distributor->getBySlot($slot); + + return $node; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionID) + { + return isset($this->pool[$connectionID]) ? $this->pool[$connectionID] : null; + } + + /** + * Retrieves a connection instance from the cluster using a key. + * + * @param string $key Key string. + * + * @return NodeConnectionInterface + */ + public function getConnectionByKey($key) + { + $hash = $this->strategy->getSlotByKey($key); + $node = $this->distributor->getBySlot($hash); + + return $node; + } + + /** + * Returns the underlying command hash strategy used to hash commands by + * using keys found in their arguments. + * + * @return StrategyInterface + */ + public function getClusterStrategy() + { + return $this->strategy; + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->pool); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator($this->pool); + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->getConnection($command)->writeRequest($command); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->getConnection($command)->readResponse($command); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + return $this->getConnection($command)->executeCommand($command); + } + + /** + * Executes the specified Redis command on all the nodes of a cluster. + * + * @param CommandInterface $command A Redis command. + * + * @return array + */ + public function executeCommandOnNodes(CommandInterface $command) + { + $responses = array(); + + foreach ($this->pool as $connection) { + $responses[] = $connection->executeCommand($command); + } + + return $responses; + } +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php b/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php new file mode 100644 index 00000000..e8c8e2c6 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php @@ -0,0 +1,676 @@ +<?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\Connection\Aggregate; + +use Predis\ClientException; +use Predis\Cluster\RedisStrategy as RedisClusterStrategy; +use Predis\Cluster\StrategyInterface; +use Predis\Command\CommandInterface; +use Predis\Command\RawCommand; +use Predis\Connection\ConnectionException; +use Predis\Connection\FactoryInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\NotSupportedException; +use Predis\Response\ErrorInterface as ErrorResponseInterface; + +/** + * Abstraction for a Redis-backed cluster of nodes (Redis >= 3.0.0). + * + * This connection backend offers smart support for redis-cluster by handling + * automatic slots map (re)generation upon -MOVED or -ASK responses returned by + * Redis when redirecting a client to a different node. + * + * The cluster can be pre-initialized using only a subset of the actual nodes in + * the cluster, Predis will do the rest by adjusting the slots map and creating + * the missing underlying connection instances on the fly. + * + * It is possible to pre-associate connections to a slots range with the "slots" + * parameter in the form "$first-$last". This can greatly reduce runtime node + * guessing and redirections. + * + * It is also possible to ask for the full and updated slots map directly to one + * of the nodes and optionally enable such a behaviour upon -MOVED redirections. + * Asking for the cluster configuration to Redis is actually done by issuing a + * CLUSTER SLOTS command to a random node in the pool. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable +{ + private $useClusterSlots = true; + private $defaultParameters = array(); + private $pool = array(); + private $slots = array(); + private $slotsMap; + private $strategy; + private $connections; + private $retryLimit = 5; + + /** + * @param FactoryInterface $connections Optional connection factory. + * @param StrategyInterface $strategy Optional cluster strategy. + */ + public function __construct( + FactoryInterface $connections, + StrategyInterface $strategy = null + ) { + $this->connections = $connections; + $this->strategy = $strategy ?: new RedisClusterStrategy(); + } + + /** + * Sets the maximum number of retries for commands upon server failure. + * + * -1 = unlimited retry attempts + * 0 = no retry attempts (fails immediatly) + * n = fail only after n retry attempts + * + * @param int $retry Number of retry attempts. + */ + public function setRetryLimit($retry) + { + $this->retryLimit = (int) $retry; + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + foreach ($this->pool as $connection) { + if ($connection->isConnected()) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if ($connection = $this->getRandomConnection()) { + $connection->connect(); + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + foreach ($this->pool as $connection) { + $connection->disconnect(); + } + } + + /** + * {@inheritdoc} + */ + public function add(NodeConnectionInterface $connection) + { + $this->pool[(string) $connection] = $connection; + unset($this->slotsMap); + } + + /** + * {@inheritdoc} + */ + public function remove(NodeConnectionInterface $connection) + { + if (false !== $id = array_search($connection, $this->pool, true)) { + unset( + $this->pool[$id], + $this->slotsMap + ); + + $this->slots = array_diff($this->slots, array($connection)); + + return true; + } + + return false; + } + + /** + * Removes a connection instance by using its identifier. + * + * @param string $connectionID Connection identifier. + * + * @return bool True if the connection was in the pool. + */ + public function removeById($connectionID) + { + if (isset($this->pool[$connectionID])) { + unset( + $this->pool[$connectionID], + $this->slotsMap + ); + + return true; + } + + return false; + } + + /** + * Generates the current slots map by guessing the cluster configuration out + * of the connection parameters of the connections in the pool. + * + * Generation is based on the same algorithm used by Redis to generate the + * cluster, so it is most effective when all of the connections supplied on + * initialization have the "slots" parameter properly set accordingly to the + * current cluster configuration. + */ + public function buildSlotsMap() + { + $this->slotsMap = array(); + + foreach ($this->pool as $connectionID => $connection) { + $parameters = $connection->getParameters(); + + if (!isset($parameters->slots)) { + continue; + } + + foreach (explode(',', $parameters->slots) as $slotRange) { + $slots = explode('-', $slotRange, 2); + + if (!isset($slots[1])) { + $slots[1] = $slots[0]; + } + + $this->setSlots($slots[0], $slots[1], $connectionID); + } + } + } + + /** + * Queries the specified node of the cluster to fetch the updated slots map. + * + * When the connection fails, this method tries to execute the same command + * on a different connection picked at random from the pool of known nodes, + * up until the retry limit is reached. + * + * @param NodeConnectionInterface $connection Connection to a node of the cluster. + * + * @return mixed + */ + private function queryClusterNodeForSlotsMap(NodeConnectionInterface $connection) + { + $retries = 0; + $command = RawCommand::create('CLUSTER', 'SLOTS'); + + RETRY_COMMAND: { + try { + $response = $connection->executeCommand($command); + } catch (ConnectionException $exception) { + $connection = $exception->getConnection(); + $connection->disconnect(); + + $this->remove($connection); + + if ($retries === $this->retryLimit) { + throw $exception; + } + + if (!$connection = $this->getRandomConnection()) { + throw new ClientException('No connections left in the pool for `CLUSTER SLOTS`'); + } + + ++$retries; + goto RETRY_COMMAND; + } + } + + return $response; + } + + /** + * Generates an updated slots map fetching the cluster configuration using + * the CLUSTER SLOTS command against the specified node or a random one from + * the pool. + * + * @param NodeConnectionInterface $connection Optional connection instance. + * + * @return array + */ + public function askSlotsMap(NodeConnectionInterface $connection = null) + { + if (!$connection && !$connection = $this->getRandomConnection()) { + return array(); + } + + $this->resetSlotsMap(); + + $response = $this->queryClusterNodeForSlotsMap($connection); + + foreach ($response as $slots) { + // We only support master servers for now, so we ignore subsequent + // elements in the $slots array identifying slaves. + list($start, $end, $master) = $slots; + + if ($master[0] === '') { + $this->setSlots($start, $end, (string) $connection); + } else { + $this->setSlots($start, $end, "{$master[0]}:{$master[1]}"); + } + } + + return $this->slotsMap; + } + + /** + * Resets the slots map cache. + */ + public function resetSlotsMap() + { + $this->slotsMap = array(); + } + + /** + * Returns the current slots map for the cluster. + * + * The order of the returned $slot => $server dictionary is not guaranteed. + * + * @return array + */ + public function getSlotsMap() + { + if (!isset($this->slotsMap)) { + $this->slotsMap = array(); + } + + return $this->slotsMap; + } + + /** + * Pre-associates a connection to a slots range to avoid runtime guessing. + * + * @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 ($first < 0x0000 || $first > 0x3FFF || + $last < 0x0000 || $last > 0x3FFF || + $last < $first + ) { + throw new \OutOfBoundsException( + "Invalid slot range for $connection: [$first-$last]." + ); + } + + $slots = array_fill($first, $last - $first + 1, (string) $connection); + $this->slotsMap = $this->getSlotsMap() + $slots; + } + + /** + * Guesses the correct node associated to a given slot using a precalculated + * slots map, falling back to the same logic used by Redis to initialize a + * cluster (best-effort). + * + * @param int $slot Slot index. + * + * @return string Connection ID. + */ + protected function guessNode($slot) + { + if (!$this->pool) { + throw new ClientException('No connections available in the pool'); + } + + if (!isset($this->slotsMap)) { + $this->buildSlotsMap(); + } + + if (isset($this->slotsMap[$slot])) { + return $this->slotsMap[$slot]; + } + + $count = count($this->pool); + $index = min((int) ($slot / (int) (16384 / $count)), $count - 1); + $nodes = array_keys($this->pool); + + return $nodes[$index]; + } + + /** + * Creates a new connection instance from the given connection ID. + * + * @param string $connectionID Identifier for the connection. + * + * @return NodeConnectionInterface + */ + protected function createConnection($connectionID) + { + $separator = strrpos($connectionID, ':'); + + $parameters = array_merge($this->defaultParameters, array( + 'host' => substr($connectionID, 0, $separator), + 'port' => substr($connectionID, $separator + 1), + )); + + $connection = $this->connections->create($parameters); + + return $connection; + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + $slot = $this->strategy->getSlot($command); + + if (!isset($slot)) { + throw new NotSupportedException( + "Cannot use '{$command->getId()}' with redis-cluster." + ); + } + + if (isset($this->slots[$slot])) { + return $this->slots[$slot]; + } else { + return $this->getConnectionBySlot($slot); + } + } + + /** + * Returns the connection currently associated to a given slot. + * + * @param int $slot Slot index. + * + * @throws \OutOfBoundsException + * + * @return NodeConnectionInterface + */ + public function getConnectionBySlot($slot) + { + if ($slot < 0x0000 || $slot > 0x3FFF) { + throw new \OutOfBoundsException("Invalid slot [$slot]."); + } + + if (isset($this->slots[$slot])) { + return $this->slots[$slot]; + } + + $connectionID = $this->guessNode($slot); + + if (!$connection = $this->getConnectionById($connectionID)) { + $connection = $this->createConnection($connectionID); + $this->pool[$connectionID] = $connection; + } + + return $this->slots[$slot] = $connection; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionID) + { + if (isset($this->pool[$connectionID])) { + return $this->pool[$connectionID]; + } + } + + /** + * Returns a random connection from the pool. + * + * @return NodeConnectionInterface|null + */ + protected function getRandomConnection() + { + if ($this->pool) { + return $this->pool[array_rand($this->pool)]; + } + } + + /** + * Permanently associates the connection instance to a new slot. + * The connection is added to the connections pool if not yet included. + * + * @param NodeConnectionInterface $connection Connection instance. + * @param int $slot Target slot index. + */ + protected function move(NodeConnectionInterface $connection, $slot) + { + $this->pool[(string) $connection] = $connection; + $this->slots[(int) $slot] = $connection; + } + + /** + * Handles -ERR responses returned by Redis. + * + * @param CommandInterface $command Command that generated the -ERR response. + * @param ErrorResponseInterface $error Redis error response object. + * + * @return mixed + */ + protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $error) + { + $details = explode(' ', $error->getMessage(), 2); + + switch ($details[0]) { + case 'MOVED': + return $this->onMovedResponse($command, $details[1]); + + case 'ASK': + return $this->onAskResponse($command, $details[1]); + + default: + return $error; + } + } + + /** + * Handles -MOVED responses by executing again the command against the node + * indicated by the Redis response. + * + * @param CommandInterface $command Command that generated the -MOVED response. + * @param string $details Parameters of the -MOVED response. + * + * @return mixed + */ + protected function onMovedResponse(CommandInterface $command, $details) + { + list($slot, $connectionID) = explode(' ', $details, 2); + + if (!$connection = $this->getConnectionById($connectionID)) { + $connection = $this->createConnection($connectionID); + } + + if ($this->useClusterSlots) { + $this->askSlotsMap($connection); + } + + $this->move($connection, $slot); + $response = $this->executeCommand($command); + + return $response; + } + + /** + * Handles -ASK responses by executing again the command against the node + * indicated by the Redis response. + * + * @param CommandInterface $command Command that generated the -ASK response. + * @param string $details Parameters of the -ASK response. + * + * @return mixed + */ + protected function onAskResponse(CommandInterface $command, $details) + { + list($slot, $connectionID) = explode(' ', $details, 2); + + if (!$connection = $this->getConnectionById($connectionID)) { + $connection = $this->createConnection($connectionID); + } + + $connection->executeCommand(RawCommand::create('ASKING')); + $response = $connection->executeCommand($command); + + return $response; + } + + /** + * Ensures that a command is executed one more time on connection failure. + * + * The connection to the node that generated the error is evicted from the + * pool before trying to fetch an updated slots map from another node. If + * the new slots map points to an unreachable server the client gives up and + * throws the exception as the nodes participating in the cluster may still + * have to agree that something changed in the configuration of the cluster. + * + * @param CommandInterface $command Command instance. + * @param string $method Actual method. + * + * @return mixed + */ + private function retryCommandOnFailure(CommandInterface $command, $method) + { + $failure = false; + + RETRY_COMMAND: { + try { + $response = $this->getConnection($command)->$method($command); + } catch (ConnectionException $exception) { + $connection = $exception->getConnection(); + $connection->disconnect(); + + $this->remove($connection); + + if ($failure) { + throw $exception; + } elseif ($this->useClusterSlots) { + $this->askSlotsMap(); + } + + $failure = true; + + goto RETRY_COMMAND; + } + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + return $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $response = $this->retryCommandOnFailure($command, __FUNCTION__); + + if ($response instanceof ErrorResponseInterface) { + return $this->onErrorResponse($command, $response); + } + + return $response; + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->pool); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator(array_values($this->pool)); + } + + /** + * Returns the underlying command hash strategy used to hash commands by + * using keys found in their arguments. + * + * @return StrategyInterface + */ + public function getClusterStrategy() + { + return $this->strategy; + } + + /** + * Returns the underlying connection factory used to create new connection + * instances to Redis nodes indicated by redis-cluster. + * + * @return FactoryInterface + */ + public function getConnectionFactory() + { + return $this->connections; + } + + /** + * Enables automatic fetching of the current slots map from one of the nodes + * using the CLUSTER SLOTS command. This option is enabled by default as + * asking the current slots map to Redis upon -MOVED responses may reduce + * overhead by eliminating the trial-and-error nature of the node guessing + * procedure, mostly when targeting many keys that would end up in a lot of + * redirections. + * + * The slots map can still be manually fetched using the askSlotsMap() + * method whether or not this option is enabled. + * + * @param bool $value Enable or disable the use of CLUSTER SLOTS. + */ + public function useClusterSlots($value) + { + $this->useClusterSlots = (bool) $value; + } + + /** + * Sets a default array of connection parameters to be applied when creating + * new connection instances on the fly when they are not part of the initial + * pool supplied upon cluster initialization. + * + * These parameters are not applied to connections added to the pool using + * the add() method. + * + * @param array $parameters Array of connection parameters. + */ + public function setDefaultParameters(array $parameters) + { + $this->defaultParameters = array_merge( + $this->defaultParameters, + $parameters ?: array() + ); + } +} diff --git a/vendor/predis/predis/src/Connection/Aggregate/ReplicationInterface.php b/vendor/predis/predis/src/Connection/Aggregate/ReplicationInterface.php new file mode 100644 index 00000000..e09e8265 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/ReplicationInterface.php @@ -0,0 +1,52 @@ +<?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\Connection\Aggregate; + +use Predis\Connection\AggregateConnectionInterface; +use Predis\Connection\NodeConnectionInterface; + +/** + * Defines a group of Redis nodes in a master / slave replication setup. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ReplicationInterface extends AggregateConnectionInterface +{ + /** + * Switches the internal connection instance in use. + * + * @param string $connection Alias of a connection + */ + public function switchTo($connection); + + /** + * Returns the connection instance currently in use by the aggregate + * connection. + * + * @return NodeConnectionInterface + */ + public function getCurrent(); + + /** + * Returns the connection instance for the master Redis node. + * + * @return NodeConnectionInterface + */ + public function getMaster(); + + /** + * Returns a list of connection instances to slave nodes. + * + * @return NodeConnectionInterface + */ + public function getSlaves(); +} diff --git a/vendor/predis/predis/src/Connection/AggregateConnectionInterface.php b/vendor/predis/predis/src/Connection/AggregateConnectionInterface.php new file mode 100644 index 00000000..7eeaede7 --- /dev/null +++ b/vendor/predis/predis/src/Connection/AggregateConnectionInterface.php @@ -0,0 +1,57 @@ +<?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\Connection; + +use Predis\Command\CommandInterface; + +/** + * Defines a virtual connection composed of multiple connection instances to + * single Redis nodes. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface AggregateConnectionInterface extends ConnectionInterface +{ + /** + * Adds a connection instance to the aggregate connection. + * + * @param NodeConnectionInterface $connection Connection instance. + */ + public function add(NodeConnectionInterface $connection); + + /** + * Removes the specified connection instance from the aggregate connection. + * + * @param NodeConnectionInterface $connection Connection instance. + * + * @return bool Returns true if the connection was in the pool. + */ + public function remove(NodeConnectionInterface $connection); + + /** + * Returns the connection instance in charge for the given command. + * + * @param CommandInterface $command Command instance. + * + * @return NodeConnectionInterface + */ + public function getConnection(CommandInterface $command); + + /** + * Returns a connection instance from the aggregate connection by its alias. + * + * @param string $connectionID Connection alias. + * + * @return NodeConnectionInterface|null + */ + public function getConnectionById($connectionID); +} diff --git a/vendor/predis/predis/src/Connection/CompositeConnectionInterface.php b/vendor/predis/predis/src/Connection/CompositeConnectionInterface.php new file mode 100644 index 00000000..286e082c --- /dev/null +++ b/vendor/predis/predis/src/Connection/CompositeConnectionInterface.php @@ -0,0 +1,49 @@ +<?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\Connection; + +/** + * Defines a connection to communicate with a single Redis server that leverages + * an external protocol processor to handle pluggable protocol handlers. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface CompositeConnectionInterface extends NodeConnectionInterface +{ + /** + * Returns the protocol processor used by the connection. + */ + public function getProtocol(); + + /** + * Writes the buffer containing over the connection. + * + * @param string $buffer String buffer to be sent over the connection. + */ + public function writeBuffer($buffer); + + /** + * Reads the given number of bytes from the connection. + * + * @param int $length Number of bytes to read from the connection. + * + * @return string + */ + public function readBuffer($length); + + /** + * Reads a line from the connection. + * + * @param string + */ + public function readLine(); +} diff --git a/vendor/predis/predis/src/Connection/CompositeStreamConnection.php b/vendor/predis/predis/src/Connection/CompositeStreamConnection.php new file mode 100644 index 00000000..7a353405 --- /dev/null +++ b/vendor/predis/predis/src/Connection/CompositeStreamConnection.php @@ -0,0 +1,125 @@ +<?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\Connection; + +use Predis\Command\CommandInterface; +use Predis\Protocol\ProtocolProcessorInterface; +use Predis\Protocol\Text\ProtocolProcessor as TextProtocolProcessor; + +/** + * Connection abstraction to Redis servers based on PHP's stream that uses an + * external protocol processor defining the protocol used for the communication. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class CompositeStreamConnection extends StreamConnection implements CompositeConnectionInterface +{ + protected $protocol; + + /** + * @param ParametersInterface $parameters Initialization parameters for the connection. + * @param ProtocolProcessorInterface $protocol Protocol processor. + */ + public function __construct( + ParametersInterface $parameters, + ProtocolProcessorInterface $protocol = null + ) { + $this->parameters = $this->assertParameters($parameters); + $this->protocol = $protocol ?: new TextProtocolProcessor(); + } + + /** + * {@inheritdoc} + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * {@inheritdoc} + */ + public function writeBuffer($buffer) + { + $this->write($buffer); + } + + /** + * {@inheritdoc} + */ + public function readBuffer($length) + { + if ($length <= 0) { + throw new \InvalidArgumentException('Length parameter must be greater than 0.'); + } + + $value = ''; + $socket = $this->getResource(); + + do { + $chunk = fread($socket, $length); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading bytes from the server.'); + } + + $value .= $chunk; + } while (($length -= strlen($chunk)) > 0); + + return $value; + } + + /** + * {@inheritdoc} + */ + public function readLine() + { + $value = ''; + $socket = $this->getResource(); + + do { + $chunk = fgets($socket); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading line from the server.'); + } + + $value .= $chunk; + } while (substr($value, -2) !== "\r\n"); + + return substr($value, 0, -2); + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->protocol->write($this, $command); + } + + /** + * {@inheritdoc} + */ + public function read() + { + return $this->protocol->read($this); + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array_merge(parent::__sleep(), array('protocol')); + } +} diff --git a/vendor/predis/predis/src/Connection/ConnectionException.php b/vendor/predis/predis/src/Connection/ConnectionException.php new file mode 100644 index 00000000..ef2e9d73 --- /dev/null +++ b/vendor/predis/predis/src/Connection/ConnectionException.php @@ -0,0 +1,23 @@ +<?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\Connection; + +use Predis\CommunicationException; + +/** + * Exception class that identifies connection-related errors. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ConnectionException extends CommunicationException +{ +} diff --git a/vendor/predis/predis/src/Connection/ConnectionInterface.php b/vendor/predis/predis/src/Connection/ConnectionInterface.php new file mode 100644 index 00000000..11ace1b6 --- /dev/null +++ b/vendor/predis/predis/src/Connection/ConnectionInterface.php @@ -0,0 +1,66 @@ +<?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\Connection; + +use Predis\Command\CommandInterface; + +/** + * Defines a connection object used to communicate with one or multiple + * Redis servers. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ConnectionInterface +{ + /** + * Opens the connection to Redis. + */ + public function connect(); + + /** + * Closes the connection to Redis. + */ + public function disconnect(); + + /** + * Checks if the connection to Redis is considered open. + * + * @return bool + */ + public function isConnected(); + + /** + * Writes the request for the given command over the connection. + * + * @param CommandInterface $command Command instance. + */ + public function writeRequest(CommandInterface $command); + + /** + * Reads the response to the given command from the connection. + * + * @param CommandInterface $command Command instance. + * + * @return mixed + */ + public function readResponse(CommandInterface $command); + + /** + * Writes a request for the given command over the connection and reads back + * the response returned by Redis. + * + * @param CommandInterface $command Command instance. + * + * @return mixed + */ + public function executeCommand(CommandInterface $command); +} diff --git a/vendor/predis/predis/src/Connection/Factory.php b/vendor/predis/predis/src/Connection/Factory.php new file mode 100644 index 00000000..c2e93f88 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Factory.php @@ -0,0 +1,151 @@ +<?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\Connection; + +use Predis\Command\RawCommand; + +/** + * Standard connection factory for creating connections to Redis nodes. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Factory implements FactoryInterface +{ + protected $schemes = array( + 'tcp' => 'Predis\Connection\StreamConnection', + 'unix' => 'Predis\Connection\StreamConnection', + 'redis' => 'Predis\Connection\StreamConnection', + 'http' => 'Predis\Connection\WebdisConnection', + ); + + /** + * Checks if the provided argument represents a valid connection class + * implementing Predis\Connection\NodeConnectionInterface. Optionally, + * callable objects are used for lazy initialization of connection objects. + * + * @param mixed $initializer FQN of a connection class or a callable for lazy initialization. + * + * @throws \InvalidArgumentException + * + * @return mixed + */ + protected function checkInitializer($initializer) + { + if (is_callable($initializer)) { + return $initializer; + } + + $class = new \ReflectionClass($initializer); + + if (!$class->isSubclassOf('Predis\Connection\NodeConnectionInterface')) { + throw new \InvalidArgumentException( + 'A connection initializer must be a valid connection class or a callable object.' + ); + } + + return $initializer; + } + + /** + * {@inheritdoc} + */ + public function define($scheme, $initializer) + { + $this->schemes[$scheme] = $this->checkInitializer($initializer); + } + + /** + * {@inheritdoc} + */ + public function undefine($scheme) + { + unset($this->schemes[$scheme]); + } + + /** + * {@inheritdoc} + */ + public function create($parameters) + { + if (!$parameters instanceof ParametersInterface) { + $parameters = $this->createParameters($parameters); + } + + $scheme = $parameters->scheme; + + if (!isset($this->schemes[$scheme])) { + throw new \InvalidArgumentException("Unknown connection scheme: '$scheme'."); + } + + $initializer = $this->schemes[$scheme]; + + if (is_callable($initializer)) { + $connection = call_user_func($initializer, $parameters, $this); + } else { + $connection = new $initializer($parameters); + $this->prepareConnection($connection); + } + + if (!$connection instanceof NodeConnectionInterface) { + throw new \UnexpectedValueException( + 'Objects returned by connection initializers must implement '. + "'Predis\Connection\NodeConnectionInterface'." + ); + } + + return $connection; + } + + /** + * {@inheritdoc} + */ + public function aggregate(AggregateConnectionInterface $connection, array $parameters) + { + foreach ($parameters as $node) { + $connection->add($node instanceof NodeConnectionInterface ? $node : $this->create($node)); + } + } + + /** + * Creates a connection parameters instance from the supplied argument. + * + * @param mixed $parameters Original connection parameters. + * + * @return ParametersInterface + */ + protected function createParameters($parameters) + { + return Parameters::create($parameters); + } + + /** + * Prepares a connection instance after its initialization. + * + * @param NodeConnectionInterface $connection Connection instance. + */ + protected function prepareConnection(NodeConnectionInterface $connection) + { + $parameters = $connection->getParameters(); + + if (isset($parameters->password)) { + $connection->addConnectCommand( + new RawCommand(array('AUTH', $parameters->password)) + ); + } + + if (isset($parameters->database)) { + $connection->addConnectCommand( + new RawCommand(array('SELECT', $parameters->database)) + ); + } + } +} diff --git a/vendor/predis/predis/src/Connection/FactoryInterface.php b/vendor/predis/predis/src/Connection/FactoryInterface.php new file mode 100644 index 00000000..2bae0839 --- /dev/null +++ b/vendor/predis/predis/src/Connection/FactoryInterface.php @@ -0,0 +1,52 @@ +<?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\Connection; + +/** + * Interface for classes providing a factory of connections to Redis nodes. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface FactoryInterface +{ + /** + * Defines or overrides the connection class identified by a scheme prefix. + * + * @param string $scheme Target connection scheme. + * @param mixed $initializer Fully-qualified name of a class or a callable for lazy initialization. + */ + public function define($scheme, $initializer); + + /** + * Undefines the connection identified by a scheme prefix. + * + * @param string $scheme Target connection scheme. + */ + public function undefine($scheme); + + /** + * Creates a new connection object. + * + * @param mixed $parameters Initialization parameters for the connection. + * + * @return NodeConnectionInterface + */ + public function create($parameters); + + /** + * Aggregates single connections into an aggregate connection instance. + * + * @param AggregateConnectionInterface $aggregate Aggregate connection instance. + * @param array $parameters List of parameters for each connection. + */ + public function aggregate(AggregateConnectionInterface $aggregate, array $parameters); +} diff --git a/vendor/predis/predis/src/Connection/NodeConnectionInterface.php b/vendor/predis/predis/src/Connection/NodeConnectionInterface.php new file mode 100644 index 00000000..665b862c --- /dev/null +++ b/vendor/predis/predis/src/Connection/NodeConnectionInterface.php @@ -0,0 +1,58 @@ +<?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\Connection; + +use Predis\Command\CommandInterface; + +/** + * Defines a connection used to communicate with a single Redis node. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface NodeConnectionInterface extends ConnectionInterface +{ + /** + * Returns a string representation of the connection. + * + * @return string + */ + public function __toString(); + + /** + * Returns the underlying resource used to communicate with Redis. + * + * @return mixed + */ + public function getResource(); + + /** + * Returns the parameters used to initialize the connection. + * + * @return ParametersInterface + */ + public function getParameters(); + + /** + * Pushes the given command into a queue of commands executed when + * establishing the actual connection to Redis. + * + * @param CommandInterface $command Instance of a Redis command. + */ + public function addConnectCommand(CommandInterface $command); + + /** + * Reads a response from the server. + * + * @return mixed + */ + public function read(); +} diff --git a/vendor/predis/predis/src/Connection/Parameters.php b/vendor/predis/predis/src/Connection/Parameters.php new file mode 100644 index 00000000..b7d98615 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Parameters.php @@ -0,0 +1,176 @@ +<?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\Connection; + +/** + * Container for connection parameters used to initialize connections to Redis. + * + * {@inheritdoc} + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Parameters implements ParametersInterface +{ + private $parameters; + + private static $defaults = array( + 'scheme' => 'tcp', + 'host' => '127.0.0.1', + 'port' => 6379, + 'timeout' => 5.0, + ); + + /** + * @param array $parameters Named array of connection parameters. + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $this->filter($parameters) + $this->getDefaults(); + } + + /** + * Returns some default parameters with their values. + * + * @return array + */ + protected function getDefaults() + { + return self::$defaults; + } + + /** + * Creates a new instance by supplying the initial parameters either in the + * form of an URI string or a named array. + * + * @param array|string $parameters Set of connection parameters. + * + * @return Parameters + */ + public static function create($parameters) + { + if (is_string($parameters)) { + $parameters = static::parse($parameters); + } + + return new static($parameters ?: array()); + } + + /** + * Parses an URI string returning an array of connection parameters. + * + * When using the "redis" and "rediss" schemes the URI is parsed according + * to the rules defined by the provisional registration documents approved + * by IANA. If the URI has a password in its "user-information" part or a + * database number in the "path" part these values override the values of + * "password" and "database" if they are present in the "query" part. + * + * @link http://www.iana.org/assignments/uri-schemes/prov/redis + * @link http://www.iana.org/assignments/uri-schemes/prov/redis + * + * @param string $uri URI string. + * + * @throws \InvalidArgumentException + * + * @return array + */ + public static function parse($uri) + { + if (stripos($uri, 'unix') === 0) { + // Hack to support URIs for UNIX sockets with minimal effort. + $uri = str_ireplace('unix:///', 'unix://localhost/', $uri); + } + + if (!$parsed = parse_url($uri)) { + throw new \InvalidArgumentException("Invalid parameters URI: $uri"); + } + + if ( + isset($parsed['host']) + && false !== strpos($parsed['host'], '[') + && false !== strpos($parsed['host'], ']') + ) { + $parsed['host'] = substr($parsed['host'], 1, -1); + } + + if (isset($parsed['query'])) { + parse_str($parsed['query'], $queryarray); + unset($parsed['query']); + + $parsed = array_merge($parsed, $queryarray); + } + + if (stripos($uri, 'redis') === 0) { + if (isset($parsed['pass'])) { + $parsed['password'] = $parsed['pass']; + unset($parsed['pass']); + } + + if (isset($parsed['path']) && preg_match('/^\/(\d+)(\/.*)?/', $parsed['path'], $path)) { + $parsed['database'] = $path[1]; + + if (isset($path[2])) { + $parsed['path'] = $path[2]; + } else { + unset($parsed['path']); + } + } + } + + return $parsed; + } + + /** + * Validates and converts each value of the connection parameters array. + * + * @param array $parameters Connection parameters. + * + * @return array + */ + protected function filter(array $parameters) + { + return $parameters ?: array(); + } + + /** + * {@inheritdoc} + */ + public function __get($parameter) + { + if (isset($this->parameters[$parameter])) { + return $this->parameters[$parameter]; + } + } + + /** + * {@inheritdoc} + */ + public function __isset($parameter) + { + return isset($this->parameters[$parameter]); + } + + /** + * {@inheritdoc} + */ + public function toArray() + { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('parameters'); + } +} diff --git a/vendor/predis/predis/src/Connection/ParametersInterface.php b/vendor/predis/predis/src/Connection/ParametersInterface.php new file mode 100644 index 00000000..fd8a908e --- /dev/null +++ b/vendor/predis/predis/src/Connection/ParametersInterface.php @@ -0,0 +1,62 @@ +<?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\Connection; + +/** + * Interface defining a container for connection parameters. + * + * The actual list of connection parameters depends on the features supported by + * each connection backend class (please refer to their specific documentation), + * but the most common parameters used through the library are: + * + * @property-read string scheme Connection scheme, such as 'tcp' or 'unix'. + * @property-read string host IP address or hostname of Redis. + * @property-read int port TCP port on which Redis is listening to. + * @property-read string path Path of a UNIX domain socket file. + * @property-read string alias Alias for the connection. + * @property-read float timeout Timeout for the connect() operation. + * @property-read float read_write_timeout Timeout for read() and write() operations. + * @property-read bool async_connect Performs the connect() operation asynchronously. + * @property-read bool tcp_nodelay Toggles the Nagle's algorithm for coalescing. + * @property-read bool persistent Leaves the connection open after a GC collection. + * @property-read string password Password to access Redis (see the AUTH command). + * @property-read string database Database index (see the SELECT command). + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ParametersInterface +{ + /** + * Checks if the specified parameters is set. + * + * @param string $parameter Name of the parameter. + * + * @return bool + */ + public function __isset($parameter); + + /** + * Returns the value of the specified parameter. + * + * @param string $parameter Name of the parameter. + * + * @return mixed|null + */ + public function __get($parameter); + + /** + * Returns an array representation of the connection parameters. + * + * @return array + */ + public function toArray(); +} diff --git a/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php b/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php new file mode 100644 index 00000000..6948f035 --- /dev/null +++ b/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php @@ -0,0 +1,393 @@ +<?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\Connection; + +use Predis\Command\CommandInterface; +use Predis\NotSupportedException; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\Status as StatusResponse; + +/** + * This class provides the implementation of a Predis connection that uses the + * PHP socket extension for network communication and wraps the phpiredis C + * extension (PHP bindings for hiredis) to parse the Redis protocol. + * + * This class is intended to provide an optional low-overhead alternative for + * processing responses from Redis compared to the standard pure-PHP classes. + * Differences in speed when dealing with short inline responses are practically + * nonexistent, the actual speed boost is for big multibulk responses when this + * protocol processor can parse and return responses very fast. + * + * For instructions on how to build and install the phpiredis extension, please + * consult the repository of the project. + * + * The connection parameters supported by this class are: + * + * - scheme: it can be either 'redis', 'tcp' or 'unix'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - path: path of a UNIX domain socket when scheme is 'unix'. + * - timeout: timeout to perform the connection. + * - read_write_timeout: timeout of read / write operations. + * + * @link http://github.com/nrk/phpiredis + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PhpiredisSocketConnection extends AbstractConnection +{ + private $reader; + + /** + * {@inheritdoc} + */ + public function __construct(ParametersInterface $parameters) + { + $this->assertExtensions(); + + parent::__construct($parameters); + + $this->reader = $this->createReader(); + } + + /** + * Disconnects from the server and destroys the underlying resource and the + * protocol reader resource when PHP's garbage collector kicks in. + */ + public function __destruct() + { + phpiredis_reader_destroy($this->reader); + + parent::__destruct(); + } + + /** + * Checks if the socket and phpiredis extensions are loaded in PHP. + */ + protected function assertExtensions() + { + if (!extension_loaded('sockets')) { + throw new NotSupportedException( + 'The "sockets" extension is required by this connection backend.' + ); + } + + if (!extension_loaded('phpiredis')) { + throw new NotSupportedException( + 'The "phpiredis" extension is required by this connection backend.' + ); + } + } + + /** + * {@inheritdoc} + */ + protected function assertParameters(ParametersInterface $parameters) + { + parent::assertParameters($parameters); + + if (isset($parameters->persistent)) { + throw new NotSupportedException( + 'Persistent connections are not supported by this connection backend.' + ); + } + + return $parameters; + } + + /** + * Creates a new instance of the protocol reader resource. + * + * @return resource + */ + private function createReader() + { + $reader = phpiredis_reader_create(); + + phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); + phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); + + return $reader; + } + + /** + * Returns the underlying protocol reader resource. + * + * @return resource + */ + protected function getReader() + { + return $this->reader; + } + + /** + * Returns the handler used by the protocol reader for inline responses. + * + * @return \Closure + */ + private function getStatusHandler() + { + return function ($payload) { + return StatusResponse::get($payload); + }; + } + + /** + * Returns the handler used by the protocol reader for error responses. + * + * @return \Closure + */ + protected function getErrorHandler() + { + return function ($payload) { + return new ErrorResponse($payload); + }; + } + + /** + * Helper method used to throw exceptions on socket errors. + */ + private function emitSocketError() + { + $errno = socket_last_error(); + $errstr = socket_strerror($errno); + + $this->disconnect(); + + $this->onConnectionError(trim($errstr), $errno); + } + + /** + * Gets the address of an host from connection parameters. + * + * @param ParametersInterface $parameters Parameters used to initialize the connection. + * + * @return string + */ + protected static function getAddress(ParametersInterface $parameters) + { + if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) { + return $host; + } + + if ($host === $address = gethostbyname($host)) { + return false; + } + + return $address; + } + + /** + * {@inheritdoc} + */ + protected function createResource() + { + $parameters = $this->parameters; + + if ($parameters->scheme === 'unix') { + $address = $parameters->path; + $domain = AF_UNIX; + $protocol = 0; + } else { + if (false === $address = self::getAddress($parameters)) { + $this->onConnectionError("Cannot resolve the address of '$parameters->host'."); + } + + $domain = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? AF_INET6 : AF_INET; + $protocol = SOL_TCP; + } + + $socket = @socket_create($domain, SOCK_STREAM, $protocol); + + if (!is_resource($socket)) { + $this->emitSocketError(); + } + + $this->setSocketOptions($socket, $parameters); + $this->connectWithTimeout($socket, $address, $parameters); + + return $socket; + } + + /** + * Sets options on the socket resource from the connection parameters. + * + * @param resource $socket Socket resource. + * @param ParametersInterface $parameters Parameters used to initialize the connection. + */ + private function setSocketOptions($socket, ParametersInterface $parameters) + { + if ($parameters->scheme !== 'unix') { + if (!socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1)) { + $this->emitSocketError(); + } + + if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) { + $this->emitSocketError(); + } + } + + if (isset($parameters->read_write_timeout)) { + $rwtimeout = (float) $parameters->read_write_timeout; + $timeoutSec = floor($rwtimeout); + $timeoutUsec = ($rwtimeout - $timeoutSec) * 1000000; + + $timeout = array( + 'sec' => $timeoutSec, + 'usec' => $timeoutUsec, + ); + + if (!socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout)) { + $this->emitSocketError(); + } + + if (!socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout)) { + $this->emitSocketError(); + } + } + } + + /** + * Opens the actual connection to the server with a timeout. + * + * @param resource $socket Socket resource. + * @param string $address IP address (DNS-resolved from hostname) + * @param ParametersInterface $parameters Parameters used to initialize the connection. + * + * @return string + */ + private function connectWithTimeout($socket, $address, ParametersInterface $parameters) + { + socket_set_nonblock($socket); + + if (@socket_connect($socket, $address, (int) $parameters->port) === false) { + $error = socket_last_error(); + + if ($error != SOCKET_EINPROGRESS && $error != SOCKET_EALREADY) { + $this->emitSocketError(); + } + } + + socket_set_block($socket); + + $null = null; + $selectable = array($socket); + + $timeout = (float) $parameters->timeout; + $timeoutSecs = floor($timeout); + $timeoutUSecs = ($timeout - $timeoutSecs) * 1000000; + + $selected = socket_select($selectable, $selectable, $null, $timeoutSecs, $timeoutUSecs); + + if ($selected === 2) { + $this->onConnectionError('Connection refused.', SOCKET_ECONNREFUSED); + } + + if ($selected === 0) { + $this->onConnectionError('Connection timed out.', SOCKET_ETIMEDOUT); + } + + if ($selected === false) { + $this->emitSocketError(); + } + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if (parent::connect() && $this->initCommands) { + foreach ($this->initCommands as $command) { + $this->executeCommand($command); + } + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->isConnected()) { + socket_close($this->getResource()); + parent::disconnect(); + } + } + + /** + * {@inheritdoc} + */ + protected function write($buffer) + { + $socket = $this->getResource(); + + while (($length = strlen($buffer)) > 0) { + $written = socket_write($socket, $buffer, $length); + + if ($length === $written) { + return; + } + + if ($written === false) { + $this->onConnectionError('Error while writing bytes to the server.'); + } + + $buffer = substr($buffer, $written); + } + } + + /** + * {@inheritdoc} + */ + public function read() + { + $socket = $this->getResource(); + $reader = $this->reader; + + while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) { + if (@socket_recv($socket, $buffer, 4096, 0) === false || $buffer === '' || $buffer === null) { + $this->emitSocketError(); + } + + phpiredis_reader_feed($reader, $buffer); + } + + if ($state === PHPIREDIS_READER_STATE_COMPLETE) { + return phpiredis_reader_get_reply($reader); + } else { + $this->onProtocolError(phpiredis_reader_get_error($reader)); + + return; + } + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $arguments = $command->getArguments(); + array_unshift($arguments, $command->getId()); + + $this->write(phpiredis_format_command($arguments)); + } + + /** + * {@inheritdoc} + */ + public function __wakeup() + { + $this->assertExtensions(); + $this->reader = $this->createReader(); + } +} diff --git a/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php b/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php new file mode 100644 index 00000000..beb23575 --- /dev/null +++ b/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php @@ -0,0 +1,228 @@ +<?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\Connection; + +use Predis\Command\CommandInterface; +use Predis\NotSupportedException; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\Status as StatusResponse; + +/** + * This class provides the implementation of a Predis connection that uses PHP's + * streams for network communication and wraps the phpiredis C extension (PHP + * bindings for hiredis) to parse and serialize the Redis protocol. + * + * This class is intended to provide an optional low-overhead alternative for + * processing responses from Redis compared to the standard pure-PHP classes. + * Differences in speed when dealing with short inline responses are practically + * nonexistent, the actual speed boost is for big multibulk responses when this + * protocol processor can parse and return responses very fast. + * + * For instructions on how to build and install the phpiredis extension, please + * consult the repository of the project. + * + * The connection parameters supported by this class are: + * + * - scheme: it can be either 'redis', 'tcp' or 'unix'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - path: path of a UNIX domain socket when scheme is 'unix'. + * - timeout: timeout to perform the connection. + * - read_write_timeout: timeout of read / write operations. + * - async_connect: performs the connection asynchronously. + * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing. + * - persistent: the connection is left intact after a GC collection. + * + * @link https://github.com/nrk/phpiredis + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class PhpiredisStreamConnection extends StreamConnection +{ + private $reader; + + /** + * {@inheritdoc} + */ + public function __construct(ParametersInterface $parameters) + { + $this->assertExtensions(); + + parent::__construct($parameters); + + $this->reader = $this->createReader(); + } + + /** + * {@inheritdoc} + */ + public function __destruct() + { + phpiredis_reader_destroy($this->reader); + + parent::__destruct(); + } + + /** + * Checks if the phpiredis extension is loaded in PHP. + */ + private function assertExtensions() + { + if (!extension_loaded('phpiredis')) { + throw new NotSupportedException( + 'The "phpiredis" extension is required by this connection backend.' + ); + } + } + + /** + * {@inheritdoc} + */ + protected function tcpStreamInitializer(ParametersInterface $parameters) + { + $uri = "tcp://[{$parameters->host}]:{$parameters->port}"; + $flags = STREAM_CLIENT_CONNECT; + $socket = null; + + if (isset($parameters->async_connect) && (bool) $parameters->async_connect) { + $flags |= STREAM_CLIENT_ASYNC_CONNECT; + } + + if (isset($parameters->persistent) && (bool) $parameters->persistent) { + $flags |= STREAM_CLIENT_PERSISTENT; + $uri .= strpos($path = $parameters->path, '/') === 0 ? $path : "/$path"; + } + + $resource = @stream_socket_client($uri, $errno, $errstr, (float) $parameters->timeout, $flags); + + if (!$resource) { + $this->onConnectionError(trim($errstr), $errno); + } + + if (isset($parameters->read_write_timeout) && function_exists('socket_import_stream')) { + $rwtimeout = (float) $parameters->read_write_timeout; + $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1; + + $timeout = array( + 'sec' => $timeoutSeconds = floor($rwtimeout), + 'usec' => ($rwtimeout - $timeoutSeconds) * 1000000, + ); + + $socket = $socket ?: socket_import_stream($resource); + @socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, $timeout); + @socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, $timeout); + } + + if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) { + $socket = $socket ?: socket_import_stream($resource); + socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay); + } + + return $resource; + } + + /** + * Creates a new instance of the protocol reader resource. + * + * @return resource + */ + private function createReader() + { + $reader = phpiredis_reader_create(); + + phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); + phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); + + return $reader; + } + + /** + * Returns the underlying protocol reader resource. + * + * @return resource + */ + protected function getReader() + { + return $this->reader; + } + + /** + * Returns the handler used by the protocol reader for inline responses. + * + * @return \Closure + */ + protected function getStatusHandler() + { + return function ($payload) { + return StatusResponse::get($payload); + }; + } + + /** + * Returns the handler used by the protocol reader for error responses. + * + * @return \Closure + */ + protected function getErrorHandler() + { + return function ($errorMessage) { + return new ErrorResponse($errorMessage); + }; + } + + /** + * {@inheritdoc} + */ + public function read() + { + $socket = $this->getResource(); + $reader = $this->reader; + + while (PHPIREDIS_READER_STATE_INCOMPLETE === $state = phpiredis_reader_get_state($reader)) { + $buffer = stream_socket_recvfrom($socket, 4096); + + if ($buffer === false || $buffer === '') { + $this->onConnectionError('Error while reading bytes from the server.'); + } + + phpiredis_reader_feed($reader, $buffer); + } + + if ($state === PHPIREDIS_READER_STATE_COMPLETE) { + return phpiredis_reader_get_reply($reader); + } else { + $this->onProtocolError(phpiredis_reader_get_error($reader)); + + return; + } + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $arguments = $command->getArguments(); + array_unshift($arguments, $command->getId()); + + $this->write(phpiredis_format_command($arguments)); + } + + /** + * {@inheritdoc} + */ + public function __wakeup() + { + $this->assertExtensions(); + $this->reader = $this->createReader(); + } +} diff --git a/vendor/predis/predis/src/Connection/StreamConnection.php b/vendor/predis/predis/src/Connection/StreamConnection.php new file mode 100644 index 00000000..955c2b1b --- /dev/null +++ b/vendor/predis/predis/src/Connection/StreamConnection.php @@ -0,0 +1,291 @@ +<?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\Connection; + +use Predis\Command\CommandInterface; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\Status as StatusResponse; + +/** + * Standard connection to Redis servers implemented on top of PHP's streams. + * The connection parameters supported by this class are:. + * + * - scheme: it can be either 'redis', 'tcp' or 'unix'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - path: path of a UNIX domain socket when scheme is 'unix'. + * - timeout: timeout to perform the connection. + * - read_write_timeout: timeout of read / write operations. + * - async_connect: performs the connection asynchronously. + * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing. + * - persistent: the connection is left intact after a GC collection. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StreamConnection extends AbstractConnection +{ + /** + * Disconnects from the server and destroys the underlying resource when the + * garbage collector kicks in only if the connection has not been marked as + * persistent. + */ + public function __destruct() + { + if (isset($this->parameters->persistent) && $this->parameters->persistent) { + return; + } + + $this->disconnect(); + } + + /** + * {@inheritdoc} + */ + protected function createResource() + { + switch ($this->parameters->scheme) { + case 'tcp': + case 'redis': + return $this->tcpStreamInitializer($this->parameters); + + case 'unix': + return $this->unixStreamInitializer($this->parameters); + + default: + throw new \InvalidArgumentException("Invalid scheme: '{$this->parameters->scheme}'."); + } + } + + /** + * Initializes a TCP stream resource. + * + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @return resource + */ + protected function tcpStreamInitializer(ParametersInterface $parameters) + { + if (!filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $uri = "tcp://$parameters->host:$parameters->port"; + } else { + $uri = "tcp://[$parameters->host]:$parameters->port"; + } + + $flags = STREAM_CLIENT_CONNECT; + + if (isset($parameters->async_connect) && (bool) $parameters->async_connect) { + $flags |= STREAM_CLIENT_ASYNC_CONNECT; + } + + if (isset($parameters->persistent) && (bool) $parameters->persistent) { + $flags |= STREAM_CLIENT_PERSISTENT; + $uri .= strpos($path = $parameters->path, '/') === 0 ? $path : "/$path"; + } + + $resource = @stream_socket_client($uri, $errno, $errstr, (float) $parameters->timeout, $flags); + + if (!$resource) { + $this->onConnectionError(trim($errstr), $errno); + } + + if (isset($parameters->read_write_timeout)) { + $rwtimeout = (float) $parameters->read_write_timeout; + $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1; + $timeoutSeconds = floor($rwtimeout); + $timeoutUSeconds = ($rwtimeout - $timeoutSeconds) * 1000000; + stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds); + } + + if (isset($parameters->tcp_nodelay) && function_exists('socket_import_stream')) { + $socket = socket_import_stream($resource); + socket_set_option($socket, SOL_TCP, TCP_NODELAY, (int) $parameters->tcp_nodelay); + } + + return $resource; + } + + /** + * Initializes a UNIX stream resource. + * + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @return resource + */ + protected function unixStreamInitializer(ParametersInterface $parameters) + { + if (!isset($parameters->path)) { + throw new \InvalidArgumentException('Missing UNIX domain socket path.'); + } + + $uri = "unix://{$parameters->path}"; + $flags = STREAM_CLIENT_CONNECT; + + if ((bool) $parameters->persistent) { + $flags |= STREAM_CLIENT_PERSISTENT; + } + + $resource = @stream_socket_client($uri, $errno, $errstr, (float) $parameters->timeout, $flags); + + if (!$resource) { + $this->onConnectionError(trim($errstr), $errno); + } + + if (isset($parameters->read_write_timeout)) { + $rwtimeout = (float) $parameters->read_write_timeout; + $rwtimeout = $rwtimeout > 0 ? $rwtimeout : -1; + $timeoutSeconds = floor($rwtimeout); + $timeoutUSeconds = ($rwtimeout - $timeoutSeconds) * 1000000; + stream_set_timeout($resource, $timeoutSeconds, $timeoutUSeconds); + } + + return $resource; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if (parent::connect() && $this->initCommands) { + foreach ($this->initCommands as $command) { + $this->executeCommand($command); + } + } + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->isConnected()) { + fclose($this->getResource()); + parent::disconnect(); + } + } + + /** + * Performs a write operation over the stream of the buffer containing a + * command serialized with the Redis wire protocol. + * + * @param string $buffer Representation of a command in the Redis wire protocol. + */ + protected function write($buffer) + { + $socket = $this->getResource(); + + while (($length = strlen($buffer)) > 0) { + $written = @fwrite($socket, $buffer); + + if ($length === $written) { + return; + } + + if ($written === false || $written === 0) { + $this->onConnectionError('Error while writing bytes to the server.'); + } + + $buffer = substr($buffer, $written); + } + } + + /** + * {@inheritdoc} + */ + public function read() + { + $socket = $this->getResource(); + $chunk = fgets($socket); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading line from the server.'); + } + + $prefix = $chunk[0]; + $payload = substr($chunk, 1, -2); + + switch ($prefix) { + case '+': + return StatusResponse::get($payload); + + case '$': + $size = (int) $payload; + + if ($size === -1) { + return; + } + + $bulkData = ''; + $bytesLeft = ($size += 2); + + do { + $chunk = fread($socket, min($bytesLeft, 4096)); + + if ($chunk === false || $chunk === '') { + $this->onConnectionError('Error while reading bytes from the server.'); + } + + $bulkData .= $chunk; + $bytesLeft = $size - strlen($bulkData); + } while ($bytesLeft > 0); + + return substr($bulkData, 0, -2); + + case '*': + $count = (int) $payload; + + if ($count === -1) { + return; + } + + $multibulk = array(); + + for ($i = 0; $i < $count; ++$i) { + $multibulk[$i] = $this->read(); + } + + return $multibulk; + + case ':': + return (int) $payload; + + case '-': + return new ErrorResponse($payload); + + default: + $this->onProtocolError("Unknown response prefix: '$prefix'."); + + return; + } + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $commandID = $command->getId(); + $arguments = $command->getArguments(); + + $cmdlen = strlen($commandID); + $reqlen = count($arguments) + 1; + + $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n"; + + foreach ($arguments as $argument) { + $arglen = strlen($argument); + $buffer .= "\${$arglen}\r\n{$argument}\r\n"; + } + + $this->write($buffer); + } +} diff --git a/vendor/predis/predis/src/Connection/WebdisConnection.php b/vendor/predis/predis/src/Connection/WebdisConnection.php new file mode 100644 index 00000000..9cff9d02 --- /dev/null +++ b/vendor/predis/predis/src/Connection/WebdisConnection.php @@ -0,0 +1,353 @@ +<?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\Connection; + +use Predis\Command\CommandInterface; +use Predis\NotSupportedException; +use Predis\Protocol\ProtocolException; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\Status as StatusResponse; + +/** + * This class implements a Predis connection that actually talks with Webdis + * instead of connecting directly to Redis. It relies on the cURL extension to + * communicate with the web server and the phpiredis extension to parse the + * protocol for responses returned in the http response bodies. + * + * Some features are not yet available or they simply cannot be implemented: + * - Pipelining commands. + * - Publish / Subscribe. + * - MULTI / EXEC transactions (not yet supported by Webdis). + * + * The connection parameters supported by this class are: + * + * - scheme: must be 'http'. + * - host: hostname or IP address of the server. + * - port: TCP port of the server. + * - timeout: timeout to perform the connection. + * - user: username for authentication. + * - pass: password for authentication. + * + * @link http://webd.is + * @link http://github.com/nicolasff/webdis + * @link http://github.com/seppo0010/phpiredis + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class WebdisConnection implements NodeConnectionInterface +{ + private $parameters; + private $resource; + private $reader; + + /** + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @throws \InvalidArgumentException + */ + public function __construct(ParametersInterface $parameters) + { + $this->assertExtensions(); + + if ($parameters->scheme !== 'http') { + throw new \InvalidArgumentException("Invalid scheme: '{$parameters->scheme}'."); + } + + $this->parameters = $parameters; + + $this->resource = $this->createCurl(); + $this->reader = $this->createReader(); + } + + /** + * Frees the underlying cURL and protocol reader resources when the garbage + * collector kicks in. + */ + public function __destruct() + { + curl_close($this->resource); + phpiredis_reader_destroy($this->reader); + } + + /** + * Helper method used to throw on unsupported methods. + * + * @param string $method Name of the unsupported method. + * + * @throws NotSupportedException + */ + private function throwNotSupportedException($method) + { + $class = __CLASS__; + throw new NotSupportedException("The method $class::$method() is not supported."); + } + + /** + * Checks if the cURL and phpiredis extensions are loaded in PHP. + */ + private function assertExtensions() + { + if (!extension_loaded('curl')) { + throw new NotSupportedException( + 'The "curl" extension is required by this connection backend.' + ); + } + + if (!extension_loaded('phpiredis')) { + throw new NotSupportedException( + 'The "phpiredis" extension is required by this connection backend.' + ); + } + } + + /** + * Initializes cURL. + * + * @return resource + */ + private function createCurl() + { + $parameters = $this->getParameters(); + + if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) { + $host = "[$host]"; + } + + $options = array( + CURLOPT_FAILONERROR => true, + CURLOPT_CONNECTTIMEOUT_MS => $parameters->timeout * 1000, + CURLOPT_URL => "$parameters->scheme://$host:$parameters->port", + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_POST => true, + CURLOPT_WRITEFUNCTION => array($this, 'feedReader'), + ); + + if (isset($parameters->user, $parameters->pass)) { + $options[CURLOPT_USERPWD] = "{$parameters->user}:{$parameters->pass}"; + } + + curl_setopt_array($resource = curl_init(), $options); + + return $resource; + } + + /** + * Initializes the phpiredis protocol reader. + * + * @return resource + */ + private function createReader() + { + $reader = phpiredis_reader_create(); + + phpiredis_reader_set_status_handler($reader, $this->getStatusHandler()); + phpiredis_reader_set_error_handler($reader, $this->getErrorHandler()); + + return $reader; + } + + /** + * Returns the handler used by the protocol reader for inline responses. + * + * @return \Closure + */ + protected function getStatusHandler() + { + return function ($payload) { + return StatusResponse::get($payload); + }; + } + + /** + * Returns the handler used by the protocol reader for error responses. + * + * @return \Closure + */ + protected function getErrorHandler() + { + return function ($payload) { + return new ErrorResponse($payload); + }; + } + + /** + * Feeds the phpredis reader resource with the data read from the network. + * + * @param resource $resource Reader resource. + * @param string $buffer Buffer of data read from a connection. + * + * @return int + */ + protected function feedReader($resource, $buffer) + { + phpiredis_reader_feed($this->reader, $buffer); + + return strlen($buffer); + } + + /** + * {@inheritdoc} + */ + public function connect() + { + // NOOP + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + // NOOP + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return true; + } + + /** + * Checks if the specified command is supported by this connection class. + * + * @param CommandInterface $command Command instance. + * + * @throws NotSupportedException + * + * @return string + */ + protected function getCommandId(CommandInterface $command) + { + switch ($commandID = $command->getId()) { + case 'AUTH': + case 'SELECT': + case 'MULTI': + case 'EXEC': + case 'WATCH': + case 'UNWATCH': + case 'DISCARD': + case 'MONITOR': + throw new NotSupportedException("Command '$commandID' is not allowed by Webdis."); + + default: + return $commandID; + } + } + + /** + * {@inheritdoc} + */ + public function writeRequest(CommandInterface $command) + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function readResponse(CommandInterface $command) + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function executeCommand(CommandInterface $command) + { + $resource = $this->resource; + $commandId = $this->getCommandId($command); + + if ($arguments = $command->getArguments()) { + $arguments = implode('/', array_map('urlencode', $arguments)); + $serializedCommand = "$commandId/$arguments.raw"; + } else { + $serializedCommand = "$commandId.raw"; + } + + curl_setopt($resource, CURLOPT_POSTFIELDS, $serializedCommand); + + if (curl_exec($resource) === false) { + $error = curl_error($resource); + $errno = curl_errno($resource); + + throw new ConnectionException($this, trim($error), $errno); + } + + if (phpiredis_reader_get_state($this->reader) !== PHPIREDIS_READER_STATE_COMPLETE) { + throw new ProtocolException($this, phpiredis_reader_get_error($this->reader)); + } + + return phpiredis_reader_get_reply($this->reader); + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function addConnectCommand(CommandInterface $command) + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function read() + { + $this->throwNotSupportedException(__FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return "{$this->parameters->host}:{$this->parameters->port}"; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array('parameters'); + } + + /** + * {@inheritdoc} + */ + public function __wakeup() + { + $this->assertExtensions(); + + $this->resource = $this->createCurl(); + $this->reader = $this->createReader(); + } +} diff --git a/vendor/predis/predis/src/Monitor/Consumer.php b/vendor/predis/predis/src/Monitor/Consumer.php new file mode 100644 index 00000000..aaf645be --- /dev/null +++ b/vendor/predis/predis/src/Monitor/Consumer.php @@ -0,0 +1,173 @@ +<?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\Monitor; + +use Predis\ClientInterface; +use Predis\Connection\AggregateConnectionInterface; +use Predis\NotSupportedException; + +/** + * Redis MONITOR consumer. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Consumer implements \Iterator +{ + private $client; + private $valid; + private $position; + + /** + * @param ClientInterface $client Client instance used by the consumer. + */ + public function __construct(ClientInterface $client) + { + $this->assertClient($client); + + $this->client = $client; + + $this->start(); + } + + /** + * Automatically stops the consumer when the garbage collector kicks in. + */ + public function __destruct() + { + $this->stop(); + } + + /** + * Checks if the passed client instance satisfies the required conditions + * needed to initialize a monitor consumer. + * + * @param ClientInterface $client Client instance used by the consumer. + * + * @throws NotSupportedException + */ + private function assertClient(ClientInterface $client) + { + if ($client->getConnection() instanceof AggregateConnectionInterface) { + throw new NotSupportedException( + 'Cannot initialize a monitor consumer over aggregate connections.' + ); + } + + if ($client->getProfile()->supportsCommand('MONITOR') === false) { + throw new NotSupportedException("The current profile does not support 'MONITOR'."); + } + } + + /** + * Initializes the consumer and sends the MONITOR command to the server. + */ + protected function start() + { + $this->client->executeCommand( + $this->client->createCommand('MONITOR') + ); + $this->valid = true; + } + + /** + * Stops the consumer. Internally this is done by disconnecting from server + * since there is no way to terminate the stream initialized by MONITOR. + */ + public function stop() + { + $this->client->disconnect(); + $this->valid = false; + } + + /** + * {@inheritdoc} + */ + public function rewind() + { + // NOOP + } + + /** + * Returns the last message payload retrieved from the server. + * + * @return object + */ + public function current() + { + return $this->getValue(); + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + ++$this->position; + } + + /** + * Checks if the the consumer is still in a valid state to continue. + * + * @return bool + */ + public function valid() + { + return $this->valid; + } + + /** + * Waits for a new message from the server generated by MONITOR and returns + * it when available. + * + * @return object + */ + private function getValue() + { + $database = 0; + $client = null; + $event = $this->client->getConnection()->read(); + + $callback = function ($matches) use (&$database, &$client) { + if (2 === $count = count($matches)) { + // Redis <= 2.4 + $database = (int) $matches[1]; + } + + if (4 === $count) { + // Redis >= 2.6 + $database = (int) $matches[2]; + $client = $matches[3]; + } + + return ' '; + }; + + $event = preg_replace_callback('/ \(db (\d+)\) | \[(\d+) (.*?)\] /', $callback, $event, 1); + @list($timestamp, $command, $arguments) = explode(' ', $event, 3); + + return (object) array( + 'timestamp' => (float) $timestamp, + 'database' => $database, + 'client' => $client, + 'command' => substr($command, 1, -1), + 'arguments' => $arguments, + ); + } +} diff --git a/vendor/predis/predis/src/NotSupportedException.php b/vendor/predis/predis/src/NotSupportedException.php new file mode 100644 index 00000000..be82aba7 --- /dev/null +++ b/vendor/predis/predis/src/NotSupportedException.php @@ -0,0 +1,22 @@ +<?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; + +/** + * Exception class thrown when trying to use features not supported by certain + * classes or abstractions of Predis. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class NotSupportedException extends PredisException +{ +} diff --git a/vendor/predis/predis/src/Pipeline/Atomic.php b/vendor/predis/predis/src/Pipeline/Atomic.php new file mode 100644 index 00000000..1c9c92aa --- /dev/null +++ b/vendor/predis/predis/src/Pipeline/Atomic.php @@ -0,0 +1,119 @@ +<?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\Pipeline; + +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ResponseInterface; +use Predis\Response\ServerException; + +/** + * Command pipeline wrapped into a MULTI / EXEC transaction. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Atomic extends Pipeline +{ + /** + * {@inheritdoc} + */ + public function __construct(ClientInterface $client) + { + if (!$client->getProfile()->supportsCommands(array('multi', 'exec', 'discard'))) { + throw new ClientException( + "The current profile does not support 'MULTI', 'EXEC' and 'DISCARD'." + ); + } + + parent::__construct($client); + } + + /** + * {@inheritdoc} + */ + protected function getConnection() + { + $connection = $this->getClient()->getConnection(); + + if (!$connection instanceof NodeConnectionInterface) { + $class = __CLASS__; + + throw new ClientException("The class '$class' does not support aggregate connections."); + } + + return $connection; + } + + /** + * {@inheritdoc} + */ + protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) + { + $profile = $this->getClient()->getProfile(); + $connection->executeCommand($profile->createCommand('multi')); + + foreach ($commands as $command) { + $connection->writeRequest($command); + } + + foreach ($commands as $command) { + $response = $connection->readResponse($command); + + if ($response instanceof ErrorResponseInterface) { + $connection->executeCommand($profile->createCommand('discard')); + throw new ServerException($response->getMessage()); + } + } + + $executed = $connection->executeCommand($profile->createCommand('exec')); + + if (!isset($executed)) { + // TODO: should be throwing a more appropriate exception. + throw new ClientException( + 'The underlying transaction has been aborted by the server.' + ); + } + + if (count($executed) !== count($commands)) { + $expected = count($commands); + $received = count($executed); + + throw new ClientException( + "Invalid number of responses [expected $expected, received $received]." + ); + } + + $responses = array(); + $sizeOfPipe = count($commands); + $exceptions = $this->throwServerExceptions(); + + for ($i = 0; $i < $sizeOfPipe; ++$i) { + $command = $commands->dequeue(); + $response = $executed[$i]; + + if (!$response instanceof ResponseInterface) { + $responses[] = $command->parseResponse($response); + } elseif ($response instanceof ErrorResponseInterface && $exceptions) { + $this->exception($connection, $response); + } else { + $responses[] = $response; + } + + unset($executed[$i]); + } + + return $responses; + } +} diff --git a/vendor/predis/predis/src/Pipeline/ConnectionErrorProof.php b/vendor/predis/predis/src/Pipeline/ConnectionErrorProof.php new file mode 100644 index 00000000..d3bc732e --- /dev/null +++ b/vendor/predis/predis/src/Pipeline/ConnectionErrorProof.php @@ -0,0 +1,130 @@ +<?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\Pipeline; + +use Predis\CommunicationException; +use Predis\Connection\Aggregate\ClusterInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\NotSupportedException; + +/** + * Command pipeline that does not throw exceptions on connection errors, but + * returns the exception instances as the rest of the response elements. + * + * @todo Awful naming! + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ConnectionErrorProof extends Pipeline +{ + /** + * {@inheritdoc} + */ + protected function getConnection() + { + return $this->getClient()->getConnection(); + } + + /** + * {@inheritdoc} + */ + protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) + { + if ($connection instanceof NodeConnectionInterface) { + return $this->executeSingleNode($connection, $commands); + } elseif ($connection instanceof ClusterInterface) { + return $this->executeCluster($connection, $commands); + } else { + $class = get_class($connection); + + throw new NotSupportedException("The connection class '$class' is not supported."); + } + } + + /** + * {@inheritdoc} + */ + protected function executeSingleNode(NodeConnectionInterface $connection, \SplQueue $commands) + { + $responses = array(); + $sizeOfPipe = count($commands); + + foreach ($commands as $command) { + try { + $connection->writeRequest($command); + } catch (CommunicationException $exception) { + return array_fill(0, $sizeOfPipe, $exception); + } + } + + for ($i = 0; $i < $sizeOfPipe; ++$i) { + $command = $commands->dequeue(); + + try { + $responses[$i] = $connection->readResponse($command); + } catch (CommunicationException $exception) { + $add = count($commands) - count($responses); + $responses = array_merge($responses, array_fill(0, $add, $exception)); + + break; + } + } + + return $responses; + } + + /** + * {@inheritdoc} + */ + protected function executeCluster(ClusterInterface $connection, \SplQueue $commands) + { + $responses = array(); + $sizeOfPipe = count($commands); + $exceptions = array(); + + foreach ($commands as $command) { + $cmdConnection = $connection->getConnection($command); + + if (isset($exceptions[spl_object_hash($cmdConnection)])) { + continue; + } + + try { + $cmdConnection->writeRequest($command); + } catch (CommunicationException $exception) { + $exceptions[spl_object_hash($cmdConnection)] = $exception; + } + } + + for ($i = 0; $i < $sizeOfPipe; ++$i) { + $command = $commands->dequeue(); + + $cmdConnection = $connection->getConnection($command); + $connectionHash = spl_object_hash($cmdConnection); + + if (isset($exceptions[$connectionHash])) { + $responses[$i] = $exceptions[$connectionHash]; + continue; + } + + try { + $responses[$i] = $cmdConnection->readResponse($command); + } catch (CommunicationException $exception) { + $responses[$i] = $exception; + $exceptions[$connectionHash] = $exception; + } + } + + return $responses; + } +} diff --git a/vendor/predis/predis/src/Pipeline/FireAndForget.php b/vendor/predis/predis/src/Pipeline/FireAndForget.php new file mode 100644 index 00000000..95a062b6 --- /dev/null +++ b/vendor/predis/predis/src/Pipeline/FireAndForget.php @@ -0,0 +1,36 @@ +<?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\Pipeline; + +use Predis\Connection\ConnectionInterface; + +/** + * Command pipeline that writes commands to the servers but discards responses. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class FireAndForget extends Pipeline +{ + /** + * {@inheritdoc} + */ + protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) + { + while (!$commands->isEmpty()) { + $connection->writeRequest($commands->dequeue()); + } + + $connection->disconnect(); + + return array(); + } +} diff --git a/vendor/predis/predis/src/Pipeline/Pipeline.php b/vendor/predis/predis/src/Pipeline/Pipeline.php new file mode 100644 index 00000000..cf9c59e4 --- /dev/null +++ b/vendor/predis/predis/src/Pipeline/Pipeline.php @@ -0,0 +1,247 @@ +<?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\Pipeline; + +use Predis\ClientContextInterface; +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\Command\CommandInterface; +use Predis\Connection\Aggregate\ReplicationInterface; +use Predis\Connection\ConnectionInterface; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ResponseInterface; +use Predis\Response\ServerException; + +/** + * Implementation of a command pipeline in which write and read operations of + * Redis commands are pipelined to alleviate the effects of network round-trips. + * + * {@inheritdoc} + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Pipeline implements ClientContextInterface +{ + private $client; + private $pipeline; + + private $responses = array(); + private $running = false; + + /** + * @param ClientInterface $client Client instance used by the context. + */ + public function __construct(ClientInterface $client) + { + $this->client = $client; + $this->pipeline = new \SplQueue(); + } + + /** + * Queues a command into the pipeline buffer. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return $this + */ + public function __call($method, $arguments) + { + $command = $this->client->createCommand($method, $arguments); + $this->recordCommand($command); + + return $this; + } + + /** + * Queues a command instance into the pipeline buffer. + * + * @param CommandInterface $command Command to be queued in the buffer. + */ + protected function recordCommand(CommandInterface $command) + { + $this->pipeline->enqueue($command); + } + + /** + * Queues a command instance into the pipeline buffer. + * + * @param CommandInterface $command Command instance to be queued in the buffer. + * + * @return $this + */ + public function executeCommand(CommandInterface $command) + { + $this->recordCommand($command); + + return $this; + } + + /** + * Throws an exception on -ERR responses returned by Redis. + * + * @param ConnectionInterface $connection Redis connection that returned the error. + * @param ErrorResponseInterface $response Instance of the error response. + * + * @throws ServerException + */ + protected function exception(ConnectionInterface $connection, ErrorResponseInterface $response) + { + $connection->disconnect(); + $message = $response->getMessage(); + + throw new ServerException($message); + } + + /** + * Returns the underlying connection to be used by the pipeline. + * + * @return ConnectionInterface + */ + protected function getConnection() + { + $connection = $this->getClient()->getConnection(); + + if ($connection instanceof ReplicationInterface) { + $connection->switchTo('master'); + } + + return $connection; + } + + /** + * Implements the logic to flush the queued commands and read the responses + * from the current connection. + * + * @param ConnectionInterface $connection Current connection instance. + * @param \SplQueue $commands Queued commands. + * + * @return array + */ + protected function executePipeline(ConnectionInterface $connection, \SplQueue $commands) + { + foreach ($commands as $command) { + $connection->writeRequest($command); + } + + $responses = array(); + $exceptions = $this->throwServerExceptions(); + + while (!$commands->isEmpty()) { + $command = $commands->dequeue(); + $response = $connection->readResponse($command); + + if (!$response instanceof ResponseInterface) { + $responses[] = $command->parseResponse($response); + } elseif ($response instanceof ErrorResponseInterface && $exceptions) { + $this->exception($connection, $response); + } else { + $responses[] = $response; + } + } + + return $responses; + } + + /** + * Flushes the buffer holding all of the commands queued so far. + * + * @param bool $send Specifies if the commands in the buffer should be sent to Redis. + * + * @return $this + */ + public function flushPipeline($send = true) + { + if ($send && !$this->pipeline->isEmpty()) { + $responses = $this->executePipeline($this->getConnection(), $this->pipeline); + $this->responses = array_merge($this->responses, $responses); + } else { + $this->pipeline = new \SplQueue(); + } + + return $this; + } + + /** + * Marks the running status of the pipeline. + * + * @param bool $bool Sets the running status of the pipeline. + * + * @throws ClientException + */ + private function setRunning($bool) + { + if ($bool && $this->running) { + throw new ClientException('The current pipeline context is already being executed.'); + } + + $this->running = $bool; + } + + /** + * Handles the actual execution of the whole pipeline. + * + * @param mixed $callable Optional callback for execution. + * + * @throws \Exception + * @throws \InvalidArgumentException + * + * @return array + */ + public function execute($callable = null) + { + if ($callable && !is_callable($callable)) { + throw new \InvalidArgumentException('The argument must be a callable object.'); + } + + $exception = null; + $this->setRunning(true); + + try { + if ($callable) { + call_user_func($callable, $this); + } + + $this->flushPipeline(); + } catch (\Exception $exception) { + // NOOP + } + + $this->setRunning(false); + + if ($exception) { + throw $exception; + } + + return $this->responses; + } + + /** + * Returns if the pipeline should throw exceptions on server errors. + * + * @return bool + */ + protected function throwServerExceptions() + { + return (bool) $this->client->getOptions()->exceptions; + } + + /** + * Returns the underlying client instance used by the pipeline object. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->client; + } +} diff --git a/vendor/predis/predis/src/PredisException.php b/vendor/predis/predis/src/PredisException.php new file mode 100644 index 00000000..122bde16 --- /dev/null +++ b/vendor/predis/predis/src/PredisException.php @@ -0,0 +1,21 @@ +<?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; + +/** + * Base exception class for Predis-related errors. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +abstract class PredisException extends \Exception +{ +} diff --git a/vendor/predis/predis/src/Profile/Factory.php b/vendor/predis/predis/src/Profile/Factory.php new file mode 100644 index 00000000..260fee42 --- /dev/null +++ b/vendor/predis/predis/src/Profile/Factory.php @@ -0,0 +1,101 @@ +<?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\Profile; + +use Predis\ClientException; + +/** + * Factory class for creating profile instances from strings. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +final class Factory +{ + private static $profiles = array( + '2.0' => 'Predis\Profile\RedisVersion200', + '2.2' => 'Predis\Profile\RedisVersion220', + '2.4' => 'Predis\Profile\RedisVersion240', + '2.6' => 'Predis\Profile\RedisVersion260', + '2.8' => 'Predis\Profile\RedisVersion280', + '3.0' => 'Predis\Profile\RedisVersion300', + '3.2' => 'Predis\Profile\RedisVersion320', + 'dev' => 'Predis\Profile\RedisUnstable', + 'default' => 'Predis\Profile\RedisVersion300', + ); + + /** + * + */ + private function __construct() + { + // NOOP + } + + /** + * Returns the default server profile. + * + * @return ProfileInterface + */ + public static function getDefault() + { + return self::get('default'); + } + + /** + * Returns the development server profile. + * + * @return ProfileInterface + */ + public static function getDevelopment() + { + return self::get('dev'); + } + + /** + * Registers a new server profile. + * + * @param string $alias Profile version or alias. + * @param string $class FQN of a class implementing Predis\Profile\ProfileInterface. + * + * @throws \InvalidArgumentException + */ + public static function define($alias, $class) + { + $reflection = new \ReflectionClass($class); + + if (!$reflection->isSubclassOf('Predis\Profile\ProfileInterface')) { + throw new \InvalidArgumentException("The class '$class' is not a valid profile class."); + } + + self::$profiles[$alias] = $class; + } + + /** + * Returns the specified server profile. + * + * @param string $version Profile version or alias. + * + * @throws ClientException + * + * @return ProfileInterface + */ + public static function get($version) + { + if (!isset(self::$profiles[$version])) { + throw new ClientException("Unknown server profile: '$version'."); + } + + $profile = self::$profiles[$version]; + + return new $profile(); + } +} diff --git a/vendor/predis/predis/src/Profile/ProfileInterface.php b/vendor/predis/predis/src/Profile/ProfileInterface.php new file mode 100644 index 00000000..abe71aa6 --- /dev/null +++ b/vendor/predis/predis/src/Profile/ProfileInterface.php @@ -0,0 +1,59 @@ +<?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\Profile; + +use Predis\Command\CommandInterface; + +/** + * A profile defines all the features and commands supported by certain versions + * of Redis. Instances of Predis\Client should use a server profile matching the + * version of Redis being used. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ProfileInterface +{ + /** + * Returns the profile version corresponding to the Redis version. + * + * @return string + */ + public function getVersion(); + + /** + * Checks if the profile supports the specified command. + * + * @param string $commandID Command ID. + * + * @return bool + */ + public function supportsCommand($commandID); + + /** + * Checks if the profile supports the specified list of commands. + * + * @param array $commandIDs List of command IDs. + * + * @return string + */ + public function supportsCommands(array $commandIDs); + + /** + * Creates a new command instance. + * + * @param string $commandID Command ID. + * @param array $arguments Arguments for the command. + * + * @return CommandInterface + */ + public function createCommand($commandID, array $arguments = array()); +} diff --git a/vendor/predis/predis/src/Profile/RedisProfile.php b/vendor/predis/predis/src/Profile/RedisProfile.php new file mode 100644 index 00000000..3ef31688 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisProfile.php @@ -0,0 +1,146 @@ +<?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\Profile; + +use Predis\ClientException; +use Predis\Command\Processor\ProcessorInterface; + +/** + * Base class implementing common functionalities for Redis server profiles. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +abstract class RedisProfile implements ProfileInterface +{ + private $commands; + private $processor; + + /** + * + */ + public function __construct() + { + $this->commands = $this->getSupportedCommands(); + } + + /** + * Returns a map of all the commands supported by the profile and their + * actual PHP classes. + * + * @return array + */ + abstract protected function getSupportedCommands(); + + /** + * {@inheritdoc} + */ + public function supportsCommand($commandID) + { + return isset($this->commands[strtoupper($commandID)]); + } + + /** + * {@inheritdoc} + */ + public function supportsCommands(array $commandIDs) + { + foreach ($commandIDs as $commandID) { + if (!$this->supportsCommand($commandID)) { + return false; + } + } + + return true; + } + + /** + * Returns the fully-qualified name of a class representing the specified + * command ID registered in the current server profile. + * + * @param string $commandID Command ID. + * + * @return string|null + */ + public function getCommandClass($commandID) + { + if (isset($this->commands[$commandID = strtoupper($commandID)])) { + return $this->commands[$commandID]; + } + } + + /** + * {@inheritdoc} + */ + public function createCommand($commandID, array $arguments = array()) + { + $commandID = strtoupper($commandID); + + if (!isset($this->commands[$commandID])) { + throw new ClientException("Command '$commandID' is not a registered Redis command."); + } + + $commandClass = $this->commands[$commandID]; + $command = new $commandClass(); + $command->setArguments($arguments); + + if (isset($this->processor)) { + $this->processor->process($command); + } + + return $command; + } + + /** + * Defines a new command in the server profile. + * + * @param string $commandID Command ID. + * @param string $class Fully-qualified name of a Predis\Command\CommandInterface. + * + * @throws \InvalidArgumentException + */ + public function defineCommand($commandID, $class) + { + $reflection = new \ReflectionClass($class); + + if (!$reflection->isSubclassOf('Predis\Command\CommandInterface')) { + throw new \InvalidArgumentException("The class '$class' is not a valid command class."); + } + + $this->commands[strtoupper($commandID)] = $class; + } + + /** + * {@inheritdoc} + */ + public function setProcessor(ProcessorInterface $processor = null) + { + $this->processor = $processor; + } + + /** + * {@inheritdoc} + */ + public function getProcessor() + { + return $this->processor; + } + + /** + * Returns the version of server profile as its string representation. + * + * @return string + */ + public function __toString() + { + return $this->getVersion(); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisUnstable.php b/vendor/predis/predis/src/Profile/RedisUnstable.php new file mode 100644 index 00000000..573cc9e6 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisUnstable.php @@ -0,0 +1,38 @@ +<?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\Profile; + +/** + * Server profile for the current unstable version of Redis. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisUnstable extends RedisVersion320 +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '3.2'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array_merge(parent::getSupportedCommands(), array( + // EMPTY + )); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion200.php b/vendor/predis/predis/src/Profile/RedisVersion200.php new file mode 100644 index 00000000..234d53c0 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion200.php @@ -0,0 +1,173 @@ +<?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\Profile; + +/** + * Server profile for Redis 2.0. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisVersion200 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.0'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfo', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion220.php b/vendor/predis/predis/src/Profile/RedisVersion220.php new file mode 100644 index 00000000..899014e2 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion220.php @@ -0,0 +1,202 @@ +<?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\Profile; + +/** + * Server profile for Redis 2.2. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisVersion220 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.2'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfo', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion240.php b/vendor/predis/predis/src/Profile/RedisVersion240.php new file mode 100644 index 00000000..0856c37c --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion240.php @@ -0,0 +1,207 @@ +<?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\Profile; + +/** + * Server profile for Redis 2.4. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisVersion240 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.4'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfo', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion260.php b/vendor/predis/predis/src/Profile/RedisVersion260.php new file mode 100644 index 00000000..ba5084aa --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion260.php @@ -0,0 +1,235 @@ +<?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\Profile; + +/** + * Server profile for Redis 2.6. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisVersion260 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.6'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + 'DUMP' => 'Predis\Command\KeyDump', + 'RESTORE' => 'Predis\Command\KeyRestore', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfoV26x', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + + /* ---------------- Redis 2.6 ---------------- */ + + /* commands operating on the key space */ + 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', + 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', + 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', + 'MIGRATE' => 'Predis\Command\KeyMigrate', + + /* commands operating on string values */ + 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', + 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', + 'BITOP' => 'Predis\Command\StringBitOp', + 'BITCOUNT' => 'Predis\Command\StringBitCount', + + /* commands operating on hashes */ + 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', + + /* scripting */ + 'EVAL' => 'Predis\Command\ServerEval', + 'EVALSHA' => 'Predis\Command\ServerEvalSHA', + 'SCRIPT' => 'Predis\Command\ServerScript', + + /* remote server control commands */ + 'TIME' => 'Predis\Command\ServerTime', + 'SENTINEL' => 'Predis\Command\ServerSentinel', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion280.php b/vendor/predis/predis/src/Profile/RedisVersion280.php new file mode 100644 index 00000000..ea17e682 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion280.php @@ -0,0 +1,267 @@ +<?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\Profile; + +/** + * Server profile for Redis 2.8. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisVersion280 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '2.8'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + 'DUMP' => 'Predis\Command\KeyDump', + 'RESTORE' => 'Predis\Command\KeyRestore', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfoV26x', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + + /* ---------------- Redis 2.6 ---------------- */ + + /* commands operating on the key space */ + 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', + 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', + 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', + 'MIGRATE' => 'Predis\Command\KeyMigrate', + + /* commands operating on string values */ + 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', + 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', + 'BITOP' => 'Predis\Command\StringBitOp', + 'BITCOUNT' => 'Predis\Command\StringBitCount', + + /* commands operating on hashes */ + 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', + + /* scripting */ + 'EVAL' => 'Predis\Command\ServerEval', + 'EVALSHA' => 'Predis\Command\ServerEvalSHA', + 'SCRIPT' => 'Predis\Command\ServerScript', + + /* remote server control commands */ + 'TIME' => 'Predis\Command\ServerTime', + 'SENTINEL' => 'Predis\Command\ServerSentinel', + + /* ---------------- Redis 2.8 ---------------- */ + + /* commands operating on the key space */ + 'SCAN' => 'Predis\Command\KeyScan', + + /* commands operating on string values */ + 'BITPOS' => 'Predis\Command\StringBitPos', + + /* commands operating on sets */ + 'SSCAN' => 'Predis\Command\SetScan', + + /* commands operating on sorted sets */ + 'ZSCAN' => 'Predis\Command\ZSetScan', + 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount', + 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex', + 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex', + 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex', + + /* commands operating on hashes */ + 'HSCAN' => 'Predis\Command\HashScan', + + /* publish - subscribe */ + 'PUBSUB' => 'Predis\Command\PubSubPubsub', + + /* commands operating on HyperLogLog */ + 'PFADD' => 'Predis\Command\HyperLogLogAdd', + 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', + 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', + + /* remote server control commands */ + 'COMMAND' => 'Predis\Command\ServerCommand', + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion300.php b/vendor/predis/predis/src/Profile/RedisVersion300.php new file mode 100644 index 00000000..8a2fac8b --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion300.php @@ -0,0 +1,270 @@ +<?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\Profile; + +/** + * Server profile for Redis 3.0. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisVersion300 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '3.0'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + 'DUMP' => 'Predis\Command\KeyDump', + 'RESTORE' => 'Predis\Command\KeyRestore', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfoV26x', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + + /* ---------------- Redis 2.6 ---------------- */ + + /* commands operating on the key space */ + 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', + 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', + 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', + 'MIGRATE' => 'Predis\Command\KeyMigrate', + + /* commands operating on string values */ + 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', + 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', + 'BITOP' => 'Predis\Command\StringBitOp', + 'BITCOUNT' => 'Predis\Command\StringBitCount', + + /* commands operating on hashes */ + 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', + + /* scripting */ + 'EVAL' => 'Predis\Command\ServerEval', + 'EVALSHA' => 'Predis\Command\ServerEvalSHA', + 'SCRIPT' => 'Predis\Command\ServerScript', + + /* remote server control commands */ + 'TIME' => 'Predis\Command\ServerTime', + 'SENTINEL' => 'Predis\Command\ServerSentinel', + + /* ---------------- Redis 2.8 ---------------- */ + + /* commands operating on the key space */ + 'SCAN' => 'Predis\Command\KeyScan', + + /* commands operating on string values */ + 'BITPOS' => 'Predis\Command\StringBitPos', + + /* commands operating on sets */ + 'SSCAN' => 'Predis\Command\SetScan', + + /* commands operating on sorted sets */ + 'ZSCAN' => 'Predis\Command\ZSetScan', + 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount', + 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex', + 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex', + 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex', + + /* commands operating on hashes */ + 'HSCAN' => 'Predis\Command\HashScan', + + /* publish - subscribe */ + 'PUBSUB' => 'Predis\Command\PubSubPubsub', + + /* commands operating on HyperLogLog */ + 'PFADD' => 'Predis\Command\HyperLogLogAdd', + 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', + 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', + + /* remote server control commands */ + 'COMMAND' => 'Predis\Command\ServerCommand', + + /* ---------------- Redis 3.0 ---------------- */ + + ); + } +} diff --git a/vendor/predis/predis/src/Profile/RedisVersion320.php b/vendor/predis/predis/src/Profile/RedisVersion320.php new file mode 100644 index 00000000..7de79573 --- /dev/null +++ b/vendor/predis/predis/src/Profile/RedisVersion320.php @@ -0,0 +1,281 @@ +<?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\Profile; + +/** + * Server profile for Redis 3.0. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RedisVersion320 extends RedisProfile +{ + /** + * {@inheritdoc} + */ + public function getVersion() + { + return '3.2'; + } + + /** + * {@inheritdoc} + */ + public function getSupportedCommands() + { + return array( + /* ---------------- Redis 1.2 ---------------- */ + + /* commands operating on the key space */ + 'EXISTS' => 'Predis\Command\KeyExists', + 'DEL' => 'Predis\Command\KeyDelete', + 'TYPE' => 'Predis\Command\KeyType', + 'KEYS' => 'Predis\Command\KeyKeys', + 'RANDOMKEY' => 'Predis\Command\KeyRandom', + 'RENAME' => 'Predis\Command\KeyRename', + 'RENAMENX' => 'Predis\Command\KeyRenamePreserve', + 'EXPIRE' => 'Predis\Command\KeyExpire', + 'EXPIREAT' => 'Predis\Command\KeyExpireAt', + 'TTL' => 'Predis\Command\KeyTimeToLive', + 'MOVE' => 'Predis\Command\KeyMove', + 'SORT' => 'Predis\Command\KeySort', + 'DUMP' => 'Predis\Command\KeyDump', + 'RESTORE' => 'Predis\Command\KeyRestore', + + /* commands operating on string values */ + 'SET' => 'Predis\Command\StringSet', + 'SETNX' => 'Predis\Command\StringSetPreserve', + 'MSET' => 'Predis\Command\StringSetMultiple', + 'MSETNX' => 'Predis\Command\StringSetMultiplePreserve', + 'GET' => 'Predis\Command\StringGet', + 'MGET' => 'Predis\Command\StringGetMultiple', + 'GETSET' => 'Predis\Command\StringGetSet', + 'INCR' => 'Predis\Command\StringIncrement', + 'INCRBY' => 'Predis\Command\StringIncrementBy', + 'DECR' => 'Predis\Command\StringDecrement', + 'DECRBY' => 'Predis\Command\StringDecrementBy', + + /* commands operating on lists */ + 'RPUSH' => 'Predis\Command\ListPushTail', + 'LPUSH' => 'Predis\Command\ListPushHead', + 'LLEN' => 'Predis\Command\ListLength', + 'LRANGE' => 'Predis\Command\ListRange', + 'LTRIM' => 'Predis\Command\ListTrim', + 'LINDEX' => 'Predis\Command\ListIndex', + 'LSET' => 'Predis\Command\ListSet', + 'LREM' => 'Predis\Command\ListRemove', + 'LPOP' => 'Predis\Command\ListPopFirst', + 'RPOP' => 'Predis\Command\ListPopLast', + 'RPOPLPUSH' => 'Predis\Command\ListPopLastPushHead', + + /* commands operating on sets */ + 'SADD' => 'Predis\Command\SetAdd', + 'SREM' => 'Predis\Command\SetRemove', + 'SPOP' => 'Predis\Command\SetPop', + 'SMOVE' => 'Predis\Command\SetMove', + 'SCARD' => 'Predis\Command\SetCardinality', + 'SISMEMBER' => 'Predis\Command\SetIsMember', + 'SINTER' => 'Predis\Command\SetIntersection', + 'SINTERSTORE' => 'Predis\Command\SetIntersectionStore', + 'SUNION' => 'Predis\Command\SetUnion', + 'SUNIONSTORE' => 'Predis\Command\SetUnionStore', + 'SDIFF' => 'Predis\Command\SetDifference', + 'SDIFFSTORE' => 'Predis\Command\SetDifferenceStore', + 'SMEMBERS' => 'Predis\Command\SetMembers', + 'SRANDMEMBER' => 'Predis\Command\SetRandomMember', + + /* commands operating on sorted sets */ + 'ZADD' => 'Predis\Command\ZSetAdd', + 'ZINCRBY' => 'Predis\Command\ZSetIncrementBy', + 'ZREM' => 'Predis\Command\ZSetRemove', + 'ZRANGE' => 'Predis\Command\ZSetRange', + 'ZREVRANGE' => 'Predis\Command\ZSetReverseRange', + 'ZRANGEBYSCORE' => 'Predis\Command\ZSetRangeByScore', + 'ZCARD' => 'Predis\Command\ZSetCardinality', + 'ZSCORE' => 'Predis\Command\ZSetScore', + 'ZREMRANGEBYSCORE' => 'Predis\Command\ZSetRemoveRangeByScore', + + /* connection related commands */ + 'PING' => 'Predis\Command\ConnectionPing', + 'AUTH' => 'Predis\Command\ConnectionAuth', + 'SELECT' => 'Predis\Command\ConnectionSelect', + 'ECHO' => 'Predis\Command\ConnectionEcho', + 'QUIT' => 'Predis\Command\ConnectionQuit', + + /* remote server control commands */ + 'INFO' => 'Predis\Command\ServerInfoV26x', + 'SLAVEOF' => 'Predis\Command\ServerSlaveOf', + 'MONITOR' => 'Predis\Command\ServerMonitor', + 'DBSIZE' => 'Predis\Command\ServerDatabaseSize', + 'FLUSHDB' => 'Predis\Command\ServerFlushDatabase', + 'FLUSHALL' => 'Predis\Command\ServerFlushAll', + 'SAVE' => 'Predis\Command\ServerSave', + 'BGSAVE' => 'Predis\Command\ServerBackgroundSave', + 'LASTSAVE' => 'Predis\Command\ServerLastSave', + 'SHUTDOWN' => 'Predis\Command\ServerShutdown', + 'BGREWRITEAOF' => 'Predis\Command\ServerBackgroundRewriteAOF', + + /* ---------------- Redis 2.0 ---------------- */ + + /* commands operating on string values */ + 'SETEX' => 'Predis\Command\StringSetExpire', + 'APPEND' => 'Predis\Command\StringAppend', + 'SUBSTR' => 'Predis\Command\StringSubstr', + + /* commands operating on lists */ + 'BLPOP' => 'Predis\Command\ListPopFirstBlocking', + 'BRPOP' => 'Predis\Command\ListPopLastBlocking', + + /* commands operating on sorted sets */ + 'ZUNIONSTORE' => 'Predis\Command\ZSetUnionStore', + 'ZINTERSTORE' => 'Predis\Command\ZSetIntersectionStore', + 'ZCOUNT' => 'Predis\Command\ZSetCount', + 'ZRANK' => 'Predis\Command\ZSetRank', + 'ZREVRANK' => 'Predis\Command\ZSetReverseRank', + 'ZREMRANGEBYRANK' => 'Predis\Command\ZSetRemoveRangeByRank', + + /* commands operating on hashes */ + 'HSET' => 'Predis\Command\HashSet', + 'HSETNX' => 'Predis\Command\HashSetPreserve', + 'HMSET' => 'Predis\Command\HashSetMultiple', + 'HINCRBY' => 'Predis\Command\HashIncrementBy', + 'HGET' => 'Predis\Command\HashGet', + 'HMGET' => 'Predis\Command\HashGetMultiple', + 'HDEL' => 'Predis\Command\HashDelete', + 'HEXISTS' => 'Predis\Command\HashExists', + 'HLEN' => 'Predis\Command\HashLength', + 'HKEYS' => 'Predis\Command\HashKeys', + 'HVALS' => 'Predis\Command\HashValues', + 'HGETALL' => 'Predis\Command\HashGetAll', + + /* transactions */ + 'MULTI' => 'Predis\Command\TransactionMulti', + 'EXEC' => 'Predis\Command\TransactionExec', + 'DISCARD' => 'Predis\Command\TransactionDiscard', + + /* publish - subscribe */ + 'SUBSCRIBE' => 'Predis\Command\PubSubSubscribe', + 'UNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribe', + 'PSUBSCRIBE' => 'Predis\Command\PubSubSubscribeByPattern', + 'PUNSUBSCRIBE' => 'Predis\Command\PubSubUnsubscribeByPattern', + 'PUBLISH' => 'Predis\Command\PubSubPublish', + + /* remote server control commands */ + 'CONFIG' => 'Predis\Command\ServerConfig', + + /* ---------------- Redis 2.2 ---------------- */ + + /* commands operating on the key space */ + 'PERSIST' => 'Predis\Command\KeyPersist', + + /* commands operating on string values */ + 'STRLEN' => 'Predis\Command\StringStrlen', + 'SETRANGE' => 'Predis\Command\StringSetRange', + 'GETRANGE' => 'Predis\Command\StringGetRange', + 'SETBIT' => 'Predis\Command\StringSetBit', + 'GETBIT' => 'Predis\Command\StringGetBit', + + /* commands operating on lists */ + 'RPUSHX' => 'Predis\Command\ListPushTailX', + 'LPUSHX' => 'Predis\Command\ListPushHeadX', + 'LINSERT' => 'Predis\Command\ListInsert', + 'BRPOPLPUSH' => 'Predis\Command\ListPopLastPushHeadBlocking', + + /* commands operating on sorted sets */ + 'ZREVRANGEBYSCORE' => 'Predis\Command\ZSetReverseRangeByScore', + + /* transactions */ + 'WATCH' => 'Predis\Command\TransactionWatch', + 'UNWATCH' => 'Predis\Command\TransactionUnwatch', + + /* remote server control commands */ + 'OBJECT' => 'Predis\Command\ServerObject', + 'SLOWLOG' => 'Predis\Command\ServerSlowlog', + + /* ---------------- Redis 2.4 ---------------- */ + + /* remote server control commands */ + 'CLIENT' => 'Predis\Command\ServerClient', + + /* ---------------- Redis 2.6 ---------------- */ + + /* commands operating on the key space */ + 'PTTL' => 'Predis\Command\KeyPreciseTimeToLive', + 'PEXPIRE' => 'Predis\Command\KeyPreciseExpire', + 'PEXPIREAT' => 'Predis\Command\KeyPreciseExpireAt', + 'MIGRATE' => 'Predis\Command\KeyMigrate', + + /* commands operating on string values */ + 'PSETEX' => 'Predis\Command\StringPreciseSetExpire', + 'INCRBYFLOAT' => 'Predis\Command\StringIncrementByFloat', + 'BITOP' => 'Predis\Command\StringBitOp', + 'BITCOUNT' => 'Predis\Command\StringBitCount', + + /* commands operating on hashes */ + 'HINCRBYFLOAT' => 'Predis\Command\HashIncrementByFloat', + + /* scripting */ + 'EVAL' => 'Predis\Command\ServerEval', + 'EVALSHA' => 'Predis\Command\ServerEvalSHA', + 'SCRIPT' => 'Predis\Command\ServerScript', + + /* remote server control commands */ + 'TIME' => 'Predis\Command\ServerTime', + 'SENTINEL' => 'Predis\Command\ServerSentinel', + + /* ---------------- Redis 2.8 ---------------- */ + + /* commands operating on the key space */ + 'SCAN' => 'Predis\Command\KeyScan', + + /* commands operating on string values */ + 'BITPOS' => 'Predis\Command\StringBitPos', + + /* commands operating on sets */ + 'SSCAN' => 'Predis\Command\SetScan', + + /* commands operating on sorted sets */ + 'ZSCAN' => 'Predis\Command\ZSetScan', + 'ZLEXCOUNT' => 'Predis\Command\ZSetLexCount', + 'ZRANGEBYLEX' => 'Predis\Command\ZSetRangeByLex', + 'ZREMRANGEBYLEX' => 'Predis\Command\ZSetRemoveRangeByLex', + 'ZREVRANGEBYLEX' => 'Predis\Command\ZSetReverseRangeByLex', + + /* commands operating on hashes */ + 'HSCAN' => 'Predis\Command\HashScan', + + /* publish - subscribe */ + 'PUBSUB' => 'Predis\Command\PubSubPubsub', + + /* commands operating on HyperLogLog */ + 'PFADD' => 'Predis\Command\HyperLogLogAdd', + 'PFCOUNT' => 'Predis\Command\HyperLogLogCount', + 'PFMERGE' => 'Predis\Command\HyperLogLogMerge', + + /* remote server control commands */ + 'COMMAND' => 'Predis\Command\ServerCommand', + + /* ---------------- Redis 3.2 ---------------- */ + + /* commands operating on hashes */ + 'HSTRLEN' => 'Predis\Command\HashStringLength', + 'BITFIELD' => 'Predis\Command\StringBitField', + + /* commands performing geospatial operations */ + 'GEOADD' => 'Predis\Command\GeospatialGeoAdd', + 'GEOHASH' => 'Predis\Command\GeospatialGeoHash', + 'GEOPOS' => 'Predis\Command\GeospatialGeoPos', + 'GEODIST' => 'Predis\Command\GeospatialGeoDist', + 'GEORADIUS' => 'Predis\Command\GeospatialGeoRadius', + 'GEORADIUSBYMEMBER' => 'Predis\Command\GeospatialGeoRadiusByMember', + ); + } +} diff --git a/vendor/predis/predis/src/Protocol/ProtocolException.php b/vendor/predis/predis/src/Protocol/ProtocolException.php new file mode 100644 index 00000000..6fe5d6d3 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/ProtocolException.php @@ -0,0 +1,24 @@ +<?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\Protocol; + +use Predis\CommunicationException; + +/** + * Exception used to indentify errors encountered while parsing the Redis wire + * protocol. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ProtocolException extends CommunicationException +{ +} diff --git a/vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php b/vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php new file mode 100644 index 00000000..b34ea181 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php @@ -0,0 +1,41 @@ +<?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\Protocol; + +use Predis\Command\CommandInterface; +use Predis\Connection\CompositeConnectionInterface; + +/** + * Defines a pluggable protocol processor capable of serializing commands and + * deserializing responses into PHP objects directly from a connection. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ProtocolProcessorInterface +{ + /** + * Writes a request over a connection to Redis. + * + * @param CompositeConnectionInterface $connection Redis connection. + * @param CommandInterface $command Command instance. + */ + public function write(CompositeConnectionInterface $connection, CommandInterface $command); + + /** + * Reads a response from a connection to Redis. + * + * @param CompositeConnectionInterface $connection Redis connection. + * + * @return mixed + */ + public function read(CompositeConnectionInterface $connection); +} diff --git a/vendor/predis/predis/src/Protocol/RequestSerializerInterface.php b/vendor/predis/predis/src/Protocol/RequestSerializerInterface.php new file mode 100644 index 00000000..eef72a64 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/RequestSerializerInterface.php @@ -0,0 +1,31 @@ +<?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\Protocol; + +use Predis\Command\CommandInterface; + +/** + * Defines a pluggable serializer for Redis commands. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface RequestSerializerInterface +{ + /** + * Serializes a Redis command. + * + * @param CommandInterface $command Redis command. + * + * @return string + */ + public function serialize(CommandInterface $command); +} diff --git a/vendor/predis/predis/src/Protocol/ResponseReaderInterface.php b/vendor/predis/predis/src/Protocol/ResponseReaderInterface.php new file mode 100644 index 00000000..86a7bdcc --- /dev/null +++ b/vendor/predis/predis/src/Protocol/ResponseReaderInterface.php @@ -0,0 +1,32 @@ +<?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\Protocol; + +use Predis\Connection\CompositeConnectionInterface; + +/** + * Defines a pluggable reader capable of parsing responses returned by Redis and + * deserializing them to PHP objects. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ResponseReaderInterface +{ + /** + * Reads a response from a connection to Redis. + * + * @param CompositeConnectionInterface $connection Redis connection. + * + * @return mixed + */ + public function read(CompositeConnectionInterface $connection); +} diff --git a/vendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php b/vendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php new file mode 100644 index 00000000..ea85ed30 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php @@ -0,0 +1,107 @@ +<?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\Protocol\Text; + +use Predis\Command\CommandInterface; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolProcessorInterface; +use Predis\Protocol\RequestSerializerInterface; +use Predis\Protocol\ResponseReaderInterface; + +/** + * Composite protocol processor for the standard Redis wire protocol using + * pluggable handlers to serialize requests and deserialize responses. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class CompositeProtocolProcessor implements ProtocolProcessorInterface +{ + /* + * @var RequestSerializerInterface + */ + protected $serializer; + + /* + * @var ResponseReaderInterface + */ + protected $reader; + + /** + * @param RequestSerializerInterface $serializer Request serializer. + * @param ResponseReaderInterface $reader Response reader. + */ + public function __construct( + RequestSerializerInterface $serializer = null, + ResponseReaderInterface $reader = null + ) { + $this->setRequestSerializer($serializer ?: new RequestSerializer()); + $this->setResponseReader($reader ?: new ResponseReader()); + } + + /** + * {@inheritdoc} + */ + public function write(CompositeConnectionInterface $connection, CommandInterface $command) + { + $connection->writeBuffer($this->serializer->serialize($command)); + } + + /** + * {@inheritdoc} + */ + public function read(CompositeConnectionInterface $connection) + { + return $this->reader->read($connection); + } + + /** + * Sets the request serializer used by the protocol processor. + * + * @param RequestSerializerInterface $serializer Request serializer. + */ + public function setRequestSerializer(RequestSerializerInterface $serializer) + { + $this->serializer = $serializer; + } + + /** + * Returns the request serializer used by the protocol processor. + * + * @return RequestSerializerInterface + */ + public function getRequestSerializer() + { + return $this->serializer; + } + + /** + * Sets the response reader used by the protocol processor. + * + * @param ResponseReaderInterface $reader Response reader. + */ + public function setResponseReader(ResponseReaderInterface $reader) + { + $this->reader = $reader; + } + + /** + * Returns the Response reader used by the protocol processor. + * + * @return ResponseReaderInterface + */ + public function getResponseReader() + { + return $this->reader; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.php new file mode 100644 index 00000000..5b0bf3c2 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.php @@ -0,0 +1,55 @@ +<?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\Protocol\Text\Handler; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; + +/** + * Handler for the bulk response type in the standard Redis wire protocol. + * It translates the payload to a string or a NULL. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class BulkResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + $length = (int) $payload; + + if ("$length" !== $payload) { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$payload' as a valid length for a bulk response." + )); + } + + if ($length >= 0) { + return substr($connection->readBuffer($length + 2), 0, -2); + } + + if ($length == -1) { + return; + } + + CommunicationException::handle(new ProtocolException( + $connection, "Value '$payload' is not a valid length for a bulk response." + )); + + return; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php new file mode 100644 index 00000000..3e18b7b9 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php @@ -0,0 +1,34 @@ +<?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\Protocol\Text\Handler; + +use Predis\Connection\CompositeConnectionInterface; +use Predis\Response\Error; + +/** + * Handler for the error response type in the standard Redis wire protocol. + * It translates the payload to a complex response object for Predis. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ErrorResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + return new Error($payload); + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php new file mode 100644 index 00000000..4639d779 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php @@ -0,0 +1,45 @@ +<?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\Protocol\Text\Handler; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; + +/** + * Handler for the integer response type in the standard Redis wire protocol. + * It translates the payload an integer or NULL. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class IntegerResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + if (is_numeric($payload)) { + return (int) $payload; + } + + if ($payload !== 'nil') { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$payload' as a valid numeric response." + )); + } + + return; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php new file mode 100644 index 00000000..820b9b4a --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php @@ -0,0 +1,68 @@ +<?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\Protocol\Text\Handler; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; + +/** + * Handler for the multibulk response type in the standard Redis wire protocol. + * It returns multibulk responses as PHP arrays. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class MultiBulkResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + $length = (int) $payload; + + if ("$length" !== $payload) { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$payload' as a valid length of a multi-bulk response." + )); + } + + if ($length === -1) { + return; + } + + $list = array(); + + if ($length > 0) { + $handlersCache = array(); + $reader = $connection->getProtocol()->getResponseReader(); + + for ($i = 0; $i < $length; ++$i) { + $header = $connection->readLine(); + $prefix = $header[0]; + + if (isset($handlersCache[$prefix])) { + $handler = $handlersCache[$prefix]; + } else { + $handler = $reader->getHandler($prefix); + $handlersCache[$prefix] = $handler; + } + + $list[$i] = $handler->handle($connection, substr($header, 1)); + } + } + + return $list; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php b/vendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php new file mode 100644 index 00000000..ca08a9c5 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php @@ -0,0 +1,33 @@ +<?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\Protocol\Text\Handler; + +use Predis\Connection\CompositeConnectionInterface; + +/** + * Defines a pluggable handler used to parse a particular type of response. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ResponseHandlerInterface +{ + /** + * Deserializes a response returned by Redis and reads more data from the + * connection if needed. + * + * @param CompositeConnectionInterface $connection Redis connection. + * @param string $payload String payload. + * + * @return mixed + */ + public function handle(CompositeConnectionInterface $connection, $payload); +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php new file mode 100644 index 00000000..7bde5558 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php @@ -0,0 +1,35 @@ +<?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\Protocol\Text\Handler; + +use Predis\Connection\CompositeConnectionInterface; +use Predis\Response\Status; + +/** + * Handler for the status response type in the standard Redis wire protocol. It + * translates certain classes of status response to PHP objects or just returns + * the payload as a string. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StatusResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + return Status::get($payload); + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php new file mode 100644 index 00000000..7cdb736a --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php @@ -0,0 +1,47 @@ +<?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\Protocol\Text\Handler; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Response\Iterator\MultiBulk as MultiBulkIterator; + +/** + * Handler for the multibulk response type in the standard Redis wire protocol. + * It returns multibulk responses as iterators that can stream bulk elements. + * + * Streamable multibulk responses are not globally supported by the abstractions + * built-in into Predis, such as transactions or pipelines. Use them with care! + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class StreamableMultiBulkResponse implements ResponseHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function handle(CompositeConnectionInterface $connection, $payload) + { + $length = (int) $payload; + + if ("$length" != $payload) { + CommunicationException::handle(new ProtocolException( + $connection, "Cannot parse '$payload' as a valid length for a multi-bulk response." + )); + } + + return new MultiBulkIterator($connection, $length); + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php b/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php new file mode 100644 index 00000000..f04c3ed5 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php @@ -0,0 +1,122 @@ +<?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\Protocol\Text; + +use Predis\Command\CommandInterface; +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ProtocolProcessorInterface; +use Predis\Response\Error as ErrorResponse; +use Predis\Response\Iterator\MultiBulk as MultiBulkIterator; +use Predis\Response\Status as StatusResponse; + +/** + * Protocol processor for the standard Redis wire protocol. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ProtocolProcessor implements ProtocolProcessorInterface +{ + protected $mbiterable; + protected $serializer; + + /** + * + */ + public function __construct() + { + $this->mbiterable = false; + $this->serializer = new RequestSerializer(); + } + + /** + * {@inheritdoc} + */ + public function write(CompositeConnectionInterface $connection, CommandInterface $command) + { + $request = $this->serializer->serialize($command); + $connection->writeBuffer($request); + } + + /** + * {@inheritdoc} + */ + public function read(CompositeConnectionInterface $connection) + { + $chunk = $connection->readLine(); + $prefix = $chunk[0]; + $payload = substr($chunk, 1); + + switch ($prefix) { + case '+': + return new StatusResponse($payload); + + case '$': + $size = (int) $payload; + if ($size === -1) { + return; + } + + return substr($connection->readBuffer($size + 2), 0, -2); + + case '*': + $count = (int) $payload; + + if ($count === -1) { + return; + } + if ($this->mbiterable) { + return new MultiBulkIterator($connection, $count); + } + + $multibulk = array(); + + for ($i = 0; $i < $count; ++$i) { + $multibulk[$i] = $this->read($connection); + } + + return $multibulk; + + case ':': + return (int) $payload; + + case '-': + return new ErrorResponse($payload); + + default: + CommunicationException::handle(new ProtocolException( + $connection, "Unknown response prefix: '$prefix'." + )); + + return; + } + } + + /** + * Enables or disables returning multibulk responses as specialized PHP + * iterators used to stream bulk elements of a multibulk response instead + * returning a plain array. + * + * Streamable multibulk responses are not globally supported by the + * abstractions built-in into Predis, such as transactions or pipelines. + * Use them with care! + * + * @param bool $value Enable or disable streamable multibulk responses. + */ + public function useIterableMultibulk($value) + { + $this->mbiterable = (bool) $value; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/RequestSerializer.php b/vendor/predis/predis/src/Protocol/Text/RequestSerializer.php new file mode 100644 index 00000000..859595b1 --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/RequestSerializer.php @@ -0,0 +1,46 @@ +<?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\Protocol\Text; + +use Predis\Command\CommandInterface; +use Predis\Protocol\RequestSerializerInterface; + +/** + * Request serializer for the standard Redis wire protocol. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RequestSerializer implements RequestSerializerInterface +{ + /** + * {@inheritdoc} + */ + public function serialize(CommandInterface $command) + { + $commandID = $command->getId(); + $arguments = $command->getArguments(); + + $cmdlen = strlen($commandID); + $reqlen = count($arguments) + 1; + + $buffer = "*{$reqlen}\r\n\${$cmdlen}\r\n{$commandID}\r\n"; + + foreach ($arguments as $argument) { + $arglen = strlen($argument); + $buffer .= "\${$arglen}\r\n{$argument}\r\n"; + } + + return $buffer; + } +} diff --git a/vendor/predis/predis/src/Protocol/Text/ResponseReader.php b/vendor/predis/predis/src/Protocol/Text/ResponseReader.php new file mode 100644 index 00000000..d96218df --- /dev/null +++ b/vendor/predis/predis/src/Protocol/Text/ResponseReader.php @@ -0,0 +1,116 @@ +<?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\Protocol\Text; + +use Predis\CommunicationException; +use Predis\Connection\CompositeConnectionInterface; +use Predis\Protocol\ProtocolException; +use Predis\Protocol\ResponseReaderInterface; + +/** + * Response reader for the standard Redis wire protocol. + * + * @link http://redis.io/topics/protocol + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ResponseReader implements ResponseReaderInterface +{ + protected $handlers; + + /** + * + */ + public function __construct() + { + $this->handlers = $this->getDefaultHandlers(); + } + + /** + * Returns the default handlers for the supported type of responses. + * + * @return array + */ + protected function getDefaultHandlers() + { + return array( + '+' => new Handler\StatusResponse(), + '-' => new Handler\ErrorResponse(), + ':' => new Handler\IntegerResponse(), + '$' => new Handler\BulkResponse(), + '*' => new Handler\MultiBulkResponse(), + ); + } + + /** + * Sets the handler for the specified prefix identifying the response type. + * + * @param string $prefix Identifier of the type of response. + * @param Handler\ResponseHandlerInterface $handler Response handler. + */ + public function setHandler($prefix, Handler\ResponseHandlerInterface $handler) + { + $this->handlers[$prefix] = $handler; + } + + /** + * Returns the response handler associated to a certain type of response. + * + * @param string $prefix Identifier of the type of response. + * + * @return Handler\ResponseHandlerInterface + */ + public function getHandler($prefix) + { + if (isset($this->handlers[$prefix])) { + return $this->handlers[$prefix]; + } + + return; + } + + /** + * {@inheritdoc} + */ + public function read(CompositeConnectionInterface $connection) + { + $header = $connection->readLine(); + + if ($header === '') { + $this->onProtocolError($connection, 'Unexpected empty reponse header.'); + } + + $prefix = $header[0]; + + if (!isset($this->handlers[$prefix])) { + $this->onProtocolError($connection, "Unknown response prefix: '$prefix'."); + } + + $payload = $this->handlers[$prefix]->handle($connection, substr($header, 1)); + + return $payload; + } + + /** + * Handles protocol errors generated while reading responses from a + * connection. + * + * @param CompositeConnectionInterface $connection Redis connection that generated the error. + * @param string $message Error message. + */ + protected function onProtocolError(CompositeConnectionInterface $connection, $message) + { + CommunicationException::handle( + new ProtocolException($connection, $message) + ); + } +} diff --git a/vendor/predis/predis/src/PubSub/AbstractConsumer.php b/vendor/predis/predis/src/PubSub/AbstractConsumer.php new file mode 100644 index 00000000..8c6a71dd --- /dev/null +++ b/vendor/predis/predis/src/PubSub/AbstractConsumer.php @@ -0,0 +1,219 @@ +<?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\PubSub; + +/** + * Base implementation of a PUB/SUB consumer abstraction based on PHP iterators. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +abstract class AbstractConsumer implements \Iterator +{ + const SUBSCRIBE = 'subscribe'; + const UNSUBSCRIBE = 'unsubscribe'; + const PSUBSCRIBE = 'psubscribe'; + const PUNSUBSCRIBE = 'punsubscribe'; + const MESSAGE = 'message'; + const PMESSAGE = 'pmessage'; + const PONG = 'pong'; + + const STATUS_VALID = 1; // 0b0001 + const STATUS_SUBSCRIBED = 2; // 0b0010 + const STATUS_PSUBSCRIBED = 4; // 0b0100 + + private $position = null; + private $statusFlags = self::STATUS_VALID; + + /** + * Automatically stops the consumer when the garbage collector kicks in. + */ + public function __destruct() + { + $this->stop(true); + } + + /** + * Checks if the specified flag is valid based on the state of the consumer. + * + * @param int $value Flag. + * + * @return bool + */ + protected function isFlagSet($value) + { + return ($this->statusFlags & $value) === $value; + } + + /** + * Subscribes to the specified channels. + * + * @param mixed $channel,... One or more channel names. + */ + public function subscribe($channel /*, ... */) + { + $this->writeRequest(self::SUBSCRIBE, func_get_args()); + $this->statusFlags |= self::STATUS_SUBSCRIBED; + } + + /** + * Unsubscribes from the specified channels. + * + * @param string ... One or more channel names. + */ + public function unsubscribe(/* ... */) + { + $this->writeRequest(self::UNSUBSCRIBE, func_get_args()); + } + + /** + * Subscribes to the specified channels using a pattern. + * + * @param mixed $pattern,... One or more channel name patterns. + */ + public function psubscribe($pattern /* ... */) + { + $this->writeRequest(self::PSUBSCRIBE, func_get_args()); + $this->statusFlags |= self::STATUS_PSUBSCRIBED; + } + + /** + * Unsubscribes from the specified channels using a pattern. + * + * @param string ... One or more channel name patterns. + */ + public function punsubscribe(/* ... */) + { + $this->writeRequest(self::PUNSUBSCRIBE, func_get_args()); + } + + /** + * PING the server with an optional payload that will be echoed as a + * PONG message in the pub/sub loop. + * + * @param string $payload Optional PING payload. + */ + public function ping($payload = null) + { + $this->writeRequest('PING', array($payload)); + } + + /** + * Closes the context by unsubscribing from all the subscribed channels. The + * context can be forcefully closed by dropping the underlying connection. + * + * @param bool $drop Indicates if the context should be closed by dropping the connection. + * + * @return bool Returns false when there are no pending messages. + */ + public function stop($drop = false) + { + if (!$this->valid()) { + return false; + } + + if ($drop) { + $this->invalidate(); + $this->disconnect(); + } else { + if ($this->isFlagSet(self::STATUS_SUBSCRIBED)) { + $this->unsubscribe(); + } + if ($this->isFlagSet(self::STATUS_PSUBSCRIBED)) { + $this->punsubscribe(); + } + } + + return !$drop; + } + + /** + * Closes the underlying connection when forcing a disconnection. + */ + abstract protected function disconnect(); + + /** + * Writes a Redis command on the underlying connection. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + */ + abstract protected function writeRequest($method, $arguments); + + /** + * {@inheritdoc} + */ + public function rewind() + { + // NOOP + } + + /** + * Returns the last message payload retrieved from the server and generated + * by one of the active subscriptions. + * + * @return array + */ + public function current() + { + return $this->getValue(); + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + if ($this->valid()) { + ++$this->position; + } + + return $this->position; + } + + /** + * Checks if the the consumer is still in a valid state to continue. + * + * @return bool + */ + public function valid() + { + $isValid = $this->isFlagSet(self::STATUS_VALID); + $subscriptionFlags = self::STATUS_SUBSCRIBED | self::STATUS_PSUBSCRIBED; + $hasSubscriptions = ($this->statusFlags & $subscriptionFlags) > 0; + + return $isValid && $hasSubscriptions; + } + + /** + * Resets the state of the consumer. + */ + protected function invalidate() + { + $this->statusFlags = 0; // 0b0000; + } + + /** + * Waits for a new message from the server generated by one of the active + * subscriptions and returns it when available. + * + * @return array + */ + abstract protected function getValue(); +} diff --git a/vendor/predis/predis/src/PubSub/Consumer.php b/vendor/predis/predis/src/PubSub/Consumer.php new file mode 100644 index 00000000..5f2d8a8b --- /dev/null +++ b/vendor/predis/predis/src/PubSub/Consumer.php @@ -0,0 +1,158 @@ +<?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\PubSub; + +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\Command\Command; +use Predis\Connection\AggregateConnectionInterface; +use Predis\NotSupportedException; + +/** + * PUB/SUB consumer abstraction. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Consumer extends AbstractConsumer +{ + private $client; + private $options; + + /** + * @param ClientInterface $client Client instance used by the consumer. + * @param array $options Options for the consumer initialization. + */ + public function __construct(ClientInterface $client, array $options = null) + { + $this->checkCapabilities($client); + + $this->options = $options ?: array(); + $this->client = $client; + + $this->genericSubscribeInit('subscribe'); + $this->genericSubscribeInit('psubscribe'); + } + + /** + * Returns the underlying client instance used by the pub/sub iterator. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->client; + } + + /** + * Checks if the client instance satisfies the required conditions needed to + * initialize a PUB/SUB consumer. + * + * @param ClientInterface $client Client instance used by the consumer. + * + * @throws NotSupportedException + */ + private function checkCapabilities(ClientInterface $client) + { + if ($client->getConnection() instanceof AggregateConnectionInterface) { + throw new NotSupportedException( + 'Cannot initialize a PUB/SUB consumer over aggregate connections.' + ); + } + + $commands = array('publish', 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe'); + + if ($client->getProfile()->supportsCommands($commands) === false) { + throw new NotSupportedException( + 'The current profile does not support PUB/SUB related commands.' + ); + } + } + + /** + * This method shares the logic to handle both SUBSCRIBE and PSUBSCRIBE. + * + * @param string $subscribeAction Type of subscription. + */ + private function genericSubscribeInit($subscribeAction) + { + if (isset($this->options[$subscribeAction])) { + $this->$subscribeAction($this->options[$subscribeAction]); + } + } + + /** + * {@inheritdoc} + */ + protected function writeRequest($method, $arguments) + { + $this->client->getConnection()->writeRequest( + $this->client->createCommand($method, + Command::normalizeArguments($arguments) + ) + ); + } + + /** + * {@inheritdoc} + */ + protected function disconnect() + { + $this->client->disconnect(); + } + + /** + * {@inheritdoc} + */ + protected function getValue() + { + $response = $this->client->getConnection()->read(); + + switch ($response[0]) { + case self::SUBSCRIBE: + case self::UNSUBSCRIBE: + case self::PSUBSCRIBE: + case self::PUNSUBSCRIBE: + if ($response[2] === 0) { + $this->invalidate(); + } + // The missing break here is intentional as we must process + // subscriptions and unsubscriptions as standard messages. + // no break + + case self::MESSAGE: + return (object) array( + 'kind' => $response[0], + 'channel' => $response[1], + 'payload' => $response[2], + ); + + case self::PMESSAGE: + return (object) array( + 'kind' => $response[0], + 'pattern' => $response[1], + 'channel' => $response[2], + 'payload' => $response[3], + ); + + case self::PONG: + return (object) array( + 'kind' => $response[0], + 'payload' => $response[1], + ); + + default: + throw new ClientException( + "Unknown message type '{$response[0]}' received in the PUB/SUB context." + ); + } + } +} diff --git a/vendor/predis/predis/src/PubSub/DispatcherLoop.php b/vendor/predis/predis/src/PubSub/DispatcherLoop.php new file mode 100644 index 00000000..d0369e73 --- /dev/null +++ b/vendor/predis/predis/src/PubSub/DispatcherLoop.php @@ -0,0 +1,170 @@ +<?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\PubSub; + +/** + * Method-dispatcher loop built around the client-side abstraction of a Redis + * PUB / SUB context. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class DispatcherLoop +{ + private $pubsub; + + protected $callbacks; + protected $defaultCallback; + protected $subscriptionCallback; + + /** + * @param Consumer $pubsub PubSub consumer instance used by the loop. + */ + public function __construct(Consumer $pubsub) + { + $this->callbacks = array(); + $this->pubsub = $pubsub; + } + + /** + * Checks if the passed argument is a valid callback. + * + * @param mixed $callable A callback. + * + * @throws \InvalidArgumentException + */ + protected function assertCallback($callable) + { + if (!is_callable($callable)) { + throw new \InvalidArgumentException('The given argument must be a callable object.'); + } + } + + /** + * Returns the underlying PUB / SUB context. + * + * @return Consumer + */ + public function getPubSubConsumer() + { + return $this->pubsub; + } + + /** + * Sets a callback that gets invoked upon new subscriptions. + * + * @param mixed $callable A callback. + */ + public function subscriptionCallback($callable = null) + { + if (isset($callable)) { + $this->assertCallback($callable); + } + + $this->subscriptionCallback = $callable; + } + + /** + * Sets a callback that gets invoked when a message is received on a + * channel that does not have an associated callback. + * + * @param mixed $callable A callback. + */ + public function defaultCallback($callable = null) + { + if (isset($callable)) { + $this->assertCallback($callable); + } + + $this->subscriptionCallback = $callable; + } + + /** + * Binds a callback to a channel. + * + * @param string $channel Channel name. + * @param callable $callback A callback. + */ + public function attachCallback($channel, $callback) + { + $callbackName = $this->getPrefixKeys().$channel; + + $this->assertCallback($callback); + $this->callbacks[$callbackName] = $callback; + $this->pubsub->subscribe($channel); + } + + /** + * Stops listening to a channel and removes the associated callback. + * + * @param string $channel Redis channel. + */ + public function detachCallback($channel) + { + $callbackName = $this->getPrefixKeys().$channel; + + if (isset($this->callbacks[$callbackName])) { + unset($this->callbacks[$callbackName]); + $this->pubsub->unsubscribe($channel); + } + } + + /** + * Starts the dispatcher loop. + */ + public function run() + { + foreach ($this->pubsub as $message) { + $kind = $message->kind; + + if ($kind !== Consumer::MESSAGE && $kind !== Consumer::PMESSAGE) { + if (isset($this->subscriptionCallback)) { + $callback = $this->subscriptionCallback; + call_user_func($callback, $message); + } + + continue; + } + + if (isset($this->callbacks[$message->channel])) { + $callback = $this->callbacks[$message->channel]; + call_user_func($callback, $message->payload); + } elseif (isset($this->defaultCallback)) { + $callback = $this->defaultCallback; + call_user_func($callback, $message); + } + } + } + + /** + * Terminates the dispatcher loop. + */ + public function stop() + { + $this->pubsub->stop(); + } + + /** + * Return the prefix used for keys. + * + * @return string + */ + protected function getPrefixKeys() + { + $options = $this->pubsub->getClient()->getOptions(); + + if (isset($options->prefix)) { + return $options->prefix->getPrefix(); + } + + return ''; + } +} diff --git a/vendor/predis/predis/src/Replication/ReplicationStrategy.php b/vendor/predis/predis/src/Replication/ReplicationStrategy.php new file mode 100644 index 00000000..cd2d0ed5 --- /dev/null +++ b/vendor/predis/predis/src/Replication/ReplicationStrategy.php @@ -0,0 +1,304 @@ +<?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\Replication; + +use Predis\Command\CommandInterface; +use Predis\NotSupportedException; + +/** + * Defines a strategy for master/slave replication. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ReplicationStrategy +{ + protected $disallowed; + protected $readonly; + protected $readonlySHA1; + + /** + * + */ + public function __construct() + { + $this->disallowed = $this->getDisallowedOperations(); + $this->readonly = $this->getReadOnlyOperations(); + $this->readonlySHA1 = array(); + } + + /** + * Returns if the specified command will perform a read-only operation + * on Redis or not. + * + * @param CommandInterface $command Command instance. + * + * @throws NotSupportedException + * + * @return bool + */ + public function isReadOperation(CommandInterface $command) + { + if (isset($this->disallowed[$id = $command->getId()])) { + throw new NotSupportedException( + "The command '$id' is not allowed in replication mode." + ); + } + + if (isset($this->readonly[$id])) { + if (true === $readonly = $this->readonly[$id]) { + return true; + } + + return call_user_func($readonly, $command); + } + + if (($eval = $id === 'EVAL') || $id === 'EVALSHA') { + $sha1 = $eval ? sha1($command->getArgument(0)) : $command->getArgument(0); + + if (isset($this->readonlySHA1[$sha1])) { + if (true === $readonly = $this->readonlySHA1[$sha1]) { + return true; + } + + return call_user_func($readonly, $command); + } + } + + return false; + } + + /** + * Returns if the specified command is not allowed for execution in a master + * / slave replication context. + * + * @param CommandInterface $command Command instance. + * + * @return bool + */ + public function isDisallowedOperation(CommandInterface $command) + { + return isset($this->disallowed[$command->getId()]); + } + + /** + * Checks if a SORT command is a readable operation by parsing the arguments + * array of the specified commad instance. + * + * @param CommandInterface $command Command instance. + * + * @return bool + */ + protected function isSortReadOnly(CommandInterface $command) + { + $arguments = $command->getArguments(); + $argc = count($arguments); + + if ($argc > 1) { + for ($i = 1; $i < $argc; ++$i) { + $argument = strtoupper($arguments[$i]); + if ($argument === 'STORE') { + return false; + } + } + } + + return true; + } + + /** + * Checks if BITFIELD performs a read-only operation by looking for certain + * SET and INCRYBY modifiers in the arguments array of the command. + * + * @param CommandInterface $command Command instance. + * + * @return bool + */ + protected function isBitfieldReadOnly(CommandInterface $command) + { + $arguments = $command->getArguments(); + $argc = count($arguments); + + if ($argc >= 2) { + for ($i = 1; $i < $argc; ++$i) { + $argument = strtoupper($arguments[$i]); + if ($argument === 'SET' || $argument === 'INCRBY') { + return false; + } + } + } + + return true; + } + + /** + * Checks if a GEORADIUS command is a readable operation by parsing the + * arguments array of the specified commad instance. + * + * @param CommandInterface $command Command instance. + * + * @return bool + */ + protected function isGeoradiusReadOnly(CommandInterface $command) + { + $arguments = $command->getArguments(); + $argc = count($arguments); + $startIndex = $command->getId() === 'GEORADIUS' ? 5 : 4; + + if ($argc > $startIndex) { + for ($i = $startIndex; $i < $argc; ++$i) { + $argument = strtoupper($arguments[$i]); + if ($argument === 'STORE' || $argument === 'STOREDIST') { + return false; + } + } + } + + return true; + } + + /** + * Marks a command as a read-only operation. + * + * When the behavior of a command can be decided only at runtime depending + * on its arguments, a callable object can be provided to dynamically check + * if the specified command performs a read or a write operation. + * + * @param string $commandID Command ID. + * @param mixed $readonly A boolean value or a callable object. + */ + public function setCommandReadOnly($commandID, $readonly = true) + { + $commandID = strtoupper($commandID); + + if ($readonly) { + $this->readonly[$commandID] = $readonly; + } else { + unset($this->readonly[$commandID]); + } + } + + /** + * Marks a Lua script for EVAL and EVALSHA as a read-only operation. When + * the behaviour of a script can be decided only at runtime depending on + * its arguments, a callable object can be provided to dynamically check + * if the passed instance of EVAL or EVALSHA performs write operations or + * not. + * + * @param string $script Body of the Lua script. + * @param mixed $readonly A boolean value or a callable object. + */ + public function setScriptReadOnly($script, $readonly = true) + { + $sha1 = sha1($script); + + if ($readonly) { + $this->readonlySHA1[$sha1] = $readonly; + } else { + unset($this->readonlySHA1[$sha1]); + } + } + + /** + * Returns the default list of disallowed commands. + * + * @return array + */ + protected function getDisallowedOperations() + { + return array( + 'SHUTDOWN' => true, + 'INFO' => true, + 'DBSIZE' => true, + 'LASTSAVE' => true, + 'CONFIG' => true, + 'MONITOR' => true, + 'SLAVEOF' => true, + 'SAVE' => true, + 'BGSAVE' => true, + 'BGREWRITEAOF' => true, + 'SLOWLOG' => true, + ); + } + + /** + * Returns the default list of commands performing read-only operations. + * + * @return array + */ + protected function getReadOnlyOperations() + { + return array( + 'EXISTS' => true, + 'TYPE' => true, + 'KEYS' => true, + 'SCAN' => true, + 'RANDOMKEY' => true, + 'TTL' => true, + 'GET' => true, + 'MGET' => true, + 'SUBSTR' => true, + 'STRLEN' => true, + 'GETRANGE' => true, + 'GETBIT' => true, + 'LLEN' => true, + 'LRANGE' => true, + 'LINDEX' => true, + 'SCARD' => true, + 'SISMEMBER' => true, + 'SINTER' => true, + 'SUNION' => true, + 'SDIFF' => true, + 'SMEMBERS' => true, + 'SSCAN' => true, + 'SRANDMEMBER' => true, + 'ZRANGE' => true, + 'ZREVRANGE' => true, + 'ZRANGEBYSCORE' => true, + 'ZREVRANGEBYSCORE' => true, + 'ZCARD' => true, + 'ZSCORE' => true, + 'ZCOUNT' => true, + 'ZRANK' => true, + 'ZREVRANK' => true, + 'ZSCAN' => true, + 'ZLEXCOUNT' => true, + 'ZRANGEBYLEX' => true, + 'ZREVRANGEBYLEX' => true, + 'HGET' => true, + 'HMGET' => true, + 'HEXISTS' => true, + 'HLEN' => true, + 'HKEYS' => true, + 'HVALS' => true, + 'HGETALL' => true, + 'HSCAN' => true, + 'HSTRLEN' => true, + 'PING' => true, + 'AUTH' => true, + 'SELECT' => true, + 'ECHO' => true, + 'QUIT' => true, + 'OBJECT' => true, + 'BITCOUNT' => true, + 'BITPOS' => true, + 'TIME' => true, + 'PFCOUNT' => true, + 'SORT' => array($this, 'isSortReadOnly'), + 'BITFIELD' => array($this, 'isBitfieldReadOnly'), + 'GEOHASH' => true, + 'GEOPOS' => true, + 'GEODIST' => true, + 'GEORADIUS' => array($this, 'isGeoradiusReadOnly'), + 'GEORADIUSBYMEMBER' => array($this, 'isGeoradiusReadOnly'), + ); + } +} diff --git a/vendor/predis/predis/src/Response/Error.php b/vendor/predis/predis/src/Response/Error.php new file mode 100644 index 00000000..3933857e --- /dev/null +++ b/vendor/predis/predis/src/Response/Error.php @@ -0,0 +1,59 @@ +<?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\Response; + +/** + * Represents an error returned by Redis (-ERR responses) during the execution + * of a command on the server. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Error implements ErrorInterface +{ + private $message; + + /** + * @param string $message Error message returned by Redis + */ + public function __construct($message) + { + $this->message = $message; + } + + /** + * {@inheritdoc} + */ + public function getMessage() + { + return $this->message; + } + + /** + * {@inheritdoc} + */ + public function getErrorType() + { + list($errorType) = explode(' ', $this->getMessage(), 2); + + return $errorType; + } + + /** + * Converts the object to its string representation. + * + * @return string + */ + public function __toString() + { + return $this->getMessage(); + } +} diff --git a/vendor/predis/predis/src/Response/ErrorInterface.php b/vendor/predis/predis/src/Response/ErrorInterface.php new file mode 100644 index 00000000..a4a4a02f --- /dev/null +++ b/vendor/predis/predis/src/Response/ErrorInterface.php @@ -0,0 +1,35 @@ +<?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\Response; + +/** + * Represents an error returned by Redis (responses identified by "-" in the + * Redis protocol) during the execution of an operation on the server. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ErrorInterface extends ResponseInterface +{ + /** + * Returns the error message. + * + * @return string + */ + public function getMessage(); + + /** + * Returns the error type (e.g. ERR, ASK, MOVED). + * + * @return string + */ + public function getErrorType(); +} diff --git a/vendor/predis/predis/src/Response/Iterator/MultiBulk.php b/vendor/predis/predis/src/Response/Iterator/MultiBulk.php new file mode 100644 index 00000000..b1d29241 --- /dev/null +++ b/vendor/predis/predis/src/Response/Iterator/MultiBulk.php @@ -0,0 +1,77 @@ +<?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\Response\Iterator; + +use Predis\Connection\NodeConnectionInterface; + +/** + * Streamable multibulk response. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class MultiBulk extends MultiBulkIterator +{ + private $connection; + + /** + * @param NodeConnectionInterface $connection Connection to Redis. + * @param int $size Number of elements of the multibulk response. + */ + public function __construct(NodeConnectionInterface $connection, $size) + { + $this->connection = $connection; + $this->size = $size; + $this->position = 0; + $this->current = $size > 0 ? $this->getValue() : null; + } + + /** + * Handles the synchronization of the client with the Redis protocol when + * the garbage collector kicks in (e.g. when the iterator goes out of the + * scope of a foreach or it is unset). + */ + public function __destruct() + { + $this->drop(true); + } + + /** + * Drop queued elements that have not been read from the connection either + * by consuming the rest of the multibulk response or quickly by closing the + * underlying connection. + * + * @param bool $disconnect Consume the iterator or drop the connection. + */ + public function drop($disconnect = false) + { + if ($disconnect) { + if ($this->valid()) { + $this->position = $this->size; + $this->connection->disconnect(); + } + } else { + while ($this->valid()) { + $this->next(); + } + } + } + + /** + * Reads the next item of the multibulk response from the connection. + * + * @return mixed + */ + protected function getValue() + { + return $this->connection->read(); + } +} diff --git a/vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.php b/vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.php new file mode 100644 index 00000000..5d328869 --- /dev/null +++ b/vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.php @@ -0,0 +1,104 @@ +<?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\Response\Iterator; + +use Predis\Response\ResponseInterface; + +/** + * Iterator that abstracts the access to multibulk responses allowing them to be + * consumed in a streamable fashion without keeping the whole payload in memory. + * + * This iterator does not support rewinding which means that the iteration, once + * consumed, cannot be restarted. + * + * Always make sure that the whole iteration is consumed (or dropped) to prevent + * protocol desynchronization issues. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +abstract class MultiBulkIterator implements \Iterator, \Countable, ResponseInterface +{ + protected $current; + protected $position; + protected $size; + + /** + * {@inheritdoc} + */ + public function rewind() + { + // NOOP + } + + /** + * {@inheritdoc} + */ + public function current() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function key() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + public function next() + { + if (++$this->position < $this->size) { + $this->current = $this->getValue(); + } + } + + /** + * {@inheritdoc} + */ + public function valid() + { + return $this->position < $this->size; + } + + /** + * Returns the number of items comprising the whole multibulk response. + * + * This method should be used instead of iterator_count() to get the size of + * the current multibulk response since the former consumes the iteration to + * count the number of elements, but our iterators do not support rewinding. + * + * @return int + */ + public function count() + { + return $this->size; + } + + /** + * Returns the current position of the iterator. + * + * @return int + */ + public function getPosition() + { + return $this->position; + } + + /** + * {@inheritdoc} + */ + abstract protected function getValue(); +} diff --git a/vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php b/vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php new file mode 100644 index 00000000..2b6f593c --- /dev/null +++ b/vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php @@ -0,0 +1,90 @@ +<?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\Response\Iterator; + +/** + * Outer iterator consuming streamable multibulk responses by yielding tuples of + * keys and values. + * + * This wrapper is useful for responses to commands such as `HGETALL` that can + * be iterater as $key => $value pairs. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class MultiBulkTuple extends MultiBulk implements \OuterIterator +{ + private $iterator; + + /** + * @param MultiBulk $iterator Inner multibulk response iterator. + */ + public function __construct(MultiBulk $iterator) + { + $this->checkPreconditions($iterator); + + $this->size = count($iterator) / 2; + $this->iterator = $iterator; + $this->position = $iterator->getPosition(); + $this->current = $this->size > 0 ? $this->getValue() : null; + } + + /** + * Checks for valid preconditions. + * + * @param MultiBulk $iterator Inner multibulk response iterator. + * + * @throws \InvalidArgumentException + * @throws \UnexpectedValueException + */ + protected function checkPreconditions(MultiBulk $iterator) + { + if ($iterator->getPosition() !== 0) { + throw new \InvalidArgumentException( + 'Cannot initialize a tuple iterator using an already initiated iterator.' + ); + } + + if (($size = count($iterator)) % 2 !== 0) { + throw new \UnexpectedValueException('Invalid response size for a tuple iterator.'); + } + } + + /** + * {@inheritdoc} + */ + public function getInnerIterator() + { + return $this->iterator; + } + + /** + * {@inheritdoc} + */ + public function __destruct() + { + $this->iterator->drop(true); + } + + /** + * {@inheritdoc} + */ + protected function getValue() + { + $k = $this->iterator->current(); + $this->iterator->next(); + + $v = $this->iterator->current(); + $this->iterator->next(); + + return array($k, $v); + } +} diff --git a/vendor/predis/predis/src/Response/ResponseInterface.php b/vendor/predis/predis/src/Response/ResponseInterface.php new file mode 100644 index 00000000..0af13574 --- /dev/null +++ b/vendor/predis/predis/src/Response/ResponseInterface.php @@ -0,0 +1,21 @@ +<?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\Response; + +/** + * Represents a complex response object from Redis. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +interface ResponseInterface +{ +} diff --git a/vendor/predis/predis/src/Response/ServerException.php b/vendor/predis/predis/src/Response/ServerException.php new file mode 100644 index 00000000..407dc5b7 --- /dev/null +++ b/vendor/predis/predis/src/Response/ServerException.php @@ -0,0 +1,44 @@ +<?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\Response; + +use Predis\PredisException; + +/** + * Exception class that identifies server-side Redis errors. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class ServerException extends PredisException implements ErrorInterface +{ + /** + * Gets the type of the error returned by Redis. + * + * @return string + */ + public function getErrorType() + { + list($errorType) = explode(' ', $this->getMessage(), 2); + + return $errorType; + } + + /** + * Converts the exception to an instance of Predis\Response\Error. + * + * @return Error + */ + public function toErrorResponse() + { + return new Error($this->getMessage()); + } +} diff --git a/vendor/predis/predis/src/Response/Status.php b/vendor/predis/predis/src/Response/Status.php new file mode 100644 index 00000000..729bb663 --- /dev/null +++ b/vendor/predis/predis/src/Response/Status.php @@ -0,0 +1,79 @@ +<?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\Response; + +/** + * Represents a status response returned by Redis. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Status implements ResponseInterface +{ + private static $OK; + private static $QUEUED; + + private $payload; + + /** + * @param string $payload Payload of the status response as returned by Redis. + */ + public function __construct($payload) + { + $this->payload = $payload; + } + + /** + * Converts the response object to its string representation. + * + * @return string + */ + public function __toString() + { + return $this->payload; + } + + /** + * Returns the payload of status response. + * + * @return string + */ + public function getPayload() + { + return $this->payload; + } + + /** + * Returns an instance of a status response object. + * + * Common status responses such as OK or QUEUED are cached in order to lower + * the global memory usage especially when using pipelines. + * + * @param string $payload Status response payload. + * + * @return string + */ + public static function get($payload) + { + switch ($payload) { + case 'OK': + case 'QUEUED': + if (isset(self::$$payload)) { + return self::$$payload; + } + + return self::$$payload = new self($payload); + + default: + return new self($payload); + } + } +} diff --git a/vendor/predis/predis/src/Session/Handler.php b/vendor/predis/predis/src/Session/Handler.php new file mode 100644 index 00000000..cecb9d53 --- /dev/null +++ b/vendor/predis/predis/src/Session/Handler.php @@ -0,0 +1,142 @@ +<?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\Session; + +use Predis\ClientInterface; + +/** + * Session handler class that relies on Predis\Client to store PHP's sessions + * data into one or multiple Redis servers. + * + * This class is mostly intended for PHP 5.4 but it can be used under PHP 5.3 + * provided that a polyfill for `SessionHandlerInterface` is defined by either + * you or an external package such as `symfony/http-foundation`. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class Handler implements \SessionHandlerInterface +{ + protected $client; + protected $ttl; + + /** + * @param ClientInterface $client Fully initialized client instance. + * @param array $options Session handler options. + */ + public function __construct(ClientInterface $client, array $options = array()) + { + $this->client = $client; + + if (isset($options['gc_maxlifetime'])) { + $this->ttl = (int) $options['gc_maxlifetime']; + } else { + $this->ttl = ini_get('session.gc_maxlifetime'); + } + } + + /** + * Registers this instance as the current session handler. + */ + public function register() + { + if (PHP_VERSION_ID >= 50400) { + session_set_save_handler($this, true); + } else { + session_set_save_handler( + array($this, 'open'), + array($this, 'close'), + array($this, 'read'), + array($this, 'write'), + array($this, 'destroy'), + array($this, 'gc') + ); + } + } + + /** + * {@inheritdoc} + */ + public function open($save_path, $session_id) + { + // NOOP + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + // NOOP + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + // NOOP + return true; + } + + /** + * {@inheritdoc} + */ + public function read($session_id) + { + if ($data = $this->client->get($session_id)) { + return $data; + } + + return ''; + } + /** + * {@inheritdoc} + */ + public function write($session_id, $session_data) + { + $this->client->setex($session_id, $this->ttl, $session_data); + + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($session_id) + { + $this->client->del($session_id); + + return true; + } + + /** + * Returns the underlying client instance. + * + * @return ClientInterface + */ + public function getClient() + { + return $this->client; + } + + /** + * Returns the session max lifetime value. + * + * @return int + */ + public function getMaxLifeTime() + { + return $this->ttl; + } +} diff --git a/vendor/predis/predis/src/Transaction/AbortedMultiExecException.php b/vendor/predis/predis/src/Transaction/AbortedMultiExecException.php new file mode 100644 index 00000000..b36f38aa --- /dev/null +++ b/vendor/predis/predis/src/Transaction/AbortedMultiExecException.php @@ -0,0 +1,45 @@ +<?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\Transaction; + +use Predis\PredisException; + +/** + * Exception class that identifies a MULTI / EXEC transaction aborted by Redis. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class AbortedMultiExecException extends PredisException +{ + private $transaction; + + /** + * @param MultiExec $transaction Transaction that generated the exception. + * @param string $message Error message. + * @param int $code Error code. + */ + public function __construct(MultiExec $transaction, $message, $code = null) + { + parent::__construct($message, $code); + $this->transaction = $transaction; + } + + /** + * Returns the transaction that generated the exception. + * + * @return MultiExec + */ + public function getTransaction() + { + return $this->transaction; + } +} diff --git a/vendor/predis/predis/src/Transaction/MultiExec.php b/vendor/predis/predis/src/Transaction/MultiExec.php new file mode 100644 index 00000000..0cf1962d --- /dev/null +++ b/vendor/predis/predis/src/Transaction/MultiExec.php @@ -0,0 +1,461 @@ +<?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\Transaction; + +use Predis\ClientContextInterface; +use Predis\ClientException; +use Predis\ClientInterface; +use Predis\Command\CommandInterface; +use Predis\CommunicationException; +use Predis\Connection\AggregateConnectionInterface; +use Predis\NotSupportedException; +use Predis\Protocol\ProtocolException; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ServerException; +use Predis\Response\Status as StatusResponse; + +/** + * Client-side abstraction of a Redis transaction based on MULTI / EXEC. + * + * {@inheritdoc} + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class MultiExec implements ClientContextInterface +{ + private $state; + + protected $client; + protected $commands; + protected $exceptions = true; + protected $attempts = 0; + protected $watchKeys = array(); + protected $modeCAS = false; + + /** + * @param ClientInterface $client Client instance used by the transaction. + * @param array $options Initialization options. + */ + public function __construct(ClientInterface $client, array $options = null) + { + $this->assertClient($client); + + $this->client = $client; + $this->state = new MultiExecState(); + + $this->configure($client, $options ?: array()); + $this->reset(); + } + + /** + * Checks if the passed client instance satisfies the required conditions + * needed to initialize the transaction object. + * + * @param ClientInterface $client Client instance used by the transaction object. + * + * @throws NotSupportedException + */ + private function assertClient(ClientInterface $client) + { + if ($client->getConnection() instanceof AggregateConnectionInterface) { + throw new NotSupportedException( + 'Cannot initialize a MULTI/EXEC transaction over aggregate connections.' + ); + } + + if (!$client->getProfile()->supportsCommands(array('MULTI', 'EXEC', 'DISCARD'))) { + throw new NotSupportedException( + 'The current profile does not support MULTI, EXEC and DISCARD.' + ); + } + } + + /** + * Configures the transaction using the provided options. + * + * @param ClientInterface $client Underlying client instance. + * @param array $options Array of options for the transaction. + **/ + protected function configure(ClientInterface $client, array $options) + { + if (isset($options['exceptions'])) { + $this->exceptions = (bool) $options['exceptions']; + } else { + $this->exceptions = $client->getOptions()->exceptions; + } + + if (isset($options['cas'])) { + $this->modeCAS = (bool) $options['cas']; + } + + if (isset($options['watch']) && $keys = $options['watch']) { + $this->watchKeys = $keys; + } + + if (isset($options['retry'])) { + $this->attempts = (int) $options['retry']; + } + } + + /** + * Resets the state of the transaction. + */ + protected function reset() + { + $this->state->reset(); + $this->commands = new \SplQueue(); + } + + /** + * Initializes the transaction context. + */ + protected function initialize() + { + if ($this->state->isInitialized()) { + return; + } + + if ($this->modeCAS) { + $this->state->flag(MultiExecState::CAS); + } + + if ($this->watchKeys) { + $this->watch($this->watchKeys); + } + + $cas = $this->state->isCAS(); + $discarded = $this->state->isDiscarded(); + + if (!$cas || ($cas && $discarded)) { + $this->call('MULTI'); + + if ($discarded) { + $this->state->unflag(MultiExecState::CAS); + } + } + + $this->state->unflag(MultiExecState::DISCARDED); + $this->state->flag(MultiExecState::INITIALIZED); + } + + /** + * Dynamically invokes a Redis command with the specified arguments. + * + * @param string $method Command ID. + * @param array $arguments Arguments for the command. + * + * @return mixed + */ + public function __call($method, $arguments) + { + return $this->executeCommand( + $this->client->createCommand($method, $arguments) + ); + } + + /** + * Executes a Redis command bypassing the transaction logic. + * + * @param string $commandID Command ID. + * @param array $arguments Arguments for the command. + * + * @throws ServerException + * + * @return mixed + */ + protected function call($commandID, array $arguments = array()) + { + $response = $this->client->executeCommand( + $this->client->createCommand($commandID, $arguments) + ); + + if ($response instanceof ErrorResponseInterface) { + throw new ServerException($response->getMessage()); + } + + return $response; + } + + /** + * Executes the specified Redis command. + * + * @param CommandInterface $command Command instance. + * + * @throws AbortedMultiExecException + * @throws CommunicationException + * + * @return $this|mixed + */ + public function executeCommand(CommandInterface $command) + { + $this->initialize(); + + if ($this->state->isCAS()) { + return $this->client->executeCommand($command); + } + + $response = $this->client->getConnection()->executeCommand($command); + + if ($response instanceof StatusResponse && $response == 'QUEUED') { + $this->commands->enqueue($command); + } elseif ($response instanceof ErrorResponseInterface) { + throw new AbortedMultiExecException($this, $response->getMessage()); + } else { + $this->onProtocolError('The server did not return a +QUEUED status response.'); + } + + return $this; + } + + /** + * Executes WATCH against one or more keys. + * + * @param string|array $keys One or more keys. + * + * @throws NotSupportedException + * @throws ClientException + * + * @return mixed + */ + public function watch($keys) + { + if (!$this->client->getProfile()->supportsCommand('WATCH')) { + throw new NotSupportedException('WATCH is not supported by the current profile.'); + } + + if ($this->state->isWatchAllowed()) { + throw new ClientException('Sending WATCH after MULTI is not allowed.'); + } + + $response = $this->call('WATCH', is_array($keys) ? $keys : array($keys)); + $this->state->flag(MultiExecState::WATCH); + + return $response; + } + + /** + * Finalizes the transaction by executing MULTI on the server. + * + * @return MultiExec + */ + public function multi() + { + if ($this->state->check(MultiExecState::INITIALIZED | MultiExecState::CAS)) { + $this->state->unflag(MultiExecState::CAS); + $this->call('MULTI'); + } else { + $this->initialize(); + } + + return $this; + } + + /** + * Executes UNWATCH. + * + * @throws NotSupportedException + * + * @return MultiExec + */ + public function unwatch() + { + if (!$this->client->getProfile()->supportsCommand('UNWATCH')) { + throw new NotSupportedException( + 'UNWATCH is not supported by the current profile.' + ); + } + + $this->state->unflag(MultiExecState::WATCH); + $this->__call('UNWATCH', array()); + + return $this; + } + + /** + * Resets the transaction by UNWATCH-ing the keys that are being WATCHed and + * DISCARD-ing pending commands that have been already sent to the server. + * + * @return MultiExec + */ + public function discard() + { + if ($this->state->isInitialized()) { + $this->call($this->state->isCAS() ? 'UNWATCH' : 'DISCARD'); + + $this->reset(); + $this->state->flag(MultiExecState::DISCARDED); + } + + return $this; + } + + /** + * Executes the whole transaction. + * + * @return mixed + */ + public function exec() + { + return $this->execute(); + } + + /** + * Checks the state of the transaction before execution. + * + * @param mixed $callable Callback for execution. + * + * @throws \InvalidArgumentException + * @throws ClientException + */ + private function checkBeforeExecution($callable) + { + if ($this->state->isExecuting()) { + throw new ClientException( + 'Cannot invoke "execute" or "exec" inside an active transaction context.' + ); + } + + if ($callable) { + if (!is_callable($callable)) { + throw new \InvalidArgumentException('The argument must be a callable object.'); + } + + if (!$this->commands->isEmpty()) { + $this->discard(); + + throw new ClientException( + 'Cannot execute a transaction block after using fluent interface.' + ); + } + } elseif ($this->attempts) { + $this->discard(); + + throw new ClientException( + 'Automatic retries are supported only when a callable block is provided.' + ); + } + } + + /** + * Handles the actual execution of the whole transaction. + * + * @param mixed $callable Optional callback for execution. + * + * @throws CommunicationException + * @throws AbortedMultiExecException + * @throws ServerException + * + * @return array + */ + public function execute($callable = null) + { + $this->checkBeforeExecution($callable); + + $execResponse = null; + $attempts = $this->attempts; + + do { + if ($callable) { + $this->executeTransactionBlock($callable); + } + + if ($this->commands->isEmpty()) { + if ($this->state->isWatching()) { + $this->discard(); + } + + return; + } + + $execResponse = $this->call('EXEC'); + + if ($execResponse === null) { + if ($attempts === 0) { + throw new AbortedMultiExecException( + $this, 'The current transaction has been aborted by the server.' + ); + } + + $this->reset(); + + continue; + } + + break; + } while ($attempts-- > 0); + + $response = array(); + $commands = $this->commands; + $size = count($execResponse); + + if ($size !== count($commands)) { + $this->onProtocolError('EXEC returned an unexpected number of response items.'); + } + + for ($i = 0; $i < $size; ++$i) { + $cmdResponse = $execResponse[$i]; + + if ($cmdResponse instanceof ErrorResponseInterface && $this->exceptions) { + throw new ServerException($cmdResponse->getMessage()); + } + + $response[$i] = $commands->dequeue()->parseResponse($cmdResponse); + } + + return $response; + } + + /** + * Passes the current transaction object to a callable block for execution. + * + * @param mixed $callable Callback. + * + * @throws CommunicationException + * @throws ServerException + */ + protected function executeTransactionBlock($callable) + { + $exception = null; + $this->state->flag(MultiExecState::INSIDEBLOCK); + + try { + call_user_func($callable, $this); + } catch (CommunicationException $exception) { + // NOOP + } catch (ServerException $exception) { + // NOOP + } catch (\Exception $exception) { + $this->discard(); + } + + $this->state->unflag(MultiExecState::INSIDEBLOCK); + + if ($exception) { + throw $exception; + } + } + + /** + * Helper method for protocol errors encountered inside the transaction. + * + * @param string $message Error message. + */ + private function onProtocolError($message) + { + // Since a MULTI/EXEC block cannot be initialized when using aggregate + // connections we can safely assume that Predis\Client::getConnection() + // will return a Predis\Connection\NodeConnectionInterface instance. + CommunicationException::handle(new ProtocolException( + $this->client->getConnection(), $message + )); + } +} diff --git a/vendor/predis/predis/src/Transaction/MultiExecState.php b/vendor/predis/predis/src/Transaction/MultiExecState.php new file mode 100644 index 00000000..4bed42af --- /dev/null +++ b/vendor/predis/predis/src/Transaction/MultiExecState.php @@ -0,0 +1,166 @@ +<?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\Transaction; + +/** + * Utility class used to track the state of a MULTI / EXEC transaction. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class MultiExecState +{ + const INITIALIZED = 1; // 0b00001 + const INSIDEBLOCK = 2; // 0b00010 + const DISCARDED = 4; // 0b00100 + const CAS = 8; // 0b01000 + const WATCH = 16; // 0b10000 + + private $flags; + + /** + * + */ + public function __construct() + { + $this->flags = 0; + } + + /** + * Sets the internal state flags. + * + * @param int $flags Set of flags + */ + public function set($flags) + { + $this->flags = $flags; + } + + /** + * Gets the internal state flags. + * + * @return int + */ + public function get() + { + return $this->flags; + } + + /** + * Sets one or more flags. + * + * @param int $flags Set of flags + */ + public function flag($flags) + { + $this->flags |= $flags; + } + + /** + * Resets one or more flags. + * + * @param int $flags Set of flags + */ + public function unflag($flags) + { + $this->flags &= ~$flags; + } + + /** + * Returns if the specified flag or set of flags is set. + * + * @param int $flags Flag + * + * @return bool + */ + public function check($flags) + { + return ($this->flags & $flags) === $flags; + } + + /** + * Resets the state of a transaction. + */ + public function reset() + { + $this->flags = 0; + } + + /** + * Returns the state of the RESET flag. + * + * @return bool + */ + public function isReset() + { + return $this->flags === 0; + } + + /** + * Returns the state of the INITIALIZED flag. + * + * @return bool + */ + public function isInitialized() + { + return $this->check(self::INITIALIZED); + } + + /** + * Returns the state of the INSIDEBLOCK flag. + * + * @return bool + */ + public function isExecuting() + { + return $this->check(self::INSIDEBLOCK); + } + + /** + * Returns the state of the CAS flag. + * + * @return bool + */ + public function isCAS() + { + return $this->check(self::CAS); + } + + /** + * Returns if WATCH is allowed in the current state. + * + * @return bool + */ + public function isWatchAllowed() + { + return $this->check(self::INITIALIZED) && !$this->check(self::CAS); + } + + /** + * Returns the state of the WATCH flag. + * + * @return bool + */ + public function isWatching() + { + return $this->check(self::WATCH); + } + + /** + * Returns the state of the DISCARDED flag. + * + * @return bool + */ + public function isDiscarded() + { + return $this->check(self::DISCARDED); + } +} |