diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-05-02 16:14:14 +0300 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-05-02 16:14:14 +0300 |
commit | dd9d359df755dfc0dc35b47bd2ef12f9a01cb57f (patch) | |
tree | a7dbc50de43c1ca647241a08048d608d4482612a /vendor | |
parent | c603cf71406ae2ff7c3771290c26a954846c1190 (diff) |
Fixed rate limit and remove unwanted dependancy
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'vendor')
48 files changed, 1618 insertions, 368 deletions
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index da997c97..f3847525 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -8,7 +8,6 @@ $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 0037c20d..df43a2da 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -11,7 +11,6 @@ class ComposerStaticInit8fa0ec799546a9f5452392a519c87a0d array ( 'Psr\\Log\\' => 8, 'Predis\\' => 7, - 'Perimeter\\RateLimiter\\' => 22, ), 'M' => array ( @@ -39,10 +38,6 @@ class ComposerStaticInit8fa0ec799546a9f5452392a519c87a0d 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 88db5999..27586dc9 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -305,84 +305,22 @@ "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", + "version": "v1.1.1", + "version_normalized": "1.1.1.0", "source": { "type": "git", "url": "https://github.com/nrk/predis.git", - "reference": "9ead747663bb1b1ae017dfa0d152aca87792b42f" + "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nrk/predis/zipball/9ead747663bb1b1ae017dfa0d152aca87792b42f", - "reference": "9ead747663bb1b1ae017dfa0d152aca87792b42f", + "url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1", + "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": ">=5.3.9" }, "require-dev": { "phpunit/phpunit": "~4.8" @@ -391,7 +329,7 @@ "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", + "time": "2016-06-16T16:22:20+00:00", "type": "library", "installation-source": "dist", "autoload": { diff --git a/vendor/perimeter/rate-limiter-php b/vendor/perimeter/rate-limiter-php deleted file mode 160000 -Subproject 121ba37284dd701afc2f546c4f49d1954564a24 diff --git a/vendor/predis/predis/CHANGELOG.md b/vendor/predis/predis/CHANGELOG.md index 2f3606a2..68d09786 100644 --- a/vendor/predis/predis/CHANGELOG.md +++ b/vendor/predis/predis/CHANGELOG.md @@ -1,3 +1,96 @@ +v1.1.1 (2016-06-16) +================================================================================ + +- __FIX__: `password` and `database` from the global `parameters` client option + were still being applied to sentinels connections making them fail (sentinels + do not understand the `AUTH` and `SELECT` commands) (PR #346). + +- __FIX__: when a sentinel instance reports no sentinel for a service, invoking + `connect()` on the redis-sentinel connection backend should fall back to the + master connection instead of failing (ISSUE #342). + +- __FIX__: the two connection backends based on ext-phpiredis has some kind of + issues with the GC and the internal use of closures as reader callbacks that + prevented connections going out of scope from being properly collected and the + underlying stream or socket resources from being closed and freed. This should + not have had any actual effect in real-world scenarios due to the lifecycle of + PHP scripts, but we fixed it anyway (ISSUE #345). + + +v1.1.0 (2016-06-02) +================================================================================ + +- The default server profile for the client now targets Redis 3.2. + +- Responses to the following commands are not casted into booleans anymore, the + original integer value is returned: `SETNX`, `MSETNX`, `SMOVE`, `SISMEMBER`, + `HSET`, `HSETNX`, `HEXISTS`, `PFADD`, `EXISTS`, `MOVE`, `PERSIST`, `EXPIRE`, + `EXPIREAT`, `RENAMENX`. This change does not have a significant impact unless + when using strict comparisons (=== and !==) the returned value. + +- Non-boolean string values passed to the `persistent` connection parameter can + be used to create different persistent connections. Note that this feature was + already present in Predis but required both `persistent` and `path` to be set + as illustrated by [#139](https://github.com/nrk/predis/pull/139). This change + is needed to prevent confusion with how `path` is used to select a database + when using the `redis` scheme. + +- The client throws exceptions when Redis returns any kind of error response to + initialization commands (the ones being automatically sent when a connection + is established, such as `SELECT` and `AUTH` when database and password are set + in connection parameters) regardless of the value of the exception option. + +- Using `unix:///path/to/socket` in URI strings to specify a UNIX domain socket + file is now deprecated in favor of the format `unix:/path/to/socket` (note the + lack of the double slash after the scheme) and will not be supported starting + with the next major release. + +- Implemented full support for redis-sentinel. + +- Implemented the ability to specify default connection parameters for aggregate + connections with the new `parameters` client option. These parameters augment + the usual user-supplied connection parameters (but do not take the precedence + over them) when creating new connections and they are mostly useful when the + client is using aggregate connections such as redis-cluster and redis-sentinel + as these backends can create new connections on the fly based on responses and + redirections from Redis. + +- Redis servers protected by SSL-encrypted connections can be accessed by using + the `tls` or `rediss` scheme in connection parameters along with SSL-specific + options in the `ssl` parameter (see http://php.net/manual/context.ssl.php). + +- `Predis\Client` implements `IteratorAggregate` making it possible to iterate + over traversable aggregate connections and get a new client instance for each + Redis node. + +- Iterating over an instance of `Predis\Connection\Aggregate\RedisCluster` will + return all the connections mapped in the slots map instead of just the ones in + the pool. This change makes it possible, when the slots map is retrieved from + Redis, to iterate over all of the master nodes in the cluster. When the use of + `CLUSTER SLOTS` is disabled via the `useClusterSlots()` method, the iteration + returns only the connections with slots ranges associated in their parameters + or the ones initialized by `-MOVED` responses in order to make the behaviour + of the iteration consistent between the two modes of operation. + +- Various improvements to `Predis\Connection\Aggregate\MasterSlaveReplication` + (the "basic" replication backend, not the new one based on redis-sentinel): + + - When the client is not able to send a read-only command to a slave because + the current connection fails or the slave is resyncing (`-LOADING` response + returned by Redis), the backend discards the failed connection and performs + a new attempt on the next slave. When no other slave is available the master + server is used for read-only commands as last resort. + + - It is possible to discover the current replication configuration on the fly + by invoking the `discover()` method which internally relies on the output of + the command `INFO REPLICATION` executed against the master server or one of + the slaves. The backend can also be configured to do this automatically when + it fails to reach one of the servers. + + - Implemented the `switchToMaster()` and `switchToSlave()` methods to make it + easier to force a switch to the master server or a random slave when needed. + + v1.0.4 (2016-05-30) ================================================================================ diff --git a/vendor/predis/predis/FAQ.md b/vendor/predis/predis/FAQ.md index 07ec1f0b..5c560b45 100644 --- a/vendor/predis/predis/FAQ.md +++ b/vendor/predis/predis/FAQ.md @@ -18,6 +18,14 @@ least to some degree). Yes. Obviously persistent connections actually work only when using PHP configured as a persistent process reused by the web server (see [PHP-FPM](http://php-fpm.org)). +### Does Predis support SSL-encrypted connections? ### + +Yes. Encrypted connections are mostly useful when connecting to Redis instances exposed by various +cloud hosting providers without the need to configure an SSL proxy, but you should also take into +account the general performances degradation especially during the connect() operation when the TLS +handshake must be performed to secure the connection. Persistent SSL-encrypted connections may help +in that respect, but they are supported only when running on PHP >= 7.0.0. + ### Does Predis support transparent (de)serialization of values? ### No and it will not ever do that by default. The reason behind this decision is that serialization is diff --git a/vendor/predis/predis/README.md b/vendor/predis/predis/README.md index f7dcfc94..fa9740f4 100644 --- a/vendor/predis/predis/README.md +++ b/vendor/predis/predis/README.md @@ -1,11 +1,12 @@ # Predis # +[![Software license][ico-license]](LICENSE) [![Latest stable][ico-version-stable]][link-packagist] [![Latest development][ico-version-dev]][link-packagist] -[![Software license][ico-license]](LICENSE) [![Monthly installs][ico-downloads-monthly]][link-downloads] [![Build status][ico-travis]][link-travis] [![HHVM support][ico-hhvm]][link-hhvm] +[![Gitter room][ico-gitter]][link-gitter] Flexible and feature-complete [Redis](http://redis.io) client for PHP >= 5.3 and HHVM >= 2.3.0. @@ -22,17 +23,17 @@ More details about this project can be found on the [frequently asked questions] - 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. +- Support for master-slave replication setups and [redis-sentinel](http://redis.io/topics/sentinel). - Transparent key prefixing of keys using a customizable prefix strategy. - Command pipelining on both single nodes and clusters (client-side sharding only). - Abstraction for Redis transactions (Redis >= 2.0) and CAS operations (Redis >= 2.2). - Abstraction for Lua scripting (Redis >= 2.6) and automatic switching between `EVALSHA` or `EVAL`. - Abstraction for `SCAN`, `SSCAN`, `ZSCAN` and `HSCAN` (Redis >= 2.8) based on PHP iterators. - Connections are established lazily by the client upon the first command and can be persisted. -- Connections can be established via TCP/IP or UNIX domain sockets. +- Connections can be established via TCP/IP (also TLS/SSL-encrypted) 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. +- Flexible system for defining custom commands and profiles and override the default ones. ## How to _install_ and use Predis ## @@ -94,12 +95,29 @@ 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'); +$client = new Predis\Client('unix:/path/to/redis.sock'); +``` + +The client can leverage TLS/SSL encryption to connect to secured remote Redis instances without the +need to configure an SSL proxy like stunnel. This can be useful when connecting to nodes running on +various cloud hosting providers. Encryption can be enabled with using the `tls` scheme and an array +of suitable [options](http://php.net/manual/context.ssl.php) passed via the `ssl` parameter: + +```php +// Named array of connection parameters: +$client = new Predis\Client([ + 'scheme' => 'tls', + 'ssl' => ['cafile' => 'private.pem', 'verify_peer' => true], +] + +// Same set of parameters, but using an URI string: +$client = new Predis\Client('tls://127.0.0.1?ssl[cafile]=private.pem&ssl[verify_peer]=1'); ``` The connection schemes [`redis`](http://www.iana.org/assignments/uri-schemes/prov/redis) (alias of -`tcp`) is also supported, with the difference that URI strings containing these schemes are parsed -following the rules described on the IANA provisional registration. +`tcp`) and [`rediss`](http://www.iana.org/assignments/uri-schemes/prov/rediss) (alias of `tls`) are +also supported, with the difference that URI strings containing these schemes are parsed following +the rules described on their respective IANA provisional registration documents. The actual list of supported connection parameters can vary depending on each connection backend so it is recommended to refer to their specific documentation or implementation for details. @@ -117,6 +135,12 @@ $client = new Predis\Client([ See the [aggregate connections](#aggregate-connections) section of this document for more details. +Connections to Redis are lazy meaning that the client connects to a server only if and when needed. +While it is recommended to let the client do its own stuff under the hood, there may be times when +it is still desired to have control of when the connection is opened or closed: this can easily be +achieved by invoking `$client->connect()` and `$client->disconnect()`. Please note that the effect +of these methods on aggregate connections may differ depending on each specific implementation. + ### Client configuration ### @@ -135,8 +159,9 @@ when needed. The client options supported by default in Predis are: - `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). + - `replication`: specifies a replication backend (`TRUE`, `sentinel` or callable object). - `aggregate`: overrides `cluster` and `replication` to provide a custom connections aggregator. + - `parameters`: list of default connection parameters for aggregate connections. Users can also provide custom options with values or callable objects (for lazy initialization) that are stored in the options container for later use through the library. @@ -198,6 +223,35 @@ $options = ['replication' => true]; $client = new Predis\Client($parameters, $options); ``` +The above configuration has a static list of servers and relies entirely on the client's logic, but +it is possible to rely on [`redis-sentinel`](http://redis.io/topics/sentinel) for a more robust HA +environment with sentinel servers acting as a source of authority for clients for service discovery. +The minimum configuration required by the client to work with redis-sentinel is a list of connection +parameters pointing to a bunch of sentinel instances, the `replication` option set to `sentinel` and +the `service` option set to the name of the service: + +```php +$sentinels = ['tcp://10.0.0.1', 'tcp://10.0.0.2', 'tcp://10.0.0.3']; +$options = ['replication' => 'sentinel', 'service' => 'mymaster']; + +$client = new Predis\Client($sentinels, $options); +``` + +If the master and slave nodes are configured to require an authentication from clients, a password +must be provided via the global `parameters` client option. This option can also be used to specify +a different database index. The client options array would then look like this: + +```php +$options = [ + 'replication' => 'sentinel', + 'service' => 'mymaster', + 'parameters' => [ + 'password' => $secretpassword, + 'database' => 10, + ], +]; +``` + While Predis is able to distinguish commands performing write and read-only operations, `EVAL` and `EVALSHA` represent a corner case in which the client switches to the master node because it cannot tell when a Lua script is safe to be executed on slaves. While this is indeed the default behavior, @@ -393,7 +447,7 @@ Predis has a comprehensive test suite covering every aspect of the library. This 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 +By default the test suite is configured to execute integration tests using the profile for Redis 3.2 (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) @@ -429,8 +483,10 @@ The code for Predis is distributed under the terms of the MIT license (see [LICE [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 +[ico-gitter]: https://img.shields.io/gitter/room/nrk/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 +[link-gitter]: https://gitter.im/nrk/predis diff --git a/vendor/predis/predis/VERSION b/vendor/predis/predis/VERSION index ee90284c..524cb552 100644 --- a/vendor/predis/predis/VERSION +++ b/vendor/predis/predis/VERSION @@ -1 +1 @@ -1.0.4 +1.1.1 diff --git a/vendor/predis/predis/composer.json b/vendor/predis/predis/composer.json index e67ba577..bde6c741 100644 --- a/vendor/predis/predis/composer.json +++ b/vendor/predis/predis/composer.json @@ -16,7 +16,7 @@ } ], "require": { - "php": ">=5.3.2" + "php": ">=5.3.9" }, "require-dev": { "phpunit/phpunit": "~4.8" diff --git a/vendor/predis/predis/examples/debuggable_connection.php b/vendor/predis/predis/examples/debuggable_connection.php index 1a30b027..346b0035 100644 --- a/vendor/predis/predis/examples/debuggable_connection.php +++ b/vendor/predis/predis/examples/debuggable_connection.php @@ -36,7 +36,7 @@ class SimpleDebuggableConnection extends StreamConnection $firtsArg = $command->getArgument(0); $timestamp = round(microtime(true) - $this->tstart, 4); - $debug = $command->getId(); + $debug = $command->getId(); $debug .= isset($firtsArg) ? " $firtsArg " : ' '; $debug .= "$direction $this"; $debug .= " [{$timestamp}s]"; diff --git a/vendor/predis/predis/examples/replication_sentinel.php b/vendor/predis/predis/examples/replication_sentinel.php new file mode 100644 index 00000000..f1e0de8a --- /dev/null +++ b/vendor/predis/predis/examples/replication_sentinel.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. + */ + +require __DIR__.'/shared.php'; + +// Predis supports redis-sentinel to provide high availability in master / slave +// scenarios. The only but relevant difference with a basic replication scenario +// is that sentinel servers can manage the master server and its slaves based on +// their state, which means that they are able to provide an authoritative and +// updated configuration to clients thus avoiding static configurations for the +// replication servers and their roles. + +// Instead of connection parameters pointing to redis nodes, we provide a list +// of instances of redis-sentinel. Users should always provide a timeout value +// low enough to not hinder operations just in case a sentinel is unreachable +// but Predis uses a default value of 100 milliseconds for sentinel parameters +// without an explicit timeout value. +// +// NOTE: in real-world scenarios sentinels should be running on different hosts! +$sentinels = array( + 'tcp://127.0.0.1:5380?timeout=0.100', + 'tcp://127.0.0.1:5381?timeout=0.100', + 'tcp://127.0.0.1:5382?timeout=0.100', +); + +$client = new Predis\Client($sentinels, array( + 'replication' => 'sentinel', + 'service' => 'mymaster', +)); + +// Read operation. +$exists = $client->exists('foo') ? 'yes' : 'no'; +$current = $client->getConnection()->getCurrent()->getParameters(); +echo "Does 'foo' exist on {$current->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-127.0.0.1:6381? yes. +Now 'foo' has been set to 'bar' on master! +We fetched 'foo' from master and its value is 'bar'. +*/ diff --git a/vendor/predis/predis/examples/shared.php b/vendor/predis/predis/examples/shared.php index 00e3faf9..b65c9d3f 100644 --- a/vendor/predis/predis/examples/shared.php +++ b/vendor/predis/predis/examples/shared.php @@ -23,22 +23,22 @@ function redis_version($info) } $single_server = array( - 'host' => '127.0.0.1', - 'port' => 6379, + 'host' => '127.0.0.1', + 'port' => 6379, 'database' => 15, ); $multiple_servers = array( array( - 'host' => '127.0.0.1', - 'port' => 6379, + 'host' => '127.0.0.1', + 'port' => 6379, 'database' => 15, - 'alias' => 'first', + 'alias' => 'first', ), array( - 'host' => '127.0.0.1', - 'port' => 6380, + 'host' => '127.0.0.1', + 'port' => 6380, 'database' => 15, - 'alias' => 'second', + 'alias' => 'second', ), ); diff --git a/vendor/predis/predis/examples/transaction_using_cas.php b/vendor/predis/predis/examples/transaction_using_cas.php index 68bc4676..f72c4655 100644 --- a/vendor/predis/predis/examples/transaction_using_cas.php +++ b/vendor/predis/predis/examples/transaction_using_cas.php @@ -28,7 +28,7 @@ function zpop($client, $key) { $element = null; $options = array( - 'cas' => true, // Initialize with support for CAS operations + 'cas' => true, // Initialize with support for CAS operations 'watch' => $key, // Key that needs to be WATCHed to detect changes 'retry' => 3, // Number of retries on aborted transactions, after // which the client bails out with an exception. diff --git a/vendor/predis/predis/package.ini b/vendor/predis/predis/package.ini index a38adf7c..39842475 100644 --- a/vendor/predis/predis/package.ini +++ b/vendor/predis/predis/package.ini @@ -10,14 +10,14 @@ 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" +version = "1.1.1" stability = "stable" channel = "pear.nrk.io" author = "Daniele Alessandri \"nrk\" <suppakilla@gmail.com>" [require] -php = ">= 5.3.2" +php = ">= 5.3.9" pearinstaller = "1.4.1" [roles] diff --git a/vendor/predis/predis/src/Client.php b/vendor/predis/predis/src/Client.php index 73fb8af6..efe5c8b5 100644 --- a/vendor/predis/predis/src/Client.php +++ b/vendor/predis/predis/src/Client.php @@ -38,9 +38,9 @@ use Predis\Transaction\MultiExec as MultiExecTransaction; * * @author Daniele Alessandri <suppakilla@gmail.com> */ -class Client implements ClientInterface +class Client implements ClientInterface, \IteratorAggregate { - const VERSION = '1.0.4'; + const VERSION = '1.1.1'; protected $connection; protected $options; @@ -120,13 +120,18 @@ class Client implements ClientInterface if ($options->defined('aggregate')) { $initializer = $this->getConnectionInitializerWrapper($options->aggregate); $connection = $initializer($parameters, $options); - } else { - if ($options->defined('replication') && $replication = $options->replication) { + } elseif ($options->defined('replication')) { + $replication = $options->replication; + + if ($replication instanceof AggregateConnectionInterface) { $connection = $replication; + $options->connections->aggregate($connection, $parameters); } else { - $connection = $options->cluster; + $initializer = $this->getConnectionInitializerWrapper($replication); + $connection = $initializer($parameters, $options); } - + } else { + $connection = $options->cluster; $options->connections->aggregate($connection, $parameters); } @@ -273,7 +278,7 @@ class Client implements ClientInterface * 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 + * It is possible to identify 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. @@ -476,7 +481,7 @@ class Client implements ClientInterface } /** - * Creates a new publis/subscribe context and returns it, or starts its loop + * Creates a new publish/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. @@ -520,4 +525,23 @@ class Client implements ClientInterface { return new MonitorConsumer($this); } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + $clients = array(); + $connection = $this->getConnection(); + + if (!$connection instanceof \Traversable) { + throw new ClientException('The underlying connection is not traversable'); + } + + foreach ($connection as $node) { + $clients[(string) $node] = new static($node, $this->getOptions()); + } + + return new \ArrayIterator($clients); + } } diff --git a/vendor/predis/predis/src/ClientContextInterface.php b/vendor/predis/predis/src/ClientContextInterface.php index a4100d4b..deb38659 100644 --- a/vendor/predis/predis/src/ClientContextInterface.php +++ b/vendor/predis/predis/src/ClientContextInterface.php @@ -38,7 +38,7 @@ use Predis\Command\CommandInterface; * @method $this append($key, $value) * @method $this bitcount($key, $start = null, $end = null) * @method $this bitop($operation, $destkey, $key) - * @method $this bitfield($key, ...) + * @method $this bitfield($key, $subcommand, ...$subcommandArg) * @method $this decr($key) * @method $this decrby($key, $decrement) * @method $this get($key) diff --git a/vendor/predis/predis/src/ClientInterface.php b/vendor/predis/predis/src/ClientInterface.php index 440d6daf..d0625266 100644 --- a/vendor/predis/predis/src/ClientInterface.php +++ b/vendor/predis/predis/src/ClientInterface.php @@ -46,7 +46,7 @@ use Predis\Profile\ProfileInterface; * @method int append($key, $value) * @method int bitcount($key, $start = null, $end = null) * @method int bitop($operation, $destkey, $key) - * @method array bitfield($key, ...) + * @method array bitfield($key, $subcommand, ...$subcommandArg) * @method int decr($key) * @method int decrby($key, $decrement) * @method string get($key) diff --git a/vendor/predis/predis/src/Command/HashExists.php b/vendor/predis/predis/src/Command/HashExists.php index a2c69b90..ed8dc89f 100644 --- a/vendor/predis/predis/src/Command/HashExists.php +++ b/vendor/predis/predis/src/Command/HashExists.php @@ -25,12 +25,4 @@ class HashExists extends Command { return 'HEXISTS'; } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Command/HashSet.php b/vendor/predis/predis/src/Command/HashSet.php index d3154a9b..cfff3c2d 100644 --- a/vendor/predis/predis/src/Command/HashSet.php +++ b/vendor/predis/predis/src/Command/HashSet.php @@ -25,12 +25,4 @@ class HashSet extends Command { return 'HSET'; } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Command/HashSetPreserve.php b/vendor/predis/predis/src/Command/HashSetPreserve.php index 582100d4..7a291164 100644 --- a/vendor/predis/predis/src/Command/HashSetPreserve.php +++ b/vendor/predis/predis/src/Command/HashSetPreserve.php @@ -25,12 +25,4 @@ class HashSetPreserve extends Command { return 'HSETNX'; } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Command/HyperLogLogAdd.php b/vendor/predis/predis/src/Command/HyperLogLogAdd.php index 18d2bd7b..8fe49fc9 100644 --- a/vendor/predis/predis/src/Command/HyperLogLogAdd.php +++ b/vendor/predis/predis/src/Command/HyperLogLogAdd.php @@ -33,12 +33,4 @@ class HyperLogLogAdd extends Command { return self::normalizeVariadic($arguments); } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Command/KeyExists.php b/vendor/predis/predis/src/Command/KeyExists.php index 5196ca16..29e06480 100644 --- a/vendor/predis/predis/src/Command/KeyExists.php +++ b/vendor/predis/predis/src/Command/KeyExists.php @@ -25,12 +25,4 @@ class KeyExists extends Command { 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 index fd7c9c80..66f44066 100644 --- a/vendor/predis/predis/src/Command/KeyExpire.php +++ b/vendor/predis/predis/src/Command/KeyExpire.php @@ -25,12 +25,4 @@ class KeyExpire extends Command { 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 index e2fe7aeb..0ae1b2d4 100644 --- a/vendor/predis/predis/src/Command/KeyExpireAt.php +++ b/vendor/predis/predis/src/Command/KeyExpireAt.php @@ -25,12 +25,4 @@ class KeyExpireAt extends Command { return 'EXPIREAT'; } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Command/KeyMove.php b/vendor/predis/predis/src/Command/KeyMove.php index 8f1ab2a7..c849f08d 100644 --- a/vendor/predis/predis/src/Command/KeyMove.php +++ b/vendor/predis/predis/src/Command/KeyMove.php @@ -25,12 +25,4 @@ class KeyMove extends Command { 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 index e7729553..f0cb679b 100644 --- a/vendor/predis/predis/src/Command/KeyPersist.php +++ b/vendor/predis/predis/src/Command/KeyPersist.php @@ -25,12 +25,4 @@ class KeyPersist extends Command { return 'PERSIST'; } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Command/KeyRenamePreserve.php b/vendor/predis/predis/src/Command/KeyRenamePreserve.php index 773ece6d..97933599 100644 --- a/vendor/predis/predis/src/Command/KeyRenamePreserve.php +++ b/vendor/predis/predis/src/Command/KeyRenamePreserve.php @@ -25,12 +25,4 @@ class KeyRenamePreserve extends KeyRename { return 'RENAMENX'; } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Command/SetIsMember.php b/vendor/predis/predis/src/Command/SetIsMember.php index 1b484907..77425224 100644 --- a/vendor/predis/predis/src/Command/SetIsMember.php +++ b/vendor/predis/predis/src/Command/SetIsMember.php @@ -25,12 +25,4 @@ class SetIsMember extends Command { return 'SISMEMBER'; } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Command/SetMove.php b/vendor/predis/predis/src/Command/SetMove.php index 72d514be..edd4e515 100644 --- a/vendor/predis/predis/src/Command/SetMove.php +++ b/vendor/predis/predis/src/Command/SetMove.php @@ -25,12 +25,4 @@ class SetMove extends Command { return 'SMOVE'; } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php b/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php index f98f1f7c..b46a88c3 100644 --- a/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php +++ b/vendor/predis/predis/src/Command/StringSetMultiplePreserve.php @@ -25,12 +25,4 @@ class StringSetMultiplePreserve extends StringSetMultiple { 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 index 726c35c8..e89c9749 100644 --- a/vendor/predis/predis/src/Command/StringSetPreserve.php +++ b/vendor/predis/predis/src/Command/StringSetPreserve.php @@ -25,12 +25,4 @@ class StringSetPreserve extends Command { return 'SETNX'; } - - /** - * {@inheritdoc} - */ - public function parseResponse($data) - { - return (bool) $data; - } } diff --git a/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php b/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php index ba38df96..bf8479c9 100644 --- a/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php +++ b/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php @@ -49,6 +49,12 @@ class ConnectionFactoryOption implements OptionInterface */ public function getDefault(OptionsInterface $options) { - return new Factory(); + $factory = new Factory(); + + if ($options->defined('parameters')) { + $factory->setDefaultParameters($options->parameters); + } + + return $factory; } } diff --git a/vendor/predis/predis/src/Configuration/ReplicationOption.php b/vendor/predis/predis/src/Configuration/ReplicationOption.php index fd2c8108..1a34f71b 100644 --- a/vendor/predis/predis/src/Configuration/ReplicationOption.php +++ b/vendor/predis/predis/src/Configuration/ReplicationOption.php @@ -13,6 +13,7 @@ namespace Predis\Configuration; use Predis\Connection\Aggregate\MasterSlaveReplication; use Predis\Connection\Aggregate\ReplicationInterface; +use Predis\Connection\Aggregate\SentinelReplication; /** * Configures an aggregate connection used for master/slave replication among @@ -39,6 +40,12 @@ class ReplicationOption implements OptionInterface return $value ? $this->getDefault($options) : null; } + if ($value === 'sentinel') { + return function ($sentinels, $options) { + return new SentinelReplication($options->service, $sentinels, $options->connections); + }; + } + if ( !is_object($value) && null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) @@ -56,6 +63,13 @@ class ReplicationOption implements OptionInterface */ public function getDefault(OptionsInterface $options) { - return new MasterSlaveReplication(); + $replication = new MasterSlaveReplication(); + + if ($options->autodiscovery) { + $replication->setConnectionFactory($options->connections); + $replication->setAutoDiscovery(true); + } + + return $replication; } } diff --git a/vendor/predis/predis/src/Connection/AbstractConnection.php b/vendor/predis/predis/src/Connection/AbstractConnection.php index 029a337e..fb865132 100644 --- a/vendor/predis/predis/src/Connection/AbstractConnection.php +++ b/vendor/predis/predis/src/Connection/AbstractConnection.php @@ -55,20 +55,7 @@ abstract class AbstractConnection implements NodeConnectionInterface * * @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; - } + abstract protected function assertParameters(ParametersInterface $parameters); /** * Creates the underlying resource used to communicate with Redis. diff --git a/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php b/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php index ca99aa8e..238cf2cc 100644 --- a/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php +++ b/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php @@ -11,9 +11,15 @@ namespace Predis\Connection\Aggregate; +use Predis\ClientException; use Predis\Command\CommandInterface; +use Predis\Command\RawCommand; +use Predis\Connection\ConnectionException; +use Predis\Connection\FactoryInterface; use Predis\Connection\NodeConnectionInterface; +use Predis\Replication\MissingMasterException; use Predis\Replication\ReplicationStrategy; +use Predis\Response\ErrorInterface as ResponseErrorInterface; /** * Aggregate connection handling replication of Redis nodes configured in a @@ -44,6 +50,16 @@ class MasterSlaveReplication implements ReplicationInterface protected $current; /** + * @var bool + */ + protected $autoDiscovery = false; + + /** + * @var FactoryInterface + */ + protected $connectionFactory; + + /** * {@inheritdoc} */ public function __construct(ReplicationStrategy $strategy = null) @@ -52,13 +68,28 @@ class MasterSlaveReplication implements ReplicationInterface } /** - * Checks if one master and at least one slave have been defined. + * Configures the automatic discovery of the replication configuration on failure. + * + * @param bool $value Enable or disable auto discovery. */ - protected function check() + public function setAutoDiscovery($value) { - if (!isset($this->master) || !$this->slaves) { - throw new \RuntimeException('Replication needs one master and at least one slave.'); + if (!$this->connectionFactory) { + throw new ClientException('Automatic discovery requires a connection factory'); } + + $this->autoDiscovery = (bool) $value; + } + + /** + * Sets the connection factory used to create the connections by the auto + * discovery procedure. + * + * @param FactoryInterface $connectionFactory Connection factory instance. + */ + public function setConnectionFactory(FactoryInterface $connectionFactory) + { + $this->connectionFactory = $connectionFactory; } /** @@ -79,7 +110,7 @@ class MasterSlaveReplication implements ReplicationInterface if ($alias === 'master') { $this->master = $connection; } else { - $this->slaves[$alias ?: count($this->slaves)] = $connection; + $this->slaves[$alias ?: "slave-$connection"] = $connection; } $this->reset(); @@ -112,21 +143,22 @@ class MasterSlaveReplication implements ReplicationInterface */ public function getConnection(CommandInterface $command) { - if ($this->current === null) { - $this->check(); - $this->current = $this->strategy->isReadOperation($command) - ? $this->pickSlave() - : $this->master; + if (!$this->current) { + if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) { + $this->current = $slave; + } else { + $this->current = $this->getMasterOrDie(); + } return $this->current; } - if ($this->current === $this->master) { - return $this->current; + if ($this->current === $master = $this->getMasterOrDie()) { + return $master; } - if (!$this->strategy->isReadOperation($command)) { - $this->current = $this->master; + if (!$this->strategy->isReadOperation($command) || !$this->slaves) { + $this->current = $master; } return $this->current; @@ -153,12 +185,14 @@ class MasterSlaveReplication implements ReplicationInterface */ public function switchTo($connection) { - $this->check(); - if (!$connection instanceof NodeConnectionInterface) { $connection = $this->getConnectionById($connection); } + if (!$connection) { + throw new \InvalidArgumentException('Invalid connection or connection not found.'); + } + if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) { throw new \InvalidArgumentException('Invalid connection or connection not found.'); } @@ -167,6 +201,23 @@ class MasterSlaveReplication implements ReplicationInterface } /** + * Switches to the master server. + */ + public function switchToMaster() + { + $this->switchTo('master'); + } + + /** + * Switches to a random slave server. + */ + public function switchToSlave() + { + $connection = $this->pickSlave(); + $this->switchTo($connection); + } + + /** * {@inheritdoc} */ public function getCurrent() @@ -183,6 +234,20 @@ class MasterSlaveReplication implements ReplicationInterface } /** + * Returns the connection associated to the master server. + * + * @return NodeConnectionInterface + */ + private function getMasterOrDie() + { + if (!$connection = $this->getMaster()) { + throw new MissingMasterException('No master server available for replication'); + } + + return $connection; + } + + /** * {@inheritdoc} */ public function getSlaves() @@ -225,9 +290,12 @@ class MasterSlaveReplication implements ReplicationInterface */ public function connect() { - if ($this->current === null) { - $this->check(); - $this->current = $this->pickSlave(); + if (!$this->current) { + if (!$this->current = $this->pickSlave()) { + if (!$this->current = $this->getMaster()) { + throw new ClientException('No available connection for replication'); + } + } } $this->current->connect(); @@ -248,11 +316,171 @@ class MasterSlaveReplication implements ReplicationInterface } /** + * Handles response from INFO. + * + * @param string $response + * + * @return array + */ + private function handleInfoResponse($response) + { + $info = array(); + + foreach (preg_split('/\r?\n/', $response) as $row) { + if (strpos($row, ':') === false) { + continue; + } + + list($k, $v) = explode(':', $row, 2); + $info[$k] = $v; + } + + return $info; + } + + /** + * Fetches the replication configuration from one of the servers. + */ + public function discover() + { + if (!$this->connectionFactory) { + throw new ClientException('Discovery requires a connection factory'); + } + + RETRY_FETCH: { + try { + if ($connection = $this->getMaster()) { + $this->discoverFromMaster($connection, $this->connectionFactory); + } elseif ($connection = $this->pickSlave()) { + $this->discoverFromSlave($connection, $this->connectionFactory); + } else { + throw new ClientException('No connection available for discovery'); + } + } catch (ConnectionException $exception) { + $this->remove($connection); + goto RETRY_FETCH; + } + } + } + + /** + * Discovers the replication configuration by contacting the master node. + * + * @param NodeConnectionInterface $connection Connection to the master node. + * @param FactoryInterface $connectionFactory Connection factory instance. + */ + protected function discoverFromMaster(NodeConnectionInterface $connection, FactoryInterface $connectionFactory) + { + $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION')); + $replication = $this->handleInfoResponse($response); + + if ($replication['role'] !== 'master') { + throw new ClientException("Role mismatch (expected master, got slave) [$connection]"); + } + + $this->slaves = array(); + + foreach ($replication as $k => $v) { + $parameters = null; + + if (strpos($k, 'slave') === 0 && preg_match('/ip=(?P<host>.*),port=(?P<port>\d+)/', $v, $parameters)) { + $slaveConnection = $connectionFactory->create(array( + 'host' => $parameters['host'], + 'port' => $parameters['port'], + )); + + $this->add($slaveConnection); + } + } + } + + /** + * Discovers the replication configuration by contacting one of the slaves. + * + * @param NodeConnectionInterface $connection Connection to one of the slaves. + * @param FactoryInterface $connectionFactory Connection factory instance. + */ + protected function discoverFromSlave(NodeConnectionInterface $connection, FactoryInterface $connectionFactory) + { + $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION')); + $replication = $this->handleInfoResponse($response); + + if ($replication['role'] !== 'slave') { + throw new ClientException("Role mismatch (expected slave, got master) [$connection]"); + } + + $masterConnection = $connectionFactory->create(array( + 'host' => $replication['master_host'], + 'port' => $replication['master_port'], + 'alias' => 'master', + )); + + $this->add($masterConnection); + + $this->discoverFromMaster($masterConnection, $connectionFactory); + } + + /** + * Retries the execution of a command upon slave failure. + * + * @param CommandInterface $command Command instance. + * @param string $method Actual method. + * + * @return mixed + */ + private function retryCommandOnFailure(CommandInterface $command, $method) + { + RETRY_COMMAND: { + try { + $connection = $this->getConnection($command); + $response = $connection->$method($command); + + if ($response instanceof ResponseErrorInterface && $response->getErrorType() === 'LOADING') { + throw new ConnectionException($connection, "Redis is loading the dataset in memory [$connection]"); + } + } catch (ConnectionException $exception) { + $connection = $exception->getConnection(); + $connection->disconnect(); + + if ($connection === $this->master && !$this->autoDiscovery) { + // Throw immediately when master connection is failing, even + // when the command represents a read-only operation, unless + // automatic discovery has been enabled. + throw $exception; + } else { + // Otherwise remove the failing slave and attempt to execute + // the command again on one of the remaining slaves... + $this->remove($connection); + } + + // ... that is, unless we have no more connections to use. + if (!$this->slaves && !$this->master) { + throw $exception; + } elseif ($this->autoDiscovery) { + $this->discover(); + } + + goto RETRY_COMMAND; + } catch (MissingMasterException $exception) { + if ($this->autoDiscovery) { + $this->discover(); + } else { + throw $exception; + } + + goto RETRY_COMMAND; + } + } + + return $response; + } + + /** * {@inheritdoc} */ public function writeRequest(CommandInterface $command) { - $this->getConnection($command)->writeRequest($command); + $this->retryCommandOnFailure($command, __FUNCTION__); } /** @@ -260,7 +488,7 @@ class MasterSlaveReplication implements ReplicationInterface */ public function readResponse(CommandInterface $command) { - return $this->getConnection($command)->readResponse($command); + return $this->retryCommandOnFailure($command, __FUNCTION__); } /** @@ -268,7 +496,7 @@ class MasterSlaveReplication implements ReplicationInterface */ public function executeCommand(CommandInterface $command) { - return $this->getConnection($command)->executeCommand($command); + return $this->retryCommandOnFailure($command, __FUNCTION__); } /** diff --git a/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php b/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php index e8c8e2c6..c749cc83 100644 --- a/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php +++ b/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php @@ -47,7 +47,6 @@ use Predis\Response\ErrorInterface as ErrorResponseInterface; class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable { private $useClusterSlots = true; - private $defaultParameters = array(); private $pool = array(); private $slots = array(); private $slotsMap; @@ -172,6 +171,8 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable * 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. + * + * @return array */ public function buildSlotsMap() { @@ -194,6 +195,8 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable $this->setSlots($slots[0], $slots[1], $connectionID); } } + + return $this->slotsMap; } /** @@ -360,14 +363,10 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable { $separator = strrpos($connectionID, ':'); - $parameters = array_merge($this->defaultParameters, array( + return $this->connections->create(array( 'host' => substr($connectionID, 0, $separator), 'port' => substr($connectionID, $separator + 1), )); - - $connection = $this->connections->create($parameters); - - return $connection; } /** @@ -613,7 +612,23 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable */ public function getIterator() { - return new \ArrayIterator(array_values($this->pool)); + if ($this->useClusterSlots) { + $slotsmap = $this->getSlotsMap() ?: $this->askSlotsMap(); + } else { + $slotsmap = $this->getSlotsMap() ?: $this->buildSlotsMap(); + } + + $connections = array(); + + foreach (array_unique($slotsmap) as $node) { + if (!$connection = $this->getConnectionById($node)) { + $this->add($connection = $this->createConnection($node)); + } + + $connections[] = $connection; + } + + return new \ArrayIterator($connections); } /** @@ -655,22 +670,4 @@ class RedisCluster implements ClusterInterface, \IteratorAggregate, \Countable { $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/SentinelReplication.php b/vendor/predis/predis/src/Connection/Aggregate/SentinelReplication.php new file mode 100644 index 00000000..5cae0168 --- /dev/null +++ b/vendor/predis/predis/src/Connection/Aggregate/SentinelReplication.php @@ -0,0 +1,720 @@ +<?php + +/* + * This file is part of the Predis package. + * + * (c) Daniele Alessandri <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\Command\RawCommand; +use Predis\CommunicationException; +use Predis\Connection\ConnectionException; +use Predis\Connection\FactoryInterface as ConnectionFactoryInterface; +use Predis\Connection\NodeConnectionInterface; +use Predis\Connection\Parameters; +use Predis\Replication\ReplicationStrategy; +use Predis\Replication\RoleException; +use Predis\Response\ErrorInterface as ErrorResponseInterface; +use Predis\Response\ServerException; + +/** + * @author Daniele Alessandri <suppakilla@gmail.com> + * @author Ville Mattila <ville@eventio.fi> + */ +class SentinelReplication implements ReplicationInterface +{ + /** + * @var NodeConnectionInterface + */ + protected $master; + + /** + * @var NodeConnectionInterface[] + */ + protected $slaves = array(); + + /** + * @var NodeConnectionInterface + */ + protected $current; + + /** + * @var string + */ + protected $service; + + /** + * @var ConnectionFactoryInterface + */ + protected $connectionFactory; + + /** + * @var ReplicationStrategy + */ + protected $strategy; + + /** + * @var NodeConnectionInterface[] + */ + protected $sentinels = array(); + + /** + * @var NodeConnectionInterface + */ + protected $sentinelConnection; + + /** + * @var float + */ + protected $sentinelTimeout = 0.100; + + /** + * Max number of automatic retries of commands upon server failure. + * + * -1 = unlimited retry attempts + * 0 = no retry attempts (fails immediatly) + * n = fail only after n retry attempts + * + * @var int + */ + protected $retryLimit = 20; + + /** + * Time to wait in milliseconds before fetching a new configuration from one + * of the sentinel servers. + * + * @var int + */ + protected $retryWait = 1000; + + /** + * Flag for automatic fetching of available sentinels. + * + * @var bool + */ + protected $updateSentinels = false; + + /** + * @param string $service Name of the service for autodiscovery. + * @param array $sentinels Sentinel servers connection parameters. + * @param ConnectionFactoryInterface $connectionFactory Connection factory instance. + * @param ReplicationStrategy $strategy Replication strategy instance. + */ + public function __construct( + $service, + array $sentinels, + ConnectionFactoryInterface $connectionFactory, + ReplicationStrategy $strategy = null + ) { + $this->sentinels = $sentinels; + $this->service = $service; + $this->connectionFactory = $connectionFactory; + $this->strategy = $strategy ?: new ReplicationStrategy(); + } + + /** + * Sets a default timeout for connections to sentinels. + * + * When "timeout" is present in the connection parameters of sentinels, its + * value overrides the default sentinel timeout. + * + * @param float $timeout Timeout value. + */ + public function setSentinelTimeout($timeout) + { + $this->sentinelTimeout = (float) $timeout; + } + + /** + * 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; + } + + /** + * Sets the time to wait (in seconds) before fetching a new configuration + * from one of the sentinels. + * + * @param float $seconds Time to wait before the next attempt. + */ + public function setRetryWait($seconds) + { + $this->retryWait = (float) $seconds; + } + + /** + * Set automatic fetching of available sentinels. + * + * @param bool $update Enable or disable automatic updates. + */ + public function setUpdateSentinels($update) + { + $this->updateSentinels = (bool) $update; + } + + /** + * Resets the current connection. + */ + protected function reset() + { + $this->current = null; + } + + /** + * Wipes the current list of master and slaves nodes. + */ + protected function wipeServerList() + { + $this->reset(); + + $this->master = null; + $this->slaves = array(); + } + + /** + * {@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 === $this->master) { + $this->master = null; + $this->reset(); + + return true; + } + + if (false !== $id = array_search($connection, $this->slaves, true)) { + unset($this->slaves[$id]); + $this->reset(); + + return true; + } + + return false; + } + + /** + * Creates a new connection to a sentinel server. + * + * @return NodeConnectionInterface + */ + protected function createSentinelConnection($parameters) + { + if ($parameters instanceof NodeConnectionInterface) { + return $parameters; + } + + if (is_string($parameters)) { + $parameters = Parameters::parse($parameters); + } + + if (is_array($parameters)) { + // We explicitly set "database" and "password" to null, + // so that no AUTH and SELECT command is send to the sentinels. + $parameters['database'] = null; + $parameters['password'] = null; + + if (!isset($parameters['timeout'])) { + $parameters['timeout'] = $this->sentinelTimeout; + } + } + + $connection = $this->connectionFactory->create($parameters); + + return $connection; + } + + /** + * Returns the current sentinel connection. + * + * If there is no active sentinel connection, a new connection is created. + * + * @return NodeConnectionInterface + */ + public function getSentinelConnection() + { + if (!$this->sentinelConnection) { + if (!$this->sentinels) { + throw new \Predis\ClientException('No sentinel server available for autodiscovery.'); + } + + $sentinel = array_shift($this->sentinels); + $this->sentinelConnection = $this->createSentinelConnection($sentinel); + } + + return $this->sentinelConnection; + } + + /** + * Fetches an updated list of sentinels from a sentinel. + */ + public function updateSentinels() + { + SENTINEL_QUERY: { + $sentinel = $this->getSentinelConnection(); + + try { + $payload = $sentinel->executeCommand( + RawCommand::create('SENTINEL', 'sentinels', $this->service) + ); + + $this->sentinels = array(); + // NOTE: sentinel server does not return itself, so we add it back. + $this->sentinels[] = $sentinel->getParameters()->toArray(); + + foreach ($payload as $sentinel) { + $this->sentinels[] = array( + 'host' => $sentinel[3], + 'port' => $sentinel[5], + ); + } + } catch (ConnectionException $exception) { + $this->sentinelConnection = null; + + goto SENTINEL_QUERY; + } + } + } + + /** + * Fetches the details for the master and slave servers from a sentinel. + */ + public function querySentinel() + { + $this->wipeServerList(); + + $this->updateSentinels(); + $this->getMaster(); + $this->getSlaves(); + } + + /** + * Handles error responses returned by redis-sentinel. + * + * @param NodeConnectionInterface $sentinel Connection to a sentinel server. + * @param ErrorResponseInterface $error Error response. + */ + private function handleSentinelErrorResponse(NodeConnectionInterface $sentinel, ErrorResponseInterface $error) + { + if ($error->getErrorType() === 'IDONTKNOW') { + throw new ConnectionException($sentinel, $error->getMessage()); + } else { + throw new ServerException($error->getMessage()); + } + } + + /** + * Fetches the details for the master server from a sentinel. + * + * @param NodeConnectionInterface $sentinel Connection to a sentinel server. + * @param string $service Name of the service. + * + * @return array + */ + protected function querySentinelForMaster(NodeConnectionInterface $sentinel, $service) + { + $payload = $sentinel->executeCommand( + RawCommand::create('SENTINEL', 'get-master-addr-by-name', $service) + ); + + if ($payload === null) { + throw new ServerException('ERR No such master with that name'); + } + + if ($payload instanceof ErrorResponseInterface) { + $this->handleSentinelErrorResponse($sentinel, $payload); + } + + return array( + 'host' => $payload[0], + 'port' => $payload[1], + 'alias' => 'master', + ); + } + + /** + * Fetches the details for the slave servers from a sentinel. + * + * @param NodeConnectionInterface $sentinel Connection to a sentinel server. + * @param string $service Name of the service. + * + * @return array + */ + protected function querySentinelForSlaves(NodeConnectionInterface $sentinel, $service) + { + $slaves = array(); + + $payload = $sentinel->executeCommand( + RawCommand::create('SENTINEL', 'slaves', $service) + ); + + if ($payload instanceof ErrorResponseInterface) { + $this->handleSentinelErrorResponse($sentinel, $payload); + } + + foreach ($payload as $slave) { + $flags = explode(',', $slave[9]); + + if (array_intersect($flags, array('s_down', 'o_down', 'disconnected'))) { + continue; + } + + $slaves[] = array( + 'host' => $slave[3], + 'port' => $slave[5], + 'alias' => "slave-$slave[1]", + ); + } + + return $slaves; + } + + /** + * {@inheritdoc} + */ + public function getCurrent() + { + return $this->current; + } + + /** + * {@inheritdoc} + */ + public function getMaster() + { + if ($this->master) { + return $this->master; + } + + if ($this->updateSentinels) { + $this->updateSentinels(); + } + + SENTINEL_QUERY: { + $sentinel = $this->getSentinelConnection(); + + try { + $masterParameters = $this->querySentinelForMaster($sentinel, $this->service); + $masterConnection = $this->connectionFactory->create($masterParameters); + + $this->add($masterConnection); + } catch (ConnectionException $exception) { + $this->sentinelConnection = null; + + goto SENTINEL_QUERY; + } + } + + return $masterConnection; + } + + /** + * {@inheritdoc} + */ + public function getSlaves() + { + if ($this->slaves) { + return array_values($this->slaves); + } + + if ($this->updateSentinels) { + $this->updateSentinels(); + } + + SENTINEL_QUERY: { + $sentinel = $this->getSentinelConnection(); + + try { + $slavesParameters = $this->querySentinelForSlaves($sentinel, $this->service); + + foreach ($slavesParameters as $slaveParameters) { + $this->add($this->connectionFactory->create($slaveParameters)); + } + } catch (ConnectionException $exception) { + $this->sentinelConnection = null; + + goto SENTINEL_QUERY; + } + } + + return array_values($this->slaves ?: array()); + } + + /** + * Returns a random slave. + * + * @return NodeConnectionInterface + */ + protected function pickSlave() + { + if ($slaves = $this->getSlaves()) { + return $slaves[rand(1, count($slaves)) - 1]; + } + } + + /** + * Returns the connection instance in charge for the given command. + * + * @param CommandInterface $command Command instance. + * + * @return NodeConnectionInterface + */ + private function getConnectionInternal(CommandInterface $command) + { + if (!$this->current) { + if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) { + $this->current = $slave; + } else { + $this->current = $this->getMaster(); + } + + return $this->current; + } + + if ($this->current === $this->master) { + return $this->current; + } + + if (!$this->strategy->isReadOperation($command)) { + $this->current = $this->getMaster(); + } + + return $this->current; + } + + /** + * Asserts that the specified connection matches an expected role. + * + * @param NodeConnectionInterface $sentinel Connection to a redis server. + * @param string $role Expected role of the server ("master", "slave" or "sentinel"). + */ + protected function assertConnectionRole(NodeConnectionInterface $connection, $role) + { + $role = strtolower($role); + $actualRole = $connection->executeCommand(RawCommand::create('ROLE')); + + if ($role !== $actualRole[0]) { + throw new RoleException($connection, "Expected $role but got $actualRole[0] [$connection]"); + } + } + + /** + * {@inheritdoc} + */ + public function getConnection(CommandInterface $command) + { + $connection = $this->getConnectionInternal($command); + + if (!$connection->isConnected()) { + // When we do not have any available slave in the pool we can expect + // read-only operations to hit the master server. + $expectedRole = $this->strategy->isReadOperation($command) && $this->slaves ? 'slave' : 'master'; + $this->assertConnectionRole($connection, $expectedRole); + } + + return $connection; + } + + /** + * {@inheritdoc} + */ + public function getConnectionById($connectionId) + { + if ($connectionId === 'master') { + return $this->getMaster(); + } + + $this->getSlaves(); + + if (isset($this->slaves[$connectionId])) { + return $this->slaves[$connectionId]; + } + } + + /** + * {@inheritdoc} + */ + public function switchTo($connection) + { + if (!$connection instanceof NodeConnectionInterface) { + $connection = $this->getConnectionById($connection); + } + + if ($connection && $connection === $this->current) { + return; + } + + if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) { + throw new \InvalidArgumentException('Invalid connection or connection not found.'); + } + + $connection->connect(); + + if ($this->current) { + $this->current->disconnect(); + } + + $this->current = $connection; + } + + /** + * Switches to the master server. + */ + public function switchToMaster() + { + $this->switchTo('master'); + } + + /** + * Switches to a random slave server. + */ + public function switchToSlave() + { + $connection = $this->pickSlave(); + $this->switchTo($connection); + } + + /** + * {@inheritdoc} + */ + public function isConnected() + { + return $this->current ? $this->current->isConnected() : false; + } + + /** + * {@inheritdoc} + */ + public function connect() + { + if (!$this->current) { + if (!$this->current = $this->pickSlave()) { + $this->current = $this->getMaster(); + } + } + + $this->current->connect(); + } + + /** + * {@inheritdoc} + */ + public function disconnect() + { + if ($this->master) { + $this->master->disconnect(); + } + + foreach ($this->slaves as $connection) { + $connection->disconnect(); + } + } + + /** + * Retries the execution of a command upon server failure after asking a new + * configuration to one of the sentinels. + * + * @param CommandInterface $command Command instance. + * @param string $method Actual method. + * + * @return mixed + */ + private function retryCommandOnFailure(CommandInterface $command, $method) + { + $retries = 0; + + SENTINEL_RETRY: { + try { + $response = $this->getConnection($command)->$method($command); + } catch (CommunicationException $exception) { + $this->wipeServerList(); + $exception->getConnection()->disconnect(); + + if ($retries == $this->retryLimit) { + throw $exception; + } + + usleep($this->retryWait * 1000); + + ++$retries; + goto SENTINEL_RETRY; + } + } + + 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) + { + return $this->retryCommandOnFailure($command, __FUNCTION__); + } + + /** + * Returns the underlying replication strategy. + * + * @return ReplicationStrategy + */ + public function getReplicationStrategy() + { + return $this->strategy; + } + + /** + * {@inheritdoc} + */ + public function __sleep() + { + return array( + 'master', 'slaves', 'service', 'sentinels', 'connectionFactory', 'strategy', + ); + } +} diff --git a/vendor/predis/predis/src/Connection/Factory.php b/vendor/predis/predis/src/Connection/Factory.php index c2e93f88..9c7272db 100644 --- a/vendor/predis/predis/src/Connection/Factory.php +++ b/vendor/predis/predis/src/Connection/Factory.php @@ -20,10 +20,14 @@ use Predis\Command\RawCommand; */ class Factory implements FactoryInterface { + private $defaults = array(); + protected $schemes = array( 'tcp' => 'Predis\Connection\StreamConnection', 'unix' => 'Predis\Connection\StreamConnection', + 'tls' => 'Predis\Connection\StreamConnection', 'redis' => 'Predis\Connection\StreamConnection', + 'rediss' => 'Predis\Connection\StreamConnection', 'http' => 'Predis\Connection\WebdisConnection', ); @@ -116,6 +120,29 @@ class Factory implements FactoryInterface } /** + * Assigns a default set of parameters applied to new connections. + * + * The set of parameters passed to create a new connection have precedence + * over the default values set for the connection factory. + * + * @param array $parameters Set of connection parameters. + */ + public function setDefaultParameters(array $parameters) + { + $this->defaults = $parameters; + } + + /** + * Returns the default set of parameters applied to new connections. + * + * @return array + */ + public function getDefaultParameters() + { + return $this->defaults; + } + + /** * Creates a connection parameters instance from the supplied argument. * * @param mixed $parameters Original connection parameters. @@ -124,7 +151,17 @@ class Factory implements FactoryInterface */ protected function createParameters($parameters) { - return Parameters::create($parameters); + if (is_string($parameters)) { + $parameters = Parameters::parse($parameters); + } else { + $parameters = $parameters ?: array(); + } + + if ($this->defaults) { + $parameters += $this->defaults; + } + + return new Parameters($parameters); } /** diff --git a/vendor/predis/predis/src/Connection/Parameters.php b/vendor/predis/predis/src/Connection/Parameters.php index b7d98615..3349c96a 100644 --- a/vendor/predis/predis/src/Connection/Parameters.php +++ b/vendor/predis/predis/src/Connection/Parameters.php @@ -26,7 +26,6 @@ class Parameters implements ParametersInterface 'scheme' => 'tcp', 'host' => '127.0.0.1', 'port' => 6379, - 'timeout' => 5.0, ); /** @@ -74,7 +73,7 @@ class Parameters implements ParametersInterface * "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 + * @link http://www.iana.org/assignments/uri-schemes/prov/rediss * * @param string $uri URI string. * @@ -84,9 +83,10 @@ class Parameters implements ParametersInterface */ 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 (stripos($uri, 'unix://') === 0) { + // parse_url() can parse unix:/path/to/sock so we do not need the + // unix:///path/to/sock hack, we will support it anyway until 2.0. + $uri = str_ireplace('unix://', 'unix:', $uri); } if (!$parsed = parse_url($uri)) { diff --git a/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php b/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php index 6948f035..edded2d0 100644 --- a/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php +++ b/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php @@ -14,6 +14,7 @@ namespace Predis\Connection; use Predis\Command\CommandInterface; use Predis\NotSupportedException; use Predis\Response\Error as ErrorResponse; +use Predis\Response\ErrorInterface as ErrorResponseInterface; use Predis\Response\Status as StatusResponse; /** @@ -36,7 +37,7 @@ use Predis\Response\Status as StatusResponse; * - 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. + * - timeout: timeout to perform the connection (default is 5 seconds). * - read_write_timeout: timeout of read / write operations. * * @link http://github.com/nrk/phpiredis @@ -93,7 +94,15 @@ class PhpiredisSocketConnection extends AbstractConnection */ protected function assertParameters(ParametersInterface $parameters) { - parent::assertParameters($parameters); + switch ($parameters->scheme) { + case 'tcp': + case 'redis': + case 'unix': + break; + + default: + throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); + } if (isset($parameters->persistent)) { throw new NotSupportedException( @@ -134,11 +143,17 @@ class PhpiredisSocketConnection extends AbstractConnection * * @return \Closure */ - private function getStatusHandler() + protected function getStatusHandler() { - return function ($payload) { - return StatusResponse::get($payload); - }; + static $statusHandler; + + if (!$statusHandler) { + $statusHandler = function ($payload) { + return StatusResponse::get($payload); + }; + } + + return $statusHandler; } /** @@ -148,9 +163,15 @@ class PhpiredisSocketConnection extends AbstractConnection */ protected function getErrorHandler() { - return function ($payload) { - return new ErrorResponse($payload); - }; + static $errorHandler; + + if (!$errorHandler) { + $errorHandler = function ($errorMessage) { + return new ErrorResponse($errorMessage); + }; + } + + return $errorHandler; } /** @@ -282,7 +303,7 @@ class PhpiredisSocketConnection extends AbstractConnection $null = null; $selectable = array($socket); - $timeout = (float) $parameters->timeout; + $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); $timeoutSecs = floor($timeout); $timeoutUSecs = ($timeout - $timeoutSecs) * 1000000; @@ -308,7 +329,11 @@ class PhpiredisSocketConnection extends AbstractConnection { if (parent::connect() && $this->initCommands) { foreach ($this->initCommands as $command) { - $this->executeCommand($command); + $response = $this->executeCommand($command); + + if ($response instanceof ErrorResponseInterface) { + $this->onConnectionError("`{$command->getId()}` failed: $response", 0); + } } } } diff --git a/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php b/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php index beb23575..f0b719bf 100644 --- a/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php +++ b/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php @@ -87,22 +87,20 @@ class PhpiredisStreamConnection extends StreamConnection /** * {@inheritdoc} */ - protected function tcpStreamInitializer(ParametersInterface $parameters) + protected function assertSslSupport(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; - } + throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.'); + } - if (isset($parameters->persistent) && (bool) $parameters->persistent) { - $flags |= STREAM_CLIENT_PERSISTENT; - $uri .= strpos($path = $parameters->path, '/') === 0 ? $path : "/$path"; - } + /** + * {@inheritdoc} + */ + protected function createStreamSocket(ParametersInterface $parameters, $address, $flags, $context = null) + { + $socket = null; + $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); - $resource = @stream_socket_client($uri, $errno, $errstr, (float) $parameters->timeout, $flags); + $resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags); if (!$resource) { $this->onConnectionError(trim($errstr), $errno); @@ -162,9 +160,15 @@ class PhpiredisStreamConnection extends StreamConnection */ protected function getStatusHandler() { - return function ($payload) { - return StatusResponse::get($payload); - }; + static $statusHandler; + + if (!$statusHandler) { + $statusHandler = function ($payload) { + return StatusResponse::get($payload); + }; + } + + return $statusHandler; } /** @@ -174,9 +178,15 @@ class PhpiredisStreamConnection extends StreamConnection */ protected function getErrorHandler() { - return function ($errorMessage) { - return new ErrorResponse($errorMessage); - }; + static $errorHandler; + + if (!$errorHandler) { + $errorHandler = function ($errorMessage) { + return new ErrorResponse($errorMessage); + }; + } + + return $errorHandler; } /** diff --git a/vendor/predis/predis/src/Connection/StreamConnection.php b/vendor/predis/predis/src/Connection/StreamConnection.php index 955c2b1b..9c26272d 100644 --- a/vendor/predis/predis/src/Connection/StreamConnection.php +++ b/vendor/predis/predis/src/Connection/StreamConnection.php @@ -13,21 +13,23 @@ namespace Predis\Connection; use Predis\Command\CommandInterface; use Predis\Response\Error as ErrorResponse; +use Predis\Response\ErrorInterface as ErrorResponseInterface; 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'. + * - scheme: it can be either 'redis', 'tcp', 'rediss', 'tls' 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. + * - timeout: timeout to perform the connection (default is 5 seconds). * - 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. + * - ssl: context options array (see http://php.net/manual/en/context.ssl.php) * * @author Daniele Alessandri <suppakilla@gmail.com> */ @@ -50,50 +52,79 @@ class StreamConnection extends AbstractConnection /** * {@inheritdoc} */ - protected function createResource() + protected function assertParameters(ParametersInterface $parameters) { - switch ($this->parameters->scheme) { + switch ($parameters->scheme) { case 'tcp': case 'redis': - return $this->tcpStreamInitializer($this->parameters); - case 'unix': - return $this->unixStreamInitializer($this->parameters); + break; + + case 'tls': + case 'rediss': + $this->assertSslSupport($parameters); + break; default: - throw new \InvalidArgumentException("Invalid scheme: '{$this->parameters->scheme}'."); + throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'."); } + + return $parameters; } /** - * Initializes a TCP stream resource. + * Checks needed conditions for SSL-encrypted connections. * * @param ParametersInterface $parameters Initialization parameters for the connection. * - * @return resource + * @throws \InvalidArgumentException */ - protected function tcpStreamInitializer(ParametersInterface $parameters) + protected function assertSslSupport(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"; + if ( + filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN) && + version_compare(PHP_VERSION, '7.0.0beta') < 0 + ) { + throw new \InvalidArgumentException('Persistent SSL connections require PHP >= 7.0.0.'); } + } - $flags = STREAM_CLIENT_CONNECT; + /** + * {@inheritdoc} + */ + protected function createResource() + { + switch ($this->parameters->scheme) { + case 'tcp': + case 'redis': + return $this->tcpStreamInitializer($this->parameters); - if (isset($parameters->async_connect) && (bool) $parameters->async_connect) { - $flags |= STREAM_CLIENT_ASYNC_CONNECT; - } + case 'unix': + return $this->unixStreamInitializer($this->parameters); + + case 'tls': + case 'rediss': + return $this->tlsStreamInitializer($this->parameters); - if (isset($parameters->persistent) && (bool) $parameters->persistent) { - $flags |= STREAM_CLIENT_PERSISTENT; - $uri .= strpos($path = $parameters->path, '/') === 0 ? $path : "/$path"; + default: + throw new \InvalidArgumentException("Invalid scheme: '{$this->parameters->scheme}'."); } + } - $resource = @stream_socket_client($uri, $errno, $errstr, (float) $parameters->timeout, $flags); + /** + * Creates a connected stream socket resource. + * + * @param ParametersInterface $parameters Connection parameters. + * @param string $address Address for stream_socket_client(). + * @param int $flags Flags for stream_socket_client(). + * + * @return resource + */ + protected function createStreamSocket(ParametersInterface $parameters, $address, $flags) + { + $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0); - if (!$resource) { + if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags)) { $this->onConnectionError(trim($errstr), $errno); } @@ -114,6 +145,42 @@ class StreamConnection extends AbstractConnection } /** + * 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)) { + $address = "tcp://$parameters->host:$parameters->port"; + } else { + $address = "tcp://[$parameters->host]:$parameters->port"; + } + + $flags = STREAM_CLIENT_CONNECT; + + if (isset($parameters->async_connect) && $parameters->async_connect) { + $flags |= STREAM_CLIENT_ASYNC_CONNECT; + } + + if (isset($parameters->persistent)) { + if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) { + $flags |= STREAM_CLIENT_PERSISTENT; + + if ($persistent === null) { + $address = "{$address}/{$parameters->persistent}"; + } + } + } + + $resource = $this->createStreamSocket($parameters, $address, $flags); + + return $resource; + } + + /** * Initializes a UNIX stream resource. * * @param ParametersInterface $parameters Initialization parameters for the connection. @@ -126,25 +193,58 @@ class StreamConnection extends AbstractConnection throw new \InvalidArgumentException('Missing UNIX domain socket path.'); } - $uri = "unix://{$parameters->path}"; $flags = STREAM_CLIENT_CONNECT; - if ((bool) $parameters->persistent) { - $flags |= STREAM_CLIENT_PERSISTENT; + if (isset($parameters->persistent)) { + if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) { + $flags |= STREAM_CLIENT_PERSISTENT; + + if ($persistent === null) { + throw new \InvalidArgumentException( + 'Persistent connection IDs are not supported when using UNIX domain sockets.' + ); + } + } } - $resource = @stream_socket_client($uri, $errno, $errstr, (float) $parameters->timeout, $flags); + $resource = $this->createStreamSocket($parameters, "unix://{$parameters->path}", $flags); - if (!$resource) { - $this->onConnectionError(trim($errstr), $errno); + return $resource; + } + + /** + * Initializes a SSL-encrypted TCP stream resource. + * + * @param ParametersInterface $parameters Initialization parameters for the connection. + * + * @return resource + */ + protected function tlsStreamInitializer(ParametersInterface $parameters) + { + $resource = $this->tcpStreamInitializer($parameters); + $metadata = stream_get_meta_data($resource); + + // Detect if crypto mode is already enabled for this stream (PHP >= 7.0.0). + if (isset($metadata['crypto'])) { + return $resource; } - 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 (is_array($parameters->ssl)) { + $options = $parameters->ssl; + } else { + $options = array(); + } + + if (!isset($options['crypto_type'])) { + $options['crypto_type'] = STREAM_CRYPTO_METHOD_TLS_CLIENT; + } + + if (!stream_context_set_option($resource, array('ssl' => $options))) { + $this->onConnectionError('Error while setting SSL context options'); + } + + if (!stream_socket_enable_crypto($resource, true, $options['crypto_type'])) { + $this->onConnectionError('Error while switching to encrypted communication'); } return $resource; @@ -157,7 +257,11 @@ class StreamConnection extends AbstractConnection { if (parent::connect() && $this->initCommands) { foreach ($this->initCommands as $command) { - $this->executeCommand($command); + $response = $this->executeCommand($command); + + if ($response instanceof ErrorResponseInterface) { + $this->onConnectionError("`{$command->getId()}` failed: $response", 0); + } } } } @@ -256,7 +360,8 @@ class StreamConnection extends AbstractConnection return $multibulk; case ':': - return (int) $payload; + $integer = (int) $payload; + return $integer == $payload ? $integer : $payload; case '-': return new ErrorResponse($payload); diff --git a/vendor/predis/predis/src/Connection/WebdisConnection.php b/vendor/predis/predis/src/Connection/WebdisConnection.php index 9cff9d02..c8d6e501 100644 --- a/vendor/predis/predis/src/Connection/WebdisConnection.php +++ b/vendor/predis/predis/src/Connection/WebdisConnection.php @@ -33,7 +33,7 @@ use Predis\Response\Status as StatusResponse; * - scheme: must be 'http'. * - host: hostname or IP address of the server. * - port: TCP port of the server. - * - timeout: timeout to perform the connection. + * - timeout: timeout to perform the connection (default is 5 seconds). * - user: username for authentication. * - pass: password for authentication. * @@ -117,6 +117,7 @@ class WebdisConnection implements NodeConnectionInterface private function createCurl() { $parameters = $this->getParameters(); + $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0) * 1000; if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP)) { $host = "[$host]"; @@ -124,7 +125,7 @@ class WebdisConnection implements NodeConnectionInterface $options = array( CURLOPT_FAILONERROR => true, - CURLOPT_CONNECTTIMEOUT_MS => $parameters->timeout * 1000, + CURLOPT_CONNECTTIMEOUT_MS => $timeout, CURLOPT_URL => "$parameters->scheme://$host:$parameters->port", CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_POST => true, @@ -162,9 +163,15 @@ class WebdisConnection implements NodeConnectionInterface */ protected function getStatusHandler() { - return function ($payload) { - return StatusResponse::get($payload); - }; + static $statusHandler; + + if (!$statusHandler) { + $statusHandler = function ($payload) { + return StatusResponse::get($payload); + }; + } + + return $statusHandler; } /** @@ -174,9 +181,15 @@ class WebdisConnection implements NodeConnectionInterface */ protected function getErrorHandler() { - return function ($payload) { - return new ErrorResponse($payload); - }; + static $errorHandler; + + if (!$errorHandler) { + $errorHandler = function ($errorMessage) { + return new ErrorResponse($errorMessage); + }; + } + + return $errorHandler; } /** diff --git a/vendor/predis/predis/src/Profile/Factory.php b/vendor/predis/predis/src/Profile/Factory.php index 260fee42..d4907a23 100644 --- a/vendor/predis/predis/src/Profile/Factory.php +++ b/vendor/predis/predis/src/Profile/Factory.php @@ -29,7 +29,7 @@ final class Factory '3.0' => 'Predis\Profile\RedisVersion300', '3.2' => 'Predis\Profile\RedisVersion320', 'dev' => 'Predis\Profile\RedisUnstable', - 'default' => 'Predis\Profile\RedisVersion300', + 'default' => 'Predis\Profile\RedisVersion320', ); /** diff --git a/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php b/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php index 4639d779..f9656019 100644 --- a/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php +++ b/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php @@ -31,7 +31,8 @@ class IntegerResponse implements ResponseHandlerInterface public function handle(CompositeConnectionInterface $connection, $payload) { if (is_numeric($payload)) { - return (int) $payload; + $integer = (int) $payload; + return $integer == $payload ? $integer : $payload; } if ($payload !== 'nil') { diff --git a/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php b/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php index f04c3ed5..99acdf86 100644 --- a/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php +++ b/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php @@ -90,7 +90,8 @@ class ProtocolProcessor implements ProtocolProcessorInterface return $multibulk; case ':': - return (int) $payload; + $integer = (int) $payload; + return $integer == $payload ? $integer : $payload; case '-': return new ErrorResponse($payload); diff --git a/vendor/predis/predis/src/Replication/MissingMasterException.php b/vendor/predis/predis/src/Replication/MissingMasterException.php new file mode 100644 index 00000000..223bd2d4 --- /dev/null +++ b/vendor/predis/predis/src/Replication/MissingMasterException.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\Replication; + +use Predis\ClientException; + +/** + * Exception class that identifies when master is missing in a replication setup. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class MissingMasterException extends ClientException +{ +} diff --git a/vendor/predis/predis/src/Replication/RoleException.php b/vendor/predis/predis/src/Replication/RoleException.php new file mode 100644 index 00000000..0d9954bf --- /dev/null +++ b/vendor/predis/predis/src/Replication/RoleException.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\Replication; + +use Predis\CommunicationException; + +/** + * Exception class that identifies a role mismatch when connecting to node + * managed by redis-sentinel. + * + * @author Daniele Alessandri <suppakilla@gmail.com> + */ +class RoleException extends CommunicationException +{ +} |