Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/nextcloud.com.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/vendor
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2018-05-01 20:06:01 +0300
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2018-05-01 20:06:01 +0300
commitc603cf71406ae2ff7c3771290c26a954846c1190 (patch)
tree8d0357fca42efaf954bf13603765472df8969aa8 /vendor
parent32aaf015a943b6ce52e5cbec6e26b85870c89cf6 (diff)
Rate limit and various fixes
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'vendor')
-rw-r--r--vendor/composer/autoload_psr4.php2
-rw-r--r--vendor/composer/autoload_static.php10
-rw-r--r--vendor/composer/installed.json114
m---------vendor/perimeter/rate-limiter-php0
-rw-r--r--vendor/predis/predis/CHANGELOG.md892
-rw-r--r--vendor/predis/predis/CONTRIBUTING.md44
-rw-r--r--vendor/predis/predis/FAQ.md169
-rw-r--r--vendor/predis/predis/LICENSE22
-rw-r--r--vendor/predis/predis/README.md436
-rw-r--r--vendor/predis/predis/VERSION1
-rw-r--r--vendor/predis/predis/autoload.php14
-rwxr-xr-xvendor/predis/predis/bin/create-command-test275
-rwxr-xr-xvendor/predis/predis/bin/create-pear233
-rwxr-xr-xvendor/predis/predis/bin/create-phar71
-rwxr-xr-xvendor/predis/predis/bin/create-single-file662
-rw-r--r--vendor/predis/predis/composer.json31
-rw-r--r--vendor/predis/predis/examples/custom_cluster_distributor.php117
-rw-r--r--vendor/predis/predis/examples/debuggable_connection.php92
-rw-r--r--vendor/predis/predis/examples/dispatcher_loop.php79
-rw-r--r--vendor/predis/predis/examples/executing_redis_commands.php57
-rw-r--r--vendor/predis/predis/examples/key_prefixing.php36
-rw-r--r--vendor/predis/predis/examples/lua_scripting_abstraction.php71
-rw-r--r--vendor/predis/predis/examples/monitor_consumer.php44
-rw-r--r--vendor/predis/predis/examples/pipelining_commands.php45
-rw-r--r--vendor/predis/predis/examples/pubsub_consumer.php59
-rw-r--r--vendor/predis/predis/examples/redis_collections_iterators.php99
-rw-r--r--vendor/predis/predis/examples/replication_complex.php85
-rw-r--r--vendor/predis/predis/examples/replication_simple.php52
-rw-r--r--vendor/predis/predis/examples/session_handler.php52
-rw-r--r--vendor/predis/predis/examples/shared.php44
-rw-r--r--vendor/predis/predis/examples/transaction_using_cas.php52
-rw-r--r--vendor/predis/predis/package.ini36
-rw-r--r--vendor/predis/predis/src/Autoloader.php62
-rw-r--r--vendor/predis/predis/src/Client.php523
-rw-r--r--vendor/predis/predis/src/ClientContextInterface.php198
-rw-r--r--vendor/predis/predis/src/ClientException.php21
-rw-r--r--vendor/predis/predis/src/ClientInterface.php239
-rw-r--r--vendor/predis/predis/src/Cluster/ClusterStrategy.php469
-rw-r--r--vendor/predis/predis/src/Cluster/Distributor/DistributorInterface.php82
-rw-r--r--vendor/predis/predis/src/Cluster/Distributor/EmptyRingException.php21
-rw-r--r--vendor/predis/predis/src/Cluster/Distributor/HashRing.php270
-rw-r--r--vendor/predis/predis/src/Cluster/Distributor/KetamaRing.php71
-rw-r--r--vendor/predis/predis/src/Cluster/Hash/CRC16.php72
-rw-r--r--vendor/predis/predis/src/Cluster/Hash/HashGeneratorInterface.php30
-rw-r--r--vendor/predis/predis/src/Cluster/PredisStrategy.php79
-rw-r--r--vendor/predis/predis/src/Cluster/RedisStrategy.php58
-rw-r--r--vendor/predis/predis/src/Cluster/StrategyInterface.php53
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.php191
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/HashKey.php60
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/Keyspace.php43
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/ListKey.php176
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/SetKey.php47
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php60
-rw-r--r--vendor/predis/predis/src/Command/Command.php129
-rw-r--r--vendor/predis/predis/src/Command/CommandInterface.php81
-rw-r--r--vendor/predis/predis/src/Command/ConnectionAuth.php28
-rw-r--r--vendor/predis/predis/src/Command/ConnectionEcho.php28
-rw-r--r--vendor/predis/predis/src/Command/ConnectionPing.php28
-rw-r--r--vendor/predis/predis/src/Command/ConnectionQuit.php28
-rw-r--r--vendor/predis/predis/src/Command/ConnectionSelect.php28
-rw-r--r--vendor/predis/predis/src/Command/GeospatialGeoAdd.php42
-rw-r--r--vendor/predis/predis/src/Command/GeospatialGeoDist.php28
-rw-r--r--vendor/predis/predis/src/Command/GeospatialGeoHash.php41
-rw-r--r--vendor/predis/predis/src/Command/GeospatialGeoPos.php41
-rw-r--r--vendor/predis/predis/src/Command/GeospatialGeoRadius.php71
-rw-r--r--vendor/predis/predis/src/Command/GeospatialGeoRadiusByMember.php28
-rw-r--r--vendor/predis/predis/src/Command/HashDelete.php36
-rw-r--r--vendor/predis/predis/src/Command/HashExists.php36
-rw-r--r--vendor/predis/predis/src/Command/HashGet.php28
-rw-r--r--vendor/predis/predis/src/Command/HashGetAll.php42
-rw-r--r--vendor/predis/predis/src/Command/HashGetMultiple.php36
-rw-r--r--vendor/predis/predis/src/Command/HashIncrementBy.php28
-rw-r--r--vendor/predis/predis/src/Command/HashIncrementByFloat.php28
-rw-r--r--vendor/predis/predis/src/Command/HashKeys.php28
-rw-r--r--vendor/predis/predis/src/Command/HashLength.php28
-rw-r--r--vendor/predis/predis/src/Command/HashScan.php85
-rw-r--r--vendor/predis/predis/src/Command/HashSet.php36
-rw-r--r--vendor/predis/predis/src/Command/HashSetMultiple.php48
-rw-r--r--vendor/predis/predis/src/Command/HashSetPreserve.php36
-rw-r--r--vendor/predis/predis/src/Command/HashStringLength.php28
-rw-r--r--vendor/predis/predis/src/Command/HashValues.php28
-rw-r--r--vendor/predis/predis/src/Command/HyperLogLogAdd.php44
-rw-r--r--vendor/predis/predis/src/Command/HyperLogLogCount.php36
-rw-r--r--vendor/predis/predis/src/Command/HyperLogLogMerge.php36
-rw-r--r--vendor/predis/predis/src/Command/KeyDelete.php36
-rw-r--r--vendor/predis/predis/src/Command/KeyDump.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyExists.php36
-rw-r--r--vendor/predis/predis/src/Command/KeyExpire.php36
-rw-r--r--vendor/predis/predis/src/Command/KeyExpireAt.php36
-rw-r--r--vendor/predis/predis/src/Command/KeyKeys.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyMigrate.php50
-rw-r--r--vendor/predis/predis/src/Command/KeyMove.php36
-rw-r--r--vendor/predis/predis/src/Command/KeyPersist.php36
-rw-r--r--vendor/predis/predis/src/Command/KeyPreciseExpire.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyPreciseExpireAt.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyPreciseTimeToLive.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyRandom.php36
-rw-r--r--vendor/predis/predis/src/Command/KeyRename.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyRenamePreserve.php36
-rw-r--r--vendor/predis/predis/src/Command/KeyRestore.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyScan.php66
-rw-r--r--vendor/predis/predis/src/Command/KeySort.php83
-rw-r--r--vendor/predis/predis/src/Command/KeyTimeToLive.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyType.php28
-rw-r--r--vendor/predis/predis/src/Command/ListIndex.php28
-rw-r--r--vendor/predis/predis/src/Command/ListInsert.php28
-rw-r--r--vendor/predis/predis/src/Command/ListLength.php28
-rw-r--r--vendor/predis/predis/src/Command/ListPopFirst.php28
-rw-r--r--vendor/predis/predis/src/Command/ListPopFirstBlocking.php41
-rw-r--r--vendor/predis/predis/src/Command/ListPopLast.php28
-rw-r--r--vendor/predis/predis/src/Command/ListPopLastBlocking.php28
-rw-r--r--vendor/predis/predis/src/Command/ListPopLastPushHead.php28
-rw-r--r--vendor/predis/predis/src/Command/ListPopLastPushHeadBlocking.php28
-rw-r--r--vendor/predis/predis/src/Command/ListPushHead.php28
-rw-r--r--vendor/predis/predis/src/Command/ListPushHeadX.php28
-rw-r--r--vendor/predis/predis/src/Command/ListPushTail.php36
-rw-r--r--vendor/predis/predis/src/Command/ListPushTailX.php28
-rw-r--r--vendor/predis/predis/src/Command/ListRange.php28
-rw-r--r--vendor/predis/predis/src/Command/ListRemove.php28
-rw-r--r--vendor/predis/predis/src/Command/ListSet.php28
-rw-r--r--vendor/predis/predis/src/Command/ListTrim.php28
-rw-r--r--vendor/predis/predis/src/Command/PrefixableCommandInterface.php27
-rw-r--r--vendor/predis/predis/src/Command/Processor/KeyPrefixProcessor.php450
-rw-r--r--vendor/predis/predis/src/Command/Processor/ProcessorChain.php130
-rw-r--r--vendor/predis/predis/src/Command/Processor/ProcessorInterface.php29
-rw-r--r--vendor/predis/predis/src/Command/PubSubPublish.php28
-rw-r--r--vendor/predis/predis/src/Command/PubSubPubsub.php61
-rw-r--r--vendor/predis/predis/src/Command/PubSubSubscribe.php36
-rw-r--r--vendor/predis/predis/src/Command/PubSubSubscribeByPattern.php28
-rw-r--r--vendor/predis/predis/src/Command/PubSubUnsubscribe.php36
-rw-r--r--vendor/predis/predis/src/Command/PubSubUnsubscribeByPattern.php28
-rw-r--r--vendor/predis/predis/src/Command/RawCommand.php131
-rw-r--r--vendor/predis/predis/src/Command/ScriptCommand.php77
-rw-r--r--vendor/predis/predis/src/Command/ServerBackgroundRewriteAOF.php36
-rw-r--r--vendor/predis/predis/src/Command/ServerBackgroundSave.php36
-rw-r--r--vendor/predis/predis/src/Command/ServerClient.php74
-rw-r--r--vendor/predis/predis/src/Command/ServerCommand.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerConfig.php49
-rw-r--r--vendor/predis/predis/src/Command/ServerDatabaseSize.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerEval.php38
-rw-r--r--vendor/predis/predis/src/Command/ServerEvalSHA.php38
-rw-r--r--vendor/predis/predis/src/Command/ServerFlushAll.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerFlushDatabase.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerInfo.php111
-rw-r--r--vendor/predis/predis/src/Command/ServerInfoV26x.php56
-rw-r--r--vendor/predis/predis/src/Command/ServerLastSave.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerMonitor.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerObject.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerSave.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerScript.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerSentinel.php66
-rw-r--r--vendor/predis/predis/src/Command/ServerShutdown.php28
-rw-r--r--vendor/predis/predis/src/Command/ServerSlaveOf.php40
-rw-r--r--vendor/predis/predis/src/Command/ServerSlowlog.php51
-rw-r--r--vendor/predis/predis/src/Command/ServerTime.php28
-rw-r--r--vendor/predis/predis/src/Command/SetAdd.php36
-rw-r--r--vendor/predis/predis/src/Command/SetCardinality.php28
-rw-r--r--vendor/predis/predis/src/Command/SetDifference.php28
-rw-r--r--vendor/predis/predis/src/Command/SetDifferenceStore.php28
-rw-r--r--vendor/predis/predis/src/Command/SetIntersection.php36
-rw-r--r--vendor/predis/predis/src/Command/SetIntersectionStore.php40
-rw-r--r--vendor/predis/predis/src/Command/SetIsMember.php36
-rw-r--r--vendor/predis/predis/src/Command/SetMembers.php28
-rw-r--r--vendor/predis/predis/src/Command/SetMove.php36
-rw-r--r--vendor/predis/predis/src/Command/SetPop.php28
-rw-r--r--vendor/predis/predis/src/Command/SetRandomMember.php28
-rw-r--r--vendor/predis/predis/src/Command/SetRemove.php36
-rw-r--r--vendor/predis/predis/src/Command/SetScan.php66
-rw-r--r--vendor/predis/predis/src/Command/SetUnion.php28
-rw-r--r--vendor/predis/predis/src/Command/SetUnionStore.php28
-rw-r--r--vendor/predis/predis/src/Command/StringAppend.php28
-rw-r--r--vendor/predis/predis/src/Command/StringBitCount.php28
-rw-r--r--vendor/predis/predis/src/Command/StringBitField.php28
-rw-r--r--vendor/predis/predis/src/Command/StringBitOp.php42
-rw-r--r--vendor/predis/predis/src/Command/StringBitPos.php28
-rw-r--r--vendor/predis/predis/src/Command/StringDecrement.php28
-rw-r--r--vendor/predis/predis/src/Command/StringDecrementBy.php28
-rw-r--r--vendor/predis/predis/src/Command/StringGet.php28
-rw-r--r--vendor/predis/predis/src/Command/StringGetBit.php28
-rw-r--r--vendor/predis/predis/src/Command/StringGetMultiple.php36
-rw-r--r--vendor/predis/predis/src/Command/StringGetRange.php28
-rw-r--r--vendor/predis/predis/src/Command/StringGetSet.php28
-rw-r--r--vendor/predis/predis/src/Command/StringIncrement.php28
-rw-r--r--vendor/predis/predis/src/Command/StringIncrementBy.php28
-rw-r--r--vendor/predis/predis/src/Command/StringIncrementByFloat.php28
-rw-r--r--vendor/predis/predis/src/Command/StringPreciseSetExpire.php28
-rw-r--r--vendor/predis/predis/src/Command/StringSet.php28
-rw-r--r--vendor/predis/predis/src/Command/StringSetBit.php28
-rw-r--r--vendor/predis/predis/src/Command/StringSetExpire.php28
-rw-r--r--vendor/predis/predis/src/Command/StringSetMultiple.php48
-rw-r--r--vendor/predis/predis/src/Command/StringSetMultiplePreserve.php36
-rw-r--r--vendor/predis/predis/src/Command/StringSetPreserve.php36
-rw-r--r--vendor/predis/predis/src/Command/StringSetRange.php28
-rw-r--r--vendor/predis/predis/src/Command/StringStrlen.php28
-rw-r--r--vendor/predis/predis/src/Command/StringSubstr.php28
-rw-r--r--vendor/predis/predis/src/Command/TransactionDiscard.php28
-rw-r--r--vendor/predis/predis/src/Command/TransactionExec.php28
-rw-r--r--vendor/predis/predis/src/Command/TransactionMulti.php28
-rw-r--r--vendor/predis/predis/src/Command/TransactionUnwatch.php28
-rw-r--r--vendor/predis/predis/src/Command/TransactionWatch.php40
-rw-r--r--vendor/predis/predis/src/Command/ZSetAdd.php43
-rw-r--r--vendor/predis/predis/src/Command/ZSetCardinality.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetCount.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetIncrementBy.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetIntersectionStore.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetLexCount.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetRange.php105
-rw-r--r--vendor/predis/predis/src/Command/ZSetRangeByLex.php55
-rw-r--r--vendor/predis/predis/src/Command/ZSetRangeByScore.php68
-rw-r--r--vendor/predis/predis/src/Command/ZSetRank.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetRemove.php36
-rw-r--r--vendor/predis/predis/src/Command/ZSetRemoveRangeByLex.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetRemoveRangeByRank.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetRemoveRangeByScore.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetReverseRange.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetReverseRangeByLex.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetReverseRangeByScore.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetReverseRank.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetScan.php85
-rw-r--r--vendor/predis/predis/src/Command/ZSetScore.php28
-rw-r--r--vendor/predis/predis/src/Command/ZSetUnionStore.php78
-rw-r--r--vendor/predis/predis/src/CommunicationException.php80
-rw-r--r--vendor/predis/predis/src/Configuration/ClusterOption.php76
-rw-r--r--vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php54
-rw-r--r--vendor/predis/predis/src/Configuration/ExceptionsOption.php37
-rw-r--r--vendor/predis/predis/src/Configuration/OptionInterface.php40
-rw-r--r--vendor/predis/predis/src/Configuration/Options.php122
-rw-r--r--vendor/predis/predis/src/Configuration/OptionsInterface.php64
-rw-r--r--vendor/predis/predis/src/Configuration/PrefixOption.php44
-rw-r--r--vendor/predis/predis/src/Configuration/ProfileOption.php69
-rw-r--r--vendor/predis/predis/src/Configuration/ReplicationOption.php61
-rw-r--r--vendor/predis/predis/src/Connection/AbstractConnection.php239
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/ClusterInterface.php24
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php281
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php235
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php676
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/ReplicationInterface.php52
-rw-r--r--vendor/predis/predis/src/Connection/AggregateConnectionInterface.php57
-rw-r--r--vendor/predis/predis/src/Connection/CompositeConnectionInterface.php49
-rw-r--r--vendor/predis/predis/src/Connection/CompositeStreamConnection.php125
-rw-r--r--vendor/predis/predis/src/Connection/ConnectionException.php23
-rw-r--r--vendor/predis/predis/src/Connection/ConnectionInterface.php66
-rw-r--r--vendor/predis/predis/src/Connection/Factory.php151
-rw-r--r--vendor/predis/predis/src/Connection/FactoryInterface.php52
-rw-r--r--vendor/predis/predis/src/Connection/NodeConnectionInterface.php58
-rw-r--r--vendor/predis/predis/src/Connection/Parameters.php176
-rw-r--r--vendor/predis/predis/src/Connection/ParametersInterface.php62
-rw-r--r--vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php393
-rw-r--r--vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php228
-rw-r--r--vendor/predis/predis/src/Connection/StreamConnection.php291
-rw-r--r--vendor/predis/predis/src/Connection/WebdisConnection.php353
-rw-r--r--vendor/predis/predis/src/Monitor/Consumer.php173
-rw-r--r--vendor/predis/predis/src/NotSupportedException.php22
-rw-r--r--vendor/predis/predis/src/Pipeline/Atomic.php119
-rw-r--r--vendor/predis/predis/src/Pipeline/ConnectionErrorProof.php130
-rw-r--r--vendor/predis/predis/src/Pipeline/FireAndForget.php36
-rw-r--r--vendor/predis/predis/src/Pipeline/Pipeline.php247
-rw-r--r--vendor/predis/predis/src/PredisException.php21
-rw-r--r--vendor/predis/predis/src/Profile/Factory.php101
-rw-r--r--vendor/predis/predis/src/Profile/ProfileInterface.php59
-rw-r--r--vendor/predis/predis/src/Profile/RedisProfile.php146
-rw-r--r--vendor/predis/predis/src/Profile/RedisUnstable.php38
-rw-r--r--vendor/predis/predis/src/Profile/RedisVersion200.php173
-rw-r--r--vendor/predis/predis/src/Profile/RedisVersion220.php202
-rw-r--r--vendor/predis/predis/src/Profile/RedisVersion240.php207
-rw-r--r--vendor/predis/predis/src/Profile/RedisVersion260.php235
-rw-r--r--vendor/predis/predis/src/Profile/RedisVersion280.php267
-rw-r--r--vendor/predis/predis/src/Profile/RedisVersion300.php270
-rw-r--r--vendor/predis/predis/src/Profile/RedisVersion320.php281
-rw-r--r--vendor/predis/predis/src/Protocol/ProtocolException.php24
-rw-r--r--vendor/predis/predis/src/Protocol/ProtocolProcessorInterface.php41
-rw-r--r--vendor/predis/predis/src/Protocol/RequestSerializerInterface.php31
-rw-r--r--vendor/predis/predis/src/Protocol/ResponseReaderInterface.php32
-rw-r--r--vendor/predis/predis/src/Protocol/Text/CompositeProtocolProcessor.php107
-rw-r--r--vendor/predis/predis/src/Protocol/Text/Handler/BulkResponse.php55
-rw-r--r--vendor/predis/predis/src/Protocol/Text/Handler/ErrorResponse.php34
-rw-r--r--vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.php45
-rw-r--r--vendor/predis/predis/src/Protocol/Text/Handler/MultiBulkResponse.php68
-rw-r--r--vendor/predis/predis/src/Protocol/Text/Handler/ResponseHandlerInterface.php33
-rw-r--r--vendor/predis/predis/src/Protocol/Text/Handler/StatusResponse.php35
-rw-r--r--vendor/predis/predis/src/Protocol/Text/Handler/StreamableMultiBulkResponse.php47
-rw-r--r--vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php122
-rw-r--r--vendor/predis/predis/src/Protocol/Text/RequestSerializer.php46
-rw-r--r--vendor/predis/predis/src/Protocol/Text/ResponseReader.php116
-rw-r--r--vendor/predis/predis/src/PubSub/AbstractConsumer.php219
-rw-r--r--vendor/predis/predis/src/PubSub/Consumer.php158
-rw-r--r--vendor/predis/predis/src/PubSub/DispatcherLoop.php170
-rw-r--r--vendor/predis/predis/src/Replication/ReplicationStrategy.php304
-rw-r--r--vendor/predis/predis/src/Response/Error.php59
-rw-r--r--vendor/predis/predis/src/Response/ErrorInterface.php35
-rw-r--r--vendor/predis/predis/src/Response/Iterator/MultiBulk.php77
-rw-r--r--vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.php104
-rw-r--r--vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php90
-rw-r--r--vendor/predis/predis/src/Response/ResponseInterface.php21
-rw-r--r--vendor/predis/predis/src/Response/ServerException.php44
-rw-r--r--vendor/predis/predis/src/Response/Status.php79
-rw-r--r--vendor/predis/predis/src/Session/Handler.php142
-rw-r--r--vendor/predis/predis/src/Transaction/AbortedMultiExecException.php45
-rw-r--r--vendor/predis/predis/src/Transaction/MultiExec.php461
-rw-r--r--vendor/predis/predis/src/Transaction/MultiExecState.php166
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);
+ }
+}