summaryrefslogtreecommitdiffstats
path: root/vendor/predis
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:38:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 12:38:42 +0000
commitc3ca98e1b35123f226c7f4c596b5dee78caa4223 (patch)
tree9b6eb109283da55e7d9064baa9fac795a40264cb /vendor/predis
parentInitial commit. (diff)
downloadicinga-php-thirdparty-upstream.tar.xz
icinga-php-thirdparty-upstream.zip
Adding upstream version 0.11.0.upstream/0.11.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--vendor/predis/predis/LICENSE22
-rw-r--r--vendor/predis/predis/autoload.php14
-rw-r--r--vendor/predis/predis/composer.json48
-rw-r--r--vendor/predis/predis/src/Autoloader.php62
-rw-r--r--vendor/predis/predis/src/Client.php550
-rw-r--r--vendor/predis/predis/src/ClientContextInterface.php199
-rw-r--r--vendor/predis/predis/src/ClientException.php21
-rw-r--r--vendor/predis/predis/src/ClientInterface.php240
-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.php74
-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.php196
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/HashKey.php58
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/Keyspace.php43
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/ListKey.php181
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/SetKey.php47
-rw-r--r--vendor/predis/predis/src/Collection/Iterator/SortedSetKey.php58
-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.php28
-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.php28
-rw-r--r--vendor/predis/predis/src/Command/HashSetMultiple.php48
-rw-r--r--vendor/predis/predis/src/Command/HashSetPreserve.php28
-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.php36
-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.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyExpire.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyExpireAt.php28
-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.php28
-rw-r--r--vendor/predis/predis/src/Command/KeyPersist.php28
-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.php28
-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.php134
-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.php69
-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.php28
-rw-r--r--vendor/predis/predis/src/Command/SetMembers.php28
-rw-r--r--vendor/predis/predis/src/Command/SetMove.php28
-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.php28
-rw-r--r--vendor/predis/predis/src/Command/StringSetPreserve.php28
-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.php85
-rw-r--r--vendor/predis/predis/src/Configuration/ClusterOption.php76
-rw-r--r--vendor/predis/predis/src/Configuration/ConnectionFactoryOption.php60
-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.php70
-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.php77
-rw-r--r--vendor/predis/predis/src/Connection/AbstractConnection.php226
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/ClusterInterface.php24
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php509
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php237
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php675
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/ReplicationInterface.php52
-rw-r--r--vendor/predis/predis/src/Connection/Aggregate/SentinelReplication.php732
-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.php192
-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.php185
-rw-r--r--vendor/predis/predis/src/Connection/ParametersInterface.php62
-rw-r--r--vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php418
-rw-r--r--vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php262
-rw-r--r--vendor/predis/predis/src/Connection/StreamConnection.php396
-rw-r--r--vendor/predis/predis/src/Connection/WebdisConnection.php366
-rw-r--r--vendor/predis/predis/src/Monitor/Consumer.php178
-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.php46
-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.php123
-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.php224
-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/MissingMasterException.php23
-rw-r--r--vendor/predis/predis/src/Replication/ReplicationStrategy.php279
-rw-r--r--vendor/predis/predis/src/Replication/RoleException.php24
-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.php110
-rw-r--r--vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php91
-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.php148
-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
274 files changed, 20821 insertions, 0 deletions
diff --git a/vendor/predis/predis/LICENSE b/vendor/predis/predis/LICENSE
new file mode 100644
index 0000000..c35b657
--- /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/autoload.php b/vendor/predis/predis/autoload.php
new file mode 100644
index 0000000..6b5a00b
--- /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/composer.json b/vendor/predis/predis/composer.json
new file mode 100644
index 0000000..bde0b54
--- /dev/null
+++ b/vendor/predis/predis/composer.json
@@ -0,0 +1,48 @@
+{
+ "name": "predis/predis",
+ "type": "library",
+ "description": "Flexible and feature-complete Redis client for PHP and HHVM",
+ "keywords": ["nosql", "redis", "predis"],
+ "homepage": "http://github.com/predis/predis",
+ "license": "MIT",
+ "support": {
+ "issues": "https://github.com/predis/predis/issues"
+ },
+ "authors": [
+ {
+ "name": "Daniele Alessandri",
+ "email": "suppakilla@gmail.com",
+ "homepage": "http://clorophilla.net",
+ "role": "Creator & Maintainer"
+ },
+ {
+ "name": "Till Krüss",
+ "homepage": "https://till.im",
+ "role": "Maintainer"
+ }
+ ],
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/tillkruss"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.9"
+ },
+ "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/"
+ }
+ },
+ "scripts": {
+ "post-update-cmd": "@php -f tests/apply-patches.php"
+ }
+}
diff --git a/vendor/predis/predis/src/Autoloader.php b/vendor/predis/predis/src/Autoloader.php
new file mode 100644
index 0000000..17ec2ff
--- /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 0000000..3538272
--- /dev/null
+++ b/vendor/predis/predis/src/Client.php
@@ -0,0 +1,550 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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, \IteratorAggregate
+{
+ const VERSION = '1.1.10';
+
+ 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);
+ } elseif ($options->defined('replication')) {
+ $replication = $options->replication;
+
+ if ($replication instanceof AggregateConnectionInterface) {
+ $connection = $replication;
+ $options->connections->aggregate($connection, $parameters);
+ } else {
+ $initializer = $this->getConnectionInitializerWrapper($replication);
+ $connection = $initializer($parameters, $options);
+ }
+ } 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 possible to identify Redis error responses from normal responses
+ * using the second optional argument which is populated by reference.
+ *
+ * @param array $arguments Command arguments as defined by the command signature.
+ * @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 publish/subscribe context and returns it, or starts its loop
+ * inside the optionally provided callable object.
+ *
+ * @param mixed ... Array of options, a callable for execution, or both.
+ *
+ * @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);
+ }
+
+ /**
+ * @return \Traversable<string, static>
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ $clients = array();
+ $connection = $this->getConnection();
+
+ if (!$connection instanceof \Traversable) {
+ return new \ArrayIterator(array(
+ (string) $connection => new static($connection, $this->getOptions())
+ ));
+ }
+
+ foreach ($connection as $node) {
+ $clients[(string) $node] = new static($node, $this->getOptions());
+ }
+
+ return new \ArrayIterator($clients);
+ }
+}
diff --git a/vendor/predis/predis/src/ClientContextInterface.php b/vendor/predis/predis/src/ClientContextInterface.php
new file mode 100644
index 0000000..85e4edc
--- /dev/null
+++ b/vendor/predis/predis/src/ClientContextInterface.php
@@ -0,0 +1,199 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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|string $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, $subcommand, ...$subcommandArg)
+ * @method $this bitpos($key, $bit, $start = null, $end = null)
+ * @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|string $keys, $timeout)
+ * @method $this brpop(array|string $keys, $timeout)
+ * @method $this brpoplpush($source, $destination, $timeout)
+ * @method $this lindex($key, $index)
+ * @method $this linsert($key, $whence, $pivot, $value)
+ * @method $this llen($key)
+ * @method $this lpop($key)
+ * @method $this lpush($key, array $values)
+ * @method $this lpushx($key, array $values)
+ * @method $this 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, array $values)
+ * @method $this sadd($key, array $members)
+ * @method $this scard($key)
+ * @method $this sdiff(array|string $keys)
+ * @method $this sdiffstore($destination, array|string $keys)
+ * @method $this sinter(array|string $keys)
+ * @method $this sinterstore($destination, array|string $keys)
+ * @method $this 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|string $keys)
+ * @method $this sunionstore($destination, array|string $keys)
+ * @method $this zadd($key, array $membersAndScoresDictionary)
+ * @method $this zcard($key)
+ * @method $this zcount($key, $min, $max)
+ * @method $this zincrby($key, $increment, $member)
+ * @method $this zinterstore($destination, array|string $keys, array $options = null)
+ * @method $this 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, $max, $min, array $options = null)
+ * @method $this zrevrank($key, $member)
+ * @method $this zunionstore($destination, array|string $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|string $sourceKeys)
+ * @method $this pfcount(array|string $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 0000000..6c07aaf
--- /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 0000000..f61dcf6
--- /dev/null
+++ b/vendor/predis/predis/src/ClientInterface.php
@@ -0,0 +1,240 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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|string $keys)
+ * @method string|null 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|null 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|null bitfield($key, $subcommand, ...$subcommandArg)
+ * @method int bitpos($key, $bit, $start = null, $end = null)
+ * @method int decr($key)
+ * @method int decrby($key, $decrement)
+ * @method string|null get($key)
+ * @method int getbit($key, $offset)
+ * @method string getrange($key, $start, $end)
+ * @method string|null 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|null 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|null blpop(array|string $keys, $timeout)
+ * @method array|null brpop(array|string $keys, $timeout)
+ * @method string|null brpoplpush($source, $destination, $timeout)
+ * @method string|null lindex($key, $index)
+ * @method int linsert($key, $whence, $pivot, $value)
+ * @method int llen($key)
+ * @method string|null lpop($key)
+ * @method int lpush($key, array $values)
+ * @method int lpushx($key, array $values)
+ * @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|null rpop($key)
+ * @method string|null rpoplpush($source, $destination)
+ * @method int rpush($key, array $values)
+ * @method int rpushx($key, array $values)
+ * @method int sadd($key, array $members)
+ * @method int scard($key)
+ * @method array sdiff(array|string $keys)
+ * @method int sdiffstore($destination, array|string $keys)
+ * @method array sinter(array|string $keys)
+ * @method int sinterstore($destination, array|string $keys)
+ * @method int sismember($key, $member)
+ * @method array smembers($key)
+ * @method int smove($source, $destination, $member)
+ * @method string|null spop($key, $count = null)
+ * @method string|null srandmember($key, $count = null)
+ * @method int srem($key, $member)
+ * @method array sscan($key, $cursor, array $options = null)
+ * @method array sunion(array|string $keys)
+ * @method int sunionstore($destination, array|string $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|string $keys, array $options = null)
+ * @method array zrange($key, $start, $stop, array $options = null)
+ * @method array zrangebyscore($key, $min, $max, array $options = null)
+ * @method int|null 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|null zrevrank($key, $member)
+ * @method int zunionstore($destination, array|string $keys, array $options = null)
+ * @method string|null 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|string $sourceKeys)
+ * @method int pfcount(array|string $keys)
+ * @method mixed pubsub($subcommand, $argument)
+ * @method int publish($channel, $message)
+ * @method mixed discard()
+ * @method array|null exec()
+ * @method mixed multi()
+ * @method mixed unwatch()
+ * @method mixed watch($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|null 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 0000000..1891907
--- /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 0000000..831f52c
--- /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 0000000..039f2f2
--- /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 0000000..a0c7ae8
--- /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 = $this->hash("$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 0000000..dc77f32
--- /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 0000000..c52ed2a
--- /dev/null
+++ b/vendor/predis/predis/src/Cluster/Hash/CRC16.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\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;
+
+ $value = (string) $value;
+ $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 0000000..271b9e7
--- /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 0000000..2066842
--- /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 0000000..df0bdb4
--- /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 0000000..cdf7d09
--- /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 0000000..a52e04f
--- /dev/null
+++ b/vendor/predis/predis/src/Collection/Iterator/CursorBasedIterator.php
@@ -0,0 +1,196 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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(strval($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}
+ */
+ #[\ReturnTypeWillChange]
+ public function rewind()
+ {
+ $this->reset();
+ $this->next();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ return $this->current;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ 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}
+ */
+ #[\ReturnTypeWillChange]
+ 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 0000000..5bd07c2
--- /dev/null
+++ b/vendor/predis/predis/src/Collection/Iterator/HashKey.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\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()
+ {
+ $this->position = key($this->elements);
+ $this->current = current($this->elements);
+
+ 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 0000000..5d985b9
--- /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 0000000..01f06e9
--- /dev/null
+++ b/vendor/predis/predis/src/Collection/Iterator/ListKey.php
@@ -0,0 +1,181 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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}
+ */
+ #[\ReturnTypeWillChange]
+ public function rewind()
+ {
+ $this->reset();
+ $this->next();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ return $this->current;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function next()
+ {
+ if (!$this->elements && $this->fetchmore) {
+ $this->fetch();
+ }
+
+ if ($this->elements) {
+ $this->extractNext();
+ } else {
+ $this->valid = false;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ 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 0000000..bf25439
--- /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 0000000..d96122a
--- /dev/null
+++ b/vendor/predis/predis/src/Collection/Iterator/SortedSetKey.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\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()
+ {
+ $this->position = key($this->elements);
+ $this->current = current($this->elements);
+
+ 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 0000000..59e1813
--- /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 && isset($arguments[0]) && 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 0000000..9f349e1
--- /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 0000000..c8c9ded
--- /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 0000000..fd49609
--- /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 0000000..fa9d734
--- /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 0000000..e59e31e
--- /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 0000000..1da8256
--- /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 0000000..adca2ca
--- /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 0000000..17c5f54
--- /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 0000000..2eccaf4
--- /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 0000000..6b7a9a3
--- /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 0000000..f205214
--- /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 0000000..abfff7b
--- /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 0000000..d5d4c38
--- /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 0000000..ed8dc89
--- /dev/null
+++ b/vendor/predis/predis/src/Command/HashExists.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/hexists
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashExists extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'HEXISTS';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/HashGet.php b/vendor/predis/predis/src/Command/HashGet.php
new file mode 100644
index 0000000..20f33da
--- /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 0000000..d698675
--- /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 0000000..820ce95
--- /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 0000000..a37359f
--- /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 0000000..bce9714
--- /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 0000000..2826602
--- /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 0000000..d70926f
--- /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 0000000..afde74e
--- /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 0000000..cfff3c2
--- /dev/null
+++ b/vendor/predis/predis/src/Command/HashSet.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/hset
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashSet extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'HSET';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/HashSetMultiple.php b/vendor/predis/predis/src/Command/HashSetMultiple.php
new file mode 100644
index 0000000..6069e2a
--- /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 0000000..7a29116
--- /dev/null
+++ b/vendor/predis/predis/src/Command/HashSetPreserve.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/hsetnx
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashSetPreserve extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'HSETNX';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/HashStringLength.php b/vendor/predis/predis/src/Command/HashStringLength.php
new file mode 100644
index 0000000..7cfda80
--- /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 0000000..0a5ea5f
--- /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 0000000..8fe49fc
--- /dev/null
+++ b/vendor/predis/predis/src/Command/HyperLogLogAdd.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/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);
+ }
+}
diff --git a/vendor/predis/predis/src/Command/HyperLogLogCount.php b/vendor/predis/predis/src/Command/HyperLogLogCount.php
new file mode 100644
index 0000000..0afe542
--- /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 0000000..c160be5
--- /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 0000000..89bdfdb
--- /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 0000000..6d9c488
--- /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 0000000..29e0648
--- /dev/null
+++ b/vendor/predis/predis/src/Command/KeyExists.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/exists
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyExists extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'EXISTS';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/KeyExpire.php b/vendor/predis/predis/src/Command/KeyExpire.php
new file mode 100644
index 0000000..66f4406
--- /dev/null
+++ b/vendor/predis/predis/src/Command/KeyExpire.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/expire
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyExpire extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'EXPIRE';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/KeyExpireAt.php b/vendor/predis/predis/src/Command/KeyExpireAt.php
new file mode 100644
index 0000000..0ae1b2d
--- /dev/null
+++ b/vendor/predis/predis/src/Command/KeyExpireAt.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/expireat
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyExpireAt extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'EXPIREAT';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/KeyKeys.php b/vendor/predis/predis/src/Command/KeyKeys.php
new file mode 100644
index 0000000..6d74c40
--- /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 0000000..3324ef9
--- /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 0000000..c849f08
--- /dev/null
+++ b/vendor/predis/predis/src/Command/KeyMove.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/move
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyMove extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'MOVE';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/KeyPersist.php b/vendor/predis/predis/src/Command/KeyPersist.php
new file mode 100644
index 0000000..f0cb679
--- /dev/null
+++ b/vendor/predis/predis/src/Command/KeyPersist.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/persist
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyPersist extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'PERSIST';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/KeyPreciseExpire.php b/vendor/predis/predis/src/Command/KeyPreciseExpire.php
new file mode 100644
index 0000000..258ec47
--- /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 0000000..e419218
--- /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 0000000..bdcd34b
--- /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 0000000..b208b2d
--- /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 0000000..82e44fb
--- /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 0000000..9793359
--- /dev/null
+++ b/vendor/predis/predis/src/Command/KeyRenamePreserve.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/renamenx
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyRenamePreserve extends KeyRename
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'RENAMENX';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/KeyRestore.php b/vendor/predis/predis/src/Command/KeyRestore.php
new file mode 100644
index 0000000..a5b0b2d
--- /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 0000000..05f5bb3
--- /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 0000000..fd449f1
--- /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 0000000..67697a6
--- /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 0000000..f4f06e4
--- /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 0000000..27c64be
--- /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 0000000..7d53d11
--- /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 0000000..6495beb
--- /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 0000000..84d5d67
--- /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 0000000..7dc7c00
--- /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 0000000..9e92db5
--- /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 0000000..781eb91
--- /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 0000000..f430eb2
--- /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 0000000..ee9c93c
--- /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 0000000..74bf7c4
--- /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 0000000..8e136b8
--- /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 0000000..f2a057c
--- /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 0000000..1af3645
--- /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 0000000..32a21a6
--- /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 0000000..c580089
--- /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 0000000..5e59864
--- /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 0000000..1931418
--- /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 0000000..6d54554
--- /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 0000000..7520127
--- /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 0000000..95e8ead
--- /dev/null
+++ b/vendor/predis/predis/src/Command/Processor/ProcessorChain.php
@@ -0,0 +1,134 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@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 \Traversable<int, ProcessorInterface>
+ */
+ 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);
+ }
+
+ /**
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetExists($index)
+ {
+ return isset($this->processors[$index]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($index)
+ {
+ return $this->processors[$index];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ 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}
+ */
+ #[\ReturnTypeWillChange]
+ 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 0000000..2f91058
--- /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 0000000..55508f8
--- /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 0000000..8cf8129
--- /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 0000000..e477b31
--- /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 0000000..0118280
--- /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 0000000..d57c3ac
--- /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 0000000..4d76508
--- /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 0000000..2dd48ca
--- /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 0000000..a30bc1d
--- /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 0000000..c66a294
--- /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 0000000..4bf67ef
--- /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 0000000..d00ebbf
--- /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 0000000..e9b3393
--- /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 0000000..81e497a
--- /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 0000000..6bc8972
--- /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 0000000..f5eefd8
--- /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 0000000..520a8e9
--- /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 0000000..c35b2ad
--- /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 0000000..3da6b32
--- /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 0000000..96d6ada
--- /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 0000000..90c9b71
--- /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 0000000..feeb19a
--- /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 0000000..1c3d330
--- /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 0000000..f921701
--- /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 0000000..addefe2
--- /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 0000000..7a01018
--- /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 0000000..bef2316
--- /dev/null
+++ b/vendor/predis/predis/src/Command/ServerSentinel.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\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)
+ {
+ $argument = $this->getArgument(0);
+ $argument = is_null($argument) ? null : strtolower($argument);
+
+ switch ($argument) {
+ 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 0000000..f5b745a
--- /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 0000000..4ff4455
--- /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 0000000..137ff59
--- /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 0000000..589f92c
--- /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 0000000..c118818
--- /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 0000000..a9f959b
--- /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 0000000..35f23f9
--- /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 0000000..0cb7815
--- /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 0000000..d18258f
--- /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 0000000..b748618
--- /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 0000000..7742522
--- /dev/null
+++ b/vendor/predis/predis/src/Command/SetIsMember.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/sismember
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class SetIsMember extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'SISMEMBER';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/SetMembers.php b/vendor/predis/predis/src/Command/SetMembers.php
new file mode 100644
index 0000000..f4076ae
--- /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 0000000..edd4e51
--- /dev/null
+++ b/vendor/predis/predis/src/Command/SetMove.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/smove
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class SetMove extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'SMOVE';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/SetPop.php b/vendor/predis/predis/src/Command/SetPop.php
new file mode 100644
index 0000000..b78d3f3
--- /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 0000000..2cb79a0
--- /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 0000000..b34710c
--- /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 0000000..d42b28d
--- /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 0000000..7da842b
--- /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 0000000..eac821a
--- /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 0000000..dac8b84
--- /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 0000000..193cce9
--- /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 0000000..9f4deaa
--- /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 0000000..e04ee79
--- /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 0000000..4295766
--- /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 0000000..aa5808c
--- /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 0000000..cbf3e11
--- /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 0000000..138e915
--- /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 0000000..3c5b4f9
--- /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 0000000..e340f9c
--- /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 0000000..bb10565
--- /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 0000000..b68870d
--- /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 0000000..fa1846e
--- /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 0000000..9d8241a
--- /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 0000000..164a086
--- /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 0000000..2faa954
--- /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 0000000..b146994
--- /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 0000000..7933b6b
--- /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 0000000..f088170
--- /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 0000000..a3c5324
--- /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 0000000..b46a88c
--- /dev/null
+++ b/vendor/predis/predis/src/Command/StringSetMultiplePreserve.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/msetnx
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class StringSetMultiplePreserve extends StringSetMultiple
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'MSETNX';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/StringSetPreserve.php b/vendor/predis/predis/src/Command/StringSetPreserve.php
new file mode 100644
index 0000000..e89c974
--- /dev/null
+++ b/vendor/predis/predis/src/Command/StringSetPreserve.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/setnx
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class StringSetPreserve extends Command
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return 'SETNX';
+ }
+}
diff --git a/vendor/predis/predis/src/Command/StringSetRange.php b/vendor/predis/predis/src/Command/StringSetRange.php
new file mode 100644
index 0000000..4d9389f
--- /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 0000000..10f492f
--- /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 0000000..3aab7ad
--- /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 0000000..44aca2b
--- /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 0000000..dbd81aa
--- /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 0000000..673bf55
--- /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 0000000..7925554
--- /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 0000000..d360780
--- /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 0000000..55e4729
--- /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 0000000..1033200
--- /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 0000000..918bd2b
--- /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 0000000..245a8e0
--- /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 0000000..572a7a3
--- /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 0000000..447b8eb
--- /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 0000000..ce72c7c
--- /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 0000000..9b2991a
--- /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 0000000..961a5bc
--- /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 0000000..d0c9c53
--- /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 0000000..cd8ada0
--- /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 0000000..9ea2d9e
--- /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 0000000..89cd5ba
--- /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 0000000..a7c3081
--- /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 0000000..6a46a7a
--- /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 0000000..5dd611d
--- /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 0000000..1078eb7
--- /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 0000000..33fb815
--- /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 0000000..1dc2352
--- /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 0000000..2e7fce8
--- /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 0000000..befc5ce
--- /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 0000000..6b7f6a2
--- /dev/null
+++ b/vendor/predis/predis/src/CommunicationException.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;
+
+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(
+ is_null($message) ? '' : $message,
+ is_null($code) ? 0 : $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 0000000..69e36de
--- /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 0000000..bf8479c
--- /dev/null
+++ b/vendor/predis/predis/src/Configuration/ConnectionFactoryOption.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\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)
+ {
+ $factory = new Factory();
+
+ if ($options->defined('parameters')) {
+ $factory->setDefaultParameters($options->parameters);
+ }
+
+ return $factory;
+ }
+}
diff --git a/vendor/predis/predis/src/Configuration/ExceptionsOption.php b/vendor/predis/predis/src/Configuration/ExceptionsOption.php
new file mode 100644
index 0000000..337733e
--- /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 0000000..b31e0c9
--- /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 0000000..c17dd54
--- /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 0000000..e0b30a4
--- /dev/null
+++ b/vendor/predis/predis/src/Configuration/OptionsInterface.php
@@ -0,0 +1,70 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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\ProcessorInterface;
+use Predis\Connection\Aggregate\ClusterInterface;
+use Predis\Connection\Aggregate\ReplicationInterface;
+use Predis\Connection\FactoryInterface;
+use Predis\Profile\ProfileInterface;
+
+/**
+ * Interface defining a container for client options.
+ *
+ * @property-read callable $aggregate Custom connection aggregator.
+ * @property-read ClusterInterface $cluster Aggregate connection for clustering.
+ * @property-read FactoryInterface $connections Connection factory.
+ * @property-read bool $exceptions Toggles exceptions in client for -ERR responses.
+ * @property-read ProcessorInterface $prefix Key prefixing strategy using the given prefix.
+ * @property-read ProfileInterface $profile Server profile.
+ * @property-read ReplicationInterface $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 0000000..5827cdc
--- /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 0000000..864936e
--- /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 0000000..808bcc4
--- /dev/null
+++ b/vendor/predis/predis/src/Configuration/ReplicationOption.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\Configuration;
+
+use Predis\Connection\Aggregate\MasterSlaveReplication;
+use Predis\Connection\Aggregate\ReplicationInterface;
+use Predis\Connection\Aggregate\SentinelReplication;
+
+/**
+ * Configures an aggregate connection used for master/slave replication among
+ * 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 ($value === 'sentinel') {
+ return function ($sentinels, $options) {
+ return new SentinelReplication($options->service, $sentinels, $options->connections);
+ };
+ }
+
+ if (
+ !is_object($value) &&
+ null !== $asbool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
+ ) {
+ if (true === $asbool) {
+ return $this->getDefault($options);
+ } else {
+ throw new \InvalidArgumentException(
+ "Values evaluating to FALSE are not accepted for `replication`"
+ );
+ }
+ }
+
+ throw new \InvalidArgumentException(
+ "An instance of type 'Predis\Connection\Aggregate\ReplicationInterface' was expected."
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDefault(OptionsInterface $options)
+ {
+ $replication = new MasterSlaveReplication();
+
+ if ($options->autodiscovery) {
+ $replication->setConnectionFactory($options->connections);
+ $replication->setAutoDiscovery(true);
+ }
+
+ return $replication;
+ }
+}
diff --git a/vendor/predis/predis/src/Connection/AbstractConnection.php b/vendor/predis/predis/src/Connection/AbstractConnection.php
new file mode 100644
index 0000000..fb86513
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/AbstractConnection.php
@@ -0,0 +1,226 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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
+ */
+ abstract protected function assertParameters(ParametersInterface $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 0000000..af0f5aa
--- /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 0000000..238cf2c
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/Aggregate/MasterSlaveReplication.php
@@ -0,0 +1,509 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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\Command\CommandInterface;
+use Predis\Command\RawCommand;
+use Predis\Connection\ConnectionException;
+use Predis\Connection\FactoryInterface;
+use Predis\Connection\NodeConnectionInterface;
+use Predis\Replication\MissingMasterException;
+use Predis\Replication\ReplicationStrategy;
+use Predis\Response\ErrorInterface as ResponseErrorInterface;
+
+/**
+ * Aggregate connection handling replication of Redis nodes configured in a
+ * 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;
+
+ /**
+ * @var bool
+ */
+ protected $autoDiscovery = false;
+
+ /**
+ * @var FactoryInterface
+ */
+ protected $connectionFactory;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct(ReplicationStrategy $strategy = null)
+ {
+ $this->strategy = $strategy ?: new ReplicationStrategy();
+ }
+
+ /**
+ * Configures the automatic discovery of the replication configuration on failure.
+ *
+ * @param bool $value Enable or disable auto discovery.
+ */
+ public function setAutoDiscovery($value)
+ {
+ if (!$this->connectionFactory) {
+ throw new ClientException('Automatic discovery requires a connection factory');
+ }
+
+ $this->autoDiscovery = (bool) $value;
+ }
+
+ /**
+ * Sets the connection factory used to create the connections by the auto
+ * discovery procedure.
+ *
+ * @param FactoryInterface $connectionFactory Connection factory instance.
+ */
+ public function setConnectionFactory(FactoryInterface $connectionFactory)
+ {
+ $this->connectionFactory = $connectionFactory;
+ }
+
+ /**
+ * 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 ?: "slave-$connection"] = $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) {
+ if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) {
+ $this->current = $slave;
+ } else {
+ $this->current = $this->getMasterOrDie();
+ }
+
+ return $this->current;
+ }
+
+ if ($this->current === $master = $this->getMasterOrDie()) {
+ return $master;
+ }
+
+ if (!$this->strategy->isReadOperation($command) || !$this->slaves) {
+ $this->current = $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)
+ {
+ if (!$connection instanceof NodeConnectionInterface) {
+ $connection = $this->getConnectionById($connection);
+ }
+
+ if (!$connection) {
+ throw new \InvalidArgumentException('Invalid connection or connection not found.');
+ }
+
+ if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) {
+ throw new \InvalidArgumentException('Invalid connection or connection not found.');
+ }
+
+ $this->current = $connection;
+ }
+
+ /**
+ * Switches to the master server.
+ */
+ public function switchToMaster()
+ {
+ $this->switchTo('master');
+ }
+
+ /**
+ * Switches to a random slave server.
+ */
+ public function switchToSlave()
+ {
+ $connection = $this->pickSlave();
+ $this->switchTo($connection);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCurrent()
+ {
+ return $this->current;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMaster()
+ {
+ return $this->master;
+ }
+
+ /**
+ * Returns the connection associated to the master server.
+ *
+ * @return NodeConnectionInterface
+ */
+ private function getMasterOrDie()
+ {
+ if (!$connection = $this->getMaster()) {
+ throw new MissingMasterException('No master server available for replication');
+ }
+
+ return $connection;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSlaves()
+ {
+ 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) {
+ if (!$this->current = $this->pickSlave()) {
+ if (!$this->current = $this->getMaster()) {
+ throw new ClientException('No available connection for replication');
+ }
+ }
+ }
+
+ $this->current->connect();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disconnect()
+ {
+ if ($this->master) {
+ $this->master->disconnect();
+ }
+
+ foreach ($this->slaves as $connection) {
+ $connection->disconnect();
+ }
+ }
+
+ /**
+ * Handles response from INFO.
+ *
+ * @param string $response
+ *
+ * @return array
+ */
+ private function handleInfoResponse($response)
+ {
+ $info = array();
+
+ foreach (preg_split('/\r?\n/', $response) as $row) {
+ if (strpos($row, ':') === false) {
+ continue;
+ }
+
+ list($k, $v) = explode(':', $row, 2);
+ $info[$k] = $v;
+ }
+
+ return $info;
+ }
+
+ /**
+ * Fetches the replication configuration from one of the servers.
+ */
+ public function discover()
+ {
+ if (!$this->connectionFactory) {
+ throw new ClientException('Discovery requires a connection factory');
+ }
+
+ RETRY_FETCH: {
+ try {
+ if ($connection = $this->getMaster()) {
+ $this->discoverFromMaster($connection, $this->connectionFactory);
+ } elseif ($connection = $this->pickSlave()) {
+ $this->discoverFromSlave($connection, $this->connectionFactory);
+ } else {
+ throw new ClientException('No connection available for discovery');
+ }
+ } catch (ConnectionException $exception) {
+ $this->remove($connection);
+ goto RETRY_FETCH;
+ }
+ }
+ }
+
+ /**
+ * Discovers the replication configuration by contacting the master node.
+ *
+ * @param NodeConnectionInterface $connection Connection to the master node.
+ * @param FactoryInterface $connectionFactory Connection factory instance.
+ */
+ protected function discoverFromMaster(NodeConnectionInterface $connection, FactoryInterface $connectionFactory)
+ {
+ $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION'));
+ $replication = $this->handleInfoResponse($response);
+
+ if ($replication['role'] !== 'master') {
+ throw new ClientException("Role mismatch (expected master, got slave) [$connection]");
+ }
+
+ $this->slaves = array();
+
+ foreach ($replication as $k => $v) {
+ $parameters = null;
+
+ if (strpos($k, 'slave') === 0 && preg_match('/ip=(?P<host>.*),port=(?P<port>\d+)/', $v, $parameters)) {
+ $slaveConnection = $connectionFactory->create(array(
+ 'host' => $parameters['host'],
+ 'port' => $parameters['port'],
+ ));
+
+ $this->add($slaveConnection);
+ }
+ }
+ }
+
+ /**
+ * Discovers the replication configuration by contacting one of the slaves.
+ *
+ * @param NodeConnectionInterface $connection Connection to one of the slaves.
+ * @param FactoryInterface $connectionFactory Connection factory instance.
+ */
+ protected function discoverFromSlave(NodeConnectionInterface $connection, FactoryInterface $connectionFactory)
+ {
+ $response = $connection->executeCommand(RawCommand::create('INFO', 'REPLICATION'));
+ $replication = $this->handleInfoResponse($response);
+
+ if ($replication['role'] !== 'slave') {
+ throw new ClientException("Role mismatch (expected slave, got master) [$connection]");
+ }
+
+ $masterConnection = $connectionFactory->create(array(
+ 'host' => $replication['master_host'],
+ 'port' => $replication['master_port'],
+ 'alias' => 'master',
+ ));
+
+ $this->add($masterConnection);
+
+ $this->discoverFromMaster($masterConnection, $connectionFactory);
+ }
+
+ /**
+ * Retries the execution of a command upon slave failure.
+ *
+ * @param CommandInterface $command Command instance.
+ * @param string $method Actual method.
+ *
+ * @return mixed
+ */
+ private function retryCommandOnFailure(CommandInterface $command, $method)
+ {
+ RETRY_COMMAND: {
+ try {
+ $connection = $this->getConnection($command);
+ $response = $connection->$method($command);
+
+ if ($response instanceof ResponseErrorInterface && $response->getErrorType() === 'LOADING') {
+ throw new ConnectionException($connection, "Redis is loading the dataset in memory [$connection]");
+ }
+ } catch (ConnectionException $exception) {
+ $connection = $exception->getConnection();
+ $connection->disconnect();
+
+ if ($connection === $this->master && !$this->autoDiscovery) {
+ // Throw immediately when master connection is failing, even
+ // when the command represents a read-only operation, unless
+ // automatic discovery has been enabled.
+ throw $exception;
+ } else {
+ // Otherwise remove the failing slave and attempt to execute
+ // the command again on one of the remaining slaves...
+ $this->remove($connection);
+ }
+
+ // ... that is, unless we have no more connections to use.
+ if (!$this->slaves && !$this->master) {
+ throw $exception;
+ } elseif ($this->autoDiscovery) {
+ $this->discover();
+ }
+
+ goto RETRY_COMMAND;
+ } catch (MissingMasterException $exception) {
+ if ($this->autoDiscovery) {
+ $this->discover();
+ } else {
+ throw $exception;
+ }
+
+ goto RETRY_COMMAND;
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function writeRequest(CommandInterface $command)
+ {
+ $this->retryCommandOnFailure($command, __FUNCTION__);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function readResponse(CommandInterface $command)
+ {
+ return $this->retryCommandOnFailure($command, __FUNCTION__);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function executeCommand(CommandInterface $command)
+ {
+ return $this->retryCommandOnFailure($command, __FUNCTION__);
+ }
+
+ /**
+ * {@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 0000000..109d90b
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php
@@ -0,0 +1,237 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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;
+ }
+
+ /**
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return count($this->pool);
+ }
+
+ /**
+ * @return \Traversable<string|int, NodeConnectionInterface>
+ */
+ #[\ReturnTypeWillChange]
+ 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 0000000..9e6082a
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/Aggregate/RedisCluster.php
@@ -0,0 +1,675 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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 $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.
+ *
+ * @return array
+ */
+ 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);
+ }
+ }
+
+ return $this->slotsMap;
+ }
+
+ /**
+ * 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, ':');
+
+ return $this->connections->create(array(
+ 'host' => substr($connectionID, 0, $separator),
+ 'port' => substr($connectionID, $separator + 1),
+ ));
+ }
+
+ /**
+ * {@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;
+ }
+
+ /**
+ * @return int
+ */
+ #[\ReturnTypeWillChange]
+ public function count()
+ {
+ return count($this->pool);
+ }
+
+ /**
+ * @return \Traversable<int, NodeConnectionInterface>
+ */
+ #[\ReturnTypeWillChange]
+ public function getIterator()
+ {
+ if ($this->useClusterSlots) {
+ $slotsmap = $this->getSlotsMap() ?: $this->askSlotsMap();
+ } else {
+ $slotsmap = $this->getSlotsMap() ?: $this->buildSlotsMap();
+ }
+
+ $connections = array();
+
+ foreach (array_unique($slotsmap) as $node) {
+ if (!$connection = $this->getConnectionById($node)) {
+ $this->add($connection = $this->createConnection($node));
+ }
+
+ $connections[] = $connection;
+ }
+
+ return new \ArrayIterator($connections);
+ }
+
+ /**
+ * 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;
+ }
+}
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 0000000..e09e826
--- /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/Aggregate/SentinelReplication.php b/vendor/predis/predis/src/Connection/Aggregate/SentinelReplication.php
new file mode 100644
index 0000000..84ef804
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/Aggregate/SentinelReplication.php
@@ -0,0 +1,732 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Connection\Aggregate;
+
+use Predis\Command\CommandInterface;
+use Predis\Command\RawCommand;
+use Predis\CommunicationException;
+use Predis\Connection\ConnectionException;
+use Predis\Connection\FactoryInterface as ConnectionFactoryInterface;
+use Predis\Connection\NodeConnectionInterface;
+use Predis\Connection\Parameters;
+use Predis\Replication\ReplicationStrategy;
+use Predis\Replication\RoleException;
+use Predis\Response\Error;
+use Predis\Response\ErrorInterface as ErrorResponseInterface;
+use Predis\Response\ServerException;
+
+/**
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ * @author Ville Mattila <ville@eventio.fi>
+ */
+class SentinelReplication implements ReplicationInterface
+{
+ /**
+ * @var NodeConnectionInterface
+ */
+ protected $master;
+
+ /**
+ * @var NodeConnectionInterface[]
+ */
+ protected $slaves = array();
+
+ /**
+ * @var NodeConnectionInterface
+ */
+ protected $current;
+
+ /**
+ * @var string
+ */
+ protected $service;
+
+ /**
+ * @var ConnectionFactoryInterface
+ */
+ protected $connectionFactory;
+
+ /**
+ * @var ReplicationStrategy
+ */
+ protected $strategy;
+
+ /**
+ * @var NodeConnectionInterface[]
+ */
+ protected $sentinels = array();
+
+ /**
+ * @var NodeConnectionInterface
+ */
+ protected $sentinelConnection;
+
+ /**
+ * @var float
+ */
+ protected $sentinelTimeout = 0.100;
+
+ /**
+ * Max number of automatic retries of commands upon server failure.
+ *
+ * -1 = unlimited retry attempts
+ * 0 = no retry attempts (fails immediatly)
+ * n = fail only after n retry attempts
+ *
+ * @var int
+ */
+ protected $retryLimit = 20;
+
+ /**
+ * Time to wait in milliseconds before fetching a new configuration from one
+ * of the sentinel servers.
+ *
+ * @var int
+ */
+ protected $retryWait = 1000;
+
+ /**
+ * Flag for automatic fetching of available sentinels.
+ *
+ * @var bool
+ */
+ protected $updateSentinels = false;
+
+ /**
+ * @param string $service Name of the service for autodiscovery.
+ * @param array $sentinels Sentinel servers connection parameters.
+ * @param ConnectionFactoryInterface $connectionFactory Connection factory instance.
+ * @param ReplicationStrategy $strategy Replication strategy instance.
+ */
+ public function __construct(
+ $service,
+ array $sentinels,
+ ConnectionFactoryInterface $connectionFactory,
+ ReplicationStrategy $strategy = null
+ ) {
+ $this->sentinels = $sentinels;
+ $this->service = $service;
+ $this->connectionFactory = $connectionFactory;
+ $this->strategy = $strategy ?: new ReplicationStrategy();
+ }
+
+ /**
+ * Sets a default timeout for connections to sentinels.
+ *
+ * When "timeout" is present in the connection parameters of sentinels, its
+ * value overrides the default sentinel timeout.
+ *
+ * @param float $timeout Timeout value.
+ */
+ public function setSentinelTimeout($timeout)
+ {
+ $this->sentinelTimeout = (float) $timeout;
+ }
+
+ /**
+ * Sets the maximum number of retries for commands upon server failure.
+ *
+ * -1 = unlimited retry attempts
+ * 0 = no retry attempts (fails immediatly)
+ * n = fail only after n retry attempts
+ *
+ * @param int $retry Number of retry attempts.
+ */
+ public function setRetryLimit($retry)
+ {
+ $this->retryLimit = (int) $retry;
+ }
+
+ /**
+ * Sets the time to wait (in milliseconds) before fetching a new configuration
+ * from one of the sentinels.
+ *
+ * @param float $milliseconds Time to wait before the next attempt.
+ */
+ public function setRetryWait($milliseconds)
+ {
+ $this->retryWait = (float) $milliseconds;
+ }
+
+ /**
+ * Set automatic fetching of available sentinels.
+ *
+ * @param bool $update Enable or disable automatic updates.
+ */
+ public function setUpdateSentinels($update)
+ {
+ $this->updateSentinels = (bool) $update;
+ }
+
+ /**
+ * Resets the current connection.
+ */
+ protected function reset()
+ {
+ $this->current = null;
+ }
+
+ /**
+ * Wipes the current list of master and slaves nodes.
+ */
+ protected function wipeServerList()
+ {
+ $this->reset();
+
+ $this->master = null;
+ $this->slaves = array();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add(NodeConnectionInterface $connection)
+ {
+ $alias = $connection->getParameters()->alias;
+
+ if ($alias === 'master') {
+ $this->master = $connection;
+ } else {
+ $this->slaves[$alias ?: count($this->slaves)] = $connection;
+ }
+
+ $this->reset();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove(NodeConnectionInterface $connection)
+ {
+ if ($connection === $this->master) {
+ $this->master = null;
+ $this->reset();
+
+ return true;
+ }
+
+ if (false !== $id = array_search($connection, $this->slaves, true)) {
+ unset($this->slaves[$id]);
+ $this->reset();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Creates a new connection to a sentinel server.
+ *
+ * @return NodeConnectionInterface
+ */
+ protected function createSentinelConnection($parameters)
+ {
+ if ($parameters instanceof NodeConnectionInterface) {
+ return $parameters;
+ }
+
+ if (is_string($parameters)) {
+ $parameters = Parameters::parse($parameters);
+ }
+
+ if (is_array($parameters)) {
+ // NOTE: sentinels do not accept AUTH and SELECT commands so we must
+ // explicitly set them to NULL to avoid problems when using default
+ // parameters set via client options. Actually AUTH is supported for
+ // sentinels starting with Redis 5 but we have to differentiate from
+ // sentinels passwords and nodes passwords, this will be implemented
+ // in a later release.
+ $parameters['database'] = null;
+ $parameters['username'] = null;
+ $parameters['password'] = null;
+
+ if (!isset($parameters['timeout'])) {
+ $parameters['timeout'] = $this->sentinelTimeout;
+ }
+ }
+
+ $connection = $this->connectionFactory->create($parameters);
+
+ return $connection;
+ }
+
+ /**
+ * Returns the current sentinel connection.
+ *
+ * If there is no active sentinel connection, a new connection is created.
+ *
+ * @return NodeConnectionInterface
+ */
+ public function getSentinelConnection()
+ {
+ if (!$this->sentinelConnection) {
+ if (!$this->sentinels) {
+ throw new \Predis\ClientException('No sentinel server available for autodiscovery.');
+ }
+
+ $sentinel = array_shift($this->sentinels);
+ $this->sentinelConnection = $this->createSentinelConnection($sentinel);
+ }
+
+ return $this->sentinelConnection;
+ }
+
+ /**
+ * Fetches an updated list of sentinels from a sentinel.
+ */
+ public function updateSentinels()
+ {
+ SENTINEL_QUERY: {
+ $sentinel = $this->getSentinelConnection();
+
+ try {
+ $payload = $sentinel->executeCommand(
+ RawCommand::create('SENTINEL', 'sentinels', $this->service)
+ );
+
+ $this->sentinels = array();
+ // NOTE: sentinel server does not return itself, so we add it back.
+ $this->sentinels[] = $sentinel->getParameters()->toArray();
+
+ foreach ($payload as $sentinel) {
+ $this->sentinels[] = array(
+ 'host' => $sentinel[3],
+ 'port' => $sentinel[5],
+ );
+ }
+ } catch (ConnectionException $exception) {
+ $this->sentinelConnection = null;
+
+ goto SENTINEL_QUERY;
+ }
+ }
+ }
+
+ /**
+ * Fetches the details for the master and slave servers from a sentinel.
+ */
+ public function querySentinel()
+ {
+ $this->wipeServerList();
+
+ $this->updateSentinels();
+ $this->getMaster();
+ $this->getSlaves();
+ }
+
+ /**
+ * Handles error responses returned by redis-sentinel.
+ *
+ * @param NodeConnectionInterface $sentinel Connection to a sentinel server.
+ * @param ErrorResponseInterface $error Error response.
+ */
+ private function handleSentinelErrorResponse(NodeConnectionInterface $sentinel, ErrorResponseInterface $error)
+ {
+ if ($error->getErrorType() === 'IDONTKNOW') {
+ throw new ConnectionException($sentinel, $error->getMessage());
+ } else {
+ throw new ServerException($error->getMessage());
+ }
+ }
+
+ /**
+ * Fetches the details for the master server from a sentinel.
+ *
+ * @param NodeConnectionInterface $sentinel Connection to a sentinel server.
+ * @param string $service Name of the service.
+ *
+ * @return array
+ */
+ protected function querySentinelForMaster(NodeConnectionInterface $sentinel, $service)
+ {
+ $payload = $sentinel->executeCommand(
+ RawCommand::create('SENTINEL', 'get-master-addr-by-name', $service)
+ );
+
+ if ($payload === null) {
+ throw new ServerException('ERR No such master with that name');
+ }
+
+ if ($payload instanceof ErrorResponseInterface) {
+ $this->handleSentinelErrorResponse($sentinel, $payload);
+ }
+
+ return array(
+ 'host' => $payload[0],
+ 'port' => $payload[1],
+ 'alias' => 'master',
+ );
+ }
+
+ /**
+ * Fetches the details for the slave servers from a sentinel.
+ *
+ * @param NodeConnectionInterface $sentinel Connection to a sentinel server.
+ * @param string $service Name of the service.
+ *
+ * @return array
+ */
+ protected function querySentinelForSlaves(NodeConnectionInterface $sentinel, $service)
+ {
+ $slaves = array();
+
+ $payload = $sentinel->executeCommand(
+ RawCommand::create('SENTINEL', 'slaves', $service)
+ );
+
+ if ($payload instanceof ErrorResponseInterface) {
+ $this->handleSentinelErrorResponse($sentinel, $payload);
+ }
+
+ foreach ($payload as $slave) {
+ $flags = explode(',', $slave[9]);
+
+ if (array_intersect($flags, array('s_down', 'o_down', 'disconnected'))) {
+ continue;
+ }
+
+ $slaves[] = array(
+ 'host' => $slave[3],
+ 'port' => $slave[5],
+ 'alias' => "slave-$slave[1]",
+ );
+ }
+
+ return $slaves;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCurrent()
+ {
+ return $this->current;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMaster()
+ {
+ if ($this->master) {
+ return $this->master;
+ }
+
+ if ($this->updateSentinels) {
+ $this->updateSentinels();
+ }
+
+ SENTINEL_QUERY: {
+ $sentinel = $this->getSentinelConnection();
+
+ try {
+ $masterParameters = $this->querySentinelForMaster($sentinel, $this->service);
+ $masterConnection = $this->connectionFactory->create($masterParameters);
+
+ $this->add($masterConnection);
+ } catch (ConnectionException $exception) {
+ $this->sentinelConnection = null;
+
+ goto SENTINEL_QUERY;
+ }
+ }
+
+ return $masterConnection;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSlaves()
+ {
+ if ($this->slaves) {
+ return array_values($this->slaves);
+ }
+
+ if ($this->updateSentinels) {
+ $this->updateSentinels();
+ }
+
+ SENTINEL_QUERY: {
+ $sentinel = $this->getSentinelConnection();
+
+ try {
+ $slavesParameters = $this->querySentinelForSlaves($sentinel, $this->service);
+
+ foreach ($slavesParameters as $slaveParameters) {
+ $this->add($this->connectionFactory->create($slaveParameters));
+ }
+ } catch (ConnectionException $exception) {
+ $this->sentinelConnection = null;
+
+ goto SENTINEL_QUERY;
+ }
+ }
+
+ return array_values($this->slaves ?: array());
+ }
+
+ /**
+ * Returns a random slave.
+ *
+ * @return NodeConnectionInterface
+ */
+ protected function pickSlave()
+ {
+ if ($slaves = $this->getSlaves()) {
+ return $slaves[rand(1, count($slaves)) - 1];
+ }
+ }
+
+ /**
+ * Returns the connection instance in charge for the given command.
+ *
+ * @param CommandInterface $command Command instance.
+ *
+ * @return NodeConnectionInterface
+ */
+ private function getConnectionInternal(CommandInterface $command)
+ {
+ if (!$this->current) {
+ if ($this->strategy->isReadOperation($command) && $slave = $this->pickSlave()) {
+ $this->current = $slave;
+ } else {
+ $this->current = $this->getMaster();
+ }
+
+ return $this->current;
+ }
+
+ if ($this->current === $this->master) {
+ return $this->current;
+ }
+
+ if (!$this->strategy->isReadOperation($command)) {
+ $this->current = $this->getMaster();
+ }
+
+ return $this->current;
+ }
+
+ /**
+ * Asserts that the specified connection matches an expected role.
+ *
+ * @param NodeConnectionInterface $connection Connection to a redis server.
+ * @param string $role Expected role of the server ("master", "slave" or "sentinel").
+ *
+ * @throws RoleException|ConnectionException
+ */
+ protected function assertConnectionRole(NodeConnectionInterface $connection, $role)
+ {
+ $role = strtolower($role);
+ $actualRole = $connection->executeCommand(RawCommand::create('ROLE'));
+
+ if ($actualRole instanceof Error) {
+ throw new ConnectionException($connection, $actualRole->getMessage());
+ }
+
+ if ($role !== $actualRole[0]) {
+ throw new RoleException($connection, "Expected $role but got $actualRole[0] [$connection]");
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConnection(CommandInterface $command)
+ {
+ $connection = $this->getConnectionInternal($command);
+
+ if (!$connection->isConnected()) {
+ // When we do not have any available slave in the pool we can expect
+ // read-only operations to hit the master server.
+ $expectedRole = $this->strategy->isReadOperation($command) && $this->slaves ? 'slave' : 'master';
+ $this->assertConnectionRole($connection, $expectedRole);
+ }
+
+ return $connection;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getConnectionById($connectionId)
+ {
+ if ($connectionId === 'master') {
+ return $this->getMaster();
+ }
+
+ $this->getSlaves();
+
+ if (isset($this->slaves[$connectionId])) {
+ return $this->slaves[$connectionId];
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function switchTo($connection)
+ {
+ if (!$connection instanceof NodeConnectionInterface) {
+ $connection = $this->getConnectionById($connection);
+ }
+
+ if ($connection && $connection === $this->current) {
+ return;
+ }
+
+ if ($connection !== $this->master && !in_array($connection, $this->slaves, true)) {
+ throw new \InvalidArgumentException('Invalid connection or connection not found.');
+ }
+
+ $connection->connect();
+
+ if ($this->current) {
+ $this->current->disconnect();
+ }
+
+ $this->current = $connection;
+ }
+
+ /**
+ * Switches to the master server.
+ */
+ public function switchToMaster()
+ {
+ $this->switchTo('master');
+ }
+
+ /**
+ * Switches to a random slave server.
+ */
+ public function switchToSlave()
+ {
+ $connection = $this->pickSlave();
+ $this->switchTo($connection);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isConnected()
+ {
+ return $this->current ? $this->current->isConnected() : false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function connect()
+ {
+ if (!$this->current) {
+ if (!$this->current = $this->pickSlave()) {
+ $this->current = $this->getMaster();
+ }
+ }
+
+ $this->current->connect();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disconnect()
+ {
+ if ($this->master) {
+ $this->master->disconnect();
+ }
+
+ foreach ($this->slaves as $connection) {
+ $connection->disconnect();
+ }
+ }
+
+ /**
+ * Retries the execution of a command upon server failure after asking a new
+ * configuration to one of the sentinels.
+ *
+ * @param CommandInterface $command Command instance.
+ * @param string $method Actual method.
+ *
+ * @return mixed
+ */
+ private function retryCommandOnFailure(CommandInterface $command, $method)
+ {
+ $retries = 0;
+
+ SENTINEL_RETRY: {
+ try {
+ $response = $this->getConnection($command)->$method($command);
+ } catch (CommunicationException $exception) {
+ $this->wipeServerList();
+ $exception->getConnection()->disconnect();
+
+ if ($retries == $this->retryLimit) {
+ throw $exception;
+ }
+
+ usleep($this->retryWait * 1000);
+
+ ++$retries;
+ goto SENTINEL_RETRY;
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function writeRequest(CommandInterface $command)
+ {
+ $this->retryCommandOnFailure($command, __FUNCTION__);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function readResponse(CommandInterface $command)
+ {
+ return $this->retryCommandOnFailure($command, __FUNCTION__);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function executeCommand(CommandInterface $command)
+ {
+ return $this->retryCommandOnFailure($command, __FUNCTION__);
+ }
+
+ /**
+ * Returns the underlying replication strategy.
+ *
+ * @return ReplicationStrategy
+ */
+ public function getReplicationStrategy()
+ {
+ return $this->strategy;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __sleep()
+ {
+ return array(
+ 'master', 'slaves', 'service', 'sentinels', 'connectionFactory', 'strategy',
+ );
+ }
+}
diff --git a/vendor/predis/predis/src/Connection/AggregateConnectionInterface.php b/vendor/predis/predis/src/Connection/AggregateConnectionInterface.php
new file mode 100644
index 0000000..7eeaede
--- /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 0000000..286e082
--- /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 0000000..7a35340
--- /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 0000000..ef2e9d7
--- /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 0000000..11ace1b
--- /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 0000000..d03e87e
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/Factory.php
@@ -0,0 +1,192 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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
+{
+ private $defaults = array();
+
+ protected $schemes = array(
+ 'tcp' => 'Predis\Connection\StreamConnection',
+ 'unix' => 'Predis\Connection\StreamConnection',
+ 'tls' => 'Predis\Connection\StreamConnection',
+ 'redis' => 'Predis\Connection\StreamConnection',
+ 'rediss' => 'Predis\Connection\StreamConnection',
+ 'http' => 'Predis\Connection\WebdisConnection',
+ );
+
+ /**
+ * 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));
+ }
+ }
+
+ /**
+ * Assigns a default set of parameters applied to new connections.
+ *
+ * The set of parameters passed to create a new connection have precedence
+ * over the default values set for the connection factory.
+ *
+ * @param array $parameters Set of connection parameters.
+ */
+ public function setDefaultParameters(array $parameters)
+ {
+ $this->defaults = $parameters;
+ }
+
+ /**
+ * Returns the default set of parameters applied to new connections.
+ *
+ * @return array
+ */
+ public function getDefaultParameters()
+ {
+ return $this->defaults;
+ }
+
+ /**
+ * Creates a connection parameters instance from the supplied argument.
+ *
+ * @param mixed $parameters Original connection parameters.
+ *
+ * @return ParametersInterface
+ */
+ protected function createParameters($parameters)
+ {
+ if (is_string($parameters)) {
+ $parameters = Parameters::parse($parameters);
+ } else {
+ $parameters = $parameters ?: array();
+ }
+
+ if ($this->defaults) {
+ $parameters += $this->defaults;
+ }
+
+ return new Parameters($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) && strlen($parameters->password)) {
+ $cmdAuthArgs = isset($parameters->username) && strlen($parameters->username)
+ ? array('AUTH', $parameters->username, $parameters->password)
+ : array('AUTH', $parameters->password);
+
+ $connection->addConnectCommand(
+ new RawCommand($cmdAuthArgs)
+ );
+ }
+
+ if (isset($parameters->database) && strlen($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 0000000..2bae083
--- /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 0000000..665b862
--- /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 0000000..c1c3a73
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/Parameters.php
@@ -0,0 +1,185 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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,
+ );
+
+ /**
+ * @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/rediss
+ *
+ * @param string $uri URI string.
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return array
+ */
+ public static function parse($uri)
+ {
+ if (stripos($uri, 'unix://') === 0) {
+ // parse_url() can parse unix:/path/to/sock so we do not need the
+ // unix:///path/to/sock hack, we will support it anyway until 2.0.
+ $uri = str_ireplace('unix://', 'unix:', $uri);
+ }
+
+ if (!$parsed = parse_url($uri)) {
+ 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['user'])) {
+ if (strlen($parsed['user'])) {
+ $parsed['username'] = $parsed['user'];
+ }
+ unset($parsed['user']);
+ }
+
+ if (isset($parsed['pass'])) {
+ if (strlen($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 0000000..fd8a908
--- /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 0000000..df88172
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/PhpiredisSocketConnection.php
@@ -0,0 +1,418 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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\ErrorInterface as ErrorResponseInterface;
+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 (default is 5 seconds).
+ * - 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()
+ {
+ parent::__destruct();
+
+ phpiredis_reader_destroy($this->reader);
+ }
+
+ /**
+ * 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)
+ {
+ switch ($parameters->scheme) {
+ case 'tcp':
+ case 'redis':
+ case 'unix':
+ break;
+
+ default:
+ throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
+ }
+
+ if (isset($parameters->persistent)) {
+ throw new NotSupportedException(
+ '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
+ */
+ protected function getStatusHandler()
+ {
+ static $statusHandler;
+
+ if (!$statusHandler) {
+ $statusHandler = function ($payload) {
+ return StatusResponse::get($payload);
+ };
+ }
+
+ return $statusHandler;
+ }
+
+ /**
+ * Returns the handler used by the protocol reader for error responses.
+ *
+ * @return \Closure
+ */
+ protected function getErrorHandler()
+ {
+ static $errorHandler;
+
+ if (!$errorHandler) {
+ $errorHandler = function ($errorMessage) {
+ return new ErrorResponse($errorMessage);
+ };
+ }
+
+ return $errorHandler;
+ }
+
+ /**
+ * 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;
+ }
+
+ if (false === $socket = @socket_create($domain, SOCK_STREAM, $protocol)) {
+ $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 = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
+ $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) {
+ $response = $this->executeCommand($command);
+
+ if ($response instanceof ErrorResponseInterface) {
+ $this->onConnectionError("`{$command->getId()}` failed: $response", 0);
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disconnect()
+ {
+ if ($this->isConnected()) {
+ phpiredis_reader_reset($this->reader);
+ 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 0000000..51503c4
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/PhpiredisStreamConnection.php
@@ -0,0 +1,262 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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()
+ {
+ parent::__destruct();
+
+ phpiredis_reader_destroy($this->reader);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function disconnect()
+ {
+ phpiredis_reader_reset($this->reader);
+
+ parent::disconnect();
+ }
+
+ /**
+ * 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 assertParameters(ParametersInterface $parameters)
+ {
+ switch ($parameters->scheme) {
+ case 'tcp':
+ case 'redis':
+ case 'unix':
+ break;
+
+ case 'tls':
+ case 'rediss':
+ throw new \InvalidArgumentException('SSL encryption is not supported by this connection backend.');
+
+ default:
+ throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
+ }
+
+ return $parameters;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createStreamSocket(ParametersInterface $parameters, $address, $flags, $context = null)
+ {
+ $socket = null;
+ $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
+
+ $resource = @stream_socket_client($address, $errno, $errstr, $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()
+ {
+ static $statusHandler;
+
+ if (!$statusHandler) {
+ $statusHandler = function ($payload) {
+ return StatusResponse::get($payload);
+ };
+ }
+
+ return $statusHandler;
+ }
+
+ /**
+ * Returns the handler used by the protocol reader for error responses.
+ *
+ * @return \Closure
+ */
+ protected function getErrorHandler()
+ {
+ static $errorHandler;
+
+ if (!$errorHandler) {
+ $errorHandler = function ($errorMessage) {
+ return new ErrorResponse($errorMessage);
+ };
+ }
+
+ return $errorHandler;
+ }
+
+ /**
+ * {@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 0000000..5af9a89
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/StreamConnection.php
@@ -0,0 +1,396 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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\ErrorInterface as ErrorResponseInterface;
+use Predis\Response\Status as StatusResponse;
+
+/**
+ * Standard connection to Redis servers implemented on top of PHP's streams.
+ * The connection parameters supported by this class are:.
+ *
+ * - scheme: it can be either 'redis', 'tcp', 'rediss', 'tls' or 'unix'.
+ * - host: hostname or IP address of the server.
+ * - port: TCP port of the server.
+ * - path: path of a UNIX domain socket when scheme is 'unix'.
+ * - timeout: timeout to perform the connection (default is 5 seconds).
+ * - read_write_timeout: timeout of read / write operations.
+ * - async_connect: performs the connection asynchronously.
+ * - tcp_nodelay: enables or disables Nagle's algorithm for coalescing.
+ * - persistent: the connection is left intact after a GC collection.
+ * - ssl: context options array (see http://php.net/manual/en/context.ssl.php)
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+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 assertParameters(ParametersInterface $parameters)
+ {
+ switch ($parameters->scheme) {
+ case 'tcp':
+ case 'redis':
+ case 'unix':
+ break;
+
+ case 'tls':
+ case 'rediss':
+ $this->assertSslSupport($parameters);
+ break;
+
+ default:
+ throw new \InvalidArgumentException("Invalid scheme: '$parameters->scheme'.");
+ }
+
+ return $parameters;
+ }
+
+ /**
+ * Checks needed conditions for SSL-encrypted connections.
+ *
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function assertSslSupport(ParametersInterface $parameters)
+ {
+ if (
+ filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN) &&
+ version_compare(PHP_VERSION, '7.0.0beta') < 0
+ ) {
+ throw new \InvalidArgumentException('Persistent SSL connections require PHP >= 7.0.0.');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createResource()
+ {
+ switch ($this->parameters->scheme) {
+ case 'tcp':
+ case 'redis':
+ return $this->tcpStreamInitializer($this->parameters);
+
+ case 'unix':
+ return $this->unixStreamInitializer($this->parameters);
+
+ case 'tls':
+ case 'rediss':
+ return $this->tlsStreamInitializer($this->parameters);
+
+ default:
+ throw new \InvalidArgumentException("Invalid scheme: '{$this->parameters->scheme}'.");
+ }
+ }
+
+ /**
+ * Creates a connected stream socket resource.
+ *
+ * @param ParametersInterface $parameters Connection parameters.
+ * @param string $address Address for stream_socket_client().
+ * @param int $flags Flags for stream_socket_client().
+ *
+ * @return resource
+ */
+ protected function createStreamSocket(ParametersInterface $parameters, $address, $flags)
+ {
+ $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0);
+
+ if (!$resource = @stream_socket_client($address, $errno, $errstr, $timeout, $flags)) {
+ $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 TCP stream resource.
+ *
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
+ *
+ * @return resource
+ */
+ protected function tcpStreamInitializer(ParametersInterface $parameters)
+ {
+ if (!filter_var($parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ $address = "tcp://$parameters->host:$parameters->port";
+ } else {
+ $address = "tcp://[$parameters->host]:$parameters->port";
+ }
+
+ $flags = STREAM_CLIENT_CONNECT;
+
+ if (isset($parameters->async_connect) && $parameters->async_connect) {
+ $flags |= STREAM_CLIENT_ASYNC_CONNECT;
+ }
+
+ if (isset($parameters->persistent)) {
+ if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
+ $flags |= STREAM_CLIENT_PERSISTENT;
+
+ if ($persistent === null) {
+ $address = "{$address}/{$parameters->persistent}";
+ }
+ }
+ }
+
+ $resource = $this->createStreamSocket($parameters, $address, $flags);
+
+ return $resource;
+ }
+
+ /**
+ * Initializes a UNIX stream resource.
+ *
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
+ *
+ * @return resource
+ */
+ protected function unixStreamInitializer(ParametersInterface $parameters)
+ {
+ if (!isset($parameters->path)) {
+ throw new \InvalidArgumentException('Missing UNIX domain socket path.');
+ }
+
+ $flags = STREAM_CLIENT_CONNECT;
+
+ if (isset($parameters->persistent)) {
+ if (false !== $persistent = filter_var($parameters->persistent, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
+ $flags |= STREAM_CLIENT_PERSISTENT;
+
+ if ($persistent === null) {
+ throw new \InvalidArgumentException(
+ 'Persistent connection IDs are not supported when using UNIX domain sockets.'
+ );
+ }
+ }
+ }
+
+ $resource = $this->createStreamSocket($parameters, "unix://{$parameters->path}", $flags);
+
+ return $resource;
+ }
+
+ /**
+ * Initializes a SSL-encrypted TCP stream resource.
+ *
+ * @param ParametersInterface $parameters Initialization parameters for the connection.
+ *
+ * @return resource
+ */
+ protected function tlsStreamInitializer(ParametersInterface $parameters)
+ {
+ $resource = $this->tcpStreamInitializer($parameters);
+ $metadata = stream_get_meta_data($resource);
+
+ // Detect if crypto mode is already enabled for this stream (PHP >= 7.0.0).
+ if (isset($metadata['crypto'])) {
+ return $resource;
+ }
+
+ if (is_array($parameters->ssl)) {
+ $options = $parameters->ssl;
+ } else {
+ $options = array();
+ }
+
+ if (!isset($options['crypto_type'])) {
+ $options['crypto_type'] = STREAM_CRYPTO_METHOD_TLS_CLIENT;
+ }
+
+ if (!stream_context_set_option($resource, array('ssl' => $options))) {
+ $this->onConnectionError('Error while setting SSL context options');
+ }
+
+ if (!stream_socket_enable_crypto($resource, true, $options['crypto_type'])) {
+ $this->onConnectionError('Error while switching to encrypted communication');
+ }
+
+ return $resource;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function connect()
+ {
+ if (parent::connect() && $this->initCommands) {
+ foreach ($this->initCommands as $command) {
+ $response = $this->executeCommand($command);
+
+ if ($response instanceof ErrorResponseInterface) {
+ $this->onConnectionError("`{$command->getId()}` failed: $response", 0);
+ }
+ }
+ }
+ }
+
+ /**
+ * {@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 ':':
+ $integer = (int) $payload;
+ return $integer == $payload ? $integer : $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(strval($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 0000000..e8cd721
--- /dev/null
+++ b/vendor/predis/predis/src/Connection/WebdisConnection.php
@@ -0,0 +1,366 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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 (default is 5 seconds).
+ * - 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();
+ $timeout = (isset($parameters->timeout) ? (float) $parameters->timeout : 5.0) * 1000;
+
+ if (filter_var($host = $parameters->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ $host = "[$host]";
+ }
+
+ $options = array(
+ CURLOPT_FAILONERROR => true,
+ CURLOPT_CONNECTTIMEOUT_MS => $timeout,
+ 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()
+ {
+ static $statusHandler;
+
+ if (!$statusHandler) {
+ $statusHandler = function ($payload) {
+ return StatusResponse::get($payload);
+ };
+ }
+
+ return $statusHandler;
+ }
+
+ /**
+ * Returns the handler used by the protocol reader for error responses.
+ *
+ * @return \Closure
+ */
+ protected function getErrorHandler()
+ {
+ static $errorHandler;
+
+ if (!$errorHandler) {
+ $errorHandler = function ($errorMessage) {
+ return new ErrorResponse($errorMessage);
+ };
+ }
+
+ return $errorHandler;
+ }
+
+ /**
+ * 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 0000000..6579883
--- /dev/null
+++ b/vendor/predis/predis/src/Monitor/Consumer.php
@@ -0,0 +1,178 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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}
+ */
+ #[\ReturnTypeWillChange]
+ public function rewind()
+ {
+ // NOOP
+ }
+
+ /**
+ * Returns the last message payload retrieved from the server.
+ *
+ * @return object
+ */
+ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ return $this->getValue();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function next()
+ {
+ ++$this->position;
+ }
+
+ /**
+ * Checks if the the consumer is still in a valid state to continue.
+ *
+ * @return bool
+ */
+ #[\ReturnTypeWillChange]
+ 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 0000000..be82aba
--- /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 0000000..1c9c92a
--- /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 0000000..d3bc732
--- /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 0000000..95a062b
--- /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 0000000..cf9c59e
--- /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 0000000..122bde1
--- /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 0000000..d4907a2
--- /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\RedisVersion320',
+ );
+
+ /**
+ *
+ */
+ 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 0000000..abe71aa
--- /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 0000000..3ef3168
--- /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 0000000..573cc9e
--- /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 0000000..234d53c
--- /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 0000000..899014e
--- /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 0000000..0856c37
--- /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 0000000..ba5084a
--- /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 0000000..ea17e68
--- /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 0000000..8a2fac8
--- /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 0000000..7de7957
--- /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 0000000..6fe5d6d
--- /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 0000000..b34ea18
--- /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 0000000..eef72a6
--- /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 0000000..86a7bdc
--- /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 0000000..ea85ed3
--- /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 0000000..5b0bf3c
--- /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 0000000..3e18b7b
--- /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 0000000..f965601
--- /dev/null
+++ b/vendor/predis/predis/src/Protocol/Text/Handler/IntegerResponse.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\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)) {
+ $integer = (int) $payload;
+ return $integer == $payload ? $integer : $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 0000000..820b9b4
--- /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 0000000..ca08a9c
--- /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 0000000..7bde555
--- /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 0000000..7cdb736
--- /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 0000000..99acdf8
--- /dev/null
+++ b/vendor/predis/predis/src/Protocol/Text/ProtocolProcessor.php
@@ -0,0 +1,123 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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 ':':
+ $integer = (int) $payload;
+ return $integer == $payload ? $integer : $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 0000000..859595b
--- /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 0000000..d96218d
--- /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 0000000..d9ce588
--- /dev/null
+++ b/vendor/predis/predis/src/PubSub/AbstractConsumer.php
@@ -0,0 +1,224 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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}
+ */
+ #[\ReturnTypeWillChange]
+ public function rewind()
+ {
+ // NOOP
+ }
+
+ /**
+ * Returns the last message payload retrieved from the server and generated
+ * by one of the active subscriptions.
+ *
+ * @return array
+ */
+ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ return $this->getValue();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ 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
+ */
+ #[\ReturnTypeWillChange]
+ 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 0000000..5f2d8a8
--- /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 0000000..d0369e7
--- /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/MissingMasterException.php b/vendor/predis/predis/src/Replication/MissingMasterException.php
new file mode 100644
index 0000000..223bd2d
--- /dev/null
+++ b/vendor/predis/predis/src/Replication/MissingMasterException.php
@@ -0,0 +1,23 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Replication;
+
+use Predis\ClientException;
+
+/**
+ * Exception class that identifies when master is missing in a replication setup.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class MissingMasterException extends ClientException
+{
+}
diff --git a/vendor/predis/predis/src/Replication/ReplicationStrategy.php b/vendor/predis/predis/src/Replication/ReplicationStrategy.php
new file mode 100644
index 0000000..8f826b4
--- /dev/null
+++ b/vendor/predis/predis/src/Replication/ReplicationStrategy.php
@@ -0,0 +1,279 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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') {
+ $argument = $command->getArgument(0);
+ $sha1 = $eval ? sha1(strval($argument)) : $argument;
+
+ 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 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,
+ '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/Replication/RoleException.php b/vendor/predis/predis/src/Replication/RoleException.php
new file mode 100644
index 0000000..0d9954b
--- /dev/null
+++ b/vendor/predis/predis/src/Replication/RoleException.php
@@ -0,0 +1,24 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Replication;
+
+use Predis\CommunicationException;
+
+/**
+ * Exception class that identifies a role mismatch when connecting to node
+ * managed by redis-sentinel.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class RoleException extends CommunicationException
+{
+}
diff --git a/vendor/predis/predis/src/Response/Error.php b/vendor/predis/predis/src/Response/Error.php
new file mode 100644
index 0000000..3933857
--- /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 0000000..a4a4a02
--- /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 0000000..b1d2924
--- /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 0000000..bac0ac9
--- /dev/null
+++ b/vendor/predis/predis/src/Response/Iterator/MultiBulkIterator.php
@@ -0,0 +1,110 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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}
+ */
+ #[\ReturnTypeWillChange]
+ public function rewind()
+ {
+ // NOOP
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ return $this->current;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->position;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function next()
+ {
+ if (++$this->position < $this->size) {
+ $this->current = $this->getValue();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ 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
+ */
+ #[\ReturnTypeWillChange]
+ 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 0000000..ed3f88e
--- /dev/null
+++ b/vendor/predis/predis/src/Response/Iterator/MultiBulkTuple.php
@@ -0,0 +1,91 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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}
+ */
+ #[\ReturnTypeWillChange]
+ 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 0000000..0af1357
--- /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 0000000..407dc5b
--- /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 0000000..729bb66
--- /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 0000000..fee90ff
--- /dev/null
+++ b/vendor/predis/predis/src/Session/Handler.php
@@ -0,0 +1,148 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <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}
+ */
+ #[\ReturnTypeWillChange]
+ public function open($save_path, $session_id)
+ {
+ // NOOP
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function close()
+ {
+ // NOOP
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function gc($maxlifetime)
+ {
+ // NOOP
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function read($session_id)
+ {
+ if ($data = $this->client->get($session_id)) {
+ return $data;
+ }
+
+ return '';
+ }
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ public function write($session_id, $session_data)
+ {
+ $this->client->setex($session_id, $this->ttl, $session_data);
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ #[\ReturnTypeWillChange]
+ 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 0000000..94499d8
--- /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, is_null($code) ? 0 : $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 0000000..0cf1962
--- /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 0000000..4bed42a
--- /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);
+ }
+}